diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 91f2043123..da71debf3b 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1074,10 +1074,12 @@ void Mob::AI_Process() { bool is_moving = IsMoving() && !(IsRooted() || IsStunned() || IsMezzed()); auto t = GetTarget(); if (is_moving && t) { - float self_z = GetZ() - GetZOffset(); - float target_z = t->GetPosition().z - t->GetZOffset(); - if (DistanceNoZ(GetPosition(), t->GetPosition()) < 75 && - std::abs(self_z - target_z) >= 25 && !CheckLosFN(t)) { + float self_z = GetZ() - GetZOffset(); + float target_z = t->GetPosition().z - t->GetZOffset(); + bool can_path_to = CastToNPC()->CanPathTo(t->GetX(), t->GetY(), t->GetZ()); + bool within_distance = DistanceNoZ(GetPosition(), t->GetPosition()) < 75; + bool within_z_distance = std::abs(self_z - target_z) >= 25; + if (within_distance && within_z_distance && !can_path_to) { float new_z = FindDestGroundZ(t->GetPosition()); GMMove(t->GetPosition().x, t->GetPosition().y, new_z + GetZOffset(), t->GetPosition().w, false); FaceTarget(t); diff --git a/zone/npc.cpp b/zone/npc.cpp index 34eee7aada..5563ffde71 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3825,9 +3825,14 @@ void NPC::HandleRoambox() auto requested_y = EQ::Clamp((GetY() + move_y), m_roambox.min_y, m_roambox.max_y); auto requested_z = GetGroundZ(requested_x, requested_y); + if (std::abs(requested_z - GetZ()) > 100) { + LogNPCRoamBox("[{}] | Failed to find reasonable ground [{}]", GetCleanName(), i); + continue; + } + std::vector heights = {0, 250, -250}; for (auto &h: heights) { - if (CheckLosFN(requested_x, requested_y, requested_z + h, GetSize())) { + if (CanPathTo(requested_x, requested_y, requested_z + h)) { LogNPCRoamBox("[{}] Found line of sight to path attempt [{}] at height [{}]", GetCleanName(), i, h); can_path = true; break; @@ -3942,3 +3947,24 @@ void NPC::SetTaunting(bool is_taunting) { GetOwner()->CastToClient()->SetPetCommandState(PET_BUTTON_TAUNT, is_taunting); } } + +bool NPC::CanPathTo(float x, float y, float z) +{ + PathfinderOptions opts; + opts.smooth_path = true; + opts.step_size = RuleR(Pathing, NavmeshStepSize); + opts.offset = GetZOffset(); + opts.flags = PathingNotDisabled ^ PathingZoneLine; + + bool partial = false; + bool stuck = false; + auto route = zone->pathing->FindPath( + glm::vec3(GetX(), GetY(), GetZ()), + glm::vec3(x, y, z), + partial, + stuck, + opts + ); + + return !route.empty(); +} diff --git a/zone/npc.h b/zone/npc.h index 578f80649b..77025f168e 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -541,6 +541,8 @@ class NPC : public Mob static LootDropEntries_Struct NewLootDropEntry(); + bool CanPathTo(float x, float y, float z); + protected: void HandleRoambox(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 6eb96cbd20..bbb9ed0a3d 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -846,8 +846,15 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { glm::vec3 current_loc(m_Position); float new_z = GetFixedZ(current_loc, z_find_offset); - if (new_z == m_Position.z) + // reject z if it is too far from the current z + if (std::abs(new_z - m_Position.z) > 100) { return; + } + + // reject if it's the same as the current z + if (new_z == m_Position.z) { + return; + } if ((new_z > -2000) && new_z != BEST_Z_INVALID) { if (RuleB(Map, MobZVisualDebug)) {