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);
|
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user