mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-02-20 18:10:02 +01:00
Compare commits
2 Commits
08c739f918
...
6effabfa42
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6effabfa42 | ||
|
|
cadbcbd447 |
@ -14,6 +14,7 @@
|
|||||||
#include "BudgetValues.h"
|
#include "BudgetValues.h"
|
||||||
#include "ChannelMgr.h"
|
#include "ChannelMgr.h"
|
||||||
#include "CharacterPackets.h"
|
#include "CharacterPackets.h"
|
||||||
|
#include "ChatHelper.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CreatureAIImpl.h"
|
#include "CreatureAIImpl.h"
|
||||||
#include "CreatureData.h"
|
#include "CreatureData.h"
|
||||||
@ -283,6 +284,15 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameObject* goSpellTarget = currentSpell->m_targets.GetGOTarget();
|
||||||
|
|
||||||
|
if (goSpellTarget && !goSpellTarget->isSpawned())
|
||||||
|
{
|
||||||
|
InterruptSpell();
|
||||||
|
YieldThread(GetReactDelay());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool isHeal = false;
|
bool isHeal = false;
|
||||||
bool isSingleTarget = true;
|
bool isSingleTarget = true;
|
||||||
|
|
||||||
|
|||||||
@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BattleGroundTactics.h"
|
#include "BattleGroundTactics.h"
|
||||||
#include "BattleGroundJoinAction.h"
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "ArenaTeam.h"
|
#include "ArenaTeam.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "BattleGroundJoinAction.h"
|
||||||
#include "Battleground.h"
|
#include "Battleground.h"
|
||||||
#include "BattlegroundAB.h"
|
#include "BattlegroundAB.h"
|
||||||
#include "BattlegroundAV.h"
|
#include "BattlegroundAV.h"
|
||||||
@ -22,11 +24,12 @@
|
|||||||
#include "BattlegroundSA.h"
|
#include "BattlegroundSA.h"
|
||||||
#include "BattlegroundWS.h"
|
#include "BattlegroundWS.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
|
#include "GameObject.h"
|
||||||
#include "IVMapMgr.h"
|
#include "IVMapMgr.h"
|
||||||
|
#include "PathGenerator.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PositionValue.h"
|
#include "PositionValue.h"
|
||||||
#include "PvpTriggers.h"
|
#include "PvpTriggers.h"
|
||||||
#include "PathGenerator.h"
|
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
|
|
||||||
@ -1754,7 +1757,7 @@ bool BGTactics::moveToStart(bool force)
|
|||||||
WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f),
|
WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f),
|
||||||
WS_WAITING_POS_ALLIANCE_2.GetPositionZ());
|
WS_WAITING_POS_ALLIANCE_2.GetPositionZ());
|
||||||
}
|
}
|
||||||
else // BB_WSG_WAIT_SPOT_SPAWN
|
else // BB_WSG_WAIT_SPOT_SPAWN
|
||||||
{
|
{
|
||||||
if (bot->GetTeamId() == TEAM_HORDE)
|
if (bot->GetTeamId() == TEAM_HORDE)
|
||||||
MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f),
|
MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f),
|
||||||
@ -3365,12 +3368,12 @@ bool BGTactics::resetObjective()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Adjust role-change chance based on battleground type
|
// Adjust role-change chance based on battleground type
|
||||||
uint32 oddsToChangeRole = 1; // default low
|
uint32 oddsToChangeRole = 1; // default low
|
||||||
BattlegroundTypeId bgType = bg->GetBgTypeID();
|
BattlegroundTypeId bgType = bg->GetBgTypeID();
|
||||||
|
|
||||||
if (bgType == BATTLEGROUND_WS)
|
if (bgType == BATTLEGROUND_WS)
|
||||||
oddsToChangeRole = 2;
|
oddsToChangeRole = 2;
|
||||||
else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB)
|
else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB)
|
||||||
oddsToChangeRole = 1;
|
oddsToChangeRole = 1;
|
||||||
else if (bgType == BATTLEGROUND_AV)
|
else if (bgType == BATTLEGROUND_AV)
|
||||||
oddsToChangeRole = 0;
|
oddsToChangeRole = 0;
|
||||||
@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
GuidVector closePlayers;
|
GuidVector closePlayers;
|
||||||
float flagRange = 0.0f;
|
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
|
// Set up appropriate search ranges and object lists based on BG type
|
||||||
switch (bgType)
|
switch (bgType)
|
||||||
{
|
{
|
||||||
@ -3607,27 +3620,82 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (closeObjects.empty())
|
if (closeObjects.empty())
|
||||||
return false;
|
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
|
// First identify which flag/base we're trying to interact with
|
||||||
GameObject* targetFlag = nullptr;
|
GameObject* targetFlag = nullptr;
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
|
||||||
|
|
||||||
// Check if this object is a valid capture target
|
// 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 = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||||
if (f == vFlagIds.end())
|
if (f == vFlagIds.end() && !isEyCenterFlag)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the object is active and ready
|
// 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)
|
// Check if we're in range (using double range for enemy detection)
|
||||||
float const dist = bot->GetDistance(go);
|
float const dist = bot->GetDistance(go);
|
||||||
if (flagRange && dist > flagRange * 2.0f)
|
if (flagRange && dist > flagRange * 2.0f)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
targetFlag = go;
|
targetFlag = go;
|
||||||
break;
|
break;
|
||||||
@ -3655,7 +3723,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if friendly players are already capturing
|
// 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
|
// Track number of friendly players capturing and the closest one
|
||||||
uint32 numCapturing = 0;
|
uint32 numCapturing = 0;
|
||||||
@ -3664,14 +3732,17 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
{
|
{
|
||||||
if (Unit* pFriend = botAI->GetUnit(guid))
|
if (Unit* pFriend = botAI->GetUnit(guid))
|
||||||
{
|
{
|
||||||
// Check if they're casting the capture spell
|
// Check if they're casting or channeling the capture spell
|
||||||
if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL))
|
Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
|
if (!spell)
|
||||||
{
|
{
|
||||||
if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||||
{
|
}
|
||||||
numCapturing++;
|
|
||||||
capturingPlayer = pFriend;
|
if (spell && spell->m_spellInfo && spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
||||||
}
|
{
|
||||||
|
numCapturing++;
|
||||||
|
capturingPlayer = pFriend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3704,9 +3775,11 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (!go)
|
if (!go)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
|
||||||
|
|
||||||
// Validate this is a capture target
|
// Validate this is a capture target
|
||||||
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
std::vector<uint32>::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||||
if (f == vFlagIds.end())
|
if (f == vFlagIds.end() && !isEyCenterFlag)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check object is active
|
// Check object is active
|
||||||
@ -3722,12 +3795,40 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Special handling for WSG and EY base flags
|
// Special handling for WSG and EY base flags
|
||||||
bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()]
|
bool isWsBaseFlag = bgType == BATTLEGROUND_WS && go->GetEntry() == vFlagsWS[bot->GetTeamId()];
|
||||||
: bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0]
|
bool isEyBaseFlag = bgType == BATTLEGROUND_EY && go->GetEntry() == vFlagsEY[0];
|
||||||
: false;
|
|
||||||
|
// 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
|
// 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)))
|
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -3743,7 +3844,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
{
|
{
|
||||||
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
|
// Dismount before capturing
|
||||||
@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (dist < INTERACTION_DISTANCE)
|
if (dist < INTERACTION_DISTANCE)
|
||||||
{
|
{
|
||||||
// Handle flag capture at base
|
// Handle flag capture at base
|
||||||
if (atBase)
|
if (isWsBaseFlag)
|
||||||
{
|
{
|
||||||
if (bot->GetTeamId() == TEAM_HORDE)
|
if (bot->GetTeamId() == TEAM_HORDE)
|
||||||
{
|
{
|
||||||
@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case BATTLEGROUND_EY:
|
case BATTLEGROUND_EY:
|
||||||
{ // Issue: Currently bots in EY take flag instantly without casttime
|
{ // Handle Netherstorm flag capture requiring a channel
|
||||||
if (dist < INTERACTION_DISTANCE)
|
if (dist < INTERACTION_DISTANCE)
|
||||||
{
|
{
|
||||||
// Dismount before interacting
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle center flag differently (requires spell cast)
|
// 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);
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
|
||||||
if (!spellInfo)
|
if (!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
||||||
spell->m_targets.SetGOTarget(go);
|
spell->m_targets.SetGOTarget(go);
|
||||||
|
|
||||||
|
bot->StopMoving();
|
||||||
spell->prepare(&spell->m_targets);
|
spell->prepare(&spell->m_targets);
|
||||||
|
|
||||||
botAI->WaitForSpellCast(spell);
|
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
|
// Pick up dropped flag
|
||||||
@ -3849,8 +3972,8 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
|
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user