Compare commits

...

2 Commits

Author SHA1 Message Date
Jay
ce2a990495 README.md edits (#1779)
I've noticed some sprawl in the README.md. The following edits have been
made around the goal of clarity, brevity, and emphases when needed:

- Removed dead link to the Spanish README.md at the top. The
README_ES.md file itself is not removed from the repo, but is however
out of date and does not seem useful at this time
- Removed the addons section. This seems to be covered by the Wiki in
the Documentation section unless I'm mistaken? We can add these back if
needed
- Reorganized installation instructions to emphasize the importance of
using the Playerbots branch of AzerothCore for all installations
- Moved the platform support from the Frequently Asked Questions section
to the installation instructions
- Modified punctuation of bulleted list. These can return if it irks
someone, but it seems easier to read without them
- Added a encouragement to star the project for updates and gain more
visibility
- Removed the Frequently Asked Questions section. I encourage this to be
covered in a wiki page but it can return if deemed necessary. I think a
lot of it can be moved to other sections if we really need them in there
- Added a "Contributing" section that replaces the Coding Standards
section
- Coding Standards are included in the Contributing section
- Added a second link to the Discord server in the Contributing section
- Removed a link to the main Playerbots branch from the introduction
section. This is covered and emphasized in the installation section
instead
- Migrated the encouragements to make bug reports from the introduction,
along with the message that this is still in active development, to the
contributing section
- Reduced redundant language when not necessary (e.g. "As noted above,")
- Updated language around custom branch and require branch for
uniformity. This will make it more clear to the users about the subject
- Removed "Classic" from the "Classic Installation" terminology for
being inaccurate. The subject is now known simply as "Cloning the
Repositories" while "Docker Installation" still remains distinct. This
will make what was formerly known as "Classic Installation" as de facto
default
- Appended the "Documentation" section with add-on information
- Unified all instances of AddOn to the Blizzard spelling without a
hyphen following the WoW 3.3.5a client spelling
- Appended "AzerothCore" to instances of "branch" to ensure readers they
are, in fact, installing AzerothCore here from our required branch
2025-11-15 22:00:19 +01:00
Alex Dcnh
6effabfa42 Core - Fix Bots can pickup the flag in Eye of the Strom instantly, without cast time from the spawn point (#1704)
# Eye of the Storm Flag Capture Behavior
 
## Previous Behavior
- Bots used to interact with the Netherstorm flag the moment they
reached interaction range, leading to instant pickups even before they
were correctly positioned.
- They did not respect the requirement to stand within the capture ring
or dismount before channeling, so the interaction finished immediately
without the intended delay.
 
## Current Behavior
- Bots now verify they are inside the Eye of the Storm capture circle
and will reposition toward the center flag or base flag until they are
within 2.5 yards of the game object before starting the channel.
- Once inside the circle they dismount, drop shapeshift forms, and come
to a full stop before beginning the channel cast.
- When channeling the center flag capture spell, bots keep checking for
the ongoing `SPELL_CAPTURE_BANNER` cast and wait for it to finish
instead of attempting repeated instant interactions.
- They will be interrupted if they receice any damage
 
These adjustments align the Eye of the Storm flow with the retail
mechanics and prevent bots from taking the flag instantly when it
spawns.

Fixes #1700.
2025-11-15 18:19:16 +01:00
2 changed files with 173 additions and 53 deletions

View File

@@ -3,7 +3,7 @@
|
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_CN.md">中文</a>
|
<a href="https://github.com/brighton-chi/mod-playerbots/blob/readme/README_ES.md">Español</a>
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_ES.md">Español</a>
</p>
@@ -18,25 +18,27 @@
</div>
# Playerbots Module
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot).
Features include:
- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more;
- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience;
- Bots capable of running most raids and battlegrounds;
- Highly configurable settings to define how bots behave;
- Excellent performance, even when running thousands of bots.
- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more
- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience
- Bots capable of running most raids and battlegrounds
- Highly configurable settings to define how bots behave
- Excellent performance, even when running thousands of bots
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
`mod-playerbots` has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
We also have a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
## Installation
### Classic Installation
Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support.
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the playerbots module to build and function. Updates from the upstream are implemneted regularly to this branch. Instructions for installing this required branch and this module are provided below.
### Cloning the Repositories
To install both the required branch of AzerothCore and the `mod-playerbots` module from source, run the following:
```bash
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
@@ -48,7 +50,7 @@ For more information, refer to the [AzerothCore Installation Guide](https://www.
### Docker Installation
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install the `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module:
```bash
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
@@ -85,24 +87,19 @@ Use `docker compose up -d --build` to build and run the server. For more informa
## Documentation
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of AddOns, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections, and contributions are welcome.
## Frequently Asked Questions
Bots are controlled via chat commands. For larger bot groups, this can be cumbersome. Because of this, community members have developed client AddOns to allow controlling bots through the in-game UI. We recommend you check out their projects listed in the [AddOns and Submodules](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Addons-and-Sub%E2%80%90Modules) page.
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support.
- **Why isn't my source compiling?** Please ensure that you are compiling with the required [custom branch of AzerothCore](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/mod-playerbots/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
## Contributing
## Code standards
- https://www.azerothcore.org/wiki/cpp-code-standards
This project is still under development. We encourage anyone to make contributions, anything from pull requests to reporting issues. If you encounter any errors or experience crashes, we encourage you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
## Addons
If you make coding contributions, `mod-playerbots` complies with the [C++ Code Standards](https://www.azerothcore.org/wiki/cpp-code-standards) established by AzerothCore. Each Pull Request must include all test scenarios the author performed, along with their results, to demonstrate that the changes were properly verified.
Typically, bots are controlled via chat commands. For larger bot groups, this can be unwieldy. As an alternative, community members have developed client Add-Ons to allow controlling bots through the in-game UI. We recommend you check out their projects:
We recommend joining the [Discord server](https://discord.gg/NQm5QShwf9) to make your contributions to the project easier, as a lot of active support is carried out through this server.
- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio), which includes English, Chinese, French, German, Korean, Russian, and Spanish support [note: active development is temporarily continuing on a fork in Macx-Lio's absence (https://github.com/Wishmaster117/MultiBot)]
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan) [note: no longer under active development]
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision) [note: no longer under active development]
Please click on the "⭐" button to stay up to date and help us gain more visibility on GitHub!
## Acknowledgements

View File

@@ -4,10 +4,12 @@
*/
#include "BattleGroundTactics.h"
#include "BattleGroundJoinAction.h"
#include <algorithm>
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
#include "BattleGroundJoinAction.h"
#include "Battleground.h"
#include "BattlegroundAB.h"
#include "BattlegroundAV.h"
@@ -22,11 +24,12 @@
#include "BattlegroundSA.h"
#include "BattlegroundWS.h"
#include "Event.h"
#include "GameObject.h"
#include "IVMapMgr.h"
#include "PathGenerator.h"
#include "Playerbots.h"
#include "PositionValue.h"
#include "PvpTriggers.h"
#include "PathGenerator.h"
#include "ServerFacade.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.GetPositionZ());
}
else // BB_WSG_WAIT_SPOT_SPAWN
else // BB_WSG_WAIT_SPOT_SPAWN
{
if (bot->GetTeamId() == TEAM_HORDE)
MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f),
@@ -3365,12 +3368,12 @@ bool BGTactics::resetObjective()
return false;
// Adjust role-change chance based on battleground type
uint32 oddsToChangeRole = 1; // default low
uint32 oddsToChangeRole = 1; // default low
BattlegroundTypeId bgType = bg->GetBgTypeID();
if (bgType == BATTLEGROUND_WS)
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;
else if (bgType == BATTLEGROUND_AV)
oddsToChangeRole = 0;
@@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
GuidVector closePlayers;
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
switch (bgType)
{
@@ -3607,27 +3620,82 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (closeObjects.empty())
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
GameObject* targetFlag = nullptr;
for (ObjectGuid const guid : closeObjects)
{
GameObject* go = botAI->GetGameObject(guid);
if (!go)
{
continue;
}
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
// 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())
std::vector<uint32>::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
if (f == vFlagIds.end() && !isEyCenterFlag)
{
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;
@@ -3655,7 +3723,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
}
// 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
uint32 numCapturing = 0;
@@ -3664,14 +3732,17 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
{
if (Unit* pFriend = botAI->GetUnit(guid))
{
// Check if they're casting the capture spell
if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL))
// Check if they're casting or channeling the capture spell
Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell)
{
if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
{
numCapturing++;
capturingPlayer = pFriend;
}
spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
}
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)
continue;
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
// Validate this is a capture target
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
if (f == vFlagIds.end())
std::vector<uint32>::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
if (f == vFlagIds.end() && !isEyCenterFlag)
continue;
// Check object is active
@@ -3722,12 +3795,40 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
continue;
// 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;
bool isWsBaseFlag = bgType == BATTLEGROUND_WS && go->GetEntry() == vFlagsWS[bot->GetTeamId()];
bool isEyBaseFlag = bgType == BATTLEGROUND_EY && go->GetEntry() == vFlagsEY[0];
// 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
if (atBase && bgType == BATTLEGROUND_WS &&
if (isWsBaseFlag && bgType == BATTLEGROUND_WS &&
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
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;
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
@@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
if (dist < INTERACTION_DISTANCE)
{
// Handle flag capture at base
if (atBase)
if (isWsBaseFlag)
{
if (bot->GetTeamId() == TEAM_HORDE)
{
@@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
}
}
case BATTLEGROUND_EY:
{ // Issue: Currently bots in EY take flag instantly without casttime
{ // Handle Netherstorm flag capture requiring a channel
if (dist < INTERACTION_DISTANCE)
{
// Dismount before interacting
if (bot->IsMounted())
{
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
}
if (bot->IsInDisallowedMountForm())
{
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
}
// 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);
if (!spellInfo)
return false;
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
spell->m_targets.SetGOTarget(go);
bot->StopMoving();
spell->prepare(&spell->m_targets);
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
@@ -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());
}
}
default:
break;
default:
break;
}
}