mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Compare commits
2 Commits
cadbcbd447
...
ce2a990495
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce2a990495 | ||
|
|
6effabfa42 |
47
README.md
47
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user