mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
fix(BotAI/BattleGrounds) Bots AI improved based around capture points (#927)
* Less Flag Derp * Comments
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user