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); 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) bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<uint32> const& vFlagIds)
{ {
// Basic sanity checks
Battleground* bg = bot->GetBattleground(); Battleground* bg = bot->GetBattleground();
if (!bg) if (!bg)
return false; return false;
// Get the actual BG type (in case of random BG)
BattlegroundTypeId bgType = bg->GetBgTypeID(); BattlegroundTypeId bgType = bg->GetBgTypeID();
if (bgType == BATTLEGROUND_RB) if (bgType == BATTLEGROUND_RB)
bgType = bg->GetBgTypeID(true); bgType = bg->GetBgTypeID(true);
// Initialize vectors for nearby objects and players
GuidVector closeObjects; GuidVector closeObjects;
GuidVector closePlayers; GuidVector closePlayers;
float flagRange = 0.0f; float flagRange = 0.0f;
// Set up appropriate search ranges and object lists based on BG type
switch (bgType) switch (bgType)
{ {
case BATTLEGROUND_AV: case BATTLEGROUND_AV:
case BATTLEGROUND_AB: case BATTLEGROUND_AB:
case BATTLEGROUND_IC: case BATTLEGROUND_IC:
{ {
// For territory control BGs, use standard interaction range
closeObjects = *context->GetValue<GuidVector>("closest game objects"); closeObjects = *context->GetValue<GuidVector>("closest game objects");
closePlayers = *context->GetValue<GuidVector>("closest friendly players"); closePlayers = *context->GetValue<GuidVector>("closest friendly players");
flagRange = INTERACTION_DISTANCE; flagRange = INTERACTION_DISTANCE;
@@ -4097,6 +4116,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
case BATTLEGROUND_WS: case BATTLEGROUND_WS:
case BATTLEGROUND_EY: case BATTLEGROUND_EY:
{ {
// For flag carrying BGs, use wider range and ignore LOS
closeObjects = *context->GetValue<GuidVector>("nearest game objects no los"); closeObjects = *context->GetValue<GuidVector>("nearest game objects no los");
closePlayers = *context->GetValue<GuidVector>("closest friendly players"); closePlayers = *context->GetValue<GuidVector>("closest friendly players");
flagRange = 25.0f; flagRange = 25.0f;
@@ -4109,41 +4129,109 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (closeObjects.empty()) if (closeObjects.empty())
return false; return false;
if (!closePlayers.empty()) // First identify which flag/base we're trying to interact with
{ GameObject* targetFlag = nullptr;
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);
for (ObjectGuid const guid : closeObjects) for (ObjectGuid const guid : closeObjects)
{ {
GameObject* go = botAI->GetGameObject(guid); GameObject* go = botAI->GetGameObject(guid);
if (!go) if (!go)
continue; continue;
// Check if this object is a valid capture target
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
if (f == vFlagIds.end()) if (f == vFlagIds.end())
continue; continue;
// Verify the object is active and ready
if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY) if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY)
continue; 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) if (!bot->CanUseBattlegroundObject(go) && bgType != BATTLEGROUND_WS)
continue; continue;
@@ -4151,41 +4239,39 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (flagRange && dist > flagRange) if (flagRange && dist > flagRange)
continue; continue;
bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()] // Special handling for WSG and EY base flags
: bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0] bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()]
: false; : bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0]
: false;
// Don't capture own flag in WSG unless carrying enemy flag
if (atBase && bgType == BATTLEGROUND_WS && if (atBase && bgType == BATTLEGROUND_WS &&
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))) !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
continue; continue;
// Handle capture mechanics based on BG type
switch (bgType) switch (bgType)
{ {
case BATTLEGROUND_AV: case BATTLEGROUND_AV:
case BATTLEGROUND_AB: case BATTLEGROUND_AB:
case BATTLEGROUND_IC: case BATTLEGROUND_IC:
{ {
// Prevent capturing from inside flag pole
if (dist == 0.0f) 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; float const moveDist = bot->GetObjectSize() + go->GetObjectSize() + 0.1f;
return MoveTo(bot->GetMapId(), go->GetPositionX() + (urand(0, 1) ? -moveDist : moveDist), 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()) if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED); bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm()) if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// std::ostringstream out; // Cast the capture spell
// out << "Flag is nearby, using " << go->GetName();
// bot->Say(out.str(), LANG_UNIVERSAL);
// botAI->SetNextCheckDelay(10000);
// cast banner spell
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
if (!spellInfo) if (!spellInfo)
return false; return false;
@@ -4196,17 +4282,14 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
botAI->WaitForSpellCast(spell); botAI->WaitForSpellCast(spell);
// WorldPacket data(CMSG_GAMEOBJ_USE);
// data << go->GetGUID();
// bot->GetSession()->HandleGameObjectUseOpcode(data);
resetObjective(); resetObjective();
return true; return true;
break;
} }
case BATTLEGROUND_WS: case BATTLEGROUND_WS:
{ {
if (dist < INTERACTION_DISTANCE) if (dist < INTERACTION_DISTANCE)
{ {
// Handle flag capture at base
if (atBase) if (atBase)
{ {
if (bot->GetTeamId() == TEAM_HORDE) 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); data << uint32(BG_WS_TRIGGER_ALLIANCE_FLAG_SPAWN);
bot->GetSession()->HandleAreaTriggerOpcode(data); bot->GetSession()->HandleAreaTriggerOpcode(data);
} }
// std::ostringstream out;
// out << "Capturing flag!";
// bot->Say(out.str(), LANG_UNIVERSAL);
return true; return true;
} }
// Dismount before picking up flag
if (bot->IsMounted()) if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED); bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm()) if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// std::ostringstream out; out << "Flag is nearby, using " << go->GetName(); // Pick up the flag
// bot->Say(out.str(), LANG_UNIVERSAL);
WorldPacket data(CMSG_GAMEOBJ_USE); WorldPacket data(CMSG_GAMEOBJ_USE);
data << go->GetGUID(); data << go->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data); bot->GetSession()->HandleGameObjectUseOpcode(data);
@@ -4244,25 +4324,22 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
} }
else else
{ {
// std::ostringstream out; // Move to flag if not in range
// 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);
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
} }
break;
} }
case BATTLEGROUND_EY: case BATTLEGROUND_EY:
{ {
if (dist < INTERACTION_DISTANCE) if (dist < INTERACTION_DISTANCE)
{ {
// Dismount before interacting
if (bot->IsMounted()) if (bot->IsMounted())
bot->RemoveAurasByType(SPELL_AURA_MOUNTED); bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
if (bot->IsInDisallowedMountForm()) if (bot->IsInDisallowedMountForm())
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
// Flag at center requires casting spell // Handle center flag differently (requires spell cast)
if (atBase) if (atBase)
{ {
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER); 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; return true;
} }
// Dropped flag is instant use // Pick up dropped flag
WorldPacket data(CMSG_GAMEOBJ_USE); WorldPacket data(CMSG_GAMEOBJ_USE);
data << go->GetGUID(); data << go->GetGUID();
bot->GetSession()->HandleGameObjectUseOpcode(data); bot->GetSession()->HandleGameObjectUseOpcode(data);
@@ -4287,9 +4364,9 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
} }
else else
{ {
// Move to flag if not in range
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
} }
break;
} }
default: default:
break; break;