mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Core - Fix Bots can pickup the flag in Eye of the Strom instantly, without cast time from the spawn point (#1704)
# Eye of the Storm Flag Capture Behavior ## Previous Behavior - Bots used to interact with the Netherstorm flag the moment they reached interaction range, leading to instant pickups even before they were correctly positioned. - They did not respect the requirement to stand within the capture ring or dismount before channeling, so the interaction finished immediately without the intended delay. ## Current Behavior - Bots now verify they are inside the Eye of the Storm capture circle and will reposition toward the center flag or base flag until they are within 2.5 yards of the game object before starting the channel. - Once inside the circle they dismount, drop shapeshift forms, and come to a full stop before beginning the channel cast. - When channeling the center flag capture spell, bots keep checking for the ongoing `SPELL_CAPTURE_BANNER` cast and wait for it to finish instead of attempting repeated instant interactions. - They will be interrupted if they receice any damage These adjustments align the Eye of the Storm flow with the retail mechanics and prevent bots from taking the flag instantly when it spawns. Fixes #1700.
This commit is contained in:
@@ -4,10 +4,12 @@
|
||||
*/
|
||||
|
||||
#include "BattleGroundTactics.h"
|
||||
#include "BattleGroundJoinAction.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
GuidVector closePlayers;
|
||||
float flagRange = 0.0f;
|
||||
|
||||
// Eye of the Storm helpers used later when handling capture positioning
|
||||
BattlegroundEY* eyeBg = nullptr;
|
||||
GameObject* eyCenterFlag = nullptr;
|
||||
if (bgType == BATTLEGROUND_EY)
|
||||
{
|
||||
eyeBg = static_cast<BattlegroundEY*>(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<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
if (closeObjects.empty())
|
||||
return false;
|
||||
|
||||
auto keepStationaryWhileCapturing = [&](CurrentSpellTypes spellType)
|
||||
{
|
||||
Spell* currentSpell = bot->GetCurrentSpell(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<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||
if (f == vFlagIds.end())
|
||||
std::vector<uint32>::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<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
}
|
||||
|
||||
// Check if friendly players are already capturing
|
||||
if (!closePlayers.empty())
|
||||
if (!closePlayers.empty() && bgType != BATTLEGROUND_EY)
|
||||
{
|
||||
// Track number of friendly players capturing and the closest one
|
||||
uint32 numCapturing = 0;
|
||||
@@ -3664,17 +3732,20 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
{
|
||||
if (Unit* pFriend = botAI->GetUnit(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)
|
||||
spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
}
|
||||
|
||||
if (spell && spell->m_spellInfo && spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
||||
{
|
||||
numCapturing++;
|
||||
capturingPlayer = pFriend;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If friendlies are capturing, stay to defend but don't capture
|
||||
if (numCapturing > 0 && capturingPlayer && bot->GetGUID() != capturingPlayer->GetGUID())
|
||||
@@ -3704,9 +3775,11 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
|
||||
|
||||
// Validate this is a capture target
|
||||
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||
if (f == vFlagIds.end())
|
||||
std::vector<uint32>::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<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
continue;
|
||||
|
||||
// Special handling for WSG and EY base flags
|
||||
bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == 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;
|
||||
|
||||
@@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
if (dist < INTERACTION_DISTANCE)
|
||||
{
|
||||
// Handle flag capture at base
|
||||
if (atBase)
|
||||
if (isWsBaseFlag)
|
||||
{
|
||||
if (bot->GetTeamId() == TEAM_HORDE)
|
||||
{
|
||||
@@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
||||
}
|
||||
}
|
||||
case BATTLEGROUND_EY:
|
||||
{ // Issue: Currently bots in EY take flag instantly without casttime
|
||||
{ // Handle Netherstorm flag capture requiring a channel
|
||||
if (dist < INTERACTION_DISTANCE)
|
||||
{
|
||||
// Dismount before interacting
|
||||
if (bot->IsMounted())
|
||||
{
|
||||
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<CurrentSpellTypes>(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
|
||||
|
||||
Reference in New Issue
Block a user