From bcd943a964523f52ada0bd824f0b5eef1c9b1369 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 30 Sep 2024 17:34:42 -0500 Subject: [PATCH] [Code Cleanup] Optimization Code Cleanup (#4489) * Initial push * More * More * Further simplify * More cleanup * More consolidation * Fix * Update * Update npc.cpp --------- Co-authored-by: Kinglykrab --- zone/attack.cpp | 8 +- zone/aura.cpp | 19 ++-- zone/bot.cpp | 12 +-- zone/client.cpp | 195 ++++++++++++++++++++++++---------- zone/client.h | 19 ++-- zone/client_packet.cpp | 104 +----------------- zone/client_process.cpp | 35 +----- zone/effects.cpp | 8 +- zone/entity.cpp | 95 ++++++++--------- zone/entity.h | 6 +- zone/mob.cpp | 65 +++++++++--- zone/mob.h | 10 +- zone/mob_ai.cpp | 25 +---- zone/mob_movement_manager.cpp | 136 ------------------------ zone/npc.cpp | 54 +++++----- zone/npc.h | 1 + zone/spells.cpp | 3 +- 17 files changed, 309 insertions(+), 486 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index e7fefe799..ab1a971fe 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1739,7 +1739,7 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool (HasOwner() && GetOwner()->IsClient() && other->IsClient()) ) ) { - for (auto const& [id, mob] : entity_list.GetCloseMobList(other)) { + for (auto const& [id, mob] : other->GetCloseMobList()) { if (!mob) { continue; } @@ -2319,8 +2319,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist)) { if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) { - auto& mob_list = entity_list.GetCloseMobList(other); - for (auto& e : mob_list) { + for (auto& e : other->GetCloseMobList()) { auto mob = e.second; if (!mob) { continue; @@ -2973,8 +2972,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy entity_list.UnMarkNPC(GetID()); entity_list.RemoveNPC(GetID()); - // entity_list.RemoveMobFromCloseLists(this); - close_mobs.clear(); + m_close_mobs.clear(); SetID(0); ApplyIllusionToCorpse(illusion_spell_id, corpse); diff --git a/zone/aura.cpp b/zone/aura.cpp index 3c9a406ec..f1285dc5e 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -72,7 +72,7 @@ Mob *Aura::GetOwner() // not 100% sure how this one should work and PVP affects ... void Aura::ProcessOnAllFriendlies(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this, distance); + auto &mob_list = GetCloseMobList(distance); std::set delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter @@ -127,7 +127,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) void Aura::ProcessOnAllGroupMembers(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this, distance); + auto &mob_list = GetCloseMobList(distance); std::set delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter @@ -369,7 +369,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) void Aura::ProcessOnGroupMembersPets(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this,distance); + auto &mob_list = GetCloseMobList(distance); std::set delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) @@ -576,7 +576,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) void Aura::ProcessTotem(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this, distance); + auto &mob_list = GetCloseMobList(distance); std::set delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter @@ -634,9 +634,7 @@ void Aura::ProcessTotem(Mob *owner) void Aura::ProcessEnterTrap(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this, distance); - - for (auto &e : mob_list) { + for (auto &e : GetCloseMobList(distance)) { auto mob = e.second; if (!mob) { continue; @@ -656,9 +654,7 @@ void Aura::ProcessEnterTrap(Mob *owner) void Aura::ProcessExitTrap(Mob *owner) { - auto &mob_list = entity_list.GetCloseMobList(this, distance); - - for (auto &e : mob_list) { + for (auto &e : GetCloseMobList(distance)) { auto mob = e.second; if (!mob) { continue; @@ -689,8 +685,7 @@ void Aura::ProcessExitTrap(Mob *owner) // and hard to reason about void Aura::ProcessSpawns() { - const auto &clients = entity_list.GetCloseMobList(this, distance); - for (auto &e : clients) { + for (auto &e: GetCloseMobList(distance)) { if (!e.second) { continue; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 331216e58..ce7edcc82 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1578,17 +1578,7 @@ bool Bot::Process() return false; } - if (mob_close_scan_timer.Check()) { - LogAIScanCloseDetail( - "is_moving [{}] bot [{}] timer [{}]", - moving ? "true" : "false", - GetCleanName(), - mob_close_scan_timer.GetDuration() - ); - - entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); - } - + ScanCloseMobProcess(); SpellProcess(); if (tic_timer.Check()) { diff --git a/zone/client.cpp b/zone/client.cpp index 3d6de0ff7..9ff5947f4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -145,49 +145,48 @@ Client::Client(EQStreamInterface *ieqs) : Mob( 0, // in_heroic_strikethrough false // in_keeps_sold_items ), - hpupdate_timer(2000), - camp_timer(29000), - process_timer(100), - consume_food_timer(CONSUMPTION_TIMER), - zoneinpacket_timer(1000), - linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), - dead_timer(2000), - global_channel_timer(1000), - fishing_timer(8000), - endupkeep_timer(1000), - autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), - client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), - client_zone_wide_full_position_update_timer(5 * 60 * 1000), - tribute_timer(Tribute_duration), - proximity_timer(ClientProximity_interval), - TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), - charm_update_timer(6000), - rest_timer(1), - pick_lock_timer(1000), - charm_class_attacks_timer(3000), - charm_cast_timer(3500), - qglobal_purge_timer(30000), - TrackingTimer(2000), - RespawnFromHoverTimer(0), - merc_timer(RuleI(Mercs, UpkeepIntervalMS)), - ItemQuestTimer(500), - anon_toggle_timer(250), - afk_toggle_timer(250), - helm_toggle_timer(250), - aggro_meter_timer(AGGRO_METER_UPDATE_MS), - m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number - m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f,-2.0f), - m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), - m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), - last_region_type(RegionTypeUnsupported), - m_dirtyautohaters(false), - mob_close_scan_timer(6000), - position_update_timer(10000), - consent_throttle_timer(2000), - tmSitting(0), - parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)), - lazy_load_bank_check_timer(1000), - bandolier_throttle_timer(0) + hpupdate_timer(2000), + camp_timer(29000), + process_timer(100), + consume_food_timer(CONSUMPTION_TIMER), + zoneinpacket_timer(1000), + linkdead_timer(RuleI(Zone, ClientLinkdeadMS)), + dead_timer(2000), + global_channel_timer(1000), + fishing_timer(8000), + endupkeep_timer(1000), + autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), + m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), + m_client_zone_wide_full_position_update_timer(5 * 60 * 1000), + tribute_timer(Tribute_duration), + proximity_timer(ClientProximity_interval), + TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), + charm_update_timer(6000), + rest_timer(1), + pick_lock_timer(1000), + charm_class_attacks_timer(3000), + charm_cast_timer(3500), + qglobal_purge_timer(30000), + TrackingTimer(2000), + RespawnFromHoverTimer(0), + merc_timer(RuleI(Mercs, UpkeepIntervalMS)), + ItemQuestTimer(500), + anon_toggle_timer(250), + afk_toggle_timer(250), + helm_toggle_timer(250), + aggro_meter_timer(AGGRO_METER_UPDATE_MS), + m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number + m_ZoneSummonLocation(-2.0f, -2.0f, -2.0f, -2.0f), + m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), + m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), + last_region_type(RegionTypeUnsupported), + m_dirtyautohaters(false), + m_position_update_timer(10000), + consent_throttle_timer(2000), + tmSitting(0), + parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)), + lazy_load_bank_check_timer(1000), + bandolier_throttle_timer(0) { for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) { SetFilter(client_filter, FilterShow); @@ -446,7 +445,7 @@ Client::~Client() { m_tradeskill_object = nullptr; } - close_mobs.clear(); + m_close_mobs.clear(); if(IsDueling() && GetDuelTarget() != 0) { Entity* entity = entity_list.GetID(GetDuelTarget()); @@ -9253,19 +9252,6 @@ bool Client::GotoPlayerRaid(const std::string& player_name) return true; } -glm::vec4 &Client::GetLastPositionBeforeBulkUpdate() -{ - return last_position_before_bulk_update; -} - -/** - * @param in_last_position_before_bulk_update - */ -void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update) -{ - Client::last_position_before_bulk_update = in_last_position_before_bulk_update; -} - void Client::SendToGuildHall() { std::string zone_short_name = "guildhall"; @@ -12749,3 +12735,98 @@ void Client::SendTopLevelInventory() } } } + +// On a normal basis we limit mob movement updates based on distance +// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes +// +// For very large zones we will also force a full update based on distance +// +// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear +// to full stop when they are actually still pathing +void Client::CheckSendBulkClientPositionUpdate() +{ + float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition()); + bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance(); + bool is_ready_to_update = ( + m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update + ); + + if (IsMoving() && is_ready_to_update) { + LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName()); + + auto &mob_movement_manager = MobMovementManager::Get(); + auto &mob_list = entity_list.GetMobList(); + + for (auto &it : mob_list) { + Mob *entity = it.second; + if (!entity->IsNPC()) { + continue; + } + + int animation_speed = 0; + if (entity->IsMoving()) { + if (entity->IsRunning()) { + animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed()); + } + else { + animation_speed = entity->GetWalkspeed(); + } + } + + mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this); + } + + m_last_position_before_bulk_update = GetPosition(); + } +} + +const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval); +const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval); + +void Client::CheckClientToNpcAggroTimer() +{ + LogAggroDetail( + "ClientUpdate [{}] {}moving, scan timer [{}]", + GetCleanName(), + IsMoving() ? "" : "NOT ", + m_client_npc_aggro_scan_timer.GetRemainingTime() + ); + + if (IsMoving()) { + if (m_client_npc_aggro_scan_timer.GetRemainingTime() > scan_npc_aggro_timer_moving) { + LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName()); + m_client_npc_aggro_scan_timer.Disable(); + m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_moving); + m_client_npc_aggro_scan_timer.Trigger(); + } + } + else if (m_client_npc_aggro_scan_timer.GetDuration() == scan_npc_aggro_timer_moving) { + LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName()); + m_client_npc_aggro_scan_timer.Disable(); + m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_idle); + } +} + +void Client::ClientToNpcAggroProcess() +{ + if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) { + int npc_scan_count = 0; + for (auto &close_mob: GetCloseMobList()) { + Mob *mob = close_mob.second; + if (!mob) { + continue; + } + + if (mob->IsClient()) { + continue; + } + + if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { + mob->AddToHateList(this, 25); + } + + npc_scan_count++; + } + LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); + } +} diff --git a/zone/client.h b/zone/client.h index 913e02b55..bb421c233 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1794,9 +1794,6 @@ public: uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. - void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update); - glm::vec4 &GetLastPositionBeforeBulkUpdate(); - Raid *p_raid_instance; void ShowDevToolsMenu(); @@ -2033,8 +2030,6 @@ private: Timer fishing_timer; Timer endupkeep_timer; Timer autosave_timer; - Timer client_scan_npc_aggro_timer; - Timer client_zone_wide_full_position_update_timer; Timer tribute_timer; Timer proximity_timer; @@ -2051,8 +2046,6 @@ private: Timer afk_toggle_timer; Timer helm_toggle_timer; Timer aggro_meter_timer; - Timer mob_close_scan_timer; - Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ Timer consent_throttle_timer; Timer dynamiczone_removal_timer; Timer task_request_timer; @@ -2065,7 +2058,17 @@ private: int m_lazy_load_sent_bank_slots = 0; glm::vec3 m_Proximity; - glm::vec4 last_position_before_bulk_update; + + // client aggro + Timer m_client_npc_aggro_scan_timer; + void CheckClientToNpcAggroTimer(); + void ClientToNpcAggroProcess(); + + // bulk position updates + glm::vec4 m_last_position_before_bulk_update; + Timer m_client_zone_wide_full_position_update_timer; + Timer m_position_update_timer; + void CheckSendBulkClientPositionUpdate(); void BulkSendInventoryItems(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 03feabbc5..57ad5fe2c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -794,7 +794,7 @@ void Client::CompleteConnect() // sent to a succor point SendMobPositions(); - SetLastPositionBeforeBulkUpdate(GetPosition()); + m_last_position_before_bulk_update = GetPosition(); /* This sub event is for if a player logs in for the first time since entering world. */ if (firstlogon == 1) { @@ -940,7 +940,7 @@ void Client::CompleteConnect() worldserver.RequestTellQueue(GetName()); - entity_list.ScanCloseMobs(close_mobs, this, true); + entity_list.ScanCloseMobs(this); if (GetGM() && IsDevToolsEnabled()) { ShowDevToolsMenu(); @@ -5012,103 +5012,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { SetMoving(!(cy == m_Position.y && cx == m_Position.x)); - /** - * Client aggro scanning - */ - const uint16 client_scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval); - const uint16 client_scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval); - - LogAggroDetail( - "ClientUpdate [{}] {}moving, scan timer [{}]", - GetCleanName(), - IsMoving() ? "" : "NOT ", - client_scan_npc_aggro_timer.GetRemainingTime() - ); - - if (IsMoving()) { - if (client_scan_npc_aggro_timer.GetRemainingTime() > client_scan_npc_aggro_timer_moving) { - LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName()); - client_scan_npc_aggro_timer.Disable(); - client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_moving); - client_scan_npc_aggro_timer.Trigger(); - } - } - else if (client_scan_npc_aggro_timer.GetDuration() == client_scan_npc_aggro_timer_moving) { - LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName()); - client_scan_npc_aggro_timer.Disable(); - client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_idle); - } - - /** - * Client mob close list cache scan timer - */ - const uint16 client_mob_close_scan_timer_moving = 6000; - const uint16 client_mob_close_scan_timer_idle = 60000; - - LogAIScanCloseDetail( - "Client [{}] {}moving, scan timer [{}]", - GetCleanName(), - IsMoving() ? "" : "NOT ", - mob_close_scan_timer.GetRemainingTime() - ); - - if (IsMoving()) { - if (mob_close_scan_timer.GetRemainingTime() > client_mob_close_scan_timer_moving) { - LogAIScanCloseDetail("Client [{}] Restarting with moving timer", GetCleanName()); - mob_close_scan_timer.Disable(); - mob_close_scan_timer.Start(client_mob_close_scan_timer_moving); - mob_close_scan_timer.Trigger(); - } - } - else if (mob_close_scan_timer.GetDuration() == client_mob_close_scan_timer_moving) { - LogAIScanCloseDetail("Client [{}] Restarting with idle timer", GetCleanName()); - mob_close_scan_timer.Disable(); - mob_close_scan_timer.Start(client_mob_close_scan_timer_idle); - } - - /** - * On a normal basis we limit mob movement updates based on distance - * This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes - * - * For very large zones we will also force a full update based on distance - * - * We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear - * to full stop when they are actually still pathing - */ - - float distance_moved = DistanceNoZ(GetLastPositionBeforeBulkUpdate(), GetPosition()); - bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance(); - bool is_ready_to_update = ( - client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update - ); - - if (IsMoving() && is_ready_to_update) { - LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName()); - - auto &mob_movement_manager = MobMovementManager::Get(); - auto &mob_list = entity_list.GetMobList(); - - for (auto &it : mob_list) { - Mob *entity = it.second; - if (!entity->IsNPC()) { - continue; - } - - int animation_speed = 0; - if (entity->IsMoving()) { - if (entity->IsRunning()) { - animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed()); - } - else { - animation_speed = entity->GetWalkspeed(); - } - } - - mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this); - } - - SetLastPositionBeforeBulkUpdate(GetPosition()); - } + CheckClientToNpcAggroTimer(); + CheckScanCloseMobsMovingTimer(); + CheckSendBulkClientPositionUpdate(); int32 new_animation = ppu->animation; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 87bf39626..d0dab4c27 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -121,7 +121,7 @@ bool Client::Process() { } /* I haven't naturally updated my position in 10 seconds, updating manually */ - if (!IsMoving() && position_update_timer.Check()) { + if (!IsMoving() && m_position_update_timer.Check()) { SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); } @@ -281,13 +281,7 @@ bool Client::Process() { } } - /** - * Scan close range mobs - * Used in aggro checks - */ - if (mob_close_scan_timer.Check()) { - entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); - } + ScanCloseMobProcess(); if (RuleB(Inventory, LazyLoadBank)) { // poll once a second to see if we are close to a banker and we haven't loaded the bank yet @@ -608,30 +602,7 @@ bool Client::Process() { } } - //At this point, we are still connected, everything important has taken - //place, now check to see if anybody wants to aggro us. - // only if client is not feigned - if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { - int npc_scan_count = 0; - for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.second; - - if (!mob) { - continue; - } - - if (mob->IsClient()) { - continue; - } - - if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { - mob->AddToHateList(this, 25); - } - - npc_scan_count++; - } - LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); - } + ClientToNpcAggroProcess(); if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) { diff --git a/zone/effects.cpp b/zone/effects.cpp index 9a3fb9279..f69333fb8 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -1036,7 +1036,7 @@ void EntityList::AETaunt(Client* taunter, float range, int bonus_hate) float range_squared = range * range; - for (auto& it: entity_list.GetCloseMobList(taunter, range)) { + for (auto& it: taunter->GetCloseMobList(range)) { Mob *them = it.second; if (!them) { continue; @@ -1120,7 +1120,7 @@ void EntityList::AESpell( distance ); - for (auto& it: entity_list.GetCloseMobList(caster_mob, distance)) { + for (auto& it: caster_mob->GetCloseMobList(distance)) { current_mob = it.second; if (!current_mob) { continue; @@ -1256,7 +1256,7 @@ void EntityList::MassGroupBuff( float distance_squared = distance * distance; bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - for (auto& it: entity_list.GetCloseMobList(caster, distance)) { + for (auto& it: caster->GetCloseMobList(distance)) { current_mob = it.second; if (!current_mob) { continue; @@ -1306,7 +1306,7 @@ void EntityList::AEAttack( float distance_squared = distance * distance; int current_hits = 0; - for (auto& it: entity_list.GetCloseMobList(attacker, distance)) { + for (auto& it: attacker->GetCloseMobList(distance)) { current_mob = it.second; if (!current_mob) { continue; diff --git a/zone/entity.cpp b/zone/entity.cpp index 202c9025e..683d1eee6 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -696,7 +696,7 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue) npc_list.emplace(std::pair(npc->GetID(), npc)); mob_list.emplace(std::pair(npc->GetID(), npc)); - entity_list.ScanCloseMobs(npc->close_mobs, npc, true); + entity_list.ScanCloseMobs(npc); if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) { parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); @@ -1742,8 +1742,7 @@ void EntityList::QueueCloseClients( } float distance_squared = distance * distance; - - for (auto &e : GetCloseMobList(sender, distance)) { + for (auto &e : sender->GetCloseMobList(distance)) { Mob *mob = e.second; if (!mob) { continue; @@ -2886,7 +2885,7 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) entity_id ); - it->second->close_mobs.erase(entity_id); + it->second->m_close_mobs.erase(entity_id); ++it; } @@ -2911,49 +2910,40 @@ void EntityList::RemoveAuraFromMobs(Mob *aura) } } -/** - * The purpose of this system is so that we cache relevant entities that are "close" - * - * In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality - * we only care about entities closest to us - * - * A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation - * of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds) - * versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close - * lists remain always up to date - * - * Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute - * After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is actually far less today) - * - * Places in the code where this logic makes a huge impact - * - * Aggro checks (zone wide -> close) - * Aura processing (zone wide -> close) - * AE Taunt (zone wide -> close) - * AOE Spells (zone wide -> close) - * Bard Pulse AOE (zone wide -> close) - * Mass Group Buff (zone wide -> close) - * AE Attack (zone wide -> close) - * Packet QueueCloseClients (zone wide -> close) - * Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close) - * AI Yell for Help (NPC Assist other NPCs) (zone wide -> close) - * - * All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude - * less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire - * entity list (zone wide) - * - * @param close_mobs - * @param scanning_mob - */ -void EntityList::ScanCloseMobs( - std::unordered_map &close_mobs, - Mob *scanning_mob, - bool add_self_to_other_lists -) +// The purpose of this system is so that we cache relevant entities that are "close" +// +// In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality +// we only care about entities closest to us +// +// A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation +// of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds) +// versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close +// lists remain always up to date +// +// Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute +// After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is // tually far less today) +// +// Places in the code where this logic makes a huge impact +// +// Aggro checks (zone wide -> close) +// Aura processing (zone wide -> close) +// AE Taunt (zone wide -> close) +// AOE Spells (zone wide -> close) +// Bard Pulse AOE (zone wide -> close) +// Mass Group Buff (zone wide -> close) +// AE Attack (zone wide -> close) +// Packet QueueCloseClients (zone wide -> close) +// Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close) +// AI Yell for Help (NPC Assist other NPCs) (zone wide -> close) +// +// All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude +// less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire +// entity list (zone wide) +void EntityList::ScanCloseMobs(Mob *scanning_mob) { float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - close_mobs.clear(); + scanning_mob->m_close_mobs.clear(); for (auto &e : mob_list) { auto mob = e.second; @@ -2963,12 +2953,13 @@ void EntityList::ScanCloseMobs( float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { - close_mobs.emplace(std::pair(mob->GetID(), mob)); + scanning_mob->m_close_mobs.emplace(std::pair(mob->GetID(), mob)); - if (add_self_to_other_lists && scanning_mob->GetID() > 0) { + // add self to other mobs close list + if (scanning_mob->GetID() > 0) { bool has_mob = false; - for (auto &cm: mob->close_mobs) { + for (auto &cm: mob->m_close_mobs) { if (scanning_mob->GetID() == cm.first) { has_mob = true; break; @@ -2976,7 +2967,7 @@ void EntityList::ScanCloseMobs( } if (!has_mob) { - mob->close_mobs.insert(std::pair(scanning_mob->GetID(), scanning_mob)); + mob->m_close_mobs.insert(std::pair(scanning_mob->GetID(), scanning_mob)); } } } @@ -2985,7 +2976,7 @@ void EntityList::ScanCloseMobs( LogAIScanCloseDetail( "[{}] Scanning Close List | list_size [{}] moving [{}]", scanning_mob->GetCleanName(), - close_mobs.size(), + scanning_mob->m_close_mobs.size(), scanning_mob->IsMoving() ? "true" : "false" ); } @@ -4448,7 +4439,7 @@ void EntityList::QuestJournalledSayClose( buf.WriteInt32(0); if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { - for (auto &e : GetCloseMobList(sender, (dist * dist))) { + for (auto &e : sender->GetCloseMobList(dist)) { Mob *mob = e.second; if (!mob) { continue; @@ -5651,7 +5642,7 @@ std::vector EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *origi std::vector spreader_list = {}; bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - for (auto &it : entity_list.GetCloseMobList(spreader, range)) { + for (auto &it : spreader->GetCloseMobList(range)) { Mob *mob = it.second; if (!mob) { continue; @@ -5781,7 +5772,7 @@ void EntityList::ReloadMerchants() { std::unordered_map &EntityList::GetCloseMobList(Mob *mob, float distance) { if (distance <= RuleI(Range, MobCloseScanDistance)) { - return mob->close_mobs; + return mob->m_close_mobs; } return mob_list; diff --git a/zone/entity.h b/zone/entity.h index dc3f8a791..8396dd386 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -566,11 +566,7 @@ public: void RefreshAutoXTargets(Client *c); void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); - void ScanCloseMobs( - std::unordered_map &close_mobs, - Mob *scanning_mob, - bool add_self_to_other_lists = false - ); + void ScanCloseMobs(Mob *scanning_mob); void GetTrapInfo(Client* c); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/mob.cpp b/zone/mob.cpp index 64ac3a6e2..d8e02e7f5 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -128,8 +128,8 @@ Mob::Mob( attack_anim_timer(500), position_update_melee_push_timer(500), hate_list_cleanup_timer(6000), - mob_close_scan_timer(6000), - mob_check_moving_timer(1000), + m_scan_close_mobs_timer(6000), + m_mob_check_moving_timer(1000), bot_attack_flag_timer(10000) { mMovementManager = &MobMovementManager::Get(); @@ -517,7 +517,7 @@ Mob::Mob( m_manual_follow = false; - mob_close_scan_timer.Trigger(); + m_scan_close_mobs_timer.Trigger(); SetCanOpenDoors(true); @@ -570,7 +570,7 @@ Mob::~Mob() entity_list.RemoveMobFromCloseLists(this); entity_list.RemoveAuraFromMobs(this); - close_mobs.clear(); + m_close_mobs.clear(); LeaveHealRotationTargetPool(); } @@ -5017,7 +5017,7 @@ void Mob::Say(const char *format, ...) int16 distance = 200; if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { - for (auto &e : entity_list.GetCloseMobList(talker, (distance * distance))) { + for (auto &e : talker->GetCloseMobList(distance)) { Mob *mob = e.second; if (!mob) { continue; @@ -6176,9 +6176,7 @@ int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker) void Mob::SetBottomRampageList() { - auto &mob_list = entity_list.GetCloseMobList(this); - - for (auto &e : mob_list) { + for (auto &e : GetCloseMobList()) { auto mob = e.second; if (!mob) { continue; @@ -6203,9 +6201,7 @@ void Mob::SetBottomRampageList() void Mob::SetTopRampageList() { - auto &mob_list = entity_list.GetCloseMobList(this); - - for (auto &e : mob_list) { + for (auto &e : GetCloseMobList()) { auto mob = e.second; if (!mob) { continue; @@ -8619,7 +8615,7 @@ void Mob::SetExtraHaste(int haste, bool need_to_save) bool Mob::IsCloseToBanker() { - for (auto &e: entity_list.GetCloseMobList(this)) { + for (auto &e: GetCloseMobList()) { auto mob = e.second; if (mob && mob->IsNPC() && mob->GetClass() == Class::Banker) { return true; @@ -8648,3 +8644,48 @@ bool Mob::HasBotAttackFlag(Mob* tar) { return false; } + +const uint16 scan_close_mobs_timer_moving = 6000; // 6 seconds +const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds + +void Mob::CheckScanCloseMobsMovingTimer() +{ + LogAIScanCloseDetail( + "Mob [{}] {}moving, scan timer [{}]", + GetCleanName(), + IsMoving() ? "" : "NOT ", + m_scan_close_mobs_timer.GetRemainingTime() + ); + + // If the moving timer triggers, lets see if we are moving or idle to restart the appropriate + // dynamic timer + if (m_mob_check_moving_timer.Check()) { + // If the mob is still moving, restart the moving timer + if (moving) { + if (m_scan_close_mobs_timer.GetRemainingTime() > scan_close_mobs_timer_moving) { + LogAIScanCloseDetail("Mob [{}] Restarting with moving timer", GetCleanName()); + m_scan_close_mobs_timer.Disable(); + m_scan_close_mobs_timer.Start(scan_close_mobs_timer_moving); + m_scan_close_mobs_timer.Trigger(); + } + } + // If the mob is not moving, restart the idle timer + else if (m_scan_close_mobs_timer.GetDuration() == scan_close_mobs_timer_moving) { + LogAIScanCloseDetail("Mob [{}] Restarting with idle timer", GetCleanName()); + m_scan_close_mobs_timer.Disable(); + m_scan_close_mobs_timer.Start(scan_close_mobs_timer_idle); + } + } +} + +void Mob::ScanCloseMobProcess() +{ + if (m_scan_close_mobs_timer.Check()) { + entity_list.ScanCloseMobs(this); + } +} + +std::unordered_map &Mob::GetCloseMobList(float distance) +{ + return entity_list.GetCloseMobList(this, distance); +} diff --git a/zone/mob.h b/zone/mob.h index 61e82c1fe..31f189cbb 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -201,9 +201,9 @@ public: void DisplayInfo(Mob *mob); - std::unordered_map close_mobs; - Timer mob_close_scan_timer; - Timer mob_check_moving_timer; + std::unordered_map m_close_mobs; + Timer m_scan_close_mobs_timer; + Timer m_mob_check_moving_timer; // Bot attack flag Timer bot_attack_flag_timer; @@ -1487,6 +1487,10 @@ public: bool IsCloseToBanker(); + void ScanCloseMobProcess(); + std::unordered_map &GetCloseMobList(float distance = 0.0f); + void CheckScanCloseMobsMovingTimer(); + 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_ai.cpp b/zone/mob_ai.cpp index 24afdec86..af10c1713 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1391,29 +1391,8 @@ void Mob::AI_Process() { StopNavigation(); } } - else if (zone->CanDoCombat() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) { - - /** - * NPC to NPC aggro (npc_aggro flag set) - */ - for (auto &close_mob : close_mobs) { - Mob *mob = close_mob.second; - - if (mob->IsClient()) { - continue; - } - - if (CheckWillAggro(mob)) { - AddToHateList(mob); - } - } - - AI_scan_area_timer->Disable(); - AI_scan_area_timer->Start( - RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)), - false - ); - + else if (zone->CanDoCombat() && IsNPC() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) { + CastToNPC()->DoNpcToNpcAggroScan(); } else if (AI_movement_timer->Check() && !IsRooted()) { if (IsPet()) { diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index b8c3efd84..3bb5996a2 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -127,11 +127,6 @@ public: } - /** - * @param mob_movement_manager - * @param mob - * @return - */ virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) { if (!mob->IsAIControlled()) { @@ -286,11 +281,6 @@ public: } - /** - * @param mob_movement_manager - * @param mob - * @return - */ virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) { if (!mob->IsAIControlled()) { @@ -707,33 +697,21 @@ void MobMovementManager::Process() } } -/** - * @param mob - */ void MobMovementManager::AddMob(Mob *mob) { _impl->Entries.insert(std::make_pair(mob, MobMovementEntry())); } -/** - * @param mob - */ void MobMovementManager::RemoveMob(Mob *mob) { _impl->Entries.erase(mob); } -/** - * @param client - */ void MobMovementManager::AddClient(Client *client) { _impl->Clients.push_back(client); } -/** - * @param client - */ void MobMovementManager::RemoveClient(Client *client) { auto iter = _impl->Clients.begin(); @@ -747,11 +725,6 @@ void MobMovementManager::RemoveClient(Client *client) } } -/** - * @param who - * @param to - * @param mob_movement_mode - */ void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode) { auto iter = _impl->Entries.find(who); @@ -764,13 +737,6 @@ void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_moveme PushRotateTo(ent.second, who, to, mob_movement_mode); } -/** - * @param who - * @param x - * @param y - * @param z - * @param heading - */ void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float heading) { auto iter = _impl->Entries.find(who); @@ -781,13 +747,6 @@ void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float hea PushTeleportTo(ent.second, x, y, z, heading); } -/** - * @param who - * @param x - * @param y - * @param z - * @param mode - */ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode) { if (IsPositionEqualWithinCertainZ(glm::vec3(x, y, z), glm::vec3(who->GetX(), who->GetY(), who->GetZ()), 6.0f)) { @@ -824,9 +783,6 @@ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMove } } -/** - * @param who - */ void MobMovementManager::StopNavigation(Mob *who) { auto iter = _impl->Entries.find(who); @@ -852,16 +808,6 @@ void MobMovementManager::StopNavigation(Mob *who) PushStopMoving(ent.second); } -/** - * @param mob - * @param delta_x - * @param delta_y - * @param delta_z - * @param delta_heading - * @param anim - * @param range - * @param single_client - */ void MobMovementManager::SendCommandToClients( Mob *mob, float delta_x, @@ -961,10 +907,6 @@ void MobMovementManager::SendCommandToClients( } } -/** - * @param in - * @return - */ float MobMovementManager::FixHeading(float in) { auto h = in; @@ -979,9 +921,6 @@ float MobMovementManager::FixHeading(float in) return h; } -/** - * @param client - */ void MobMovementManager::DumpStats(Client *client) { auto current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; @@ -1062,13 +1001,6 @@ void MobMovementManager::FillCommandStruct( } } -/** - * @param who - * @param x - * @param y - * @param z - * @param mob_movement_mode - */ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { Mob *target=who->GetTarget(); @@ -1114,13 +1046,6 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove } } -/** - * @param who - * @param x - * @param y - * @param z - * @param mode - */ void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode) { PathfinderOptions opts; @@ -1253,13 +1178,6 @@ void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, M } } -/** - * @param who - * @param x - * @param y - * @param z - * @param movement_mode - */ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode) { auto eiter = _impl->Entries.find(who); @@ -1368,13 +1286,6 @@ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float } } -/** - * @param who - * @param x - * @param y - * @param z - * @param mode - */ void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode) { auto eiter = _impl->Entries.find(who); @@ -1386,48 +1297,21 @@ void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, Mob PushStopMoving(ent.second); } -/** - * @param ent - * @param x - * @param y - * @param z - * @param heading - */ void MobMovementManager::PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading) { ent.Commands.emplace_back(std::unique_ptr(new TeleportToCommand(x, y, z, heading))); } -/** - * @param ent - * @param x - * @param y - * @param z - * @param mob_movement_mode - */ void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) { ent.Commands.emplace_back(std::unique_ptr(new MoveToCommand(x, y, z, mob_movement_mode))); } -/** - * @param ent - * @param x - * @param y - * @param z - * @param mob_movement_mode - */ void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) { ent.Commands.emplace_back(std::unique_ptr(new SwimToCommand(x, y, z, mob_movement_mode))); } -/** - * @param ent - * @param who - * @param to - * @param mob_movement_mode - */ void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode) { auto from = FixHeading(who->GetHeading()); @@ -1450,41 +1334,21 @@ void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, ent.Commands.emplace_back(std::unique_ptr(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mob_movement_mode))); } -/** - * @param ent - * @param x - * @param y - * @param z - * @param mob_movement_mode - */ void MobMovementManager::PushFlyTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) { ent.Commands.emplace_back(std::unique_ptr(new FlyToCommand(x, y, z, mob_movement_mode))); } -/** - * @param mob_movement_entry - */ void MobMovementManager::PushStopMoving(MobMovementEntry &mob_movement_entry) { mob_movement_entry.Commands.emplace_back(std::unique_ptr(new StopMovingCommand())); } -/** - * @param mob_movement_entry - */ void MobMovementManager::PushEvadeCombat(MobMovementEntry &mob_movement_entry) { mob_movement_entry.Commands.emplace_back(std::unique_ptr(new EvadeCombatCommand())); } -/** - * @param who - * @param x - * @param y - * @param z - * @param mob_movement_mode - */ void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { LogDebug("Handle stuck behavior for {0} at ({1}, {2}, {3}) with movement_mode {4}", who->GetName(), x, y, z, static_cast(mob_movement_mode)); diff --git a/zone/npc.cpp b/zone/npc.cpp index d776fb8bc..e625af7c3 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -601,28 +601,8 @@ bool NPC::Process() DepopSwarmPets(); } - if (mob_close_scan_timer.Check()) { - entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); - } - - const uint16 npc_mob_close_scan_timer_moving = 6000; - const uint16 npc_mob_close_scan_timer_idle = 60000; - - if (mob_check_moving_timer.Check()) { - if (moving) { - if (mob_close_scan_timer.GetRemainingTime() > npc_mob_close_scan_timer_moving) { - LogAIScanCloseDetail("NPC [{}] Restarting with moving timer", GetCleanName()); - mob_close_scan_timer.Disable(); - mob_close_scan_timer.Start(npc_mob_close_scan_timer_moving); - mob_close_scan_timer.Trigger(); - } - } - else if (mob_close_scan_timer.GetDuration() == npc_mob_close_scan_timer_moving) { - LogAIScanCloseDetail("NPC [{}] Restarting with idle timer", GetCleanName()); - mob_close_scan_timer.Disable(); - mob_close_scan_timer.Start(npc_mob_close_scan_timer_idle); - } - } + ScanCloseMobProcess(); + CheckScanCloseMobsMovingTimer(); if (hp_regen_per_second > 0 && hp_regen_per_second_timer.Check()) { if (GetHP() < GetMaxHP()) { @@ -3327,7 +3307,7 @@ bool NPC::AICheckCloseBeneficialSpells( /** * Check through close range mobs */ - for (auto & close_mob : entity_list.GetCloseMobList(caster, cast_range)) { + for (auto & close_mob : caster->GetCloseMobList(cast_range)) { Mob *mob = close_mob.second; if (!mob) { continue; @@ -3406,8 +3386,8 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) GetID() ); - for (auto &close_mob : entity_list.GetCloseMobList(sender)) { - Mob *mob = close_mob.second; + for (auto &close_mob: sender->GetCloseMobList()) { + Mob *mob = close_mob.second; if (!mob) { continue; } @@ -4019,3 +3999,27 @@ void NPC::DescribeSpecialAbilities(Client* c) c->Message(Chat::White, e.c_str()); } } + +void NPC::DoNpcToNpcAggroScan() +{ + for (auto &close_mob : GetCloseMobList(GetAggroRange())) { + Mob *mob = close_mob.second; + if (!mob) { + continue; + } + + if (!mob->IsNPC()) { + continue; + } + + if (CheckWillAggro(mob)) { + AddToHateList(mob); + } + } + + AI_scan_area_timer->Disable(); + AI_scan_area_timer->Start( + RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)), + false + ); +} diff --git a/zone/npc.h b/zone/npc.h index 1dc4ec1e9..20b6f74f6 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -557,6 +557,7 @@ public: bool CanPathTo(float x, float y, float z); + void DoNpcToNpcAggroScan(); protected: void HandleRoambox(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 2da02fe12..df3fe0dbf 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2473,8 +2473,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) { if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) { - auto& mob_list = entity_list.GetCloseMobList(spell_target); - for (auto& e : mob_list) { + for (auto& e : spell_target->GetCloseMobList()) { auto mob = e.second; if (!mob) { continue;