From 8b8b41dab36136829ed755ffb2898cf3cb8890c3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:13:54 -0600 Subject: [PATCH] [Bots] Add checks to ensure bots and pets do not engage on ^pull (#4708) --- zone/bot.cpp | 160 +++++++++++++++++++++++++++++---------------------- zone/bot.h | 1 + 2 files changed, 93 insertions(+), 68 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index dc7a09dfd..1ff33c0f0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1856,6 +1856,7 @@ bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { if ( !GetPullingFlag() && + !GetReturningFlag() && ( ( GetBotStance() != Stance::Aggressive && @@ -2115,8 +2116,10 @@ void Bot::SetHoldMode() { void Bot::AI_Process() { -#define PULLING_BOT (GetPullingFlag() || GetReturningFlag()) -#define NOT_PULLING_BOT (!GetPullingFlag() && !GetReturningFlag()) +#define PULLING_BOT (GetPullingFlag()) +#define NOT_PULLING_BOT (!GetPullingFlag()) +#define RETURNING_BOT (GetReturningFlag()) +#define NOT_RETURNING_BOT (!GetReturningFlag()) #define GUARDING (GetGuardFlag()) #define NOT_GUARDING (!GetGuardFlag()) #define HOLDING (GetHoldFlag()) @@ -2224,7 +2227,7 @@ void Bot::AI_Process() // PULLING FLAG (TARGET VALIDATION) - if (GetPullingFlag()) { + if (PULLING_BOT) { if (!PullingFlagChecks(bot_owner)) { return; } @@ -2232,10 +2235,10 @@ void Bot::AI_Process() // RETURNING FLAG - if (GetReturningFlag()) { - ReturningFlagChecks(bot_owner, leash_owner, fm_distance); - - return; + if (RETURNING_BOT) { + if (ReturningFlagChecks(bot_owner, leash_owner, fm_distance)) { + return; + } } // DEFAULT (ACQUIRE TARGET) @@ -2268,7 +2271,7 @@ void Bot::AI_Process() } // This causes conflicts with default pet handler (bounces between targets) - if (NOT_PULLING_BOT && HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + if (NOT_PULLING_BOT && NOT_RETURNING_BOT && HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { // We don't add to hate list here because it's assumed to already be on the list GetPet()->SetTarget(tar); } @@ -2309,7 +2312,7 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) - if (GetPullingFlag()) { + if (PULLING_BOT || RETURNING_BOT) { if (!TargetValidation(tar)) { return; } if (!DoLosChecks(tar)) { @@ -2368,7 +2371,11 @@ void Bot::AI_Process() // ENGAGED AT COMBAT RANGE // We can fight - if (at_combat_range) { + bool other_bot_pulling = + (bot_owner->GetBotPulling() && NOT_PULLING_BOT) && + (bot_owner->GetBotPulling() && NOT_RETURNING_BOT); + + if (!other_bot_pulling && at_combat_range) { bool jitter_cooldown = false; if (m_combat_jitter_timer.GetRemainingTime() > 1 && m_combat_jitter_timer.Enabled()) { @@ -2459,60 +2466,20 @@ void Bot::AI_Process() // ENGAGED NOT AT COMBAT RANGE - else if (!TryPursueTarget(leash_distance, Goal)) { + else if (!other_bot_pulling && !TryPursueTarget(leash_distance, Goal)) { return; } // End not in combat range - - TryMeditate(); - } - else { // Out-of-combat behavior - SetAttackFlag(false); - SetCombatRoundForAlerts(false); - SetAttackingFlag(false); - - if (!bot_owner->GetBotPulling()) { - SetPullingFlag(false); - SetReturningFlag(false); - } - -// AUTO DEFEND - - if (TryAutoDefend(bot_owner, leash_distance) ) { - return; - } - - SetTarget(nullptr); - - if ( - HasPet() && - ( - GetClass() != Class::Enchanter || - GetPet()->GetPetType() != petAnimation || - GetAA(aaAnimationEmpathy) >= 1 - ) - ) { + if (bot_owner->GetBotPulling() && HasPet()) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } - if (m_PlayerState & static_cast(PlayerState::Aggressive)) { - SendRemovePlayerState(PlayerState::Aggressive); - } - -// OK TO IDLE - - // Ok to idle - if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { - return; - } - if (!IsBotNonSpellFighter() && AI_HasSpells() && TryIdleChecks(fm_distance)) { - return; - } - if (GetClass() == Class::Bard && AI_HasSpells() && TryBardMovementCasts()) { - return; - } + TryMeditate(); + } + else { // Out-of-combat behavior + DoOutOfCombatChecks(bot_owner, follow_mob, Goal, leash_distance, fm_distance); } } @@ -2540,7 +2507,7 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g float destination_distance = DistanceSquared(GetPosition(), Goal); - if ((!bot_owner->GetBotPulling() || PULLING_BOT) && (destination_distance > GetFollowDistance())) { + if (destination_distance > GetFollowDistance()) { if (!IsRooted()) { if (rest_timer.Enabled()) { rest_timer.Disable(); @@ -2580,9 +2547,56 @@ bool Bot::TryIdleChecks(float fm_distance) { return true; } + return false; } +void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, glm::vec3& Goal, float leash_distance, float fm_distance) { + SetAttackFlag(false); + SetCombatRoundForAlerts(false); + SetAttackingFlag(false); + + if (PULLING_BOT || RETURNING_BOT || !bot_owner->GetBotPulling()) { + SetPullingFlag(false); + SetReturningFlag(false); + } + + if (TryAutoDefend(bot_owner, leash_distance) ) { + return; + } + + SetTarget(nullptr); + + if ( + HasPet() && + ( + GetClass() != Class::Enchanter || + GetPet()->GetPetType() != petAnimation || + GetAA(aaAnimationEmpathy) >= 1 + ) + ) { + GetPet()->WipeHateList(); + GetPet()->SetTarget(nullptr); + } + + if (m_PlayerState & static_cast(PlayerState::Aggressive)) { + SendRemovePlayerState(PlayerState::Aggressive); + } + + // Ok to idle + if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { + return; + } + + if (!IsBotNonSpellFighter() && AI_HasSpells() && TryIdleChecks(fm_distance)) { + return; + } + + if (GetClass() == Class::Bard && AI_HasSpells() && TryBardMovementCasts()) { + return; + } +} + // This is as close as I could get without modifying the aggro mechanics and making it an expensive process... // 'class Client' doesn't make use of hate_list bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { @@ -3230,7 +3244,7 @@ bool Bot::IsValidTarget( SetCombatRoundForAlerts(false); SetAttackingFlag(false); - if (PULLING_BOT) { + if (PULLING_BOT || RETURNING_BOT) { SetPullingFlag(false); SetReturningFlag(false); bot_owner->SetBotPulling(false); @@ -3263,7 +3277,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner) SetAttackFlag(false); SetAttackingFlag(false); - if (PULLING_BOT) { + if (PULLING_BOT || RETURNING_BOT) { // 'Flags' should only be set on the bot that is pulling SetPullingFlag(false); SetReturningFlag(false); @@ -3293,23 +3307,33 @@ bool Bot::TargetValidation(Mob* other) { } bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) { + auto engage_range = (GetBotDistanceRanged() < 30 ? 30 : GetBotDistanceRanged()); + if ( - (NOT_GUARDING && fm_distance <= GetFollowDistance()) || - (GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance()) + (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 + WipeHateList(); + SetTarget(nullptr); + SetPullingFlag(false); SetReturningFlag(false); bot_owner->SetBotPulling(false); if (GetPet()) { GetPet()->SetPetOrder(m_previous_pet_order); + + if (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1) { + GetPet()->WipeHateList(); + GetPet()->SetTarget(nullptr); + } } return false; } - // Need to keep puller out of combat until they reach their 'return to' destination - WipeHateList(); - if (!IsMoving()) { glm::vec3 Goal(0, 0, 0); @@ -3348,8 +3372,6 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { return false; } else if (GetTarget()->GetHateList().size()) { - WipeHateList(); - SetTarget(nullptr); SetPullingFlag(false); SetReturningFlag(); @@ -3363,6 +3385,8 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { if (GetPlayerState() & static_cast(PlayerState::Aggressive)) { SendRemovePlayerState(PlayerState::Aggressive); } + + return false; } return true; @@ -3516,7 +3540,7 @@ Client* Bot::SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint } void Bot::SetOwnerTarget(Client* bot_owner) { - if (GetPet() && PULLING_BOT) { + if (GetPet() && (PULLING_BOT || RETURNING_BOT)) { GetPet()->SetPetOrder(m_previous_pet_order); } @@ -4959,7 +4983,7 @@ bool Bot::Death(Mob *killer_mob, int64 damage, uint16 spell_id, EQ::skills::Skil LeaveHealRotationMemberPool(); - if ((GetPullingFlag() || GetReturningFlag()) && my_owner && my_owner->IsClient()) { + if ((PULLING_BOT || RETURNING_BOT) && my_owner && my_owner->IsClient()) { my_owner->CastToClient()->SetBotPulling(false); } diff --git a/zone/bot.h b/zone/bot.h index 123407f61..ad9bbb18d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1127,6 +1127,7 @@ public: bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); + void DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, glm::vec3& Goal, float leash_distance, float fm_distance); bool TryBardMovementCasts(); bool BotRangedAttack(Mob* other, bool can_double_attack = false); bool CheckDoubleRangedAttack();