From 404f7cada8680e284ab2b4b25d5de7f64bdf09cc Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 6 Feb 2023 17:24:03 -0600 Subject: [PATCH] [Pathing] Improvements to handling tight corridors pathing, clipping detection and recovery (#2826) --- common/ruletypes.h | 1 + zone/gm_commands/loc.cpp | 9 +++++++++ zone/mob.h | 2 ++ zone/mob_movement_manager.cpp | 11 +++++++++-- zone/spells.cpp | 20 +++++++++++++++++--- zone/waypoints.cpp | 31 ++++++++++++++++++++++++------- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9070d78ee..78269a598 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -317,6 +317,7 @@ RULE_BOOL(Map, FixPathingZOnSendTo, false, "Try to repair Z coordinates in the S RULE_BOOL(Map, FixZWhenPathing, true, "Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)") RULE_REAL(Map, DistanceCanTravelBeforeAdjustment, 10.0, "Distance a mob can path before FixZ is called, depends on FixZWhenPathing") RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)") +RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging") RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply") RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position") RULE_CATEGORY_END() diff --git a/zone/gm_commands/loc.cpp b/zone/gm_commands/loc.cpp index 8928b8051..62fdf4be4 100755 --- a/zone/gm_commands/loc.cpp +++ b/zone/gm_commands/loc.cpp @@ -29,6 +29,7 @@ void command_loc(Client *c, const Seperator *sep) glm::vec3 hit; auto best_z = zone->zonemap->FindBestZ(tarloc, &hit); + auto fixed_z = c->GetFixedZ(c->GetPosition()); if (best_z != BEST_Z_INVALID) { c->Message( @@ -39,6 +40,14 @@ void command_loc(Client *c, const Seperator *sep) best_z ).c_str() ); + c->Message( + Chat::White, + fmt::format( + "Fixed Z for {} | {:.2f}", + c->GetTargetDescription(target, TargetDescriptionType::UCSelf), + fixed_z + ).c_str() + ); } else { c->Message(Chat::White, "Could not find Best Z."); } diff --git a/zone/mob.h b/zone/mob.h index ac22ab096..c3f9251df 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1451,6 +1451,8 @@ public: void SetManualFollow(bool flag) { m_manual_follow = flag; } bool GetManualFollow() const { return m_manual_follow; } + void DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec); + protected: void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None); static uint16 GetProcID(uint16 spell_id, uint8 effect_index); diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 001bee9db..9f7d97784 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -138,7 +138,7 @@ public: return true; } - //Send a movement packet when you start moving + //Send a movement packet when you start moving double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; int current_speed = 0; @@ -1053,6 +1053,13 @@ void MobMovementManager::FillCommandStruct( position_update->delta_z = FloatToEQ13(delta_z); position_update->delta_heading = FloatToEQ10(delta_heading); position_update->animation = (mob->IsBot() ? (int) ((float) anim / 1.785714f) : anim); + + if (RuleB(Map, MobPathingVisualDebug)) { + mob->DrawDebugCoordinateNode( + fmt::format("{} position update", mob->GetCleanName()), + mob->GetPosition() + ); + } } /** @@ -1093,7 +1100,7 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove } // Below for npcs that can traverse land or water so they don't sink else if (who->GetFlyMode() == GravityBehavior::Water && - zone->watermap->InLiquid(who->GetPosition()) && + zone->watermap->InLiquid(who->GetPosition()) && zone->watermap->InLiquid(glm::vec3(x, y, z)) && zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { auto iter = _impl->Entries.find(who); diff --git a/zone/spells.cpp b/zone/spells.cpp index 4838b51e8..1c405b4dd 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6415,7 +6415,7 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) recast_delay = std::max(recast_delay, 0); if (recast_delay > 0) { - + if (recast_type != RECAST_TYPE_UNLINKED_ITEM) { GetPTimers().Start((pTimerItemStart + recast_type), static_cast(recast_delay)); database.UpdateItemRecast( @@ -6441,14 +6441,14 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) void Client::DeleteItemRecastTimer(uint32 item_id) { const auto* d = database.GetItem(item_id); - + if (!d) { return; } const auto recast_type = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? d->RecastType : item_id; const int timer_id = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? (pTimerItemStart + recast_type) : (pTimerNegativeItemReuse * item_id); - + database.DeleteItemRecast(CharacterID(), recast_type); GetPTimers().Clear(&database, timer_id); @@ -7002,3 +7002,17 @@ void Mob::SetHP(int64 hp) current_hp = hp; } + +void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec) +{ + NPC* node = nullptr; + for (const auto& n : entity_list.GetNPCList()) { + if (n.second->GetCleanName() == node_name) { + node = n.second; + break; + } + } + if (!node) { + node = NPC::SpawnNodeNPC(node_name, "", GetPosition()); + } +} diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index f92cc6a26..cd4b8b396 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -771,17 +771,15 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) { float new_z = destination.z; if (zone->HasMap()) { - - if (flymode == GravityBehavior::Flying) + if (flymode == GravityBehavior::Flying) { return new_z; + } - if (zone->HasWaterMap() && zone->watermap->InLiquid(glm::vec3(m_Position))) + if (zone->HasWaterMap() && zone->watermap->InLiquid(glm::vec3(m_Position))) { return new_z; + } - /* - * Any more than 5 in the offset makes NPC's hop/snap to ceiling in small corridors - */ - new_z = FindDestGroundZ(destination, z_find_offset); + new_z = FindDestGroundZ(destination, (-GetZOffset() / 2)); if (new_z != BEST_Z_INVALID) { new_z += GetZOffset(); @@ -790,6 +788,20 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) { } } + // prevent ceiling clipping + // if client is close in distance (not counting Z) and we clipped up into a ceiling + // this helps us snap back down (or up) if it were to happen + // other fixes were put in place to prevent clipping into the ceiling to begin with + if (std::abs(new_z - m_Position.z) > 15) { + LogFixZ("TRIGGER clipping detection"); + auto t = GetTarget(); + if (t && DistanceNoZ(GetPosition(), t->GetPosition()) < 20) { + new_z = FindDestGroundZ(t->GetPosition(), -t->GetZOffset()); + new_z += GetZOffset(); + GMMove(t->GetPosition().x, t->GetPosition().y, new_z, t->GetPosition().w); + } + } + auto duration = timer.elapsed(); LogFixZ("[{}] returned [{}] at [{}] [{}] [{}] - Took [{}]", @@ -833,6 +845,10 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { } m_Position.z = new_z; + + if (RuleB(Map, MobPathingVisualDebug)) { + DrawDebugCoordinateNode(fmt::format("{} new fixed z node", GetCleanName()), GetPosition()); + } } else { if (RuleB(Map, MobZVisualDebug)) { @@ -928,6 +944,7 @@ float Mob::GetZOffset() const { case RACE_RABBIT_668: offset = 5.0f; break; + case RACE_WURM_158: case RACE_BLIND_DREAMER_669: offset = 7.0f; break;