From a52ab7ae48b46a2bbf74951024c162b7e2a93cb1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jul 2015 22:38:32 -0400 Subject: [PATCH] Rework spell aggro based on http://www.eqemulator.org/forums/showthread.php?t=39819 --- changelog.txt | 5 +- zone/aggro.cpp | 227 +++++++++++++++++++++++----------------------- zone/bot.cpp | 20 ++-- zone/bot.h | 4 +- zone/entity.cpp | 58 +++--------- zone/entity.h | 4 +- zone/lua_mob.cpp | 8 +- zone/mob.h | 4 +- zone/perl_mob.cpp | 6 +- zone/spells.cpp | 36 ++++---- 10 files changed, 169 insertions(+), 203 deletions(-) diff --git a/changelog.txt b/changelog.txt index 85428ca82..68ef9c2b1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 07/16/2013 == +mackal: Rework spell aggro based on http://www.eqemulator.org/forums/showthread.php?t=39819 + == 07/15/2015 == -Hateborne: Added optional ability to enforce task level requirements in perl and lua via an added, optional parameter to $client->AssignTask and quest::assigntask. +Hateborne: Added optional ability to enforce task level requirements in perl and lua via an added, optional parameter to $client->AssignTask and quest::assigntask. Use cases: quest::assigntask(703); # this still assigns the task as normal, no functional change quest::assigntask(703, 1); # this will assign the task, provided the character meets the db-stored level requirements diff --git a/zone/aggro.cpp b/zone/aggro.cpp index e983b8ea9..75e3434b0 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -44,12 +44,8 @@ void EntityList::CheckClientAggro(Client *around) if (mob->IsClient()) //also ensures that mob != around continue; - if (mob->CheckWillAggro(around)) { - if (mob->IsEngaged()) - mob->AddToHateList(around); - else - mob->AddToHateList(around, mob->GetLevel()); - } + if (mob->CheckWillAggro(around) && !mob->CheckAggro(around)) + mob->AddToHateList(around, 100); } } @@ -343,7 +339,7 @@ bool Mob::CheckWillAggro(Mob *mob) { { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { - Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); + Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } @@ -468,8 +464,8 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", - sender->GetName(), attacker->GetName(), mob->GetName(), - attacker->GetName(), DistanceSquared(mob->GetPosition(), + sender->GetName(), attacker->GetName(), mob->GetName(), + attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 1, 0, false); @@ -880,11 +876,11 @@ bool Mob::CombatRange(Mob* other) float _DistNoRoot = DistanceSquared(m_Position, other->GetPosition()); if (GetSpecialAbility(NPC_CHASE_DISTANCE)){ - + bool DoLoSCheck = true; float max_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0)); float min_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); - + if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2)) DoLoSCheck = false; //Ignore line of sight check @@ -897,10 +893,10 @@ bool Mob::CombatRange(Mob* other) min_dist = size_mod; //Default to melee range else min_dist = min_dist * min_dist; - + if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist)) - SetPseudoRoot(true); - else + SetPseudoRoot(true); + else SetPseudoRoot(false); } @@ -919,7 +915,7 @@ bool Mob::CheckLosFN(Mob* other) { Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize()); SetLastLosState(Result); - + return Result; } @@ -954,11 +950,24 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) { } //offensive spell aggro -int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) +int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) { int32 AggroAmount = 0; int32 nonModifiedAggro = 0; uint16 slevel = GetLevel(); + bool add_default = false; + bool stun_proc = false; + bool dispel = false; + bool on_hatelist = target ? target->CheckAggro(this) : false; + + int32 target_hp = target ? target->GetMaxHP() : 18000; // default to max + int32 default_aggro = 0; + if (target_hp >= 18000) // max + default_aggro = 1200; + else if (target_hp < 390) // min, 390 is the first number with int division that is 26 + default_aggro = 25; + else + default_aggro = target_hp / 15; for (int o = 0; o < EFFECT_COUNT; o++) { switch (spells[spell_id].effectid[o]) { @@ -972,7 +981,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) case SE_MovementSpeed: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) - AggroAmount += (2 + ((slevel * slevel) / 8)); + add_default = true; break; } case SE_AttackSpeed: @@ -980,60 +989,35 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) case SE_AttackSpeed3: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 100) - AggroAmount += (5 + ((slevel * slevel) / 5)); + add_default = true; break; } - case SE_Stun: { - int val = (5 + ((slevel * slevel) / 6)); - if (isproc && RuleI(Aggro,MaxStunProcAggro) > -1 && (val > RuleI(Aggro,MaxStunProcAggro))) - val = RuleI(Aggro,MaxStunProcAggro); - AggroAmount += val; + case SE_Stun: + add_default = true; + stun_proc = isproc; break; - } - case SE_Blind: { - AggroAmount += (5 + ((slevel * slevel) / 6)); + case SE_Blind: + case SE_Mez: + case SE_Charm: + case SE_Fear: + add_default = true; break; - } - case SE_Mez: { - AggroAmount += (5 + ((slevel * slevel) / 5)); + case SE_Root: + AggroAmount += 10; break; - } - case SE_Charm: { - AggroAmount += (5 + ((slevel * slevel) / 5)); - break; - } - case SE_Root: { - AggroAmount += (2 + ((slevel * slevel) / 8)); - break; - } - case SE_Fear: { - AggroAmount += (5 + ((slevel * slevel) / 6)); - break; - } case SE_ATK: case SE_ACv2: case SE_ArmorClass: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) - AggroAmount -= val * 2; + add_default = true; break; } case SE_ResistMagic: case SE_ResistFire: case SE_ResistCold: case SE_ResistPoison: - case SE_ResistDisease: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); - if (val < 0) - AggroAmount -= val * 3; - break; - } - case SE_ResistAll: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); - if (val < 0) - AggroAmount -= val * 6; - break; - } + case SE_ResistDisease: case SE_STR: case SE_STA: case SE_DEX: @@ -1043,32 +1027,31 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) case SE_CHA: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) - AggroAmount -= val * 2; + AggroAmount += 10; + break; + } + case SE_ResistAll: { + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + if (val < 0) + AggroAmount += 50; break; } case SE_AllStats: { int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); if (val < 0) - AggroAmount -= val * 6; + AggroAmount += 70; break; } - case SE_BardAEDot: { - AggroAmount += slevel * 2; + case SE_BardAEDot: + AggroAmount += 10; break; - } - case SE_SpinTarget: { - AggroAmount += (5 + ((slevel * slevel) / 5)); - break; - } + case SE_SpinTarget: case SE_Amnesia: - case SE_Silence: { - AggroAmount += slevel * 2; + case SE_Silence: + case SE_Destroy: + add_default = true; break; - } - case SE_Destroy: { - AggroAmount += slevel * 2; - break; - } + // unsure -- leave them this for now case SE_Harmony: case SE_CastingLevel: case SE_MeleeMitigation: @@ -1091,6 +1074,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) AggroAmount += slevel * 2; break; } + // unsure -- leave them this for now case SE_CurrentMana: case SE_ManaRegen_v2: case SE_ManaPool: @@ -1101,94 +1085,105 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) break; } case SE_CancelMagic: - case SE_DispelDetrimental: { - AggroAmount += slevel; + case SE_DispelDetrimental: + dispel = true; break; - } case SE_ReduceHate: - case SE_InstantHate: { + case SE_InstantHate: nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); break; - } } } - if (IsAEDurationSpell(spell_id)) - AggroAmount /= 2; + if (add_default) { + if (stun_proc && RuleI(Aggro, MaxStunProcAggro) > -1 && (default_aggro > RuleI(Aggro, MaxStunProcAggro))) + AggroAmount += RuleI(Aggro, MaxStunProcAggro); + else if (IsBardSong(spell_id) && default_aggro > 40) + AggroAmount += 40; // bard songs seem to cap to 40 for most of their spells? + else + AggroAmount += default_aggro; + } - if (spells[spell_id].HateAdded > 0) + if (dispel && target && target->GetHateAmount(this) < 100) + AggroAmount += 50; + + if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash) AggroAmount = spells[spell_id].HateAdded; - if (IsBardSong(spell_id)) - AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; if (AggroAmount > 0) { - int HateMod = RuleI(Aggro, SpellAggroMod); - HateMod += GetFocusEffect(focusSpellHateMod, spell_id); AggroAmount = (AggroAmount * HateMod) / 100; - - //made up number probably scales a bit differently on live but it seems like it will be close enough - //every time you cast on live you get a certain amount of "this is a spell" aggro - //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected - //by hate modifiers either. - //AggroAmount += (slevel*slevel/72); - // Saved so I can reimplement it; - // this should only be on the spell to aggro the npc not every spell - } + // initial aggro gets a bonus 100 + if (!dispel && spells[spell_id].HateAdded == 0 && !on_hatelist) + AggroAmount += 100; + return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro; } //healing and buffing aggro -int32 Mob::CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible) +int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible) { int32 AggroAmount = 0; + auto target_level = target ? target->GetLevel() : GetLevel(); + bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default for (int o = 0; o < EFFECT_COUNT; o++) { switch (spells[spell_id].effectid[o]) { - case SE_CurrentHP: { - AggroAmount += IsBuffSpell(spell_id) ? spells[spell_id].mana / 4 : spells[spell_id].mana; + case SE_CurrentHP: { + if (heal_possible == 0) { + AggroAmount += 1; break; } - case SE_Rune: { - AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], spells[spell_id].base[0], spells[spell_id].max[o], GetLevel(), spell_id) * 2; - break; - } - case SE_HealOverTime: { - AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id); - break; - } - default: { - break; + // hate based on base healing power of the spell + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], + spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id); + if (val > 0) { + if (heal_possible < val) + val = heal_possible; // capped to amount healed + val = 2 * val / 3; // 3:2 ratio + + if (target_level > 50 && val > 1500) + val = 1500; // target 51+ seems ~1500 + else if (target_level <= 50 && val > 800) + val = 800; // per live patch notes, capped to 800 } + AggroAmount += std::max(val, 1); + break; + } + case SE_Rune: + AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], + spells[spell_id].base[0], spells[spell_id].max[o], GetLevel(), spell_id) * 2; + ignore_default_buff = true; + break; + case SE_HealOverTime: + AggroAmount += 10; + ignore_default_buff = true; + break; + default: + break; } } - if (IsBardSong(spell_id)) - AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; + if (!ignore_default_buff && IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) + AggroAmount = 9; + if (AggroAmount > 0) { int HateMod = RuleI(Aggro, SpellAggroMod); - HateMod += GetFocusEffect(focusSpellHateMod, spell_id); //Live AA - Spell casting subtlety HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; AggroAmount = (AggroAmount * HateMod) / 100; - - //made up number probably scales a bit differently on live but it seems like it will be close enough - //every time you cast on live you get a certain amount of "this is a spell" aggro - //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected - //by hate modifiers either. - //AggroAmount += (slevel*slevel/72); // Moved Below } if (AggroAmount < 0) @@ -1249,7 +1244,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { return true; float resist_check = 0; - + if(IsCharmSpell(spell_id)) { if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration. @@ -1265,7 +1260,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true); //2: The mob makes a resistance check against the charm - if (resist_check == 100) + if (resist_check == 100) return true; else diff --git a/zone/bot.cpp b/zone/bot.cpp index 94b7d9e90..479fd3030 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6220,14 +6220,14 @@ bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { } int32 Bot::CheckAggroAmount(uint16 spellid) { - int32 AggroAmount = Mob::CheckAggroAmount(spellid); + int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr); int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } -int32 Bot::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { - int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, heal_possible); +int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) { + int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible); int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; @@ -9081,7 +9081,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } } - + uint32 botId = GetBotIDByBotName(std::string(sep->arg[2])); if(GetBotOwnerCharacterID(botId, &TempErrorMessage) != c->CharacterID()) { c->Message(0, "You can't spawn a bot that you don't own."); @@ -9106,7 +9106,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "You can't summon bots while you are engaged."); return; } - + if(g && g->members[i] && g->members[i]->qglobal) return; } @@ -9264,12 +9264,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } - + if(item == nullptr) { c->Message(15, "I need something for my %s (Item %i)", equipped[i], (i == 22 ? 9999 : i)); continue; } - + if((i == MainPrimary) && ((item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HPiercing))) { is2Hweapon = true; } @@ -9284,7 +9284,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } else c->Message(15, "You must target a bot first."); - + return; } @@ -9301,7 +9301,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(15, "A bot has 22 slots in its inventory, please choose a slot between 0 and 21 or 9999."); return; } - + const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" }; @@ -9336,7 +9336,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Bot *gearbot = c->GetTarget()->CastToBot(); if((slotId == MainRange)||(slotId == MainAmmo)||(slotId == MainPrimary)||(slotId == MainSecondary)) gearbot->SetBotArcher(false); - + gearbot->RemoveBotItemBySlot(slotId, &TempErrorMessage); if(!TempErrorMessage.empty()) { diff --git a/zone/bot.h b/zone/bot.h index 9a2f2a308..c78abdd8f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -200,7 +200,7 @@ public: virtual Mob* GetOwner(); virtual Mob* GetOwnerOrSelf(); inline virtual bool HasOwner() { return (GetBotOwner() ? true : false); } - virtual int32 CheckHealAggroAmount(uint16 spellid, uint32 heal_possible = 0); + virtual int32 CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible = 0); virtual int32 CalcMaxMana(); virtual void SetAttackTimer(); uint32 GetClassHPFactor(); @@ -547,7 +547,7 @@ public: void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; } void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;} void SetShowHelm(bool showhelm) { _showhelm = showhelm; } - + std::string CreateSayLink(Client* botOwner, const char* message, const char* name); // Class Destructors diff --git a/zone/entity.cpp b/zone/entity.cpp index 8749b102c..e9dd76c5e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3068,56 +3068,24 @@ bool EntityList::Fighting(Mob *targ) return false; } -void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam) +void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate) { - NPC *cur = nullptr; - uint16 count = 0; - std::list npc_sub_list; - auto it = npc_list.begin(); - while (it != npc_list.end()) { - cur = it->second; + if (hate == 0) + return; - if (!cur->CheckAggro(target)) { - ++it; + for (auto &e : npc_list) { + auto &npc = e.second; + if (!npc->CheckAggro(target) || npc->IsFeared()) continue; - } - if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) { - npc_sub_list.push_back(cur); - ++count; - } - ++it; - } + if (zone->random.Roll(15)) // witness check -- place holder + // This is either a level check (con color check?) or a stat roll + continue; - if (thedam > 1) { - if (count > 0) - thedam /= count; - - if (thedam < 1) - thedam = 1; - } - - cur = nullptr; - auto sit = npc_sub_list.begin(); - while (sit != npc_sub_list.end()) { - cur = *sit; - - if (cur->IsPet()) { - if (caster) { - if (cur->CheckAggro(caster)) { - cur->AddToHateList(caster, thedam); - } - } - } else { - if (caster) { - if (cur->CheckAggro(caster)) { - cur->AddToHateList(caster, thedam); - } else { - cur->AddToHateList(caster, thedam * 0.33); - } - } - } - ++sit; + if ((npc->IsMezzed() || npc->IsStunned()) && hate > 4) // patch notes say stunned/mezzed NPCs get a fraction of the hate + npc->AddToHateList(caster, hate / 4); // made up number + else + npc->AddToHateList(caster, hate); } } diff --git a/zone/entity.h b/zone/entity.h index 1adf23783..0b9521179 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -41,7 +41,7 @@ class EntityList; class Group; class Merc; class Mob; -class NPC; +class NPC; class Object; class Petition; class Raid; @@ -333,7 +333,7 @@ public: void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos); Trap* FindNearbyTrap(Mob* searcher, float max_dist); - void AddHealAggro(Mob* target, Mob* caster, uint16 thedam); + void AddHealAggro(Mob* target, Mob* caster, uint16 hate); Mob* FindDefenseNPC(uint32 npcid); void OpenDoorsNear(NPC* opener); void UpdateWho(bool iSendFullUpdate = false); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 431f18ffe..2f0712ec0 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1172,22 +1172,22 @@ bool Lua_Mob::Charmed() { int Lua_Mob::CheckAggroAmount(int spell_id) { Lua_Safe_Call_Int(); - return self->CheckAggroAmount(spell_id); + return self->CheckAggroAmount(spell_id, nullptr); } int Lua_Mob::CheckAggroAmount(int spell_id, bool is_proc) { Lua_Safe_Call_Int(); - return self->CheckAggroAmount(spell_id, is_proc); + return self->CheckAggroAmount(spell_id, nullptr, is_proc); } int Lua_Mob::CheckHealAggroAmount(int spell_id) { Lua_Safe_Call_Int(); - return self->CheckHealAggroAmount(spell_id); + return self->CheckHealAggroAmount(spell_id, nullptr); } int Lua_Mob::CheckHealAggroAmount(int spell_id, uint32 heal_possible) { Lua_Safe_Call_Int(); - return self->CheckHealAggroAmount(spell_id, heal_possible); + return self->CheckHealAggroAmount(spell_id, nullptr, heal_possible); } int Lua_Mob::GetAA(int id) { diff --git a/zone/mob.h b/zone/mob.h index fcabf93fd..5cca7d526 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -879,8 +879,8 @@ public: bool Charmed() const { return charmed; } static uint32 GetLevelHP(uint8 tlevel); uint32 GetZoneID() const; //for perl - virtual int32 CheckAggroAmount(uint16 spell_id, bool isproc = false); - virtual int32 CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible = 0); + virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false); + virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0); uint32 GetInstrumentMod(uint16 spell_id) const; int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index dcd999a3e..323d22b36 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6316,7 +6316,7 @@ XS(XS_Mob_CheckAggroAmount) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - RETVAL = THIS->CheckAggroAmount(spellid); + RETVAL = THIS->CheckAggroAmount(spellid, nullptr); XSprePUSH; PUSHu((UV)RETVAL); } XSRETURN(1); @@ -6349,7 +6349,7 @@ XS(XS_Mob_CheckHealAggroAmount) possible = (uint32)SvUV(ST(2)); } - RETVAL = THIS->CheckHealAggroAmount(spellid, possible); + RETVAL = THIS->CheckHealAggroAmount(spellid, nullptr, possible); XSprePUSH; PUSHu((UV)RETVAL); } XSRETURN(1); @@ -7405,7 +7405,7 @@ XS(XS_Mob_GetGlobal) RETVAL = ret_val.c_str(); sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; } - XSRETURN(1); + XSRETURN(1); } XS(XS_Mob_SetGlobal); diff --git a/zone/spells.cpp b/zone/spells.cpp index 0a4dc29b3..1eb84ad9a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3704,7 +3704,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } if (spelltar->IsAIControlled()) { - int32 aggro = CheckAggroAmount(spell_id); + int32 aggro = CheckAggroAmount(spell_id, spelltar); if (aggro > 0) { if (!IsHarmonySpell(spell_id)) spelltar->AddToHateList(this, aggro); @@ -3733,20 +3733,20 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r spelltar->DamageShield(this, true); if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { - int32 aggro_amount = CheckAggroAmount(spell_id, isproc); - Log.Out(Logs::Detail, Logs::Spells, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount); - if(aggro_amount > 0) - spelltar->AddToHateList(this, aggro_amount); else{ + int32 aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc); + Log.Out(Logs::Detail, Logs::Spells, "Spell %d cast on %s generated %d hate", spell_id, + spelltar->GetName(), aggro_amount); + if (aggro_amount > 0) { + spelltar->AddToHateList(this, aggro_amount); + } else { int32 newhate = spelltar->GetHateAmount(this) + aggro_amount; - if (newhate < 1) { - spelltar->SetHateAmountOnEnt(this,1); - } else { - spelltar->SetHateAmountOnEnt(this,newhate); - } + spelltar->SetHateAmountOnEnt(this, std::max(newhate, 1)); } + } else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) { + entity_list.AddHealAggro( + spelltar, this, + CheckHealAggroAmount(spell_id, spelltar, (spelltar->GetMaxHP() - spelltar->GetHP()))); } - else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) - entity_list.AddHealAggro(spelltar, this, CheckHealAggroAmount(spell_id, (spelltar->GetMaxHP() - spelltar->GetHP()))); // make sure spelltar is high enough level for the buff if(RuleB(Spells, BuffLevelRestrictions) && !spelltar->CheckSpellLevelRestriction(spell_id)) @@ -4059,7 +4059,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if(GetSpecialAbility(UNMEZABLE)) { Log.Out(Logs::Detail, Logs::Spells, "We are immune to Mez spells."); caster->Message_StringID(MT_Shout, CANNOT_MEZ); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { AddToHateList(caster, aggro); } else { @@ -4086,7 +4086,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { Log.Out(Logs::Detail, Logs::Spells, "We are immune to Slow spells."); caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { AddToHateList(caster, aggro); } else { @@ -4102,7 +4102,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if(GetSpecialAbility(UNFEARABLE)) { Log.Out(Logs::Detail, Logs::Spells, "We are immune to Fear spells."); caster->Message_StringID(MT_Shout, IMMUNE_FEAR); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { AddToHateList(caster, aggro); } else { @@ -4119,7 +4119,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { Log.Out(Logs::Detail, Logs::Spells, "Level is %d, cannot be feared by this spell.", GetLevel()); caster->Message_StringID(MT_Shout, FEAR_TOO_HIGH); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if (aggro > 0) { AddToHateList(caster, aggro); } else { @@ -4142,7 +4142,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { Log.Out(Logs::Detail, Logs::Spells, "We are immune to Charm spells."); caster->Message_StringID(MT_Shout, CANNOT_CHARM); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { AddToHateList(caster, aggro); } else { @@ -4182,7 +4182,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if(GetSpecialAbility(UNSNAREABLE)) { Log.Out(Logs::Detail, Logs::Spells, "We are immune to Snare spells."); caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT); - int32 aggro = caster->CheckAggroAmount(spell_id); + int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { AddToHateList(caster, aggro); } else {