From 8cb51eb253f5f00c073f7f80d1b4ea31d3b7661a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 25 Dec 2019 03:16:14 -0600 Subject: [PATCH 01/17] Scanning optimization work from over a year ago from EZ - cleaned up a bit --- common/eqemu_logsys.h | 8 +- common/eqemu_logsys_log_aliases.h | 30 ++++ common/ruletypes.h | 1 + world/world_server_command_handler.cpp | 4 +- zone/aggro.cpp | 110 +----------- zone/client.h | 1 - zone/client_process.cpp | 18 +- zone/entity.cpp | 54 +++++- zone/entity.h | 7 +- zone/mob.cpp | 14 +- zone/mob.h | 9 +- zone/mob_ai.cpp | 132 +++++---------- zone/npc.cpp | 224 ++++++++++++++++++++++++- zone/npc.h | 3 + 14 files changed, 393 insertions(+), 222 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d2d860101..460ff2fef 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -107,6 +107,9 @@ namespace Logs { Emergency, Alert, Notice, + AIScanClose, + AIYellForHelp, + AICastBeneficialClose, MaxCategoryID /* Don't Remove this */ }; @@ -172,7 +175,10 @@ namespace Logs { "Critical", "Emergency", "Alert", - "Notice" + "Notice", + "AI Scan Close", + "AI Yell For Help", + "AI Cast Beneficial Close", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index f90d0e223..d569fba77 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -491,6 +491,36 @@ OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAIScanClose(message, ...) do {\ + if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIScanCloseDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIYellForHelp(message, ...) do {\ + if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIYellForHelpDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAICastBeneficialClose(message, ...) do {\ + if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAICastBeneficialCloseDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/ruletypes.h b/common/ruletypes.h index c7b4a1aee..6933b00e4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -568,6 +568,7 @@ RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") RULE_INT(Range, ClientNPCScan, 300, "") +RULE_INT(Range, MobCloseScanDistance, 300, "") RULE_CATEGORY_END() diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 003e9abe2..3a929d89f 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -189,10 +189,10 @@ namespace WorldserverCommandHandler { Json::Value schema; - schema["server_tables"] = server_tables_json; - schema["player_tables"] = player_tables_json; schema["content_tables"] = content_tables_json; schema["login_tables"] = login_tables_json; + schema["player_tables"] = player_tables_json; + schema["server_tables"] = server_tables_json; schema["state_tables"] = state_tables_json; schema["version_tables"] = version_tables_json; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1d579567f..ff0077353 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -36,19 +36,6 @@ extern Zone* zone; //#define LOSDEBUG 6 -//look around a client for things which might aggro the client. -void EntityList::CheckClientAggro(Client *around) -{ - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - Mob *mob = it->second; - if (mob->IsClient()) //also ensures that mob != around - continue; - - if (mob->CheckWillAggro(around) && !mob->CheckAggro(around)) - mob->AddToHateList(around, 25); - } -} - void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) { float d2 = d*d; @@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) { return(false); } -Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange) { - if (!sender || !sender->IsNPC()) - return(nullptr); - - auto it = npc_list.begin(); - while (it != npc_list.end()) { - Mob *mob = it->second; - - if (sender->CheckWillAggro(mob)) - return mob; - ++it; - } - - return nullptr; -} - int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker @@ -462,82 +433,11 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) return Count; } -void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { - if(!sender || !attacker) - return; - if (sender->GetPrimaryFaction() == 0 ) - return; // well, if we dont have a faction set, we're gonna be indiff to everybody - - if (sender->HasAssistAggro()) - return; - - for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { - NPC *mob = it->second; - if (!mob) - continue; - - if (mob->CheckAggro(attacker)) - continue; - - if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) - break; - - float r = mob->GetAssistRange(); - r = r * r; - - if ( - mob != sender - && mob != attacker -// && !mob->IsCorpse() -// && mob->IsAIControlled() - && mob->GetPrimaryFaction() != 0 - && DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r - && !mob->IsEngaged() - && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) - // If we're a pet we don't react to any calls for help if our owner is a client - ) - { - //if they are in range, make sure we are not green... - //then jump in if they are our friend - if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) - { - bool useprimfaction = false; - if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) - { - const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); - if(cf){ - if(cf->assistprimaryfaction != 0) - useprimfaction = true; - } - } - - if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) - { - //attacking someone on same faction, or a friend - //Father Nitwit: make sure we can see them. - if(mob->CheckLosFN(sender)) { -#if (EQDEBUG>=5) - LogDebug("AIYellForHelp(\"[{}]\",\"[{}]\") [{}] attacking [{}] Dist [{}] Z [{}]", - sender->GetName(), attacker->GetName(), mob->GetName(), - attacker->GetName(), DistanceSquared(mob->GetPosition(), - sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ())); -#endif - mob->AddToHateList(attacker, 25, 0, false); - sender->AddAssistCap(); - } - } - } - } - } -} - -/* -returns false if attack should not be allowed -I try to list every type of conflict that's possible here, so it's easy -to see how the decision is made. Yea, it could be condensed and made -faster, but I'm doing it this way to make it readable and easy to modify -*/ - +/** + * @param target + * @param isSpellAttack + * @return + */ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) { diff --git a/zone/client.h b/zone/client.h index 82b8c7475..8876e4645 100644 --- a/zone/client.h +++ b/zone/client.h @@ -226,7 +226,6 @@ public: Client(EQStreamInterface * ieqs); ~Client(); - std::unordered_map close_mobs; bool is_client_moving; void SetDisplayMobInfoWindow(bool display_mob_info_window); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a857c3c21..e0068d6e9 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -251,17 +251,21 @@ bool Client::Process() { } } - /* Build a close range list of NPC's */ + /** + * Scan close range mobs + * + * Used in aggro checks + */ if (npc_close_scan_timer.Check()) { close_mobs.clear(); - //Force spawn updates when traveled far - bool force_spawn_updates = false; - float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange)); + float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); - auto &mob_list = entity_list.GetMobList(); - for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { - Mob* mob = itr->second; + auto &mob_list = entity_list.GetMobList(); + + for (auto itr : mob_list) { + Mob *mob = itr.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); + if (mob->IsNPC()) { if (distance <= scan_range) { close_mobs.insert(std::pair(mob, distance)); diff --git a/zone/entity.cpp b/zone/entity.cpp index 7f016f197..597780878 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2494,7 +2494,7 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { - RemoveMobFromClientCloseLists(it->second); + RemoveMobFromCloseLists(it->second); if (npc_list.count(delete_id)) entity_list.RemoveNPC(delete_id); @@ -2512,17 +2512,19 @@ bool EntityList::RemoveMob(uint16 delete_id) // This is for if the ID is deleted for some reason bool EntityList::RemoveMob(Mob *delete_mob) { - if (delete_mob == 0) + if (delete_mob == 0) { return true; + } auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { - RemoveMobFromClientCloseLists(it->second); + RemoveMobFromCloseLists(it->second); safe_delete(it->second); - if (!corpse_list.count(it->first)) + if (!corpse_list.count(it->first)) { free_ids.push(it->first); + } mob_list.erase(it); return true; } @@ -2539,28 +2541,62 @@ bool EntityList::RemoveNPC(uint16 delete_id) // make sure its proximity is removed RemoveProximity(delete_id); // remove from client close lists - RemoveMobFromClientCloseLists(npc->CastToMob()); + RemoveMobFromCloseLists(npc->CastToMob()); // remove from the list npc_list.erase(it); // remove from limit list if needed - if (npc_limit_list.count(delete_id)) + if (npc_limit_list.count(delete_id)) { npc_limit_list.erase(delete_id); + } + return true; } return false; } -bool EntityList::RemoveMobFromClientCloseLists(Mob *mob) +/** + * @param mob + * @return + */ +bool EntityList::RemoveMobFromCloseLists(Mob *mob) { - auto it = client_list.begin(); - while (it != client_list.end()) { + auto it = mob_list.begin(); + while (it != mob_list.end()) { it->second->close_mobs.erase(mob); ++it; } return false; } +/** + * @param close_mobs + * @param scanning_mob + */ +void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) +{ + float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + int list_count = 0; + + close_mobs.clear(); + + auto it = mob_list.begin(); + while (it != mob_list.end()) { + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); + if (distance <= scan_range) { + close_mobs.insert(std::pair(it->second, distance)); + list_count++; + } + else if (it->second->GetAggroRange() >= scan_range) { + close_mobs.insert(std::pair(it->second, distance)); + list_count++; + } + ++it; + } + + LogAIScanClose("Close List Size [{}] for mob [{}]", list_count, scanning_mob->GetCleanName()); +} + bool EntityList::RemoveMerc(uint16 delete_id) { auto it = merc_list.find(delete_id); diff --git a/zone/entity.h b/zone/entity.h index 7631f0532..561e9387a 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -284,7 +284,7 @@ public: bool RemoveTrap(uint16 delete_id); bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); - bool RemoveMobFromClientCloseLists(Mob *mob); + bool RemoveMobFromCloseLists(Mob *mob); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); @@ -443,11 +443,7 @@ public: bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count); bool LimitCheckName(const char* npc_name); - void CheckClientAggro(Client *around); - Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange); int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con); - void AIYellForHelp(Mob* sender, Mob* attacker); - bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); Mob* GetTargetForMez(Mob* caster); uint32 CheckNPCsClose(Mob *center); @@ -501,6 +497,7 @@ public: void RefreshAutoXTargets(Client *c); void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); + void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); void GetTrapInfo(Client* client); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/mob.cpp b/zone/mob.cpp index 44ce45d93..f4d3acab6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -116,7 +116,9 @@ Mob::Mob( m_specialattacks(eSpecialAttacks::None), attack_anim_timer(1000), position_update_melee_push_timer(500), - hate_list_cleanup_timer(6000) + hate_list_cleanup_timer(6000), + mob_scan_close(6000), + mob_check_moving_timer(1000) { mMovementManager = &MobMovementManager::Get(); mMovementManager->AddMob(this); @@ -524,6 +526,16 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { return(ANIM_STAND); } +void Mob::GetCloseMobList(std::list> &m_list) +{ + m_list.clear(); + auto it = close_mobs.begin(); + while (it != close_mobs.end()) { + m_list.push_back(std::make_pair(it->first, it->second)); + ++it; + } +} + void Mob::SetInvisible(uint8 state) { invisible = state; diff --git a/zone/mob.h b/zone/mob.h index ab3df4277..5f9dcae03 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1,3 +1,4 @@ + /* EQEMu: Everquest Server Emulator Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org) @@ -169,6 +170,12 @@ public: void DisplayInfo(Mob *mob); + std::unordered_map close_mobs; + Timer mob_scan_close; + Timer mob_check_moving_timer; + + void GetCloseMobList(std::list> &m_list); + //Somewhat sorted: needs documenting! //Attack @@ -968,7 +975,7 @@ public: void SetEntityVariable(const char *id, const char *m_var); bool EntityVariableExists(const char *id); - void AI_Event_Engaged(Mob* attacker, bool iYellForHelp = true); + void AI_Event_Engaged(Mob* attacker, bool yell_for_help = true); void AI_Event_NoLongerEngaged(); FACTION_VALUE GetSpecialFactionCon(Mob* iOther); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 5958fe18a..bb020060d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -378,59 +378,6 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust)); } -bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { - //according to live, you can buff and heal through walls... - //now with PCs, this only applies if you can TARGET the target, but - // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. - // - // This check was put in to address an idle-mob CPU issue - LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); - return(false); - } - - if(!caster) - return false; - - if(caster->AI_HasSpells() == false) - return false; - - if(caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) - return false; - - if (iChance < 100) { - uint8 tmp = zone->random.Int(0, 99); - if (tmp >= iChance) - return false; - } - if (caster->GetPrimaryFaction() == 0 ) - return(false); // well, if we dont have a faction set, we're gonna be indiff to everybody - - float iRange2 = iRange*iRange; - - //Only iterate through NPCs - for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { - NPC* mob = it->second; - - if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) { - continue; - } - - if (DistanceSquared(caster->GetPosition(), mob->GetPosition()) > iRange2) { - continue; - } - - if ((iSpellTypes & SpellType_Buff) && !RuleB(NPC, BuffFriends)) { - if (mob != caster) - iSpellTypes = SpellType_Heal; - } - - if (caster->AICastSpell(mob, 100, iSpellTypes)) - return true; - } - return false; -} - void Mob::AI_Init() { pAIControlled = false; @@ -1415,12 +1362,20 @@ void Mob::AI_Process() { } else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) { - /* - * NPC to NPC aggro checking, npc needs npc_aggro flag - */ - Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange()); - if (temp_target) { - AddToHateList(temp_target); + /** + * NPC to NPC aggro (npc_aggro flag set) + */ + for (auto &close_mob : close_mobs) { + Mob *mob = close_mob.first; + float distance = close_mob.second; + + if (mob->IsClient()) { + continue; + } + + if (this->CheckWillAggro(mob)) { + this->AddToHateList(mob); + } } AI_scan_area_timer->Disable(); @@ -1877,47 +1832,46 @@ void NPC::AI_SetupNextWaypoint() { } } -// Note: Mob that caused this may not get added to the hate list until after this function call completes -void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { - if (!IsAIControlled()) +/** + * @param attacker + * @param yell_for_help + */ +void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) +{ + if (!IsAIControlled()) { return; + } SetAppearance(eaStanding); - /* - Kick off auto cast timer - */ - if (this->IsNPC()) - this->CastToNPC()->AIautocastspell_timer->Start(300, false); + if (IsNPC()) { + CastToNPC()->AIautocastspell_timer->Start(300, false); - if (iYellForHelp) { - if(IsPet()) { - GetOwner()->AI_Event_Engaged(attacker, iYellForHelp); - } else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { - entity_list.AIYellForHelp(this, attacker); - if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) - assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); + if (yell_for_help) { + if (IsPet()) { + GetOwner()->AI_Event_Engaged(attacker, yell_for_help); + } + else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { + CastToNPC()->AIYellForHelp(this, attacker); + if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) { + assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); + } + } } - } - if(IsNPC()) - { - if(CastToNPC()->GetGrid() > 0) - { + if (CastToNPC()->GetGrid() > 0) { DistractedFromGrid = true; } - if(attacker && !attacker->IsCorpse()) - { + if (attacker && !attacker->IsCorpse()) { //Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged //if the target dies before it goes off - if(attacker->GetHP() > 0) - { - if(!CastToNPC()->GetCombatEvent() && GetHP() > 0) - { + if (attacker->GetHP() > 0) { + if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) { parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); uint16 emoteid = GetEmoteID(); - if(emoteid != 0) - CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid); + if (emoteid != 0) { + CastToNPC()->DoNPCEmote(ENTERCOMBAT, emoteid); + } CastToNPC()->SetCombatEvent(true); } } @@ -1996,7 +1950,7 @@ bool NPC::AI_EngagedCastCheck() { // try casting a heal or gate if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) { // try casting a heal on nearby - if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) { + if (!AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) { //nobody to heal, try some detrimental spells. if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) { //no spell to cast, try again soon. @@ -2033,7 +1987,7 @@ bool NPC::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. if (!AICastSpell(this, AISpellVar.idle_beneficial_chance, SpellType_Heal | SpellType_Buff | SpellType_Pet)) { - if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { + if(!AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { //if we didnt cast any spells, our autocast timer just resets to the //last duration it was set to... try to put up a more reasonable timer... AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false); diff --git a/zone/npc.cpp b/zone/npc.cpp index efbe98be1..38de9529a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -704,6 +704,30 @@ bool NPC::Process() SpellProcess(); + if (mob_scan_close.Check()) { + LogAIScanClose( + "is_moving [{}] npc [{}] timer [{}]", + moving ? "true" : "false", + GetCleanName(), + mob_scan_close.GetDuration() + ); + + entity_list.ScanCloseMobs(close_mobs, this); + + if (moving) { + mob_scan_close.Disable(); + mob_scan_close.Start(RandomTimer(3000, 6000)); + } + else { + mob_scan_close.Disable(); + mob_scan_close.Start(RandomTimer(6000, 60000)); + } + } + + if (mob_check_moving_timer.Check() && moving) { + mob_scan_close.Trigger(); + } + if (tic_timer.Check()) { parse->EventNPC(EVENT_TICK, this, nullptr, "", 0); BuffProcess(); @@ -851,7 +875,7 @@ bool NPC::Process() if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { - entity_list.AIYellForHelp(this, GetTarget()); + AIYellForHelp(this, GetTarget()); if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); } @@ -2975,6 +2999,11 @@ bool NPC::IsProximitySet() return false; } +/** + * @param box_size + * @param move_distance + * @param move_delay + */ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) { AI_SetRoambox( @@ -2986,3 +3015,196 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) move_delay ); } + +/** + * @param caster + * @param chance + * @param in_cast_range + * @param spell_types + * @return + */ +bool NPC::AICheckCloseBeneficialSpells( + NPC *caster, + uint8 chance, + float in_cast_range, + uint32 spell_types +) +{ + if((spell_types & SPELL_TYPES_DETRIMENTAL) != 0) { + LogError("Detrimental spells requested from AICheckCloseBeneficialSpells!"); + return false; + } + + if (!caster) { + return false; + } + + if (!caster->AI_HasSpells()) { + return false; + } + + if (caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) { + return false; + } + + if (chance < 100) { + uint8 tmp = zone->random.Int(0, 99); + if (tmp >= chance) { + return false; + } + } + + /** + * Indifferent + */ + if (caster->GetPrimaryFaction() == 0) { + return false; + } + + /** + * Cast range + */ + in_cast_range = (in_cast_range * in_cast_range); + + /** + * Check through close range mobs + */ + for (auto & close_mob : close_mobs) { + Mob *mob = close_mob.first; + float cached_close_mob_distance = close_mob.second; + + if (mob->IsClient()) { + continue; + } + + if (cached_close_mob_distance > in_cast_range) { + continue; + } + + LogAICastBeneficialClose( + "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", + mob->GetCleanName(), + cached_close_mob_distance, + in_cast_range, + caster->GetCleanName() + ); + + if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) { + continue; + } + + if ((spell_types & SpellType_Buff) && !RuleB(NPC, BuffFriends)) { + if (mob != caster) { + spell_types = SpellType_Heal; + } + } + + if (caster->AICastSpell(mob, 100, spell_types)) { + return true; + } + } + + return false; +} + +/** + * @param sender + * @param attacker + */ +void NPC::AIYellForHelp(Mob *sender, Mob *attacker) +{ + if (!sender || !attacker) { + return; + } + + /** + * If we dont have a faction set, we're gonna be indiff to everybody + */ + if (sender->GetPrimaryFaction() == 0) { + return; + } + + if (sender->HasAssistAggro()) + return; + + LogAIYellForHelp( + "NPC [{}] ID [{}] is starting to scan", + GetCleanName(), + GetID() + ); + + for (auto & close_mob : close_mobs) { + Mob *mob = close_mob.first; + + float distance = DistanceSquared(m_Position, mob->GetPosition()); + + if (mob->IsClient()) { + continue; + } + + float assist_range = (mob->GetAssistRange() * mob->GetAssistRange()); + + if (distance > assist_range) { + continue; + } + + LogAIYellForHelpDetail( + "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}]", + GetCleanName(), + GetID(), + mob->GetCleanName(), + assist_range, + distance + ); + + if (mob->CheckAggro(attacker)) { + continue; + } + + if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) { + break; + } + + if ( + mob != sender + && mob != attacker + && mob->GetPrimaryFaction() != 0 + && !mob->IsEngaged() + && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) + ) { + + /** + * if they are in range, make sure we are not green... + * then jump in if they are our friend + */ + if (mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { + bool use_primary_faction = false; + if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { + const NPCFactionList *cf = database.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID()); + if (cf) { + if (cf->assistprimaryfaction != 0) { + use_primary_faction = true; + } + } + } + + if (use_primary_faction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) { + //attacking someone on same faction, or a friend + //Father Nitwit: make sure we can see them. + if (mob->CheckLosFN(sender)) { + mob->AddToHateList(attacker, 25, 0, false); + sender->AddAssistCap(); + + LogAIYellForHelpDetail( + "NPC [{}] is assisting [{}] against target [{}]", + mob->GetCleanName(), + this->GetCleanName(), + attacker->GetCleanName() + ); + } + } + } + } + } + +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h index 5d7a7bd73..6d53d2106 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -143,6 +143,9 @@ public: virtual bool AI_IdleCastCheck(); virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float in_cast_range, uint32 spell_types); + void AIYellForHelp(Mob* sender, Mob* attacker); + void LevelScale(); virtual void SetTarget(Mob* mob); From f9e822072f2585f6516cc08f057afb61d2a6568d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 17:08:34 -0600 Subject: [PATCH 02/17] AE Scanning adjustments, testing --- common/database.cpp | 1 + common/spdat.cpp | 3 +- zone/attack.cpp | 2 + zone/effects.cpp | 307 +++++++++++++++++++++++++++++++------------- zone/entity.cpp | 32 +++-- zone/entity.h | 36 +++++- zone/spells.cpp | 34 +++-- zone/zone.cpp | 27 ++-- 8 files changed, 312 insertions(+), 130 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 4e762f917..fde091322 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -683,6 +683,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->RestTimer // " RestTimer) " ); auto results = QueryDatabase(query); + /* Save Bind Points */ query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), " diff --git a/common/spdat.cpp b/common/spdat.cpp index 3fd6d19e4..3f0ae6e41 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -87,8 +87,9 @@ bool IsTargetableAESpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) + if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) { return true; + } return false; } diff --git a/zone/attack.cpp b/zone/attack.cpp index f39fb6b90..78aecd22d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2153,6 +2153,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); + entity_list.RemoveMobFromCloseLists(CastToMob()); + Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); diff --git a/zone/effects.cpp b/zone/effects.cpp index ba82cf3f0..4b908cec6 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -697,110 +697,241 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) } } -// causes caster to hit every mob within dist range of center with -// spell_id. -// NPC spells will only affect other NPCs with compatible faction -void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets) +/** + * Causes caster to hit every mob within dist range of center with spell_id + * + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +void EntityList::AESpell( + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + bool affect_caster, + int16 resist_adjust, + int *max_targets +) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster_mob->IsNPC(); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; - float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - float dist_targ = 0; + const auto &cast_target_position = + spells[spell_id].targettype == ST_Ring ? + caster_mob->GetTargetRingLocation() : + static_cast(center_mob->GetPosition()); - const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast(center->GetPosition()); - glm::vec2 min = { position.x - dist, position.y - dist }; - glm::vec2 max = { position.x + dist, position.y + dist }; + /** + * If using Old Rain Targets - there is no max target limitation + */ + if (RuleB(Spells, OldRainTargets)) { + max_targets = nullptr; + } - bool bad = IsDetrimentalSpell(spell_id); - bool isnpc = caster->IsNPC(); - - if (RuleB(Spells, OldRainTargets)) - max_targets = nullptr; // ignore it! - - // if we have a passed in value, use it, otherwise default to data - // detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs + /** + * Max AOE targets + */ int max_targets_allowed = 0; // unlimited - if (max_targets) // rains pass this in since they need to preserve the count through waves + if (max_targets) { // rains pass this in since they need to preserve the count through waves max_targets_allowed = *max_targets; - else if (spells[spell_id].aemaxtargets) + } + else if (spells[spell_id].aemaxtargets) { max_targets_allowed = spells[spell_id].aemaxtargets; - else if (IsTargetableAESpell(spell_id) && bad && !isnpc) + } + else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) { max_targets_allowed = 4; + } - int iCounter = 0; + int target_hit_counter = 0; + float distance_to_target = 0; - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized - if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) - continue; - if (curmob == caster && !affect_caster) //watch for caster too - continue; - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) - continue; - if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) - continue; - if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) - continue; - // check PC/NPC only flag 1 = PCs, 2 = NPCs - if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc()) - continue; - if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) - continue; - if (!IsWithinAxisAlignedBox(static_cast(curmob->GetPosition()), min, max)) - continue; + for (auto &it : caster_mob->close_mobs) { + current_mob = it.first; - dist_targ = DistanceSquared(curmob->GetPosition(), position); + if (!current_mob) { + continue; + } - if (dist_targ > dist2) //make sure they are in range + LogDebug("iterating [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { continue; - if (dist_targ < min_range2) //make sure they are in range - continue; - if (isnpc && curmob->IsNPC() && spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting - FACTION_VALUE f = curmob->GetReverseFactionCon(caster); - if (bad) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) - continue; - } else { - //only affect mobs we would assist. - if (!(f <= FACTION_AMIABLE)) - continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + } + + LogDebug("Done iterating [{}]", caster_mob->GetCleanName()); + + if (max_targets && max_targets_allowed) { + *max_targets = *max_targets - target_hit_counter; + } +} + +/** + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +bool EntityList::AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster, + int16 resist_adjust +) { + + if (!current_mob) { + return false; + } + + bool is_npc = caster_mob->IsNPC(); + float distance = caster_mob->GetAOERange(spell_id); + float distance_squared = distance * distance; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; + glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; + + if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { + return false; + } + + if (current_mob == caster_mob && !affect_caster) { + return false; + } + + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + return false; + } + + /** + * Check PC / NPC + * 1 = PC + * 2 = NPC + */ + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + return false; + } + + if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + return false; + } + + if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { + return false; + } + + distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); + + if (distance_to_target > distance_squared) { + return false; + } + + if (distance_to_target < min_range2) { + return false; + } + + if (is_npc && current_mob->IsNPC() && + spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); + if (is_detrimental_spell) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if ( + !(caster_mob->CheckAggro(current_mob) || + faction_value == FACTION_THREATENLY || + faction_value == FACTION_SCOWLS)) { + return false; } } - //finally, make sure they are within range - if (bad) { - if (!caster->IsAttackAllowed(curmob, true)) - continue; - if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) - continue; - if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) - continue; - } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // This does not check faction for beneficial AE buffs..only agro and attackable. - // I've tested for spells that I can find without problem, but a faction-based - // check may still be needed. Any changes here should also reflect in BardAEPulse() - if (caster->IsAttackAllowed(curmob, true)) - continue; - if (caster->CheckAggro(curmob)) - continue; - } - - curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - - if (max_targets_allowed) { // if we have a limit, increment count - iCounter++; - if (iCounter >= max_targets_allowed) // we done - break; + else { + //only affect mobs we would assist. + if (!(faction_value <= FACTION_AMIABLE)) { + return false; + } } } - if (max_targets && max_targets_allowed) - *max_targets = *max_targets - iCounter; + /** + * Finally, make sure they are within range + */ + if (is_detrimental_spell) { + if (!caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { + return false; + } + if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( + caster_mob->GetTargetRingX(), + caster_mob->GetTargetRingY(), + caster_mob->GetTargetRingZ(), + current_mob->GetSize())) { + return false; + } + } + else { + + /** + * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + * This does not check faction for beneficial AE buffs... only agro and attackable. + * I've tested for spells that I can find without problem, but a faction-based + * check may still be needed. Any changes here should also reflect in BardAEPulse() + */ + if (caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (caster_mob->CheckAggro(current_mob)) { + return false; + } + } + + /** + * Increment hit count if max targets + */ + if (max_targets_allowed) { + target_hit_counter++; + if (target_hit_counter >= max_targets_allowed) { + return false; + } + } + + return true; } void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) @@ -812,8 +943,8 @@ void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool a bool bad = IsDetrimentalSpell(spell_id); - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; + for (auto & it : mob_list) { + curmob = it.second; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too diff --git a/zone/entity.cpp b/zone/entity.cpp index 597780878..c7ecb0f80 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2486,30 +2486,41 @@ void EntityList::RemoveAllEncounters() } } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveMob(uint16 delete_id) { - if (delete_id == 0) + if (delete_id == 0) { return true; + } auto it = mob_list.find(delete_id); if (it != mob_list.end()) { RemoveMobFromCloseLists(it->second); - if (npc_list.count(delete_id)) + if (npc_list.count(delete_id)) { entity_list.RemoveNPC(delete_id); - else if (client_list.count(delete_id)) + } + else if (client_list.count(delete_id)) { entity_list.RemoveClient(delete_id); + } safe_delete(it->second); - if (!corpse_list.count(delete_id)) + if (!corpse_list.count(delete_id)) { free_ids.push(it->first); + } mob_list.erase(it); return true; } return false; } -// This is for if the ID is deleted for some reason +/** + * @param delete_mob + * @return + */ bool EntityList::RemoveMob(Mob *delete_mob) { if (delete_mob == 0) { @@ -2533,19 +2544,19 @@ bool EntityList::RemoveMob(Mob *delete_mob) return false; } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveNPC(uint16 delete_id) { auto it = npc_list.find(delete_id); if (it != npc_list.end()) { NPC *npc = it->second; - // make sure its proximity is removed RemoveProximity(delete_id); - // remove from client close lists RemoveMobFromCloseLists(npc->CastToMob()); - // remove from the list npc_list.erase(it); - // remove from limit list if needed if (npc_limit_list.count(delete_id)) { npc_limit_list.erase(delete_id); } @@ -2561,11 +2572,14 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { + LogDebug("Removing mob [{}] from close lists", mob->GetCleanName()); + auto it = mob_list.begin(); while (it != mob_list.end()) { it->second->close_mobs.erase(mob); ++it; } + return false; } diff --git a/zone/entity.h b/zone/entity.h index 561e9387a..94884167c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -388,11 +388,37 @@ public: void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); - void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::invslot::slotPrimary, int count = 0, bool IsFromSpell = false); - void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); - void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); - void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); - void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEAttack( + Mob *attacker, + float dist, + int Hand = EQEmu::invslot::slotPrimary, + int count = 0, + bool IsFromSpell = false + ); + void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); + void AESpell( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster = true, + int16 resist_adjust = 0, + int *max_targets = nullptr + ); + static bool AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster = true, + int16 resist_adjust = 0 + ); + void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); //trap stuff Mob* GetTrapTrigger(Trap* trap); diff --git a/zone/spells.cpp b/zone/spells.cpp index cec6e8142..07de6b715 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4793,24 +4793,32 @@ int16 Mob::CalcFearResistChance() return resistchance; } -float Mob::GetAOERange(uint16 spell_id) { - float range; +/** + * @param spell_id + * @return + */ +float Mob::GetAOERange(uint16 spell_id) +{ + float range = spells[spell_id].aoerange; - range = spells[spell_id].aoerange; - if(range == 0) //for TGB spells, they prolly do not have an aoe range + /** + * For TGB + */ + if (range == 0) { range = spells[spell_id].range; - if(range == 0) - range = 10; //something.... - - if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { - //Live AA - Extended Notes, SionachiesCrescendo - float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); - range += range*song_bonus /100.0f; } - range = GetActSpellRange(spell_id, range); + if (range == 0) { + range = 10; + } - return(range); + if (IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { + //Live AA - Extended Notes, SionachiesCrescendo + float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); + range += range * song_bonus / 100.0f; + } + + return GetActSpellRange(spell_id, range); } /////////////////////////////////////////////////////////////////////////////// diff --git a/zone/zone.cpp b/zone/zone.cpp index 99c120cab..b88ddd071 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1997,7 +1997,6 @@ const char *Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &locat if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) { continue; } - switch (blocked_spells[x].type) { case ZoneBlockedSpellTypes::ZoneWide: { return blocked_spells[x].message; @@ -2033,21 +2032,21 @@ void Zone::SetInstanceTimer(uint32 new_duration) void Zone::LoadLDoNTraps() { - const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; - auto results = database.QueryDatabase(query); - if (!results.Success()) { + const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { return; - } + } - for (auto row = results.begin();row != results.end(); ++row) { - auto lt = new LDoNTrapTemplate; - lt->id = atoi(row[0]); - lt->type = (LDoNChestTypes)atoi(row[1]); - lt->spell_id = atoi(row[2]); - lt->skill = atoi(row[3]); - lt->locked = atoi(row[4]); - ldon_trap_list[lt->id] = lt; - } + for (auto row = results.begin(); row != results.end(); ++row) { + auto lt = new LDoNTrapTemplate; + lt->id = atoi(row[0]); + lt->type = (LDoNChestTypes) atoi(row[1]); + lt->spell_id = atoi(row[2]); + lt->skill = atoi(row[3]); + lt->locked = atoi(row[4]); + ldon_trap_list[lt->id] = lt; + } } From 9481e9eb2d2a179bab63cf08839c90dfe543130f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 23:58:18 -0600 Subject: [PATCH 03/17] More scanning changes around AE cast --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 ++++ common/ruletypes.h | 2 +- zone/attack.cpp | 7 ++- zone/effects.cpp | 86 ++++++++++++++++++++++--------- zone/entity.cpp | 6 --- zone/mob.cpp | 19 ++++--- 7 files changed, 91 insertions(+), 41 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 460ff2fef..7514f34a1 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -110,6 +110,7 @@ namespace Logs { AIScanClose, AIYellForHelp, AICastBeneficialClose, + AoeCast, MaxCategoryID /* Don't Remove this */ }; @@ -179,6 +180,7 @@ namespace Logs { "AI Scan Close", "AI Yell For Help", "AI Cast Beneficial Close", + "AOE Cast", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index d569fba77..5b9b249ba 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -521,6 +521,16 @@ OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAoeCast(message, ...) do {\ + if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAoeCastDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/ruletypes.h b/common/ruletypes.h index 6933b00e4..5aeaa3021 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -568,7 +568,7 @@ RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") RULE_INT(Range, ClientNPCScan, 300, "") -RULE_INT(Range, MobCloseScanDistance, 300, "") +RULE_INT(Range, MobCloseScanDistance, 600, "") RULE_CATEGORY_END() diff --git a/zone/attack.cpp b/zone/attack.cpp index 78aecd22d..3f1bb6ebd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -850,11 +850,12 @@ int Mob::ACSum() auto over_cap = ac - softcap; ac = softcap + (over_cap * returns); } - LogCombat("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); + LogCombatDetail("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); } else { - LogCombat("ACSum ac [{}]", ac); + LogCombatDetail("ACSum ac [{}]", ac); } + return ac; } @@ -2153,8 +2154,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); - entity_list.RemoveMobFromCloseLists(CastToMob()); - Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); diff --git a/zone/effects.cpp b/zone/effects.cpp index 4b908cec6..d9292d2e4 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -748,37 +748,77 @@ void EntityList::AESpell( int target_hit_counter = 0; float distance_to_target = 0; + float distance = caster_mob->GetAOERange(spell_id); - for (auto &it : caster_mob->close_mobs) { - current_mob = it.first; + LogAoeCast( + "Close scan distance [{}] cast distance [{}]", + RuleI(Range, MobCloseScanDistance), + distance + ); - if (!current_mob) { - continue; + if (distance <= RuleI(Range, MobCloseScanDistance)) { + + LogAoeCast("Using close scan mob list"); + + for (auto &it : caster_mob->close_mobs) { + current_mob = it.first; + + if (!current_mob) { + continue; + } + + LogAoeCast("Checking against close scan mob [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { + continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } + } else { - LogDebug("iterating [{}]", current_mob->GetCleanName()); + LogAoeCast("Using full entity mob list"); - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { - continue; + for (auto &it : mob_list) { + current_mob = it.second; + + LogAoeCast("Checking against full zone scan mob [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { + continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } - LogDebug("Done iterating [{}]", caster_mob->GetCleanName()); + LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName()); if (max_targets && max_targets_allowed) { *max_targets = *max_targets - target_hit_counter; diff --git a/zone/entity.cpp b/zone/entity.cpp index c7ecb0f80..815e703cd 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2498,9 +2498,6 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { - - RemoveMobFromCloseLists(it->second); - if (npc_list.count(delete_id)) { entity_list.RemoveNPC(delete_id); } @@ -2530,8 +2527,6 @@ bool EntityList::RemoveMob(Mob *delete_mob) auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { - RemoveMobFromCloseLists(it->second); - safe_delete(it->second); if (!corpse_list.count(it->first)) { free_ids.push(it->first); @@ -2554,7 +2549,6 @@ bool EntityList::RemoveNPC(uint16 delete_id) if (it != npc_list.end()) { NPC *npc = it->second; RemoveProximity(delete_id); - RemoveMobFromCloseLists(npc->CastToMob()); npc_list.erase(it); if (npc_limit_list.count(delete_id)) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 6c04d2c13..c0c2cc092 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -467,35 +467,40 @@ Mob::~Mob() AI_Stop(); if (GetPet()) { - if (GetPet()->Charmed()) + if (GetPet()->Charmed()) { GetPet()->BuffFadeByEffect(SE_Charm); - else + } + else { SetPet(0); + } } EQApplicationPacket app; CreateDespawnPacket(&app, !IsCorpse()); - Corpse* corpse = entity_list.GetCorpseByID(GetID()); - if(!corpse || (corpse && !corpse->IsPlayerCorpse())) + Corpse *corpse = entity_list.GetCorpseByID(GetID()); + if (!corpse || (corpse && !corpse->IsPlayerCorpse())) { entity_list.QueueClients(this, &app, true); + } entity_list.RemoveFromTargets(this, true); - if(trade) { + if (trade) { Mob *with = trade->With(); - if(with && with->IsClient()) { + if (with && with->IsClient()) { with->CastToClient()->FinishTrade(with); with->trade->Reset(); } delete trade; } - if(HasTempPetsActive()){ + if (HasTempPetsActive()) { entity_list.DestroyTempPets(this); } entity_list.UnMarkNPC(GetID()); UninitializeBuffSlots(); + entity_list.RemoveMobFromCloseLists(this); + #ifdef BOTS LeaveHealRotationTargetPool(); #endif From 6b465c576da91efdf5cba81b2b80f4847068dac8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 02:01:48 -0600 Subject: [PATCH 04/17] More scanning work to unify data structures --- common/eqemu_logsys.h | 2 ++ common/eqemu_logsys_log_aliases.h | 10 ++++++++++ zone/attack.cpp | 2 +- zone/client_process.cpp | 6 +++--- zone/effects.cpp | 2 +- zone/entity.cpp | 26 ++++++++++++++++++++------ zone/entity.h | 12 ++++++++++-- zone/mob.cpp | 11 +---------- zone/mob.h | 4 +--- zone/mob_ai.cpp | 3 +-- zone/npc.cpp | 14 +++++++------- 11 files changed, 57 insertions(+), 35 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 7514f34a1..fc1dd36f2 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -111,6 +111,7 @@ namespace Logs { AIYellForHelp, AICastBeneficialClose, AoeCast, + EntityManagement, MaxCategoryID /* Don't Remove this */ }; @@ -181,6 +182,7 @@ namespace Logs { "AI Yell For Help", "AI Cast Beneficial Close", "AOE Cast", + "Entity Management", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 5b9b249ba..24abfdfc9 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -531,6 +531,16 @@ OutF(LogSys, Logs::Detail, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogEntityManagement(message, ...) do {\ + if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogEntityManagementDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/zone/attack.cpp b/zone/attack.cpp index 3f1bb6ebd..cd68f5d57 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5488,4 +5488,4 @@ int32 Mob::GetHPRegen() const int32 Mob::GetManaRegen() const { return mana_regen; -} +} \ No newline at end of file diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e0068d6e9..e75d53d7c 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -268,10 +268,10 @@ bool Client::Process() { if (mob->IsNPC()) { if (distance <= scan_range) { - close_mobs.insert(std::pair(mob, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); } else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) { - close_mobs.insert(std::pair(mob, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); } } } @@ -597,7 +597,7 @@ bool Client::Process() { 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.first; + Mob *mob = close_mob.second; if (!mob) continue; diff --git a/zone/effects.cpp b/zone/effects.cpp index d9292d2e4..5d9730a54 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -761,7 +761,7 @@ void EntityList::AESpell( LogAoeCast("Using close scan mob list"); for (auto &it : caster_mob->close_mobs) { - current_mob = it.first; + current_mob = it.second; if (!current_mob) { continue; diff --git a/zone/entity.cpp b/zone/entity.cpp index 815e703cd..b9756827e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -62,7 +62,8 @@ extern char errorname[32]; Entity::Entity() { - id = 0; + id = 0; + initial_id = 0; spawn_timestamp = time(nullptr); } @@ -2566,11 +2567,22 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { - LogDebug("Removing mob [{}] from close lists", mob->GetCleanName()); + LogEntityManagement( + "Attempting to remove mob [{}] from close lists entity_id ({})", + mob->GetCleanName(), + mob->GetInitialId() + ); auto it = mob_list.begin(); while (it != mob_list.end()) { - it->second->close_mobs.erase(mob); + LogEntityManagement( + "Removing mob [{}] from [{}] close list entity_id ({})", + mob->GetCleanName(), + it->second->GetCleanName(), + mob->GetInitialId() + ); + + it->second->close_mobs.erase(mob->GetInitialId()); ++it; } @@ -2581,7 +2593,7 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) * @param close_mobs * @param scanning_mob */ -void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) +void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) { float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); int list_count = 0; @@ -2590,13 +2602,15 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob auto it = mob_list.begin(); while (it != mob_list.end()) { + Mob *mob = it->second; + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { - close_mobs.insert(std::pair(it->second, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); list_count++; } else if (it->second->GetAggroRange() >= scan_range) { - close_mobs.insert(std::pair(it->second, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); list_count++; } ++it; diff --git a/zone/entity.h b/zone/entity.h index 94884167c..d251eeb57 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -109,6 +109,7 @@ public: const Beacon *CastToBeacon() const; const Encounter *CastToEncounter() const; + inline const uint16& GetInitialId() const { return initial_id; } inline const uint16& GetID() const { return id; } inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } @@ -122,10 +123,17 @@ public: protected: friend class EntityList; - inline virtual void SetID(uint16 set_id) { id = set_id; } + inline virtual void SetID(uint16 set_id) { + id = set_id; + + if (initial_id == 0 && set_id > 0) { + initial_id = set_id; + } + } uint32 pDBAsyncWorkID; private: uint16 id; + uint16 initial_id; time_t spawn_timestamp; }; @@ -523,7 +531,7 @@ public: void RefreshAutoXTargets(Client *c); void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); - void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); + void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); void GetTrapInfo(Client* client); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/mob.cpp b/zone/mob.cpp index c0c2cc092..6a4c12969 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -500,6 +500,7 @@ Mob::~Mob() UninitializeBuffSlots(); entity_list.RemoveMobFromCloseLists(this); + close_mobs.clear(); #ifdef BOTS LeaveHealRotationTargetPool(); @@ -531,16 +532,6 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { return(ANIM_STAND); } -void Mob::GetCloseMobList(std::list> &m_list) -{ - m_list.clear(); - auto it = close_mobs.begin(); - while (it != close_mobs.end()) { - m_list.push_back(std::make_pair(it->first, it->second)); - ++it; - } -} - void Mob::SetInvisible(uint8 state) { invisible = state; diff --git a/zone/mob.h b/zone/mob.h index 5f9dcae03..cdd6f7041 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -170,12 +170,10 @@ public: void DisplayInfo(Mob *mob); - std::unordered_map close_mobs; + std::unordered_map close_mobs; Timer mob_scan_close; Timer mob_check_moving_timer; - void GetCloseMobList(std::list> &m_list); - //Somewhat sorted: needs documenting! //Attack diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index bb020060d..525948bb2 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1366,8 +1366,7 @@ void Mob::AI_Process() { * NPC to NPC aggro (npc_aggro flag set) */ for (auto &close_mob : close_mobs) { - Mob *mob = close_mob.first; - float distance = close_mob.second; + Mob *mob = close_mob.second; if (mob->IsClient()) { continue; diff --git a/zone/npc.cpp b/zone/npc.cpp index 38de9529a..5325ea836 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3070,21 +3070,22 @@ bool NPC::AICheckCloseBeneficialSpells( * Check through close range mobs */ for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.first; - float cached_close_mob_distance = close_mob.second; + Mob *mob = close_mob.second; if (mob->IsClient()) { continue; } - if (cached_close_mob_distance > in_cast_range) { + float distance = DistanceSquared(mob->GetPosition(), GetPosition()); + + if (distance > in_cast_range) { continue; } LogAICastBeneficialClose( "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", mob->GetCleanName(), - cached_close_mob_distance, + distance, in_cast_range, caster->GetCleanName() ); @@ -3133,9 +3134,8 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) GetID() ); - for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.first; - + for (auto &close_mob : close_mobs) { + Mob *mob = close_mob.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); if (mob->IsClient()) { From ec5faea9b1793932998c0d663f3a9c1e9356e4fb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 02:42:52 -0600 Subject: [PATCH 05/17] Use GetSmartMobList in AESpell --- zone/effects.cpp | 328 ++++++++++++++++++----------------------------- zone/entity.cpp | 15 +++ zone/entity.h | 17 +-- zone/mob.h | 1 + 4 files changed, 143 insertions(+), 218 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 5d9730a54..ef2d1d842 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -716,15 +716,20 @@ void EntityList::AESpell( int *max_targets ) { - Mob *current_mob = nullptr; - bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - bool is_npc = caster_mob->IsNPC(); - const auto &cast_target_position = spells[spell_id].targettype == ST_Ring ? caster_mob->GetTargetRingLocation() : static_cast(center_mob->GetPosition()); + Mob *current_mob = nullptr; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster_mob->IsNPC(); + float distance = caster_mob->GetAOERange(spell_id); + float distance_squared = distance * distance; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; + glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; + /** * If using Old Rain Targets - there is no max target limitation */ @@ -748,7 +753,6 @@ void EntityList::AESpell( int target_hit_counter = 0; float distance_to_target = 0; - float distance = caster_mob->GetAOERange(spell_id); LogAoeCast( "Close scan distance [{}] cast distance [{}]", @@ -756,66 +760,129 @@ void EntityList::AESpell( distance ); - if (distance <= RuleI(Range, MobCloseScanDistance)) { + for (auto &it : entity_list.GetCloseMobList(caster_mob, distance)) { + current_mob = it.second; - LogAoeCast("Using close scan mob list"); - - for (auto &it : caster_mob->close_mobs) { - current_mob = it.second; - - if (!current_mob) { - continue; - } - - LogAoeCast("Checking against close scan mob [{}]", current_mob->GetCleanName()); - - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { - continue; - } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + if (!current_mob) { + continue; } - } else { - LogAoeCast("Using full entity mob list"); + LogAoeCast("Checking AOE against mob [{}]", current_mob->GetCleanName()); - for (auto &it : mob_list) { - current_mob = it.second; + if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { + continue; + } - LogAoeCast("Checking against full zone scan mob [{}]", current_mob->GetCleanName()); + if (current_mob == caster_mob && !affect_caster) { + continue; + } - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + continue; + } + + if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + continue; + } + + if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + continue; + } + + /** + * Check PC / NPC + * 1 = PC + * 2 = NPC + */ + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + continue; + } + + if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + continue; + } + + if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { + continue; + } + + distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); + + if (distance_to_target > distance_squared) { + continue; + } + + if (distance_to_target < min_range2) { + continue; + } + + if (is_npc && current_mob->IsNPC() && + spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); + if (is_detrimental_spell) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if ( + !(caster_mob->CheckAggro(current_mob) || + faction_value == FACTION_THREATENLY || + faction_value == FACTION_SCOWLS)) { + continue; + } + } + else { + //only affect mobs we would assist. + if (!(faction_value <= FACTION_AMIABLE)) { + continue; + } + } + } + + /** + * Finally, make sure they are within range + */ + if (is_detrimental_spell) { + if (!caster_mob->IsAttackAllowed(current_mob, true)) { + continue; + } + if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { + continue; + } + if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( + caster_mob->GetTargetRingX(), + caster_mob->GetTargetRingY(), + caster_mob->GetTargetRingZ(), + current_mob->GetSize())) { continue; } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } + else { + + /** + * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + * This does not check faction for beneficial AE buffs... only agro and attackable. + * I've tested for spells that I can find without problem, but a faction-based + * check may still be needed. Any changes here should also reflect in BardAEPulse() + */ + if (caster_mob->IsAttackAllowed(current_mob, true)) { + continue; + } + if (caster_mob->CheckAggro(current_mob)) { + continue; + } + } + + /** + * Increment hit count if max targets + */ + if (max_targets_allowed) { + target_hit_counter++; + if (target_hit_counter >= max_targets_allowed) { + break; + } + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName()); @@ -825,155 +892,6 @@ void EntityList::AESpell( } } -/** - * @param caster_mob - * @param center_mob - * @param spell_id - * @param affect_caster - * @param resist_adjust - * @param max_targets - */ -bool EntityList::AESpellFilterCriteria( - Mob *current_mob, - Mob *caster_mob, - Mob *center_mob, - uint16 spell_id, - int *max_targets, - int &max_targets_allowed, - int &target_hit_counter, - float &distance_to_target, - const glm::vec3 &cast_target_position, - bool affect_caster, - int16 resist_adjust -) { - - if (!current_mob) { - return false; - } - - bool is_npc = caster_mob->IsNPC(); - float distance = caster_mob->GetAOERange(spell_id); - float distance_squared = distance * distance; - float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; - glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; - - if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { - return false; - } - - if (current_mob == caster_mob && !affect_caster) { - return false; - } - - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { - return false; - } - - if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { - return false; - } - - if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { - return false; - } - - /** - * Check PC / NPC - * 1 = PC - * 2 = NPC - */ - if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { - return false; - } - - if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { - return false; - } - - if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { - return false; - } - - distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); - - if (distance_to_target > distance_squared) { - return false; - } - - if (distance_to_target < min_range2) { - return false; - } - - if (is_npc && current_mob->IsNPC() && - spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting - FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); - if (is_detrimental_spell) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if ( - !(caster_mob->CheckAggro(current_mob) || - faction_value == FACTION_THREATENLY || - faction_value == FACTION_SCOWLS)) { - return false; - } - } - else { - //only affect mobs we would assist. - if (!(faction_value <= FACTION_AMIABLE)) { - return false; - } - } - } - - /** - * Finally, make sure they are within range - */ - if (is_detrimental_spell) { - if (!caster_mob->IsAttackAllowed(current_mob, true)) { - return false; - } - if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { - return false; - } - if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( - caster_mob->GetTargetRingX(), - caster_mob->GetTargetRingY(), - caster_mob->GetTargetRingZ(), - current_mob->GetSize())) { - return false; - } - } - else { - - /** - * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - * This does not check faction for beneficial AE buffs... only agro and attackable. - * I've tested for spells that I can find without problem, but a faction-based - * check may still be needed. Any changes here should also reflect in BardAEPulse() - */ - if (caster_mob->IsAttackAllowed(current_mob, true)) { - return false; - } - if (caster_mob->CheckAggro(current_mob)) { - return false; - } - } - - /** - * Increment hit count if max targets - */ - if (max_targets_allowed) { - target_hit_counter++; - if (target_hit_counter >= max_targets_allowed) { - return false; - } - } - - return true; -} - void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) { Mob *curmob = nullptr; diff --git a/zone/entity.cpp b/zone/entity.cpp index b9756827e..fa9a198f1 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5030,3 +5030,18 @@ void EntityList::ReloadMerchants() { } } } + +/** + * @param mob + * @param distance + * @return + */ +std::unordered_map &EntityList::GetCloseMobList(Mob *mob, float distance) +{ + if (distance <= RuleI(Range, MobCloseScanDistance)) { + return mob->close_mobs; + } + + return mob_list; +} + diff --git a/zone/entity.h b/zone/entity.h index d251eeb57..125bd1c7a 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -412,19 +412,6 @@ public: int16 resist_adjust = 0, int *max_targets = nullptr ); - static bool AESpellFilterCriteria( - Mob *current_mob, - Mob *caster_mob, - Mob *center_mob, - uint16 spell_id, - int *max_targets, - int &max_targets_allowed, - int &target_hit_counter, - float &distance_to_target, - const glm::vec3 &cast_target_position, - bool affect_caster = true, - int16 resist_adjust = 0 - ); void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); @@ -525,6 +512,8 @@ public: inline const std::unordered_map &GetObjectList() { return object_list; } inline const std::unordered_map &GetDoorsList() { return door_list; } + std::unordered_map &GetCloseMobList(Mob *mob, float distance); + void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); uint16 GetFreeID(); @@ -537,6 +526,7 @@ public: bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); void UpdateAllTraps(bool respawn, bool repopnow = false); void ClearTrapPointers(); + protected: friend class Zone; void Depop(bool StartSpawnTimer = false); @@ -592,6 +582,7 @@ private: private: std::list bot_list; #endif + }; class BulkZoneSpawnPacket { diff --git a/zone/mob.h b/zone/mob.h index cdd6f7041..c0449af4b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -171,6 +171,7 @@ public: void DisplayInfo(Mob *mob); std::unordered_map close_mobs; + std::unordered_map& GetSmartMobList(float distance = 0); Timer mob_scan_close; Timer mob_check_moving_timer; From 233f26996b472208e2882edf485eb44ee49738de Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:15:42 -0600 Subject: [PATCH 06/17] Add GetCloseMobList into other calls --- zone/entity.cpp | 3 +++ zone/entity.h | 2 +- zone/mob.h | 1 - zone/npc.cpp | 26 ++++++++++---------------- zone/npc.h | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index fa9a198f1..e20090491 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5032,6 +5032,9 @@ void EntityList::ReloadMerchants() { } /** + * If we have a distance requested that is greater than our scanning distance + * then we return the full list + * * @param mob * @param distance * @return diff --git a/zone/entity.h b/zone/entity.h index 125bd1c7a..8e37d5342 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -512,7 +512,7 @@ public: inline const std::unordered_map &GetObjectList() { return object_list; } inline const std::unordered_map &GetDoorsList() { return door_list; } - std::unordered_map &GetCloseMobList(Mob *mob, float distance); + std::unordered_map &GetCloseMobList(Mob *mob, float distance = 0); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/mob.h b/zone/mob.h index c0449af4b..cdd6f7041 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -171,7 +171,6 @@ public: void DisplayInfo(Mob *mob); std::unordered_map close_mobs; - std::unordered_map& GetSmartMobList(float distance = 0); Timer mob_scan_close; Timer mob_check_moving_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 5325ea836..8b4996177 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3019,14 +3019,14 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) /** * @param caster * @param chance - * @param in_cast_range + * @param cast_range * @param spell_types * @return */ bool NPC::AICheckCloseBeneficialSpells( NPC *caster, uint8 chance, - float in_cast_range, + float cast_range, uint32 spell_types ) { @@ -3061,24 +3061,18 @@ bool NPC::AICheckCloseBeneficialSpells( return false; } - /** - * Cast range - */ - in_cast_range = (in_cast_range * in_cast_range); - /** * Check through close range mobs */ - for (auto & close_mob : close_mobs) { + for (auto & close_mob : entity_list.GetCloseMobList(caster, cast_range)) { Mob *mob = close_mob.second; if (mob->IsClient()) { continue; } - float distance = DistanceSquared(mob->GetPosition(), GetPosition()); - - if (distance > in_cast_range) { + float distance = Distance(mob->GetPosition(), caster->GetPosition()); + if (distance > cast_range) { continue; } @@ -3086,7 +3080,7 @@ bool NPC::AICheckCloseBeneficialSpells( "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", mob->GetCleanName(), distance, - in_cast_range, + cast_range, caster->GetCleanName() ); @@ -3134,7 +3128,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) GetID() ); - for (auto &close_mob : close_mobs) { + for (auto &close_mob : entity_list.GetCloseMobList(sender)) { Mob *mob = close_mob.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); @@ -3143,18 +3137,18 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } float assist_range = (mob->GetAssistRange() * mob->GetAssistRange()); - if (distance > assist_range) { continue; } LogAIYellForHelpDetail( - "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}]", + "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}] in_range [{}]", GetCleanName(), GetID(), mob->GetCleanName(), assist_range, - distance + distance, + (distance < assist_range) ); if (mob->CheckAggro(attacker)) { diff --git a/zone/npc.h b/zone/npc.h index 6d53d2106..84ab8b4ef 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -143,7 +143,7 @@ public: virtual bool AI_IdleCastCheck(); virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); - bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float in_cast_range, uint32 spell_types); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float cast_range, uint32 spell_types); void AIYellForHelp(Mob* sender, Mob* attacker); void LevelScale(); From e531e68b6d1b43fa7aad0a8c69178c14fcb46a3e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:22:14 -0600 Subject: [PATCH 07/17] Port AEBardPulse to use GetCloseMobList logic --- zone/effects.cpp | 91 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index ef2d1d842..95654909f 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -930,57 +930,88 @@ void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool a } } -// causes caster to hit every mob within dist range of center with -// a bard pulse of spell_id. -// NPC spells will only affect other NPCs with compatible faction -void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +/** + * Causes caster to hit every mob within dist range of center with a bard pulse of spell_id + * NPC spells will only affect other NPCs with compatible faction + * + * @param caster + * @param center + * @param spell_id + * @param affect_caster + */ +void EntityList::AEBardPulse( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + float distance = caster->GetAOERange(spell_id); + float distance_squared = distance * distance; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster->IsNPC(); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(caster, distance)) { + current_mob = it.second; - bool bad = IsDetrimentalSpell(spell_id); - bool isnpc = caster->IsNPC(); + /** + * Skip self + */ + if (current_mob == center) { + continue; + } - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - if (curmob == center) //do not affect center + if (current_mob == caster && !affect_caster) { continue; - if (curmob == caster && !affect_caster) //watch for caster too + } + + if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range continue; - if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range - continue; - if (isnpc && curmob->IsNPC()) { //check npc->npc casting - FACTION_VALUE f = curmob->GetReverseFactionCon(caster); - if (bad) { + } + + /** + * check npc->npc casting + */ + if (is_npc && current_mob->IsNPC()) { + FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster); + if (is_detrimental_spell) { //affect mobs that are on our hate list, or //which have bad faction with us - if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) + if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) { continue; - } else { + } + } + else { //only affect mobs we would assist. - if (!(f <= FACTION_AMIABLE)) + if (!(faction <= FACTION_AMIABLE)) { continue; + } } } - //finally, make sure they are within range - if (bad) { - if (!center->CheckLosFN(curmob)) + + /** + * LOS + */ + if (is_detrimental_spell) { + if (!center->CheckLosFN(current_mob)) { continue; - } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + } + } + else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // See notes in AESpell() above for more info. - if (caster->IsAttackAllowed(curmob, true)) + if (caster->IsAttackAllowed(current_mob, true)) { continue; - if (caster->CheckAggro(curmob)) + } + if (caster->CheckAggro(current_mob)) { continue; + } } - //if we get here... cast the spell. - curmob->BardPulse(spell_id, caster); + current_mob->BardPulse(spell_id, caster); } - if (caster->IsClient()) + if (caster->IsClient()) { caster->CastToClient()->CheckSongSkillIncrease(spell_id); + } } // Rampage and stuff for clients. Normal and Duration rampages From 0f9c34cf3cb6b620b3c278dbce88cc0337f7b75b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:28:48 -0600 Subject: [PATCH 08/17] Port more AE functions to use flexible GetCloseMobList call --- zone/effects.cpp | 104 +++++++++++++++++++++++++++++++---------------- zone/entity.h | 4 +- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 95654909f..0af580524 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -673,27 +673,42 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration) } } -void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) +/** + * @param taunter + * @param range + * @param bonus_hate + */ +void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate) { - if (range == 0) - range = 40; //Live AE taunt range - Hardcoded. - range = range * range; + /** + * Live AE taunt range - Hardcoded. + */ + if (range == 0) { + range = 40; + } - auto it = npc_list.begin(); - while (it != npc_list.end()) { - NPC *them = it->second; - float zdiff = taunter->GetZ() - them->GetZ(); - if (zdiff < 0) - zdiff *= -1; - if (zdiff < 10 - && taunter->IsAttackAllowed(them) - && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range) { + float range_squared = range * range; + + for (auto &it : entity_list.GetCloseMobList(taunter, range)) { + Mob *them = it.second; + + if (!them->IsNPC()) { + continue; + } + + float z_difference = taunter->GetZ() - them->GetZ(); + if (z_difference < 0) { + z_difference *= -1; + } + + if (z_difference < 10 + && taunter->IsAttackAllowed(them) + && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) { if (taunter->CheckLosFN(them)) { - taunter->Taunt(them, true,0,true,bonus_hate); + taunter->Taunt(them, true, 0, true, bonus_hate); } } - ++it; } } @@ -1014,31 +1029,48 @@ void EntityList::AEBardPulse( } } -// Rampage and stuff for clients. Normal and Duration rampages -//NPCs handle it differently in Mob::Rampage -void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { -//Dook- Will need tweaking, currently no pets or players or horses - Mob *curmob = nullptr; +/** + * Rampage - Normal and Duration rampages + * NPCs handle it differently in Mob::Rampage + * + * @param attacker + * @param distance + * @param Hand + * @param count + * @param is_from_spell + */ +void EntityList::AEAttack( + Mob *attacker, + float distance, + int Hand, + int count, + bool is_from_spell) +{ + Mob *current_mob = nullptr; + float distance_squared = distance * distance; + int hit_count = 0; - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(attacker, distance)) { + current_mob = it.second; - int hit = 0; + if (current_mob->IsNPC() + && current_mob != attacker //this is not needed unless NPCs can use this + && (attacker->IsAttackAllowed(current_mob)) + && current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */ + && (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared) + ) { - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - if (curmob->IsNPC() - && curmob != attacker //this is not needed unless NPCs can use this - &&(attacker->IsAttackAllowed(curmob)) - && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ - && (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2) - ) { - if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) - attacker->Attack(curmob, Hand, false, false, IsFromSpell); - else - attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell); - hit++; - if (count != 0 && hit >= count) + if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) { + attacker->Attack(current_mob, Hand, false, false, is_from_spell); + } + else { + attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell); + } + + hit_count++; + if (count != 0 && hit_count >= count) { return; + } } } } diff --git a/zone/entity.h b/zone/entity.h index 8e37d5342..5ce1cedda 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -398,10 +398,10 @@ public: void AEAttack( Mob *attacker, - float dist, + float distance, int Hand = EQEmu::invslot::slotPrimary, int count = 0, - bool IsFromSpell = false + bool is_from_spell = false ); void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); void AESpell( From 54f73d7420b08fe0d6dbe10b9a49ae766e2f1097 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:33:07 -0600 Subject: [PATCH 09/17] Port MassGroupBuff --- zone/effects.cpp | 61 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0af580524..c6261dae6 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -907,41 +907,64 @@ void EntityList::AESpell( } } -void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +/** + * @param caster + * @param center + * @param spell_id + * @param affect_caster + */ +void EntityList::MassGroupBuff( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + float distance = caster->GetAOERange(spell_id); + float distance_squared = distance * distance; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(caster, distance)) { + current_mob = it.second; - bool bad = IsDetrimentalSpell(spell_id); - - for (auto & it : mob_list) { - curmob = it.second; - if (curmob == center) //do not affect center - continue; - if (curmob == caster && !affect_caster) //watch for caster too - continue; - if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range + /** + * Skip center + */ + if (current_mob == center) { continue; + } - //Only npcs mgb should hit are client pets... - if (curmob->IsNPC()) { - Mob *owner = curmob->GetOwner(); + /** + * Skip self + */ + if (current_mob == caster && !affect_caster) { + continue; + } + + if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range + continue; + } + + /** + * Pets + */ + if (current_mob->IsNPC()) { + Mob *owner = current_mob->GetOwner(); if (owner) { if (!owner->IsClient()) { continue; } - } else { + } + else { continue; } } - if (bad) { + if (is_detrimental_spell) { continue; } - caster->SpellOnTarget(spell_id, curmob); + caster->SpellOnTarget(spell_id, current_mob); } } From 59b2d18a950635639397fcb1ff50fb98375f73bb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:37:01 -0600 Subject: [PATCH 10/17] Adjust pointer --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index c6261dae6..5528f784a 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -706,7 +706,7 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate) && taunter->IsAttackAllowed(them) && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) { if (taunter->CheckLosFN(them)) { - taunter->Taunt(them, true, 0, true, bonus_hate); + taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate); } } } From ccce630cb2b053856f2e11b991d6f68cb564c654 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 13:30:05 -0600 Subject: [PATCH 11/17] Adjustments [skip ci] --- zone/attack.cpp | 3 +++ zone/entity.cpp | 14 +++++++------- zone/mob.cpp | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index cd68f5d57..a48f4008e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2466,6 +2466,9 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil entity_list.UnMarkNPC(GetID()); entity_list.RemoveNPC(GetID()); + // entity_list.RemoveMobFromCloseLists(this); + close_mobs.clear(); + this->SetID(0); if (killer != 0 && emoteid != 0) diff --git a/zone/entity.cpp b/zone/entity.cpp index e20090491..019c921a8 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2567,22 +2567,25 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { + uint16 entity_id = mob->GetID() > 0 ? mob->GetID() : mob->GetInitialId(); + LogEntityManagement( "Attempting to remove mob [{}] from close lists entity_id ({})", mob->GetCleanName(), - mob->GetInitialId() + entity_id ); auto it = mob_list.begin(); while (it != mob_list.end()) { + LogEntityManagement( "Removing mob [{}] from [{}] close list entity_id ({})", mob->GetCleanName(), it->second->GetCleanName(), - mob->GetInitialId() + entity_id ); - it->second->close_mobs.erase(mob->GetInitialId()); + it->second->close_mobs.erase(entity_id); ++it; } @@ -2596,7 +2599,6 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) { float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - int list_count = 0; close_mobs.clear(); @@ -2607,16 +2609,14 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); - list_count++; } else if (it->second->GetAggroRange() >= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); - list_count++; } ++it; } - LogAIScanClose("Close List Size [{}] for mob [{}]", list_count, scanning_mob->GetCleanName()); + LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); } bool EntityList::RemoveMerc(uint16 delete_id) diff --git a/zone/mob.cpp b/zone/mob.cpp index 6a4c12969..8b8758771 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -496,6 +496,7 @@ Mob::~Mob() if (HasTempPetsActive()) { entity_list.DestroyTempPets(this); } + entity_list.UnMarkNPC(GetID()); UninitializeBuffSlots(); From 78d63165cb7061f80ff8ac9afd85f2c4179fdc4d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:10:53 -0600 Subject: [PATCH 12/17] Tweaks [skip ci] --- zone/entity.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 019c921a8..9c561ce7f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2606,6 +2606,10 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo while (it != mob_list.end()) { Mob *mob = it->second; + if (!mob->IsNPC() && !mob->IsClient()) { + continue; + } + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); From 0232a8a188bcbe92952c86f3d954f58ae7c30258 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:20:39 -0600 Subject: [PATCH 13/17] More tweaks [skip ci] --- zone/entity.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 9c561ce7f..63059bdfa 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2602,22 +2602,24 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo close_mobs.clear(); - auto it = mob_list.begin(); - while (it != mob_list.end()) { - Mob *mob = it->second; + for (auto &e : mob_list) { + auto mob = e.second; if (!mob->IsNPC() && !mob->IsClient()) { continue; } - float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); + if (mob->GetID() <= 0) { + continue; + } + + float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } - else if (it->second->GetAggroRange() >= scan_range) { + else if (mob->GetAggroRange() >= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } - ++it; } LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); From a5d41b02b79e40df29327931618ee755a06499e4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:34:50 -0600 Subject: [PATCH 14/17] More tweaks [skip ci] --- zone/client_process.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e75d53d7c..9678d31cd 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -266,7 +266,11 @@ bool Client::Process() { Mob *mob = itr.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); - if (mob->IsNPC()) { + if (mob->GetID() <= 0) { + continue; + } + + if (mob->IsNPC() || mob->IsClient()) { if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } From 67562e3e42bfc139182cf77236c97bca5003ba5a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 20:46:40 -0600 Subject: [PATCH 15/17] Unify scanning rules [skip ci] --- common/ruletypes.h | 1 - zone/client.cpp | 84 ++++++++++++++++++++--------------------- zone/client.h | 2 +- zone/client_process.cpp | 5 +-- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5aeaa3021..c6e99e5f4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -567,7 +567,6 @@ RULE_INT(Range, MobPositionUpdates, 600, "") RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") -RULE_INT(Range, ClientNPCScan, 300, "") RULE_INT(Range, MobCloseScanDistance, 600, "") RULE_CATEGORY_END() diff --git a/zone/client.cpp b/zone/client.cpp index a99b2818a..f77dc1fd4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -123,49 +123,49 @@ Client::Client(EQStreamInterface* ieqs) 0, 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), - shield_timer(500), - fishing_timer(8000), - endupkeep_timer(1000), - forget_timer(0), - autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), - client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), - 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), - charm_class_attacks_timer(3000), - charm_cast_timer(3500), - qglobal_purge_timer(30000), - TrackingTimer(2000), - RespawnFromHoverTimer(0), - merc_timer(RuleI(Mercs, UpkeepIntervalMS)), - ItemTickTimer(10000), - 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 + 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), + shield_timer(500), + fishing_timer(8000), + endupkeep_timer(1000), + forget_timer(0), + autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), + client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), + 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), + charm_class_attacks_timer(3000), + charm_cast_timer(3500), + qglobal_purge_timer(30000), + TrackingTimer(2000), + RespawnFromHoverTimer(0), + merc_timer(RuleI(Mercs, UpkeepIntervalMS)), + ItemTickTimer(10000), + 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), - 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), - npc_close_scan_timer(6000), - hp_self_update_throttle_timer(300), - hp_other_update_throttle_timer(500), - position_update_timer(10000), - tmSitting(0) + 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), + hp_self_update_throttle_timer(300), + hp_other_update_throttle_timer(500), + position_update_timer(10000), + tmSitting(0) { for (int client_filter = 0; client_filter < _FilterCount; client_filter++) diff --git a/zone/client.h b/zone/client.h index 8876e4645..4ff431520 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1523,7 +1523,7 @@ private: Timer afk_toggle_timer; Timer helm_toggle_timer; Timer aggro_meter_timer; - Timer npc_close_scan_timer; + Timer mob_close_scan_timer; Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */ Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 9678d31cd..14b2036cf 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -253,13 +253,12 @@ bool Client::Process() { /** * Scan close range mobs - * * Used in aggro checks */ - if (npc_close_scan_timer.Check()) { + if (mob_close_scan_timer.Check()) { close_mobs.clear(); - float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); + float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); auto &mob_list = entity_list.GetMobList(); for (auto itr : mob_list) { From d71afda954e3bcbb012568b32a3323ce4749ecd0 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 30 Dec 2019 20:15:33 -0600 Subject: [PATCH 16/17] Optimize cleanup QueueCloseClients [skip ci] --- zone/entity.cpp | 74 +++++++++++++++++++++++++++++++++++-------------- zone/entity.h | 2 +- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 63059bdfa..27288998b 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1583,41 +1583,73 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a } } -void EntityList::QueueCloseClients(Mob *sender, const EQApplicationPacket *app, - bool ignore_sender, float dist, Mob *SkipThisMob, bool ackreq, eqFilterType filter) +/** + * @param sender + * @param app + * @param ignore_sender + * @param distance + * @param skipped_mob + * @param is_ack_required + * @param filter + */ +void EntityList::QueueCloseClients( + Mob *sender, + const EQApplicationPacket *app, + bool ignore_sender, + float distance, + Mob *skipped_mob, + bool is_ack_required, + eqFilterType filter +) { if (sender == nullptr) { QueueClients(sender, app, ignore_sender); return; } - if (dist <= 0) - dist = 600; - float dist2 = dist * dist; //pow(dist, 2); + if (distance <= 0) { + distance = 600; + } - auto it = client_list.begin(); - while (it != client_list.end()) { - Client *ent = it->second; + float distance_squared = distance * distance; - if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) { - eqFilterMode filter2 = ent->GetFilter(filter); - if(ent->Connected() && - (filter == FilterNone - || filter2 == FilterShow - || (filter2 == FilterShowGroupOnly && (sender == ent || - (ent->GetGroup() && ent->GetGroup()->IsGroupMember(sender)))) - || (filter2 == FilterShowSelfOnly && ent == sender)) - && (DistanceSquared(ent->GetPosition(), sender->GetPosition()) <= dist2)) { - ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + for (auto &e : GetCloseMobList(sender, distance)) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + + if ((!ignore_sender || client != sender) && (client != skipped_mob)) { + + if (DistanceSquared(client->GetPosition(), sender->GetPosition()) <= distance_squared) { + continue; + } + + if (!client->Connected()) { + continue; + } + + eqFilterMode client_filter = client->GetFilter(filter); + if ( + filter == FilterNone || client_filter == FilterShow || + (client_filter == FilterShowGroupOnly && + (sender == client || (client->GetGroup() && client->GetGroup()->IsGroupMember(sender)))) || + (client_filter == FilterShowSelfOnly && client == sender) + ) { + client->QueuePacket(app, is_ack_required, Client::CLIENT_CONNECTED); } } - ++it; } } //sender can be null -void EntityList::QueueClients(Mob *sender, const EQApplicationPacket *app, - bool ignore_sender, bool ackreq) +void EntityList::QueueClients( + Mob *sender, const EQApplicationPacket *app, + bool ignore_sender, bool ackreq +) { auto it = client_list.begin(); while (it != client_list.end()) { diff --git a/zone/entity.h b/zone/entity.h index 5ce1cedda..ea97446a6 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -384,7 +384,7 @@ public: void RemoveFromXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); - void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float dist=200, Mob* SkipThisMob = 0, bool ackreq = true,eqFilterType filter=FilterNone); + void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0); void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); From 9a08b1be9303263e7cb72da7148c7029ed64b1a1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 30 Dec 2019 20:18:44 -0600 Subject: [PATCH 17/17] Flip [skip ci] --- zone/entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 27288998b..adb29011f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1624,7 +1624,7 @@ void EntityList::QueueCloseClients( if ((!ignore_sender || client != sender) && (client != skipped_mob)) { - if (DistanceSquared(client->GetPosition(), sender->GetPosition()) <= distance_squared) { + if (DistanceSquared(client->GetPosition(), sender->GetPosition()) >= distance_squared) { continue; }