diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 2acb30b9b..5b3af0190 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -42,27 +42,26 @@ HateList::~HateList() { } -void HateList::WipeHateList() -{ +void HateList::WipeHateList(bool npc_only) { auto iterator = list.begin(); + while (iterator != list.end()) { + Mob *m = (*iterator)->entity_on_hatelist; + if (m && (m->IsClient() || (m->IsPet() && m->GetOwner()->IsClient())) && npc_only) { + iterator++; + } else { + if (m) { + if (parse->HasQuestSub(hate_owner->GetNPCTypeID(), EVENT_HATE_LIST)) { + parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), m, "0", 0); + } - while (iterator != list.end()) - { - Mob* m = (*iterator)->entity_on_hatelist; - if (m) - { - if (parse->HasQuestSub(hate_owner->GetNPCTypeID(), EVENT_HATE_LIST)) { - parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), m, "0", 0); - } - - if (m->IsClient()) { - m->CastToClient()->DecrementAggroCount(); - m->CastToClient()->RemoveXTarget(hate_owner, true); + if (m->IsClient()) { + m->CastToClient()->DecrementAggroCount(); + m->CastToClient()->RemoveXTarget(hate_owner, true); + } + delete (*iterator); + iterator = list.erase(iterator); } } - delete (*iterator); - iterator = list.erase(iterator); - } } diff --git a/zone/hate_list.h b/zone/hate_list.h index ec1f7d634..531671168 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -88,7 +88,7 @@ public: void SetHateAmountOnEnt(Mob *other, int64 in_hate, uint64 in_damage); void SetHateOwner(Mob *new_hate_owner) { hate_owner = new_hate_owner; } void SpellCast(Mob *caster, uint32 spell_id, float range, Mob *ae_center = nullptr); - void WipeHateList(); + void WipeHateList(bool npc_only = false); void RemoveStaleEntries(int time_ms, float dist); diff --git a/zone/mob.cpp b/zone/mob.cpp index 137b24975..92468dcbb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4888,16 +4888,14 @@ bool Mob::RemoveFromHateList(Mob* mob) return bFound; } -void Mob::WipeHateList() -{ - if(IsEngaged()) - { - hate_list.WipeHateList(); - AI_Event_NoLongerEngaged(); - } - else - { - hate_list.WipeHateList(); +void Mob::WipeHateList(bool npc_only) { + if (IsEngaged()) { + hate_list.WipeHateList(npc_only); + if (hate_list.IsHateListEmpty()) { + AI_Event_NoLongerEngaged(); + } + } else { + hate_list.WipeHateList(npc_only); } } diff --git a/zone/mob.h b/zone/mob.h index 388553876..8960c0491 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -767,7 +767,7 @@ public: void SetAssistAggro(bool value) { AssistAggro = value; if (PrimaryAggro) AssistAggro = false; } bool HateSummon(); void FaceTarget(Mob* mob_to_face = 0); - void WipeHateList(); + void WipeHateList(bool npc_only = false); void AddFeignMemory(Mob* attacker); void RemoveFromFeignMemory(Mob* attacker); void ClearFeignMemory(); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index bae828059..5995ba6a7 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1062,6 +1062,10 @@ void Mob::AI_Process() { SetTarget(hate_list.GetClosestEntOnHateList(this)); else { if (AI_target_check_timer->Check()) { + if (IsNPC() && (!IsPet() || (HasOwner() && GetOwner()->IsNPC())) && !CastToNPC()->WillAggroNPCs()) { + WipeHateList(true); // wipe NPCs from hate list to prevent faction war + } + if (IsFocused()) { if (!target) { SetTarget(hate_list.GetEntWithMostHateOnList(this)); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 60bf17d2d..44ab67c46 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3885,11 +3885,12 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) if (IsDetrimentalSpell(buff.spellid)) { if (caster->IsClient()) { - if (!caster->CastToClient()->GetFeigned()) + if (!caster->CastToClient()->GetFeigned()) { AddToHateList(caster, -effect_value); - } else if (!IsClient()) // Allow NPC's to generate hate if casted on other - // NPC's. + } + } else if (!IsClient()) { // Allow NPC's to generate hate if casted on other NPC's AddToHateList(caster, -effect_value); + } } effect_value = caster->GetActDoTDamage(buff.spellid, effect_value, this); @@ -3990,6 +3991,12 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_Charm: { if (!caster || !PassCharismaCheck(caster, buff.spellid)) { BuffFadeByEffect(SE_Charm); + + // Remove from hate list of any NPC's hate list and remove all NPCs this hate list + if (IsNPC()) { + entity_list.RemoveFromHateLists(this); + WipeHateList(true); + } } break; diff --git a/zone/spells.cpp b/zone/spells.cpp index 115e843b5..0e30adf9f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4244,41 +4244,39 @@ bool Mob::SpellOnTarget( spelltar->DamageShield(this, true); } - if ( - spelltar->IsAIControlled() && - IsDetrimentalSpell(spell_id) && - !IsHarmonySpell(spell_id) - ) { - auto aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc); - LogSpells("Spell [{}] cast on [{}] generated [{}] hate", spell_id, - spelltar->GetName(), aggro_amount); - if (aggro_amount > 0) { - spelltar->AddToHateList(this, aggro_amount); - } else { - int64 newhate = spelltar->GetHateAmount(this) + aggro_amount; - spelltar->SetHateAmountOnEnt(this, std::max(newhate, static_cast(1))); - } - } else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) { - if (this != spelltar && IsClient()){ - if (spelltar->IsClient()) { - CastToClient()->UpdateRestTimer(spelltar->CastToClient()->GetRestTimer()); - } else if (spelltar->IsPet()) { - auto* owner = spelltar->GetOwner(); - if (owner && owner != this && owner->IsClient()) { - CastToClient()->UpdateRestTimer(owner->CastToClient()->GetRestTimer()); + if (!(spelltar->IsNPC() && IsNPC() && !(IsPet() && GetOwner()->IsClient()))) { + if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { + auto aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc); + LogSpellsDetail("Spell {} cast on {} generated {} hate", spell_id, + spelltar->GetName(), aggro_amount); + if (aggro_amount > 0) { + spelltar->AddToHateList(this, aggro_amount); + } else { + int64 newhate = spelltar->GetHateAmount(this) + aggro_amount; + spelltar->SetHateAmountOnEnt(this, std::max(newhate, static_cast(1))); + } + } else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) { + if (this != spelltar && IsClient()){ + if (spelltar->IsClient()) { + CastToClient()->UpdateRestTimer(spelltar->CastToClient()->GetRestTimer()); + } else if (spelltar->IsPet()) { + auto* owner = spelltar->GetOwner(); + if (owner && owner != this && owner->IsClient()) { + CastToClient()->UpdateRestTimer(owner->CastToClient()->GetRestTimer()); + } } } - } - entity_list.AddHealAggro( - spelltar, - this, - CheckHealAggroAmount( - spell_id, + entity_list.AddHealAggro( spelltar, - (spelltar->GetMaxHP() - spelltar->GetHP()) - ) - ); + this, + CheckHealAggroAmount( + spell_id, + spelltar, + (spelltar->GetMaxHP() - spelltar->GetHP()) + ) + ); + } } // make sure spelltar is high enough level for the buff