diff --git a/zone/attack.cpp b/zone/attack.cpp index 4f919a8fe..10cec2877 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3068,11 +3068,11 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo else SetAssistAggro(true); - bool wasengaged = IsEngaged(); + bool was_engaged = IsEngaged(); Mob* owner = other->GetOwner(); - Mob* mypet = GetPet(); - Mob* myowner = GetOwner(); - Mob* targetmob = GetTarget(); + Mob* my_pet = GetPet(); + Mob* my_owner = GetOwner(); + Mob* target_mob = GetTarget(); bool on_hatelist = CheckAggro(other); AddRampage(other); @@ -3101,7 +3101,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo if (IsPet()) { if ((IsGHeld() || (IsHeld() && IsFocused())) && !on_hatelist) // we want them to be able to climb the hate list return; - if ((IsHeld() || IsPetStop() || IsPetRegroup()) && !wasengaged) // not 100% sure on stop/regroup kind of hard to test, but regroup is like "classic hold" + if ((IsHeld() || IsPetStop() || IsPetRegroup()) && !was_engaged) // not 100% sure on stop/regroup kind of hard to test, but regroup is like "classic hold" return; } } @@ -3134,7 +3134,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo return; } - if (other == myowner) { + if (other == my_owner) { return; } @@ -3236,26 +3236,39 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo } } - if (mypet && !mypet->IsHeld() && !mypet->IsPetStop()) { // I have a pet, add other to it - if ( - !mypet->IsFamiliar() && - !mypet->GetSpecialAbility(SpecialAbility::AggroImmunity) && - !(IsBot() && mypet->GetSpecialAbility(SpecialAbility::BotAggroImmunity)) && - !(IsClient() && mypet->GetSpecialAbility(SpecialAbility::ClientAggroImmunity)) && - !(IsNPC() && mypet->GetSpecialAbility(SpecialAbility::NPCAggroImmunity)) - ) { - mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); + if (my_pet) { + bool aggro_immunity = my_pet->GetSpecialAbility(SpecialAbility::AggroImmunity); + bool bot_aggro_immunity = IsBot() && my_pet->GetSpecialAbility(SpecialAbility::BotAggroImmunity); + bool client_aggro_immunity = IsClient() && my_pet->GetSpecialAbility(SpecialAbility::ClientAggroImmunity); + bool npc_aggro_immunity = IsNPC() && my_pet->GetSpecialAbility(SpecialAbility::NPCAggroImmunity); + bool can_add_to_hatelist = !my_pet->IsFamiliar() && + !aggro_immunity && + !bot_aggro_immunity && + !client_aggro_immunity && + !npc_aggro_immunity; + + if (can_add_to_hatelist) { + bool bot_with_controllable_pet = IsBot() && CastToBot()->HasControllablePet(BotAnimEmpathy::Attack); + + if (!IsBot() || bot_with_controllable_pet) { + my_pet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); + } } } - else if (myowner) { // I am a pet, add other to owner if it's NPC/LD - if ( - myowner->IsAIControlled() && - !myowner->GetSpecialAbility(SpecialAbility::AggroImmunity) && - !(myowner->IsBot() && GetSpecialAbility(SpecialAbility::BotAggroImmunity)) && - !(myowner->IsClient() && GetSpecialAbility(SpecialAbility::ClientAggroImmunity)) && - !(myowner->IsNPC() && GetSpecialAbility(SpecialAbility::NPCAggroImmunity)) - ) { - myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); + else if (my_owner) { // I am a pet, add other to owner if it's NPC/LD + if (my_owner->IsAIControlled()) { + bool aggro_immunity = my_owner->GetSpecialAbility(SpecialAbility::AggroImmunity); + bool bot_aggro_immunity = my_owner->IsBot() && GetSpecialAbility(SpecialAbility::BotAggroImmunity); + bool client_aggro_immunity = my_owner->IsClient() && GetSpecialAbility(SpecialAbility::ClientAggroImmunity); + bool npc_aggro_immunity = my_owner->IsNPC() && GetSpecialAbility(SpecialAbility::NPCAggroImmunity); + bool can_add_to_hatelist = !aggro_immunity && + !bot_aggro_immunity && + !client_aggro_immunity && + !npc_aggro_immunity; + + if (can_add_to_hatelist) { + my_owner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); + } } } @@ -3264,7 +3277,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo entity_list.AddTempPetsToHateList(this, other, bFrenzy); } - if (!wasengaged) { + if (!was_engaged) { if (IsNPC() && other->IsClient() && other->CastToClient()) { if (parse->HasQuestSub(GetNPCTypeID(), EVENT_AGGRO)) { parse->EventNPC(EVENT_AGGRO, CastToNPC(), other, "", 0); diff --git a/zone/bot.cpp b/zone/bot.cpp index 01adfb886..ad3a4d4a0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2102,10 +2102,6 @@ void Bot::SetGuardMode() { StopMoving(); m_GuardPoint = GetPosition(); SetGuardFlag(); - - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - GetPet()->StopMoving(); - } } void Bot::SetHoldMode() { @@ -2270,7 +2266,7 @@ void Bot::AI_Process() } // This causes conflicts with default pet handler (bounces between targets) - if (NOT_PULLING_BOT && NOT_RETURNING_BOT && HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + if (NOT_PULLING_BOT && NOT_RETURNING_BOT && HasControllablePet(BotAnimEmpathy::Attack)) { // We don't add to hate list here because it's assumed to already be on the list GetPet()->SetTarget(tar); } @@ -2284,11 +2280,11 @@ void Bot::AI_Process() } // COMBAT RANGE CALCS - bool front_mob = InFrontMob(tar, GetX(), GetY()); - bool behind_mob = BehindMob(tar, GetX(), GetY()); - bool stop_melee_level = GetLevel() >= GetStopMeleeLevel(); + bool front_mob = InFrontMob(tar, GetX(), GetY()); + bool behind_mob = BehindMob(tar, GetX(), GetY()); + bool stop_melee_level = GetLevel() >= GetStopMeleeLevel(); + tar_distance = sqrt(tar_distance); // sqrt this for future calculations - // Item variables const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary); const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary); @@ -2310,20 +2306,36 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) - if (PULLING_BOT || RETURNING_BOT) { - if (!TargetValidation(tar)) { return; } + if (PULLING_BOT) { + if (!TargetValidation(tar)) { + SetPullFlag(false); + SetPullingFlag(false); + bot_owner->SetBotPulling(false); + + if (GetPet()) { + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); + } - if (RuleB(Bots, BotsRequireLoS) && !HasLoS()) { return; } - if (at_combat_range) { - if ( - !tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && + if (!at_combat_range && RuleB(Bots, UseSpellPulling)) { + uint16 pull_spell_id = RuleI(Bots, PullSpellID); + + if (IsValidSpell(pull_spell_id) && tar_distance <= spells[pull_spell_id].range) { + at_combat_range = true; + } + } + + if (at_combat_range && DoLosChecks(tar)) { + bool ai_cast_successful = false; + bool can_range_attack = !tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && RuleB(Bots, AllowRangedPulling) && IsBotRanged() && - ranged_timer.Check(false) - ) { + ranged_timer.Check(false); + + if (can_range_attack) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { @@ -2335,30 +2347,33 @@ void Bot::AI_Process() return; } - if ( - RuleB(Bots, AllowAISpellPulling) && + bool can_ai_spell_pull = RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && - AI_HasSpells() - ) { + AI_HasSpells(); + + if (can_ai_spell_pull) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); SetPullingSpell(true); - AI_EngagedCastCheck(); + ai_cast_successful = AI_EngagedCastCheck(); SetPullingSpell(false); - return; + if (ai_cast_successful) { + return; + } } - } - if (RuleB(Bots, UseSpellPulling)) { - uint16 spell_id = RuleI(Bots, PullSpellID); + if (RuleB(Bots, UseSpellPulling)) { + uint16 pull_spell_id = RuleI(Bots, PullSpellID); - if (tar_distance <= spells[spell_id].range) { - StopMoving(); - SetPullingSpell(true); - CastSpell(spell_id, tar->GetID()); - SetPullingSpell(false); - - return; + if (IsValidSpell(pull_spell_id) && tar_distance <= spells[pull_spell_id].range) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + SetPullingSpell(true); + CastSpell(pull_spell_id, tar->GetID()); + SetPullingSpell(false); + } } + + return; } TryPursueTarget(leash_distance); @@ -2551,6 +2566,12 @@ void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_di if (PULLING_BOT || RETURNING_BOT || !bot_owner->GetBotPulling()) { SetPullingFlag(false); SetReturningFlag(false); + bot_owner->SetBotPulling(false); + + if (GetPet()) { + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); + } } if (TryAutoDefend(bot_owner, leash_distance) ) { @@ -2559,14 +2580,7 @@ void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_di SetTarget(nullptr); - if ( - HasPet() && - ( - GetClass() != Class::Enchanter || - GetPet()->GetPetType() != petAnimation || - GetAA(aaAnimationEmpathy) >= 1 - ) - ) { + if (HasControllablePet(BotAnimEmpathy::BackOff)) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -2729,7 +2743,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { SetTarget(hater); SetAttackingFlag(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + if (HasControllablePet(BotAnimEmpathy::Attack)) { GetPet()->AddToHateList(hater, 1); GetPet()->SetTarget(hater); } @@ -2799,14 +2813,7 @@ bool Bot::TryPursueTarget(float leash_distance) { WipeHateList(); SetTarget(nullptr); - if ( - HasPet() && - ( - GetClass() != Class::Enchanter || - GetPet()->GetPetType() != petAnimation || - GetAA(aaAnimationEmpathy) >= 2 - ) - ) { + if (HasControllablePet(BotAnimEmpathy::BackOff)) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -3191,7 +3198,8 @@ bool Bot::IsValidTarget( bot_owner->SetBotPulling(false); if (GetPet()) { - GetPet()->SetPetOrder(m_previous_pet_order); + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); } } @@ -3225,7 +3233,8 @@ Mob* Bot::GetBotTarget(Client* bot_owner) bot_owner->SetBotPulling(false); if (GetPet()) { - GetPet()->SetPetOrder(m_previous_pet_order); + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); } } @@ -3248,15 +3257,11 @@ bool Bot::TargetValidation(Mob* other) { } bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) { - auto engage_range = (GetBotDistanceRanged() < 30 ? 30 : GetBotDistanceRanged()); + bool target_check = !GetTarget() || Distance(GetPosition(), GetTarget()->GetPosition()) <= 75.0f; + bool returned_check = (NOT_GUARDING && fm_distance <= GetFollowDistance()) || + (GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance()); - if ( - (GetTarget() && Distance(GetPosition(), GetTarget()->GetPosition()) <= engage_range) && - ( - (NOT_GUARDING && fm_distance <= GetFollowDistance()) || - (GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance()) - ) - ) { // Once we're back, clear blocking flags so everyone else can join in + if (target_check && returned_check) { // Once we're back, clear blocking flags so everyone else can join in WipeHateList(); SetTarget(nullptr); SetPullingFlag(false); @@ -3264,9 +3269,10 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_dist bot_owner->SetBotPulling(false); if (GetPet()) { - GetPet()->SetPetOrder(m_previous_pet_order); + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); - if (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1) { + if (HasControllablePet(BotAnimEmpathy::BackOff)) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -3307,7 +3313,8 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { bot_owner->SetBotPulling(false); if (GetPet()) { - GetPet()->SetPetOrder(m_previous_pet_order); + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); } return false; @@ -3316,11 +3323,16 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { SetPullingFlag(false); SetReturningFlag(); - if (HasPet() && - (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + Mob* my_pet = GetPet(); - GetPet()->WipeHateList(); - GetPet()->SetTarget(nullptr); + if (my_pet) { + if (HasControllablePet(BotAnimEmpathy::BackOff)) { + my_pet->WipeHateList(); + my_pet->SetTarget(nullptr); + } else { + my_pet->AddToHateList(GetTarget(), 1); + my_pet->SetTarget(GetTarget()); + } } if (GetPlayerState() & static_cast(PlayerState::Aggressive)) { @@ -3482,7 +3494,7 @@ Client* Bot::SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint void Bot::SetOwnerTarget(Client* bot_owner) { if (GetPet() && (PULLING_BOT || RETURNING_BOT)) { - GetPet()->SetPetOrder(m_previous_pet_order); + GetPet()->SetPetOrder(SPO_Follow); } SetAttackFlag(false); @@ -3502,7 +3514,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { SetTarget(attack_target); SetAttackingFlag(); - if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + if (HasControllablePet(BotAnimEmpathy::Attack)) { GetPet()->WipeHateList(); GetPet()->AddToHateList(attack_target, 1); GetPet()->SetTarget(attack_target); @@ -3520,20 +3532,21 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetReturningFlag(false); bot_owner->SetBotPulling(false); + if (GetPet()) { + GetPet()->SetPetOrder(SPO_Follow); + GetPet()->CastToNPC()->SaveGuardSpot(true); + } + if (NOT_HOLDING && NOT_PASSIVE) { auto pull_target = bot_owner->GetTarget(); + if (pull_target) { - if (raid) { - const auto msg = fmt::format("Pulling {}.", pull_target->GetCleanName()); - raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100); - } else { - RaidGroupSay( - fmt::format( - "Pulling {}.", - pull_target->GetCleanName() - ).c_str() - ); - } + RaidGroupSay( + fmt::format( + "Pulling {}.", + pull_target->GetCleanName() + ).c_str() + ); InterruptSpell(); WipeHateList(); @@ -3542,12 +3555,15 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetPullingFlag(); bot_owner->SetBotPulling(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + if (GetPet()) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); - m_previous_pet_order = GetPet()->GetPetOrder(); - GetPet()->CastToNPC()->SaveGuardSpot(GetPosition()); - GetPet()->SetPetOrder(SPO_Guard); + + if (HasControllablePet(BotAnimEmpathy::Guard)) { + m_previous_pet_order = GetPet()->GetPetOrder(); + GetPet()->CastToNPC()->SaveGuardSpot(GetPosition()); + GetPet()->SetPetOrder(SPO_Guard); + } } } } @@ -13438,3 +13454,13 @@ bool Bot::IsValidBotStance(uint8 stance) { return false; } + +bool Bot::HasControllablePet(uint8 ranks_required) { + if (!GetPet()) { + return false; + } + + return GetClass() != Class::Enchanter || + GetPet()->GetPetType() != petAnimation || + GetAA(aaAnimationEmpathy) >= ranks_required; +} \ No newline at end of file diff --git a/zone/bot.h b/zone/bot.h index 95ace3958..434e8f703 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -229,6 +229,12 @@ static std::map botSubType_names = { { CommandedSubTypes::Selo, "Selo" } }; +namespace BotAnimEmpathy { + constexpr uint8 Guard = 1; + constexpr uint8 Attack = 2; + constexpr uint8 BackOff = 3; +}; + class Bot : public NPC { friend class Mob; public: @@ -786,6 +792,7 @@ public: EQ::ItemInstance* GetBotItem(uint16 slot_id); bool GetSpawnStatus() { return _spawnStatus; } uint8 GetPetChooserID() { return _petChooserID; } + bool HasControllablePet(uint8 ranks_required = 0); bool IsBotRanged() { return _botRangedSetting; } bool IsBotCharmer() { return _botCharmer; } bool IsBot() const override { return true; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index a620e9e66..647e8d75b 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1465,8 +1465,11 @@ void bot_command_summon(Client *c, const Seperator *sep) continue; } - bot_iter->GetPet()->WipeHateList(); - bot_iter->GetPet()->SetTarget(nullptr); + if (bot_iter->HasControllablePet(BotAnimEmpathy::BackOff)) { + bot_iter->GetPet()->WipeHateList(); + bot_iter->GetPet()->SetTarget(nullptr); + } + bot_iter->GetPet()->Teleport(c->GetPosition()); } diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index 127ec14ab..6bab8b0a6 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -5,8 +5,8 @@ void bot_command_pull(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_pull", sep->arg[0], "pull")) { return; } - if (helper_is_help_or_usage(sep->arg[1])) { + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } @@ -40,7 +40,7 @@ void bot_command_pull(Client *c, const Seperator *sep) if ( !target_mob || - target_mob == c || + target_mob->IsOfClientBotMerc() || !c->IsAttackAllowed(target_mob) ) { c->Message(Chat::White, "Your current target is not attackable!"); @@ -55,12 +55,16 @@ void bot_command_pull(Client *c, const Seperator *sep) } if (target_mob->IsNPC() && target_mob->GetHateList().size()) { - c->Message(Chat::White, "Your current target is already engaged!"); + return; } Bot* bot_puller = nullptr; + Bot* backup_bot_puller = nullptr; + Bot* alternate_bot_puller = nullptr; + bool backup_puller_found = false; + bool alternate_puller_found = false; for (auto bot_iter : sbl) { if (!bot_iter->ValidStateCheck(c)) { @@ -72,72 +76,37 @@ void bot_command_pull(Client *c, const Seperator *sep) case Class::Monk: case Class::Bard: case Class::Ranger: - bot_puller = bot_iter; - break; - case Class::Warrior: - case Class::ShadowKnight: - case Class::Paladin: - case Class::Berserker: - case Class::Beastlord: - if (!bot_puller) { + bot_iter->SetPullFlag(); - bot_puller = bot_iter; - continue; - } - - switch (bot_puller->GetClass()) { - case Class::Druid: - case Class::Shaman: - case Class::Cleric: - case Class::Wizard: - case Class::Necromancer: - case Class::Magician: - case Class::Enchanter: - bot_puller = bot_iter; - default: - continue; - } - - continue; - case Class::Druid: - case Class::Shaman: - case Class::Cleric: - if (!bot_puller) { - - bot_puller = bot_iter; - continue; - } - - switch (bot_puller->GetClass()) { - case Class::Wizard: - case Class::Necromancer: - case Class::Magician: - case Class::Enchanter: - bot_puller = bot_iter; - default: - continue; - } - - continue; - case Class::Wizard: - case Class::Necromancer: - case Class::Magician: - case Class::Enchanter: - if (!bot_puller) { - bot_puller = bot_iter; - } - - continue; + return; default: - continue; + break; } + if (!backup_puller_found) { + switch (bot_iter->GetClass()) { + case Class::Warrior: + case Class::ShadowKnight: + case Class::Paladin: + case Class::Berserker: + case Class::Beastlord: + backup_bot_puller = bot_iter; + backup_puller_found = true; - bot_puller = bot_iter; + break; + default: + break; + } + } - break; + if (!backup_puller_found && !alternate_puller_found) { + alternate_bot_puller = bot_iter; + alternate_puller_found = true; + } } + bot_puller = backup_bot_puller ? backup_bot_puller : alternate_bot_puller; + if (bot_puller) { bot_puller->SetPullFlag(); } diff --git a/zone/bot_commands/release.cpp b/zone/bot_commands/release.cpp index 9bd9d979c..36f5df96a 100644 --- a/zone/bot_commands/release.cpp +++ b/zone/bot_commands/release.cpp @@ -17,6 +17,12 @@ void bot_command_release(Client *c, const Seperator *sep) sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { bot_iter->WipeHateList(); + + if (bot_iter->GetPet() && bot_iter->HasControllablePet(BotAnimEmpathy::BackOff)) { + bot_iter->GetPet()->WipeHateList(); + bot_iter->GetPet()->SetTarget(nullptr); + } + bot_iter->SetPauseAI(false); }