From 03592e58f903d2a5066c3710c46c90fe485e3061 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 18 Dec 2015 17:41:57 -0500 Subject: [PATCH] Port EQMacEmu's Assist Aggro code This code also allows you to toggle on Tick Pulling (Aggro:AllowTickPulling) which was a pulling technique (exploit) fixed sometime in 2006 This code also implements assist caps to cut down on trains (5 by default) Unsure if live what this number is (it exists) or if it's a per NPC basis An NPC with Assist Aggro will not call for help, only NPCs with Primary Aggro will --- common/ruletypes.h | 3 +++ zone/aggro.cpp | 10 ++++++++++ zone/attack.cpp | 5 +++++ zone/mob.cpp | 5 +++++ zone/mob.h | 14 ++++++++++++++ zone/mob_ai.cpp | 6 +++++- zone/npc.cpp | 12 +++++++++++- 7 files changed, 53 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 91d3479be..83ef9aff3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -459,6 +459,8 @@ RULE_BOOL(Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arr RULE_BOOL(Combat, MeleePush, true) // enable melee push RULE_INT(Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad RULE_BOOL(Combat, UseLiveCombatRounds, true) // turn this false if you don't want to worry about fixing up combat rounds for NPCs +RULE_INT(Combat, NPCAssistCap, 5) // Maxiumium number of NPCs that will assist another NPC at once +RULE_INT(Combat, NPCAssistCapTimer, 6000) // Time in milliseconds a NPC will take to clear assist aggro cap space RULE_CATEGORY_END() RULE_CATEGORY(NPC) @@ -494,6 +496,7 @@ RULE_INT(Aggro, PetSpellAggroMod, 10) RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate generate this much hate on a Tunnel Vision mob RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add. RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference. +RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live RULE_CATEGORY_END() RULE_CATEGORY(TaskSystem) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 5febe5767..a2ee52357 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -431,11 +431,20 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { 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; @@ -477,6 +486,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 25, 0, false); + sender->AddAssistCap(); } } } diff --git a/zone/attack.cpp b/zone/attack.cpp index 2752752aa..6b2534a43 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2422,6 +2422,11 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b hate = 1; } + if (iYellForHelp) + SetPrimaryAggro(true); + else + SetAssistAggro(true); + bool wasengaged = IsEngaged(); Mob* owner = other->GetOwner(); Mob* mypet = this->GetPet(); diff --git a/zone/mob.cpp b/zone/mob.cpp index eccf04a76..6e778f004 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -433,6 +433,9 @@ Mob::Mob(const char* in_name, emoteid = 0; endur_upkeep = false; + PrimaryAggro = false; + AssistAggro = false; + npc_assist_cap = 0; } Mob::~Mob() @@ -2693,6 +2696,8 @@ bool Mob::RemoveFromHateList(Mob* mob) { AI_Event_NoLongerEngaged(); zone->DelAggroMob(); + if (IsNPC() && !RuleB(Aggro, AllowTickPulling)) + ResetAssistCap(); } } if(GetTarget() == mob) diff --git a/zone/mob.h b/zone/mob.h index 2ca027023..d02e0f089 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -504,6 +504,10 @@ public: Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();} Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();} bool IsEngaged() { return(!hate_list.IsHateListEmpty()); } + bool HasPrimaryAggro() { return PrimaryAggro; } + bool HasAssistAggro() { return AssistAggro; } + void SetPrimaryAggro(bool value) { PrimaryAggro = value; if (value) AssistAggro = false; } + void SetAssistAggro(bool value) { AssistAggro = value; if (PrimaryAggro) AssistAggro = false; } bool HateSummon(); void FaceTarget(Mob* MobToFace = 0); void SetHeading(float iHeading) { if(m_Position.w != iHeading) { pLastChange = Timer::GetCurrentTime(); @@ -993,6 +997,11 @@ public: void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon); bool CheckAATimer(int timer); + int NPCAssistCap() { return npc_assist_cap; } + void AddAssistCap() { ++npc_assist_cap; } + void DelAssistCap() { --npc_assist_cap; } + void ResetAssistCap() { npc_assist_cap = 0; } + protected: void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, int special = 0); static uint16 GetProcID(uint16 spell_id, uint8 effect_index); @@ -1293,6 +1302,11 @@ protected: glm::vec4 m_CurrentWayPoint; int cur_wp_pause; + bool PrimaryAggro; + bool AssistAggro; + int npc_assist_cap; + Timer assist_cap_timer; // clear assist cap so more nearby mobs can be called for help + int patrol; glm::vec3 m_FearWalkTarget; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 25ed72441..79c4493ea 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1735,8 +1735,10 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { if (iYellForHelp) { if(IsPet()) { GetOwner()->AI_Event_Engaged(attacker, iYellForHelp); - } else { + } 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)); } } @@ -1787,6 +1789,8 @@ void Mob::AI_Event_NoLongerEngaged() { if(IsNPC()) { + SetPrimaryAggro(false); + SetAssistAggro(false); if(CastToNPC()->GetCombatEvent() && GetHP() > 0) { if(entity_list.GetNPCByID(this->GetID())) diff --git a/zone/npc.cpp b/zone/npc.cpp index ebdd8a79d..fd7f6880c 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -712,8 +712,18 @@ bool NPC::Process() } //Handle assists... - if(assist_timer.Check() && IsEngaged() && !Charmed()) { + if (assist_cap_timer.Check()) { + if (NPCAssistCap() > 0) + DelAssistCap(); + else + assist_cap_timer.Disable(); + } + + if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() && + NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { entity_list.AIYellForHelp(this, GetTarget()); + if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) + assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); } if(qGlobals)