mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 00:58:33 +00:00
Compare commits
27 Commits
revert-149
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35b52b65ba | ||
|
|
0a88aa9abe | ||
|
|
bcc173d920 | ||
|
|
24d4c1f0a0 | ||
|
|
4c87a04201 | ||
|
|
f791ab61c4 | ||
|
|
3260ca1429 | ||
|
|
e1fa733aa5 | ||
|
|
525eceb5a2 | ||
|
|
bd13d6be80 | ||
|
|
d0ac9452f4 | ||
|
|
8a30d10617 | ||
|
|
5a0c27637e | ||
|
|
cea1e90f57 | ||
|
|
1fb66e9d75 | ||
|
|
31ed5cbb65 | ||
|
|
5b128b3300 | ||
|
|
5681f29060 | ||
|
|
cf4f0f6dc7 | ||
|
|
e00c8fca2a | ||
|
|
c90b155a70 | ||
|
|
780f6d60e0 | ||
|
|
1faf20f567 | ||
|
|
d26c2a3549 | ||
|
|
21ea3a7226 | ||
|
|
377ac199a7 | ||
|
|
387c491265 |
4
.github/workflows/core_build.yml
vendored
4
.github/workflows/core_build.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: Checkout AzerothCore
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'liyunfan1223/azerothcore-wotlk'
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
|
||||
- name: Set reusable strings
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'liyunfan1223/mod-playerbots'
|
||||
repository: 'mod-playerbots/mod-playerbots'
|
||||
path: 'modules/mod-playerbots'
|
||||
|
||||
- name: Cache
|
||||
|
||||
4
.github/workflows/macos_build.yml
vendored
4
.github/workflows/macos_build.yml
vendored
@@ -22,12 +22,12 @@ jobs:
|
||||
- name: Checkout AzerothCore
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'liyunfan1223/azerothcore-wotlk'
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'liyunfan1223/mod-playerbots'
|
||||
repository: 'mod-playerbots/mod-playerbots'
|
||||
path: 'modules/mod-playerbots'
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
|
||||
4
.github/workflows/windows_build.yml
vendored
4
.github/workflows/windows_build.yml
vendored
@@ -23,12 +23,12 @@ jobs:
|
||||
- name: Checkout AzerothCore
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'liyunfan1223/azerothcore-wotlk'
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'liyunfan1223/mod-playerbots'
|
||||
repository: 'mod-playerbots/mod-playerbots'
|
||||
path: 'modules/mod-playerbots'
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
|
||||
36
README.md
36
README.md
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
|
||||
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README.md">English</a>
|
||||
|
|
||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
|
||||
<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>
|
||||
</p>
|
||||
@@ -12,13 +12,13 @@
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
|
||||
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
|
||||
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
|
||||
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
|
||||
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
|
||||
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
|
||||
</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: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/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) 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).
|
||||
|
||||
Features include:
|
||||
|
||||
@@ -28,7 +28,7 @@ Features include:
|
||||
- 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/liyunfan1223/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
|
||||
**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!
|
||||
|
||||
@@ -36,12 +36,12 @@ Features include:
|
||||
|
||||
### Classic Installation
|
||||
|
||||
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
|
||||
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:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||
cd azerothcore-wotlk/modules
|
||||
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
|
||||
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
|
||||
```
|
||||
|
||||
For more information, refer to the [AzerothCore Installation Guide](https://www.azerothcore.org/wiki/installation) and [Installing a Module](https://www.azerothcore.org/wiki/installing-a-module) pages.
|
||||
@@ -51,9 +51,9 @@ For more information, refer to the [AzerothCore Installation Guide](https://www.
|
||||
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||
cd azerothcore-wotlk/modules
|
||||
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
|
||||
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
|
||||
```
|
||||
|
||||
Afterwards, create a `docker-compose.override.yml` file in the `azerothcore-wotlk` directory. This override file allows for mounting the modules directory to the `ac-worldserver` service which is required for it to run. Put the following inside and save:
|
||||
@@ -77,21 +77,21 @@ services:
|
||||
- ./modules:/azerothcore/modules:ro
|
||||
```
|
||||
|
||||
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/liyunfan1223/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
|
||||
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/mod-playerbots/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
|
||||
|
||||
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
|
||||
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
|
||||
|
||||
Use `docker compose up -d --build` to build and run the server. For more information, including how to create an account and taking backups, refer to the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) page.
|
||||
|
||||
## Documentation
|
||||
|
||||
The [Playerbots Wiki](https://github.com/liyunfan1223/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. Contributions are welcome.
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
- **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/liyunfan1223/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/liyunfan1223/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
|
||||
- **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.
|
||||
|
||||
## Addons
|
||||
|
||||
@@ -107,6 +107,6 @@ Typically, bots are controlled via chat commands. For larger bot groups, this ca
|
||||
|
||||
Also, a thank you to the many contributors who've helped build this project:
|
||||
|
||||
<a href="https://github.com/liyunfan1223/mod-playerbots/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=liyunfan1223/mod-playerbots" />
|
||||
<a href="https://github.com/mod-playerbots/mod-playerbots/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=mod-playerbots/mod-playerbots" />
|
||||
</a>
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
## 安装
|
||||
|
||||
请注意,此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性,您必须使用我fork的自定义分支来编译它,可以在这里找到:[liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot)。
|
||||
请注意,此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性,您必须使用我fork的自定义分支来编译它,可以在这里找到:[mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot)。
|
||||
|
||||
要安装此模块,请参考AzerothCore Wiki的详细说明:[AzerothCore安装指南](https://www.azerothcore.org/wiki/installation)。
|
||||
|
||||
我们提供了一个简单的方法来克隆该模块:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||
cd azerothcore-wotlk/modules
|
||||
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
|
||||
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
|
||||
```
|
||||
|
||||
## 快速开始与文档
|
||||
@@ -60,7 +60,7 @@ git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
|
||||
|
||||
- 我们支持Ubuntu、Windows和macOS。
|
||||
|
||||
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/liyunfan1223/mod-playerbots/actions)中查看构建状态。
|
||||
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/mod-playerbots/mod-playerbots/actions)中查看构建状态。
|
||||
|
||||
- 如果最新的构建状态失败,请恢复到上一个提交。我们将尽快解决此问题。
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Overview
|
||||
# "Randombot": randomly generated bots that log in separately from players and populate the world. Depending on settings, randombots may automatically grind, quest, and upgrade equipment and can be invited to groups and given commands.
|
||||
# "Altbot": characters created on player accounts, which may be logged in by the player and invited to groups and given commands like randombots. Depending on settings, altbots can be limited to characters on the active player's account or in the active player's guild.
|
||||
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/liyunfan1223/mod-playerbots/wiki/Playerbot-Commands.
|
||||
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands.
|
||||
|
||||
####################################################################################################
|
||||
# SECTION INDEX
|
||||
@@ -284,9 +284,6 @@ AiPlayerbot.TwoRoundsGearInit = 0
|
||||
#
|
||||
#
|
||||
|
||||
# Bots will say information about items when collecting them
|
||||
AiPlayerbot.SayWhenCollectingItems = 1
|
||||
|
||||
# Bots keep looting when loot system is set to free for all
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.FreeMethodLoot = 0
|
||||
@@ -360,15 +357,15 @@ AiPlayerbot.LootDelay = 1000
|
||||
#
|
||||
#
|
||||
|
||||
# Distances are in yards
|
||||
AiPlayerbot.FarDistance = 20.0
|
||||
AiPlayerbot.SightDistance = 75.0
|
||||
AiPlayerbot.SightDistance = 100.0
|
||||
AiPlayerbot.SpellDistance = 28.5
|
||||
AiPlayerbot.ShootDistance = 5.0
|
||||
AiPlayerbot.ReactDistance = 150.0
|
||||
AiPlayerbot.GrindDistance = 75.0
|
||||
AiPlayerbot.HealDistance = 38.5
|
||||
AiPlayerbot.LootDistance = 15.0
|
||||
AiPlayerbot.FleeDistance = 5.0
|
||||
AiPlayerbot.AggroDistance = 22
|
||||
AiPlayerbot.TooCloseDistance = 5.0
|
||||
AiPlayerbot.MeleeDistance = 0.75
|
||||
AiPlayerbot.FollowDistance = 1.5
|
||||
@@ -376,7 +373,8 @@ AiPlayerbot.WhisperDistance = 6000.0
|
||||
AiPlayerbot.ContactDistance = 0.45
|
||||
AiPlayerbot.AoeRadius = 10
|
||||
AiPlayerbot.RpgDistance = 200
|
||||
AiPlayerbot.AggroDistance = 22
|
||||
AiPlayerbot.GrindDistance = 75.0
|
||||
AiPlayerbot.ReactDistance = 150.0
|
||||
|
||||
#
|
||||
#
|
||||
@@ -484,7 +482,8 @@ AiPlayerbot.RPWarningCooldown = 30
|
||||
#
|
||||
#
|
||||
|
||||
# Enable/Disable maintenance command (learn all available spells and skills, supplement consumables, repair, enchant equipment if bot's level is above AiPlayerbot.MinEnchantingBotLevel)
|
||||
# Enable/Disable maintenance command
|
||||
# Learn all available spells and skills, refresh consumables, repair, enchant equipment and socket gems if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.MaintenanceCommand = 1
|
||||
|
||||
@@ -548,7 +547,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
||||
# "mana" (bots have infinite mana)
|
||||
# "power" (bots have infinite energy, rage, and runic power)
|
||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||
# "raid" (bots use cheats implemented into raid strategies)
|
||||
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
|
||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
||||
# Default: food, taxi, and raid are enabled
|
||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||
@@ -597,29 +596,20 @@ AiPlayerbot.RandomBotRandomPassword = 0
|
||||
# Prefix for account names to create for randombots
|
||||
AiPlayerbot.RandomBotAccountPrefix = "rndbot"
|
||||
|
||||
# Minimum and maximum initialization levels for randombots
|
||||
# Minimum and maximum levels for randombots
|
||||
AiPlayerbot.RandomBotMinLevel = 1
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
|
||||
# Minimum and maximum level range for randombots allowed to login
|
||||
# If level filtration is used, minRandomBots and maxRandomBots might be automatically adjusted to lower values,
|
||||
# depending on available eligible bots in the database
|
||||
# Default Min,Max: 1,80 (no level filtration)
|
||||
AiPlayerbot.RandomBotMinLoginLevel = 1
|
||||
AiPlayerbot.RandomBotMaxLoginLevel = 80
|
||||
|
||||
# log-out randombots if they they level outside the allowed login range
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.RandomBotLogoutOutsideLoginRange = 0
|
||||
|
||||
# Sync max randombot level with max level of online players
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.SyncLevelWithPlayers = 0
|
||||
|
||||
# Mark many quests ≤ bot level as complete (slows down bot creation)
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.PreQuests = 0
|
||||
|
||||
# Enable LFG for randombots
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.RandomBotJoinLfg = 1
|
||||
|
||||
# Enable/Disable periodic online - offline of randombots to mimic the real-world scenario where not all players are online simultaneously
|
||||
@@ -640,7 +630,8 @@ AiPlayerbot.RandomBotHordeRatio = 50
|
||||
AiPlayerbot.DisableDeathKnightLogin = 0
|
||||
|
||||
# Enable simulated expansion limitation for talents and glyphs
|
||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61 and 7 rows plus the middle talent of the 8th row for bots until level 71
|
||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
||||
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.LimitTalentsExpansion = 0
|
||||
|
||||
@@ -724,8 +715,8 @@ AiPlayerbot.IncrementalGearInit = 1
|
||||
# Default: 60
|
||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||
|
||||
# Enable expansion limitation for bot enchants
|
||||
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants until level 71
|
||||
# Enable expansion limitation for bot enchants and gems
|
||||
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants and gems until level 71
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.LimitEnchantExpansion = 1
|
||||
|
||||
@@ -821,7 +812,7 @@ AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
|
||||
#
|
||||
|
||||
# Quest that will be completed and rewarded for all randombots
|
||||
AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,13188,13189,24499,24511,24710,24712"
|
||||
AiPlayerbot.RandomBotQuestIds = "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"
|
||||
|
||||
# Randombots will group with nearby randombots to do shared quests
|
||||
AiPlayerbot.RandomBotGroupNearby = 0
|
||||
@@ -882,9 +873,9 @@ AiPlayerbot.RandomBotNonCombatStrategies = ""
|
||||
AiPlayerbot.CombatStrategies = ""
|
||||
AiPlayerbot.NonCombatStrategies = ""
|
||||
|
||||
# Remove "healer dps" strategy on specified maps.
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.HealerDPSMapRestriction = 0
|
||||
# Remove "healer dps" strategy on the maps specified below.
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.HealerDPSMapRestriction = 1
|
||||
|
||||
# List of Map IDs where "healer dps" strategy will be removed if AiPlayerbot.HealerDPSMapRestriction is enabled
|
||||
# Default: (Dungeon and Raid maps) "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
|
||||
@@ -1068,7 +1059,8 @@ AiPlayerbot.ZoneBracket.4197 = 79,80
|
||||
#
|
||||
#
|
||||
|
||||
# Maps where bots can be teleported to
|
||||
# Map IDs where bots can be teleported to
|
||||
# Defaults: 0 = Eastern Kingdoms, 1 = Kalimdor, 530 = Outland, 571 = Northrend
|
||||
AiPlayerbot.RandomBotMaps = 0,1,530,571
|
||||
|
||||
# Probabilty bots teleport to banker (city)
|
||||
|
||||
1976
data/sql/playerbots/updates/2025_10_09_00_ai_playerbot_texts_fix.sql
Normal file
1976
data/sql/playerbots/updates/2025_10_09_00_ai_playerbot_texts_fix.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -378,10 +378,7 @@ void PlayerbotAI::UpdateAIGroupMembership()
|
||||
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
|
||||
if (leaderAI && !leaderAI->IsRealPlayer())
|
||||
{
|
||||
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
|
||||
bot->GetSession()->QueuePacket(packet);
|
||||
// bot->RemoveFromGroup();
|
||||
ResetStrategies();
|
||||
LeaveOrDisbandGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,10 +402,7 @@ void PlayerbotAI::UpdateAIGroupMembership()
|
||||
}
|
||||
if (!hasRealPlayer)
|
||||
{
|
||||
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
|
||||
bot->GetSession()->QueuePacket(packet);
|
||||
// bot->RemoveFromGroup();
|
||||
ResetStrategies();
|
||||
LeaveOrDisbandGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -791,6 +785,16 @@ void PlayerbotAI::Reset(bool full)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotAI::LeaveOrDisbandGroup()
|
||||
{
|
||||
if (!bot || !bot->GetGroup() || IsRealPlayer())
|
||||
return;
|
||||
|
||||
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
|
||||
bot->GetSession()->QueuePacket(packet);
|
||||
ResetStrategies();
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsAllowedCommand(std::string const text)
|
||||
{
|
||||
if (unsecuredCommands.empty())
|
||||
|
||||
@@ -415,6 +415,7 @@ public:
|
||||
void ResetStrategies(bool load = false);
|
||||
void ReInitCurrentEngine();
|
||||
void Reset(bool full = false);
|
||||
void LeaveOrDisbandGroup();
|
||||
static bool IsTank(Player* player, bool bySpec = false);
|
||||
static bool IsHeal(Player* player, bool bySpec = false);
|
||||
static bool IsDps(Player* player, bool bySpec = false);
|
||||
@@ -601,6 +602,7 @@ public:
|
||||
NewRpgInfo rpgInfo;
|
||||
NewRpgStatistic rpgStatistic;
|
||||
std::unordered_set<uint32> lowPriorityQuest;
|
||||
time_t bgReleaseAttemptTime = 0;
|
||||
|
||||
// Schedules a callback to run once after <delayMs> milliseconds.
|
||||
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
|
||||
|
||||
@@ -57,58 +57,54 @@ void LoadListString(std::string const value, T& list)
|
||||
|
||||
bool PlayerbotAIConfig::Initialize()
|
||||
{
|
||||
LOG_INFO("server.loading", "Initializing AI Playerbots by ike3, based on the original Playerbots by blueboy");
|
||||
LOG_INFO("server.loading", "Initializing mod-playerbots, based on AI Playerbots by ike and the original Playerbots by blueboy");
|
||||
|
||||
enabled = sConfigMgr->GetOption<bool>("AiPlayerbot.Enabled", true);
|
||||
if (!enabled)
|
||||
{
|
||||
LOG_INFO("server.loading", "AI Playerbots is Disabled in aiplayerbot.conf");
|
||||
LOG_INFO("server.loading", "Playerbots Module is disabled in playerbots.conf");
|
||||
return false;
|
||||
}
|
||||
|
||||
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 1500);
|
||||
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 500);
|
||||
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
|
||||
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
|
||||
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
|
||||
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
|
||||
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000);
|
||||
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 700);
|
||||
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
|
||||
dynamicReactDelay = sConfigMgr->GetOption<bool>("AiPlayerbot.DynamicReactDelay", true);
|
||||
passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
|
||||
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
|
||||
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
|
||||
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 100);
|
||||
rpgDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgDelay", 10000);
|
||||
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
|
||||
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
|
||||
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 20000);
|
||||
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 2000);
|
||||
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
|
||||
// Buff system
|
||||
minBotsForGreaterBuff = sConfigMgr->GetOption<int32>("AiPlayerbot.MinBotsForGreaterBuff", 3);
|
||||
rpWarningCooldown = sConfigMgr->GetOption<int32>("AiPlayerbot.RPWarningCooldown", 30);
|
||||
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
|
||||
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
|
||||
randomBotMinLoginLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMinLoginLevel", 1);
|
||||
randomBotMaxLoginLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLoginLevel", 80);
|
||||
randomBotLogoutOutsideLoginRange = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotLogoutOutsideLoginRange", false);
|
||||
|
||||
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
|
||||
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
|
||||
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 25.0f);
|
||||
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 25.0f);
|
||||
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 25.0f);
|
||||
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 100.0f);
|
||||
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 28.5f);
|
||||
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 5.0f);
|
||||
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 38.5f);
|
||||
lootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.LootDistance", 15.0f);
|
||||
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f);
|
||||
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 5.0f);
|
||||
aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f);
|
||||
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.0f);
|
||||
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 0.75f);
|
||||
followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
|
||||
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
|
||||
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f);
|
||||
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 5.0f);
|
||||
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.45f);
|
||||
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 10.0f);
|
||||
rpgDistance = sConfigMgr->GetOption<float>("AiPlayerbot.RpgDistance", 200.0f);
|
||||
grindDistance = sConfigMgr->GetOption<float>("AiPlayerbot.GrindDistance", 75.0f);
|
||||
reactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ReactDistance", 150.0f);
|
||||
|
||||
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 20);
|
||||
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 25);
|
||||
lowHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.LowHealth", 45);
|
||||
mediumHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumHealth", 65);
|
||||
almostFullHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.AlmostFullHealth", 85);
|
||||
@@ -119,7 +115,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
|
||||
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
|
||||
maxAoeAvoidRadius = sConfigMgr->GetOption<float>("AiPlayerbot.MaxAoeAvoidRadius", 15.0f);
|
||||
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810"),
|
||||
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810,29946"),
|
||||
aoeAvoidSpellWhitelist);
|
||||
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
|
||||
|
||||
@@ -132,7 +128,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.1f);
|
||||
randomBotRpgChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotRpgChance", 0.20f);
|
||||
|
||||
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);
|
||||
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 10);
|
||||
|
||||
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
|
||||
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
|
||||
@@ -146,11 +142,11 @@ bool PlayerbotAIConfig::Initialize()
|
||||
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
|
||||
probTeleToBankers = sConfigMgr->GetOption<float>("AiPlayerbot.ProbTeleToBankers", 0.25f);
|
||||
enableWeightTeleToCityBankers = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableWeightTeleToCityBankers", false);
|
||||
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 1);
|
||||
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 2);
|
||||
weightTeleToIronforge = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToIronforgeWeight", 1);
|
||||
weightTeleToDarnassus = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDarnassusWeight", 1);
|
||||
weightTeleToExodar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToExodarWeight", 1);
|
||||
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 1);
|
||||
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 2);
|
||||
weightTeleToUndercity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToUndercityWeight", 1);
|
||||
weightTeleToThunderBluff = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToThunderBluffWeight", 1);
|
||||
weightTeleToSilvermoonCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToSilvermoonCityWeight", 1);
|
||||
@@ -158,7 +154,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
weightTeleToDalaran = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDalaranWeight", 1);
|
||||
LoadList<std::vector<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestItems",
|
||||
"6948,5175,5176,5177,5178,16309,12382,13704,11000"),
|
||||
"5175,5176,5177,5178,6948,11000,12382,13704,16309"),
|
||||
randomBotQuestItems);
|
||||
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotSpellIds", "54197"),
|
||||
randomBotSpellIds);
|
||||
@@ -173,7 +169,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
pvpProhibitedAreaIds);
|
||||
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
||||
LoadList<std::vector<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
|
||||
randomBotQuestIds);
|
||||
|
||||
LoadSet<std::set<uint32>>(
|
||||
@@ -184,8 +180,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
disallowedGameObjects);
|
||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
|
||||
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 50);
|
||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
|
||||
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 500);
|
||||
randomBotUpdateInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotUpdateInterval", 20);
|
||||
randomBotCountChangeMinInterval =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE);
|
||||
@@ -394,8 +390,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "");
|
||||
randomBotNonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotNonCombatStrategies", "");
|
||||
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "+custom::say");
|
||||
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
|
||||
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "");
|
||||
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "");
|
||||
applyInstanceStrategies = sConfigMgr->GetOption<bool>("AiPlayerbot.ApplyInstanceStrategies", true);
|
||||
|
||||
commandPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.CommandPrefix", "");
|
||||
@@ -515,7 +511,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
randomBotGuildSizeMax = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildSizeMax", 15);
|
||||
deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false);
|
||||
|
||||
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", true);
|
||||
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", false);
|
||||
minGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskChangeTime", 3 * 24 * 3600);
|
||||
maxGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskChangeTime", 4 * 24 * 3600);
|
||||
minGuildTaskAdvertisementTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskAdvertisementTime", 300);
|
||||
@@ -601,12 +597,12 @@ bool PlayerbotAIConfig::Initialize()
|
||||
minEnchantingBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.MinEnchantingBotLevel", 60);
|
||||
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 1);
|
||||
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 1);
|
||||
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 5);
|
||||
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 1);
|
||||
enablePeriodicOnlineOffline = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePeriodicOnlineOffline", false);
|
||||
enableRandomBotTrading = sConfigMgr->GetOption<int32>("AiPlayerbot.EnableRandomBotTrading", 1);
|
||||
periodicOnlineOfflineRatio = sConfigMgr->GetOption<float>("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0);
|
||||
gearscorecheck = sConfigMgr->GetOption<bool>("AiPlayerbot.GearScoreCheck", false);
|
||||
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", true);
|
||||
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", false);
|
||||
|
||||
// SPP automation
|
||||
freeMethodLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeMethodLoot", false);
|
||||
@@ -639,7 +635,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
|
||||
|
||||
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
|
||||
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
|
||||
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", false);
|
||||
|
||||
// arena
|
||||
randomBotArenaTeam2v2Count = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotArenaTeam2v2Count", 10);
|
||||
@@ -682,7 +678,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.ExcludedHunterPetFamilies", ""), excludedHunterPetFamilies);
|
||||
|
||||
LOG_INFO("server.loading", "---------------------------------------");
|
||||
LOG_INFO("server.loading", " AI Playerbots initialized ");
|
||||
LOG_INFO("server.loading", " mod-playerbots initialized ");
|
||||
LOG_INFO("server.loading", "---------------------------------------");
|
||||
|
||||
return true;
|
||||
|
||||
@@ -247,8 +247,6 @@ public:
|
||||
std::string randomBotCombatStrategies, randomBotNonCombatStrategies;
|
||||
bool applyInstanceStrategies;
|
||||
uint32 randomBotMinLevel, randomBotMaxLevel;
|
||||
uint32 randomBotMinLoginLevel, randomBotMaxLoginLevel;
|
||||
bool randomBotLogoutOutsideLoginRange;
|
||||
float randomChangeMultiplier;
|
||||
|
||||
// std::string premadeLevelSpec[MAX_CLASSES][10][91]; //lvl 10 - 100
|
||||
|
||||
@@ -227,6 +227,12 @@ void PlayerbotHolder::HandleBotPackets(WorldSession* session)
|
||||
{
|
||||
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
||||
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
|
||||
if (!opHandle)
|
||||
{
|
||||
LOG_ERROR("playerbots", "Unhandled opcode {} queued for bot session {}. Packet dropped.", static_cast<uint32>(opcode), session->GetAccountId());
|
||||
delete packet;
|
||||
continue;
|
||||
}
|
||||
opHandle->Call(session, *packet);
|
||||
delete packet;
|
||||
}
|
||||
@@ -514,7 +520,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
|
||||
if (!groupValid)
|
||||
{
|
||||
bot->RemoveFromGroup();
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
||||
PLAYERHOOK_ON_GIVE_EXP,
|
||||
PLAYERHOOK_ON_LEVEL_CHANGED
|
||||
PLAYERHOOK_ON_BEFORE_TELEPORT
|
||||
}) {}
|
||||
|
||||
void OnPlayerLogin(Player* player) override
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
{
|
||||
ChatHandler(player->GetSession()).SendSysMessage(
|
||||
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
|
||||
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
|
||||
"|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r");
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
|
||||
@@ -122,31 +122,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerLevelChanged(Player* player, uint8 oldLevel) override
|
||||
bool OnPlayerBeforeTeleport(Player* player, uint32 mapid, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
|
||||
{
|
||||
// Check if feature is enabled and required objects are valid
|
||||
if (!sPlayerbotAIConfig || !sPlayerbotAIConfig->randomBotLogoutOutsideLoginRange || !sRandomPlayerbotMgr)
|
||||
return;
|
||||
// Only apply to bots to prevent affecting real players
|
||||
if (!player || !player->GetSession()->IsBot())
|
||||
return true;
|
||||
|
||||
// Only apply to bots from rndBotTypeAccounts (type 1)
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
if (!sRandomPlayerbotMgr->IsAccountType(accountId, 1))
|
||||
return;
|
||||
|
||||
uint32 newLevel = player->GetLevel();
|
||||
|
||||
// Check if the new level is outside the allowed login range
|
||||
if (newLevel < sPlayerbotAIConfig->randomBotMinLoginLevel ||
|
||||
newLevel > sPlayerbotAIConfig->randomBotMaxLoginLevel)
|
||||
// If changing maps, proactively clean visibility references to prevent
|
||||
// stale pointers in other players' visibility maps during the teleport.
|
||||
// This fixes a race condition where:
|
||||
// 1. Bot A teleports and its visible objects start getting cleaned up
|
||||
// 2. Bot B is simultaneously updating visibility and tries to access objects in Bot A's old visibility map
|
||||
// 3. Those objects may already be freed, causing a segmentation fault
|
||||
if (player->GetMapId() != mapid && player->IsInWorld())
|
||||
{
|
||||
LOG_INFO("playerbots", "Bot {} changed levels from {} to {}, outside login range ({}-{}). Marking for logout",
|
||||
player->GetName(), oldLevel, newLevel,
|
||||
sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel);
|
||||
|
||||
// Mark the bot for removal in the next update cycle
|
||||
sRandomPlayerbotMgr->MarkBotForLogout(player->GetGUID().GetCounter());
|
||||
sRandomPlayerbotMgr->ForceRecount();
|
||||
player->GetObjectVisibilityContainer().CleanVisibilityReferences();
|
||||
}
|
||||
|
||||
return true; // Allow teleport to continue
|
||||
}
|
||||
|
||||
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
|
||||
@@ -323,7 +316,7 @@ public:
|
||||
LOG_INFO("server.loading", "║ mod-playerbots is a community-driven open-source ║");
|
||||
LOG_INFO("server.loading", "║ project based on AzerothCore, licensed under AGPLv3.0 ║");
|
||||
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
|
||||
LOG_INFO("server.loading", "║ https://github.com/liyunfan1223/mod-playerbots ║");
|
||||
LOG_INFO("server.loading", "║ https://github.com/mod-playerbots/mod-playerbots ║");
|
||||
LOG_INFO("server.loading", "╚══════════════════════════════════════════════════════════╝");
|
||||
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <climits>
|
||||
|
||||
#include "AccountMgr.h"
|
||||
#include "AiFactory.h"
|
||||
@@ -375,76 +374,13 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
}*/
|
||||
|
||||
uint32 maxAllowedBotCount = GetEventValue(0, "bot_count");
|
||||
|
||||
// Check if level filtering is active and populate eligible bots
|
||||
if (!levelFilterAdjusted && IsLevelFilterActive())
|
||||
if (!maxAllowedBotCount || (maxAllowedBotCount < sPlayerbotAIConfig->minRandomBots ||
|
||||
maxAllowedBotCount > sPlayerbotAIConfig->maxRandomBots))
|
||||
{
|
||||
PopulateEligibleBots();
|
||||
|
||||
// Count total eligible bots from RNDbot accounts
|
||||
uint32 eligibleBotCount = 0;
|
||||
|
||||
for (uint32 accountId : rndBotTypeAccounts)
|
||||
{
|
||||
QueryResult result = CharacterDatabase.Query(
|
||||
"SELECT COUNT(*) FROM characters WHERE account = {} AND level >= {} AND level <= {}",
|
||||
accountId, sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel
|
||||
);
|
||||
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
eligibleBotCount += fields[0].Get<uint32>();
|
||||
}
|
||||
}
|
||||
|
||||
if (eligibleBotCount > 0)
|
||||
{
|
||||
// Cap eligible bots by maxRandomBots
|
||||
uint32 effectiveMaxBots = std::min(eligibleBotCount, sPlayerbotAIConfig->maxRandomBots);
|
||||
|
||||
LOG_INFO("playerbots", "Level filter active: {} eligible bots found in range {}-{}. Effective maximum: {} bots.",
|
||||
eligibleBotCount, sPlayerbotAIConfig->randomBotMinLoginLevel,
|
||||
sPlayerbotAIConfig->randomBotMaxLoginLevel, effectiveMaxBots);
|
||||
|
||||
if (effectiveMaxBots >= sPlayerbotAIConfig->minRandomBots)
|
||||
{
|
||||
maxAllowedBotCount = urand(sPlayerbotAIConfig->minRandomBots, effectiveMaxBots);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxAllowedBotCount = effectiveMaxBots;
|
||||
}
|
||||
|
||||
SetEventValue(0, "bot_count", maxAllowedBotCount,
|
||||
urand(sPlayerbotAIConfig->randomBotCountChangeMinInterval,
|
||||
sPlayerbotAIConfig->randomBotCountChangeMaxInterval));
|
||||
|
||||
currentBots.clear();
|
||||
levelFilterAdjusted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("playerbots", "No eligible bots found with level filter {}-{}. Change the level range.",
|
||||
sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel);
|
||||
SetEventValue(0, "bot_count", 0, INT_MAX);
|
||||
maxAllowedBotCount = 0;
|
||||
currentBots.clear();
|
||||
levelFilterAdjusted = true;
|
||||
}
|
||||
}
|
||||
else if (!levelFilterAdjusted)
|
||||
{
|
||||
// Normal bot count logic (no level filtering)
|
||||
if (!maxAllowedBotCount || (maxAllowedBotCount < sPlayerbotAIConfig->minRandomBots ||
|
||||
maxAllowedBotCount > sPlayerbotAIConfig->maxRandomBots))
|
||||
{
|
||||
maxAllowedBotCount = urand(sPlayerbotAIConfig->minRandomBots, sPlayerbotAIConfig->maxRandomBots);
|
||||
SetEventValue(0, "bot_count", maxAllowedBotCount,
|
||||
urand(sPlayerbotAIConfig->randomBotCountChangeMinInterval,
|
||||
sPlayerbotAIConfig->randomBotCountChangeMaxInterval));
|
||||
}
|
||||
levelFilterAdjusted = true;
|
||||
maxAllowedBotCount = urand(sPlayerbotAIConfig->minRandomBots, sPlayerbotAIConfig->maxRandomBots);
|
||||
SetEventValue(0, "bot_count", maxAllowedBotCount,
|
||||
urand(sPlayerbotAIConfig->randomBotCountChangeMinInterval,
|
||||
sPlayerbotAIConfig->randomBotCountChangeMaxInterval));
|
||||
}
|
||||
|
||||
GetBots();
|
||||
@@ -836,41 +772,23 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
||||
|
||||
for (uint32 accountId : accountsToUse)
|
||||
{
|
||||
// Lambda to process results regardless of query type
|
||||
auto processResults = [&](auto result)
|
||||
{
|
||||
if (!result)
|
||||
return;
|
||||
CharacterDatabasePreparedStatement* stmt =
|
||||
CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
|
||||
stmt->SetData(0, accountId);
|
||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||
if (!result)
|
||||
continue;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
CharacterInfo info;
|
||||
info.guid = fields[0].Get<uint32>();
|
||||
info.rClass = fields[1].Get<uint8>();
|
||||
info.rRace = fields[2].Get<uint8>();
|
||||
info.accountId = accountId;
|
||||
allCharacters.push_back(info);
|
||||
} while (result->NextRow());
|
||||
};
|
||||
|
||||
if (IsLevelFilterActive())
|
||||
do
|
||||
{
|
||||
// Custom query with level filtering
|
||||
auto result = CharacterDatabase.Query(
|
||||
"SELECT guid, class, race FROM characters WHERE account = {} AND level >= {} AND level <= {}",
|
||||
accountId, sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel
|
||||
);
|
||||
processResults(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt =
|
||||
CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
|
||||
stmt->SetData(0, accountId);
|
||||
auto result = CharacterDatabase.Query(stmt);
|
||||
processResults(result);
|
||||
}
|
||||
Field* fields = result->Fetch();
|
||||
CharacterInfo info;
|
||||
info.guid = fields[0].Get<uint32>();
|
||||
info.rClass = fields[1].Get<uint8>();
|
||||
info.rRace = fields[2].Get<uint8>();
|
||||
info.accountId = accountId;
|
||||
allCharacters.push_back(info);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
// Shuffle for class balance
|
||||
@@ -1599,33 +1517,38 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RandomPlayerbotMgr::ProcessBot(Player* player)
|
||||
bool RandomPlayerbotMgr::ProcessBot(Player* bot)
|
||||
{
|
||||
uint32 bot = player->GetGUID().GetCounter();
|
||||
|
||||
if (player->InBattleground())
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
return false;
|
||||
|
||||
if (player->InBattlegroundQueue())
|
||||
if (bot->InBattleground())
|
||||
return false;
|
||||
|
||||
if (bot->InBattlegroundQueue())
|
||||
return false;
|
||||
|
||||
uint32 botId = bot->GetGUID().GetCounter();
|
||||
|
||||
// if death revive
|
||||
if (player->isDead())
|
||||
if (bot->isDead())
|
||||
{
|
||||
if (!GetEventValue(bot, "dead"))
|
||||
if (!GetEventValue(botId, "dead"))
|
||||
{
|
||||
uint32 randomTime =
|
||||
urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime);
|
||||
LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", player->GetName().c_str(),
|
||||
LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", bot->GetName().c_str(),
|
||||
randomTime);
|
||||
SetEventValue(bot, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime);
|
||||
SetEventValue(bot, "revive", 1, randomTime);
|
||||
SetEventValue(botId, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime);
|
||||
SetEventValue(botId, "revive", 1, randomTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetEventValue(bot, "revive"))
|
||||
if (!GetEventValue(botId, "revive"))
|
||||
{
|
||||
Revive(player);
|
||||
Revive(bot);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1633,34 +1556,31 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
|
||||
}
|
||||
|
||||
// leave group if leader is rndbot
|
||||
Group* group = player->GetGroup();
|
||||
Group* group = bot->GetGroup();
|
||||
if (group && !group->isLFGGroup() && IsRandomBot(group->GetLeader()))
|
||||
{
|
||||
player->RemoveFromGroup();
|
||||
LOG_INFO("playerbots", "Bot {} remove from group since leader is random bot.", player->GetName().c_str());
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
LOG_INFO("playerbots", "Bot {} remove from group since leader is random bot.", bot->GetName().c_str());
|
||||
}
|
||||
|
||||
// only randomize and teleport idle bots
|
||||
bool idleBot = false;
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (botAI)
|
||||
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
|
||||
{
|
||||
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
|
||||
{
|
||||
if (target->getTravelState() == TravelState::TRAVEL_STATE_IDLE)
|
||||
{
|
||||
idleBot = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (target->getTravelState() == TravelState::TRAVEL_STATE_IDLE)
|
||||
{
|
||||
idleBot = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
idleBot = true;
|
||||
}
|
||||
|
||||
if (idleBot)
|
||||
{
|
||||
// randomize
|
||||
uint32 randomize = GetEventValue(bot, "randomize");
|
||||
uint32 randomize = GetEventValue(botId, "randomize");
|
||||
if (!randomize)
|
||||
{
|
||||
// bool randomiser = true;
|
||||
@@ -1684,12 +1604,12 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
|
||||
// }
|
||||
// if (randomiser)
|
||||
// {
|
||||
Randomize(player);
|
||||
LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", bot,
|
||||
player->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", player->GetLevel(), player->GetName());
|
||||
Randomize(bot);
|
||||
LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", botId,
|
||||
bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName());
|
||||
uint32 randomTime =
|
||||
urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime);
|
||||
ScheduleRandomize(bot, randomTime);
|
||||
ScheduleRandomize(botId, randomTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1701,15 +1621,15 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
|
||||
// return true;
|
||||
// }
|
||||
|
||||
uint32 teleport = GetEventValue(bot, "teleport");
|
||||
uint32 teleport = GetEventValue(botId, "teleport");
|
||||
if (!teleport)
|
||||
{
|
||||
LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", bot, player->GetName());
|
||||
Refresh(player);
|
||||
RandomTeleportForLevel(player);
|
||||
LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", botId, bot->GetName());
|
||||
Refresh(bot);
|
||||
RandomTeleportForLevel(bot);
|
||||
uint32 time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval,
|
||||
sPlayerbotAIConfig->maxRandomBotTeleportInterval);
|
||||
ScheduleTeleport(bot, time);
|
||||
ScheduleTeleport(botId, time);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1853,6 +1773,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (botAI)
|
||||
botAI->Reset(true);
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(loc.GetMapId(), x, y, z, 0);
|
||||
bot->SendMovementFlagUpdate();
|
||||
|
||||
@@ -2458,6 +2379,10 @@ void RandomPlayerbotMgr::IncreaseLevel(Player* bot)
|
||||
|
||||
void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
return;
|
||||
|
||||
uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel;
|
||||
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
@@ -2515,7 +2440,6 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
}
|
||||
|
||||
SetValue(bot, "level", level);
|
||||
|
||||
PlayerbotFactory factory(bot, level);
|
||||
factory.Randomize(false);
|
||||
|
||||
@@ -2537,11 +2461,10 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
PlayerbotsDatabase.Execute(stmt);
|
||||
|
||||
// teleport to a random inn for bot level
|
||||
if (GET_PLAYERBOT_AI(bot))
|
||||
GET_PLAYERBOT_AI(bot)->Reset(true);
|
||||
botAI->Reset(true);
|
||||
|
||||
if (bot->GetGroup())
|
||||
bot->RemoveFromGroup();
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
@@ -2551,12 +2474,13 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
|
||||
void RandomPlayerbotMgr::RandomizeMin(Player* bot)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
return;
|
||||
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
|
||||
|
||||
uint32 level = sPlayerbotAIConfig->randomBotMinLevel;
|
||||
|
||||
SetValue(bot, "level", level);
|
||||
|
||||
PlayerbotFactory factory(bot, level);
|
||||
factory.Randomize(false);
|
||||
|
||||
@@ -2578,11 +2502,10 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot)
|
||||
PlayerbotsDatabase.Execute(stmt);
|
||||
|
||||
// teleport to a random inn for bot level
|
||||
if (GET_PLAYERBOT_AI(bot))
|
||||
GET_PLAYERBOT_AI(bot)->Reset(true);
|
||||
botAI->Reset(true);
|
||||
|
||||
if (bot->GetGroup())
|
||||
bot->RemoveFromGroup();
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
@@ -2664,7 +2587,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
||||
bot->SetMoney(money + 500 * sqrt(urand(1, bot->GetLevel() * 5)));
|
||||
|
||||
if (bot->GetGroup())
|
||||
bot->RemoveFromGroup();
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
@@ -2752,55 +2675,17 @@ void RandomPlayerbotMgr::GetBots()
|
||||
stmt->SetData(0, 0);
|
||||
stmt->SetData(1, "add");
|
||||
uint32 maxAllowedBotCount = GetEventValue(0, "bot_count");
|
||||
|
||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 bot = fields[0].Get<uint32>();
|
||||
|
||||
if (GetEventValue(bot, "add"))
|
||||
{
|
||||
// Single query to get both account and level
|
||||
QueryResult charResult = CharacterDatabase.Query("SELECT account, level FROM characters WHERE guid = {}", bot);
|
||||
if (!charResult)
|
||||
continue;
|
||||
|
||||
Field* charFields = charResult->Fetch();
|
||||
uint32 botAccountId = charFields[0].Get<uint32>();
|
||||
uint32 botLevel = charFields[1].Get<uint32>();
|
||||
|
||||
// Skip if not an RNDbot account
|
||||
bool isRndBotAccount = false;
|
||||
for (uint32 accountId : rndBotTypeAccounts)
|
||||
{
|
||||
if (accountId == botAccountId)
|
||||
{
|
||||
isRndBotAccount = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRndBotAccount)
|
||||
continue;
|
||||
|
||||
// If level filtering is active, check bot level
|
||||
if (IsLevelFilterActive())
|
||||
{
|
||||
// Skip bots outside the allowed level range
|
||||
if (botLevel < sPlayerbotAIConfig->randomBotMinLoginLevel ||
|
||||
botLevel > sPlayerbotAIConfig->randomBotMaxLoginLevel)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
currentBots.push_back(bot);
|
||||
|
||||
if (currentBots.size() >= maxAllowedBotCount)
|
||||
break;
|
||||
}
|
||||
if (currentBots.size() >= maxAllowedBotCount)
|
||||
break;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
@@ -2828,51 +2713,6 @@ std::vector<uint32> RandomPlayerbotMgr::GetBgBots(uint32 bracket)
|
||||
return std::move(BgBots);
|
||||
}
|
||||
|
||||
void RandomPlayerbotMgr::PopulateEligibleBots()
|
||||
{
|
||||
if (!sPlayerbotAIConfig || !(sPlayerbotAIConfig->randomBotMinLoginLevel > 1 ||
|
||||
sPlayerbotAIConfig->randomBotMaxLoginLevel < 80))
|
||||
return;
|
||||
|
||||
LOG_INFO("playerbots", "Populating eligible bots for level filter ({}-{})...",
|
||||
sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel);
|
||||
|
||||
bool botsAdded = false;
|
||||
|
||||
// Use only RNDbot type accounts (type 1)
|
||||
for (uint32 accountId : rndBotTypeAccounts)
|
||||
{
|
||||
QueryResult result = CharacterDatabase.Query(
|
||||
"SELECT guid, level, online FROM characters WHERE account = {} AND level >= {} AND level <= {}",
|
||||
accountId, sPlayerbotAIConfig->randomBotMinLoginLevel, sPlayerbotAIConfig->randomBotMaxLoginLevel
|
||||
);
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 botGuid = fields[0].Get<uint32>();
|
||||
bool isOnline = fields[2].Get<bool>();
|
||||
|
||||
// Skip bots that are already online or already have "add" event
|
||||
if (isOnline || GetEventValue(botGuid, "add"))
|
||||
continue;
|
||||
|
||||
uint32 add_time = urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
|
||||
sPlayerbotAIConfig->maxRandomBotInWorldTime);
|
||||
SetEventValue(botGuid, "add", 1, add_time);
|
||||
botsAdded = true;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
if (botsAdded)
|
||||
{
|
||||
currentBots.clear(); // Force reload
|
||||
}
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const event)
|
||||
{
|
||||
// load all events at once on first event load
|
||||
@@ -2900,13 +2740,23 @@ uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const event)
|
||||
}
|
||||
|
||||
CachedEvent& e = eventCache[bot][event];
|
||||
/*if (e.IsEmpty())
|
||||
{
|
||||
QueryResult results = PlayerbotsDatabase.Query("SELECT `value`, `time`, validIn, `data` FROM
|
||||
playerbots_random_bots WHERE owner = 0 AND bot = {} AND event = {}", bot, event.c_str());
|
||||
|
||||
bool shouldExpire = (time(0) - e.lastChangeTime) >= e.validIn &&
|
||||
event != "specNo" &&
|
||||
event != "specLink" &&
|
||||
!(event == "bot_count" && IsLevelFilterActive()); // Don't expire bot_count when level filtering is active
|
||||
if (results)
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
e.value = fields[0].Get<uint32>();
|
||||
e.lastChangeTime = fields[1].Get<uint32>();
|
||||
e.validIn = fields[2].Get<uint32>();
|
||||
e.data = fields[3].Get<std::string>();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (shouldExpire)
|
||||
if ((time(0) - e.lastChangeTime) >= e.validIn && event != "specNo" && event != "specLink")
|
||||
e.value = 0;
|
||||
|
||||
return e.value;
|
||||
@@ -3245,6 +3095,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
||||
} while (true);
|
||||
}
|
||||
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
player->TeleportTo(botPos);
|
||||
|
||||
// player->Relocate(botPos.getX(), botPos.getY(), botPos.getZ(), botPos.getO());
|
||||
|
||||
@@ -193,14 +193,6 @@ public:
|
||||
void AssignAccountTypes();
|
||||
bool IsAccountType(uint32 accountId, uint8 accountType);
|
||||
|
||||
// Allowed login range management
|
||||
void ForceRecount() { SetEventValue(0, "bot_count", 0, 0); }
|
||||
void MarkBotForLogout(uint32 bot)
|
||||
{
|
||||
SetEventValue(bot, "add", 0, 0); // Clear the "add" event to trigger logout
|
||||
SetEventValue(bot, "logout", 1, 1); // Also set logout for clarity
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnBotLoginInternal(Player* const bot) override;
|
||||
|
||||
@@ -245,16 +237,6 @@ private:
|
||||
std::vector<uint32> rndBotTypeAccounts; // Accounts marked as RNDbot (type 1)
|
||||
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
|
||||
|
||||
// Login level filtering
|
||||
bool levelFilterAdjusted = false;
|
||||
void PopulateEligibleBots();
|
||||
bool IsLevelFilterActive() const
|
||||
{
|
||||
return sPlayerbotAIConfig &&
|
||||
(sPlayerbotAIConfig->randomBotMinLoginLevel > 1 ||
|
||||
sPlayerbotAIConfig->randomBotMaxLoginLevel < 80);
|
||||
}
|
||||
|
||||
//void ScaleBotActivity(); // Deprecated function
|
||||
};
|
||||
|
||||
|
||||
@@ -178,6 +178,9 @@ uint32 TravelNodePath::getPrice()
|
||||
|
||||
TaxiPathEntry const* taxiPath = sTaxiPathStore.LookupEntry(pathObject);
|
||||
|
||||
if (!taxiPath)
|
||||
return 0;
|
||||
|
||||
return taxiPath->price;
|
||||
}
|
||||
|
||||
|
||||
@@ -4287,7 +4287,7 @@ void PlayerbotFactory::ApplyEnchantTemplate(uint8 spec)
|
||||
// const SpellItemEnchantmentEntry* a = sSpellItemEnchantmentStore.LookupEntry(1);
|
||||
}
|
||||
|
||||
void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
|
||||
{
|
||||
//int32 bestGemEnchantId[4] = {-1, -1, -1, -1}; // 1, 2, 4, 8 color //not used, line marked for removal.
|
||||
//float bestGemScore[4] = {0, 0, 0, 0}; //not used, line marked for removal.
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
void InitFood();
|
||||
void InitMounts();
|
||||
void InitBags(bool destroyOld = true);
|
||||
void ApplyEnchantAndGemsNew(bool destoryOld = true);
|
||||
void ApplyEnchantAndGemsNew(bool destroyOld = true);
|
||||
void InitInstanceQuests();
|
||||
void UnbindInstance();
|
||||
void InitKeyring();
|
||||
|
||||
@@ -176,6 +176,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type)
|
||||
continue;
|
||||
|
||||
memberBotAI->Reset();
|
||||
member->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
|
||||
|
||||
LOG_INFO("playerbots", "Bot {} <{}>: Member of <{}>", member->GetGUID().ToString().c_str(),
|
||||
|
||||
@@ -4289,9 +4289,15 @@ bool ArenaTactics::moveToCenter(Battleground* bg)
|
||||
{
|
||||
// they like to hang around at the tip of the pipes doing nothing, so we just teleport them down
|
||||
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4)
|
||||
{
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
|
||||
}
|
||||
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4)
|
||||
{
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BATTLEGROUND_RV:
|
||||
|
||||
@@ -106,6 +106,7 @@ bool FollowChatShortcutAction::Execute(Event event)
|
||||
else
|
||||
botAI->TellMaster("You are too far away from me! I will there soon.");
|
||||
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(),
|
||||
master->GetOrientation()); return true;
|
||||
}
|
||||
|
||||
@@ -72,8 +72,10 @@ bool UninviteAction::Execute(Event event)
|
||||
|
||||
bool LeaveGroupAction::Leave(Player* player)
|
||||
{
|
||||
if (player && !GET_PLAYERBOT_AI(player) &&
|
||||
if (player &&
|
||||
!botAI &&
|
||||
!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player))
|
||||
|
||||
return false;
|
||||
|
||||
bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr;
|
||||
@@ -84,7 +86,7 @@ bool LeaveGroupAction::Leave(Player* player)
|
||||
bool shouldStay = randomBot && bot->GetGroup() && player == bot;
|
||||
if (!shouldStay)
|
||||
{
|
||||
bot->RemoveFromGroup();
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
}
|
||||
|
||||
if (randomBot)
|
||||
|
||||
@@ -210,7 +210,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
||||
mm.Clear();
|
||||
if (!backwards)
|
||||
{
|
||||
mm.MovePoint(0, x, y, z, generatePath);
|
||||
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -245,7 +245,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
||||
mm.Clear();
|
||||
if (!backwards)
|
||||
{
|
||||
mm.MovePoint(0, x, y, z, generatePath);
|
||||
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -287,7 +287,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
||||
mm.Clear();
|
||||
if (!backwards)
|
||||
{
|
||||
mm.MovePoint(0, x, y, z, generatePath);
|
||||
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1148,6 +1148,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
|
||||
if ((target->GetMap() && target->GetMap()->IsBattlegroundOrArena()) || (bot->GetMap() &&
|
||||
bot->GetMap()->IsBattlegroundOrArena())) return false;
|
||||
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(target->GetMapId(), x, y, z, bot->GetOrientation());
|
||||
}
|
||||
else
|
||||
@@ -1175,6 +1176,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
|
||||
|
||||
bot->CombatStop(true);
|
||||
botAI->TellMasterNoFacing("I will there soon.");
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),
|
||||
target->GetOrientation()); return false;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ bool AutoReleaseSpiritAction::HandleBattlegroundSpiritHealer()
|
||||
// and in IOC it's not within clicking range when they res in own base
|
||||
|
||||
// Teleport to nearest friendly Spirit Healer when not currently in range of one.
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f);
|
||||
RESET_AI_VALUE(bool, "combat::self target");
|
||||
RESET_AI_VALUE(WorldPosition, "current position");
|
||||
@@ -191,12 +192,11 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
|
||||
{
|
||||
// The below delays release to spirit with 6 seconds.
|
||||
// This prevents currently casted (ranged) spells to be re-directed to the died bot's ghost.
|
||||
const int32_t botId = bot->GetGUID().GetRawValue();
|
||||
|
||||
// If the bot already is a spirit, erase release time and return true
|
||||
// If the bot already is a spirit, reset release time and return true
|
||||
if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
|
||||
{
|
||||
m_botReleaseTimes.erase(botId);
|
||||
botAI->bgReleaseAttemptTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -204,14 +204,13 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
|
||||
const time_t now = time(nullptr);
|
||||
constexpr time_t RELEASE_DELAY = 6;
|
||||
|
||||
auto& lastReleaseTime = m_botReleaseTimes[botId];
|
||||
if (lastReleaseTime == 0)
|
||||
lastReleaseTime = now;
|
||||
if (botAI->bgReleaseAttemptTime == 0)
|
||||
botAI->bgReleaseAttemptTime = now;
|
||||
|
||||
if (now - lastReleaseTime < RELEASE_DELAY)
|
||||
if (now - botAI->bgReleaseAttemptTime < RELEASE_DELAY)
|
||||
return false;
|
||||
|
||||
m_botReleaseTimes.erase(botId);
|
||||
botAI->bgReleaseAttemptTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -244,6 +243,7 @@ int64 RepopAction::CalculateDeadTime() const
|
||||
|
||||
void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const
|
||||
{
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f);
|
||||
RESET_AI_VALUE(bool, "combat::self target");
|
||||
RESET_AI_VALUE(WorldPosition, "current position");
|
||||
|
||||
@@ -38,7 +38,6 @@ private:
|
||||
bool ShouldAutoRelease() const;
|
||||
bool ShouldDelayBattlegroundRelease() const;
|
||||
|
||||
inline static std::unordered_map<uint32_t, time_t> m_botReleaseTimes;
|
||||
time_t m_bgGossipTime = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ bool FindCorpseAction::Execute(Event event)
|
||||
if (deadTime > delay)
|
||||
{
|
||||
bot->GetMotionMaster()->Clear();
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
|
||||
}
|
||||
|
||||
@@ -350,6 +351,7 @@ bool SpiritHealerAction::Execute(Event event)
|
||||
// if (!botAI->HasActivePlayerMaster())
|
||||
// {
|
||||
context->GetValue<uint32>("death count")->Set(dCount + 1);
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
|
||||
// }
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
|
||||
player->GetMotionMaster()->Clear();
|
||||
AI_VALUE(LastMovement&, "last movement").clear();
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
player->TeleportTo(mapId, x, y, z, 0);
|
||||
|
||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||
|
||||
@@ -67,6 +67,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||
bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
|
||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
|
||||
zone_name);
|
||||
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
return bot->TeleportTo(dest);
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ ItemUsage ItemUsageValue::Calculate()
|
||||
|
||||
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, int32 randomPropertyId)
|
||||
{
|
||||
if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK)
|
||||
if (bot->BotCanUseItem(itemProto) != EQUIP_ERR_OK)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
if (itemProto->InventoryType == INVTYPE_NON_EQUIP)
|
||||
|
||||
@@ -119,7 +119,7 @@ bool HasManaValue::Calculate()
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
constexpr uint32 PRIEST_SPIRIT_OF_REDEMPTION_SPELL_ID = 20711u;
|
||||
constexpr uint32 PRIEST_SPIRIT_OF_REDEMPTION_SPELL_ID = 27827u;
|
||||
if (target->HasAura(PRIEST_SPIRIT_OF_REDEMPTION_SPELL_ID))
|
||||
return false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user