diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index db5235f5..827ab019 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -4,10 +4,12 @@ */ #include "BattleGroundTactics.h" -#include "BattleGroundJoinAction.h" + +#include #include "ArenaTeam.h" #include "ArenaTeamMgr.h" +#include "BattleGroundJoinAction.h" #include "Battleground.h" #include "BattlegroundAB.h" #include "BattlegroundAV.h" @@ -22,11 +24,12 @@ #include "BattlegroundSA.h" #include "BattlegroundWS.h" #include "Event.h" +#include "GameObject.h" #include "IVMapMgr.h" +#include "PathGenerator.h" #include "Playerbots.h" #include "PositionValue.h" #include "PvpTriggers.h" -#include "PathGenerator.h" #include "ServerFacade.h" #include "Vehicle.h" @@ -1754,7 +1757,7 @@ bool BGTactics::moveToStart(bool force) WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f), WS_WAITING_POS_ALLIANCE_2.GetPositionZ()); } - else // BB_WSG_WAIT_SPOT_SPAWN + else // BB_WSG_WAIT_SPOT_SPAWN { if (bot->GetTeamId() == TEAM_HORDE) MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f), @@ -3365,12 +3368,12 @@ bool BGTactics::resetObjective() return false; // Adjust role-change chance based on battleground type - uint32 oddsToChangeRole = 1; // default low + uint32 oddsToChangeRole = 1; // default low BattlegroundTypeId bgType = bg->GetBgTypeID(); if (bgType == BATTLEGROUND_WS) oddsToChangeRole = 2; - else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB) + else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB) oddsToChangeRole = 1; else if (bgType == BATTLEGROUND_AV) oddsToChangeRole = 0; @@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vector(bg); + if (eyeBg) + eyCenterFlag = eyeBg->GetBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM); + } + // Set up appropriate search ranges and object lists based on BG type switch (bgType) { @@ -3607,27 +3620,82 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetCurrentSpell(spellType); + if (!currentSpell || !currentSpell->m_spellInfo || currentSpell->m_spellInfo->Id != SPELL_CAPTURE_BANNER) + return false; + + // If the capture target is no longer available (another bot already captured it), stop channeling + if (GameObject* targetFlag = currentSpell->m_targets.GetGOTarget()) + { + if (!targetFlag->isSpawned() || targetFlag->GetGoState() != GO_STATE_READY) + { + bot->InterruptNonMeleeSpells(true); + resetObjective(); + return false; + } + } + else + { + bot->InterruptNonMeleeSpells(true); + resetObjective(); + return false; + } + + if (bot->IsMounted()) + { + bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } + + if (bot->IsInDisallowedMountForm()) + { + bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } + + if (bot->isMoving()) + { + bot->StopMoving(); + } + + return true; + }; + + // If we are already channeling the capture spell, keep the bot stationary and dismounted + if (keepStationaryWhileCapturing(CURRENT_CHANNELED_SPELL) || keepStationaryWhileCapturing(CURRENT_GENERIC_SPELL)) + return true; + // First identify which flag/base we're trying to interact with GameObject* targetFlag = nullptr; for (ObjectGuid const guid : closeObjects) { GameObject* go = botAI->GetGameObject(guid); if (!go) + { continue; + } + + bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID(); // Check if this object is a valid capture target - std::vector::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); - if (f == vFlagIds.end()) + std::vector::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); + if (f == vFlagIds.end() && !isEyCenterFlag) + { continue; + } // Verify the object is active and ready if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY) + { continue; + } // Check if we're in range (using double range for enemy detection) float const dist = bot->GetDistance(go); if (flagRange && dist > flagRange * 2.0f) + { continue; + } targetFlag = go; break; @@ -3655,7 +3723,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vector const& vPaths, std::vectorGetUnit(guid)) { - // Check if they're casting the capture spell - if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL)) + // Check if they're casting or channeling the capture spell + Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) { - if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) - { - numCapturing++; - capturingPlayer = pFriend; - } + spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + + if (spell && spell->m_spellInfo && spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) + { + numCapturing++; + capturingPlayer = pFriend; } } } @@ -3704,9 +3775,11 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetGUID() == go->GetGUID(); + // Validate this is a capture target - std::vector::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); - if (f == vFlagIds.end()) + std::vector::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); + if (f == vFlagIds.end() && !isEyCenterFlag) continue; // Check object is active @@ -3722,12 +3795,40 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetEntry() == vFlagsWS[bot->GetTeamId()] - : bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0] - : false; + bool isWsBaseFlag = bgType == BATTLEGROUND_WS && go->GetEntry() == vFlagsWS[bot->GetTeamId()]; + bool isEyBaseFlag = bgType == BATTLEGROUND_EY && go->GetEntry() == vFlagsEY[0]; + + // Ensure bots are inside the Eye of the Storm capture circle before casting + if (bgType == BATTLEGROUND_EY) + { + GameObject* captureFlag = (isEyBaseFlag && eyCenterFlag) ? eyCenterFlag : go; + float const requiredRange = 2.5f; + if (!bot->IsWithinDistInMap(captureFlag, requiredRange)) + { + // Stay mounted while relocating to avoid mount/dismount loops + return MoveTo(bot->GetMapId(), captureFlag->GetPositionX(), captureFlag->GetPositionY(), + captureFlag->GetPositionZ()); + } + + // Once inside the circle, dismount and stop before starting the channel + if (bot->IsMounted()) + { + bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } + + if (bot->IsInDisallowedMountForm()) + { + bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } + + if (bot->isMoving()) + { + bot->StopMoving(); + } + } // Don't capture own flag in WSG unless carrying enemy flag - if (atBase && bgType == BATTLEGROUND_WS && + if (isWsBaseFlag && bgType == BATTLEGROUND_WS && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))) continue; @@ -3743,7 +3844,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetObjectSize() + go->GetObjectSize() + 0.1f; return MoveTo(bot->GetMapId(), go->GetPositionX() + (urand(0, 1) ? -moveDist : moveDist), - go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ()); + go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ()); } // Dismount before capturing @@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetTeamId() == TEAM_HORDE) { @@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorIsMounted()) + { bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } if (bot->IsInDisallowedMountForm()) + { bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } // Handle center flag differently (requires spell cast) - if (atBase) + if (isEyCenterFlag) { + for (uint8 type = CURRENT_MELEE_SPELL; type <= CURRENT_CHANNELED_SPELL; ++type) + { + if (Spell* currentSpell = bot->GetCurrentSpell(static_cast(type))) + { + // m_spellInfo may be null in some states: protect access + if (currentSpell->m_spellInfo && currentSpell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) + { + bot->StopMoving(); + botAI->SetNextCheckDelay(500); + return true; + } + } + } + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER); if (!spellInfo) return false; Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); spell->m_targets.SetGOTarget(go); + + bot->StopMoving(); spell->prepare(&spell->m_targets); + botAI->WaitForSpellCast(spell); - //return true; Intended to make a bot cast SPELL_CAPTURE_BANNER and wait for spell finish, but doesn't work and causes infinite loop + resetObjective(); + return true; } // Pick up dropped flag @@ -3849,8 +3972,8 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); } } - default: - break; + default: + break; } }