fix(BotAI/BattleGrounds) Bots AI improved based around capture points (#927)

* Less Flag Derp

* Comments
This commit is contained in:
blinkysc
2025-01-30 07:16:59 -06:00
committed by GitHub
parent 27b86f8b1f
commit d770e0a9a0

View File

@@ -4069,26 +4069,45 @@ bool BGTactics::startNewPathFree(std::vector<BattleBotPath*> const& vPaths)
return moveToObjectiveWp(currentPath, currentPoint, reverse);
}
/**
* @brief Handles flag/base capturing gameplay in battlegrounds
*
* This function manages the logic for capturing flags and bases in various battlegrounds.
* It handles:
* - Enemy detection and combat near objectives
* - Coordination with friendly players who are capturing
* - Different capture mechanics for each battleground type
* - Proper positioning and movement
*
* @param vPaths Vector of possible paths the bot can take
* @param vFlagIds Vector of flag/base GameObjects that can be captured
* @return true if handling a flag/base action, false otherwise
*/
bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<uint32> const& vFlagIds)
{
// Basic sanity checks
Battleground* bg = bot->GetBattleground();
if (!bg)
return false;
// Get the actual BG type (in case of random BG)
BattlegroundTypeId bgType = bg->GetBgTypeID();
if (bgType == BATTLEGROUND_RB)
bgType = bg->GetBgTypeID(true);
// Initialize vectors for nearby objects and players
GuidVector closeObjects;
GuidVector closePlayers;
float flagRange = 0.0f;
// Set up appropriate search ranges and object lists based on BG type
switch (bgType)
{
case BATTLEGROUND_AV:
case BATTLEGROUND_AB:
case BATTLEGROUND_IC:
{
// For territory control BGs, use standard interaction range
closeObjects = *context->GetValue<GuidVector>("closest game objects");
closePlayers = *context->GetValue<GuidVector>("closest friendly players");
flagRange = INTERACTION_DISTANCE;
@@ -4097,6 +4116,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
case BATTLEGROUND_WS:
case BATTLEGROUND_EY:
{
// For flag carrying BGs, use wider range and ignore LOS
closeObjects = *context->GetValue<GuidVector>("nearest game objects no los");
closePlayers = *context->GetValue<GuidVector>("closest friendly players");
flagRange = 25.0f;
@@ -4109,41 +4129,109 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (closeObjects.empty())
return false;
if (!closePlayers.empty())
{
for (auto& guid : closePlayers)
{
if (Unit* pFriend = botAI->GetUnit(guid))
{
if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL))
{
if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
{
resetObjective();
startNewPathBegin(vPaths);
return false;
}
}
}
}
}
// std::ostringstream out; out << "Found " << closeObjects.size() << " nearby objects";
// bot->Say(out.str(), LANG_UNIVERSAL);
// 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;
// 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())
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;
}
// If we found a valid flag/base to interact with
if (targetFlag)
{
// Check for enemy players near the flag using bot's targeting system
Unit* enemyPlayer = AI_VALUE(Unit*, "enemy player target");
if (enemyPlayer && enemyPlayer->IsAlive())
{
// If enemy is near the flag, engage them before attempting capture
float enemyDist = enemyPlayer->GetDistance(targetFlag);
if (enemyDist < flagRange * 2.0f)
{
// Set enemy as current target and let combat AI handle it
context->GetValue<Unit*>("current target")->Set(enemyPlayer);
return false;
}
}
}
// Check if friendly players are already capturing
if (!closePlayers.empty())
{
// Track number of friendly players capturing and the closest one
uint32 numCapturing = 0;
Unit* capturingPlayer = nullptr;
for (auto& guid : closePlayers)
{
if (Unit* pFriend = botAI->GetUnit(guid))
{
// Check if they're casting the capture spell
if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL))
{
if (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)
{
// Move away if too close to avoid crowding
if (bot->GetDistance2d(capturingPlayer) < 3.0f)
{
float angle = bot->GetAngle(capturingPlayer);
float x = bot->GetPositionX() + 5.0f * cos(angle);
float y = bot->GetPositionY() + 5.0f * sin(angle);
MoveTo(bot->GetMapId(), x, y, bot->GetPositionZ());
}
// Reset objective and take new path for defending
resetObjective();
startNewPathBegin(vPaths);
return true;
}
}
// Area is clear of enemies and no friendlies are capturing
// Proceed with capture mechanics
for (ObjectGuid const guid : closeObjects)
{
GameObject* go = botAI->GetGameObject(guid);
if (!go)
continue;
// Validate this is a capture target
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
if (f == vFlagIds.end())
continue;
// Check object is active
if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY)
continue;
// Verify we can interact with it
if (!bot->CanUseBattlegroundObject(go) && bgType != BATTLEGROUND_WS)
continue;
@@ -4151,41 +4239,39 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (flagRange && dist > flagRange)
continue;
bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()]
: bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0]
: false;
// 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;
// Don't capture own flag in WSG unless carrying enemy flag
if (atBase && bgType == BATTLEGROUND_WS &&
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
continue;
// Handle capture mechanics based on BG type
switch (bgType)
{
case BATTLEGROUND_AV:
case BATTLEGROUND_AB:
case BATTLEGROUND_IC:
{
// Prevent capturing from inside flag pole
if (dist == 0.0f)
{
// this is to prevent bots capping while standing INSIDE the flag pole (which can be thick enough to
// hide player entirely) note that dist is taking into account size of object and bot (it's the
// space between outside of both) so moveDist needs to as well
float const moveDist = bot->GetObjectSize() + 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
if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// std::ostringstream out;
// out << "Flag is nearby, using " << go->GetName();
// bot->Say(out.str(), LANG_UNIVERSAL);
// botAI->SetNextCheckDelay(10000);
// cast banner spell
// Cast the capture spell
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
if (!spellInfo)
return false;
@@ -4196,17 +4282,14 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
botAI->WaitForSpellCast(spell);
// WorldPacket data(CMSG_GAMEOBJ_USE);
// data << go->GetGUID();
// bot->GetSession()->HandleGameObjectUseOpcode(data);
resetObjective();
return true;
break;
}
case BATTLEGROUND_WS:
{
if (dist < INTERACTION_DISTANCE)
{
// Handle flag capture at base
if (atBase)
{
if (bot->GetTeamId() == TEAM_HORDE)
@@ -4221,20 +4304,17 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
data << uint32(BG_WS_TRIGGER_ALLIANCE_FLAG_SPAWN);
bot->GetSession()->HandleAreaTriggerOpcode(data);
}
// std::ostringstream out;
// out << "Capturing flag!";
// bot->Say(out.str(), LANG_UNIVERSAL);
return true;
}
// Dismount before picking up flag
if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// std::ostringstream out; out << "Flag is nearby, using " << go->GetName();
// bot->Say(out.str(), LANG_UNIVERSAL);
// Pick up the flag
WorldPacket data(CMSG_GAMEOBJ_USE);
data << go->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data);
@@ -4244,25 +4324,22 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
}
else
{
// std::ostringstream out;
// out << "Flag is far, moving to " << go->GetName() << " " << go->GetPositionX() << " " <<
// go->GetPositionY() << " Distance:" << sServerFacade->GetDistance2d(bot, go->GetPositionX(),
// go->GetPositionY()); bot->Say(out.str(), LANG_UNIVERSAL);
// Move to flag if not in range
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
}
break;
}
case BATTLEGROUND_EY:
{
if (dist < INTERACTION_DISTANCE)
{
// Dismount before interacting
if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// Flag at center requires casting spell
// Handle center flag differently (requires spell cast)
if (atBase)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
@@ -4277,7 +4354,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
return true;
}
// Dropped flag is instant use
// Pick up dropped flag
WorldPacket data(CMSG_GAMEOBJ_USE);
data << go->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data);
@@ -4287,9 +4364,9 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
}
else
{
// Move to flag if not in range
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
}
break;
}
default:
break;