mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Compare commits
1 Commits
directory_
...
hermensbas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2acf8e4b13 |
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,30 +7,29 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Bug Description**
|
||||
A clear and concise description of what the bug is. If the bug is a crash, a crash log must be posted or the issue will be removed.
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Commit Hash**
|
||||
**Commit hash**
|
||||
The hash of the current commit.
|
||||
|
||||
**How To Reproduce Bug**
|
||||
Detailed steps to reproduce the behavior.
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected Behavior**
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Modules**
|
||||
Please list all modules used as many are known to cause conflicts with Playerbots.
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Playerbot Settings**
|
||||
Number of bots, scaling settings, etc if performance related.
|
||||
|
||||
**System**
|
||||
OS: [e.g. Windows, Linux]
|
||||
Hardware: [e.g. CPU if performance related]
|
||||
|
||||
**Additional Context**
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
6
.github/workflows/code_style.yml
vendored
6
.github/workflows/code_style.yml
vendored
@@ -6,10 +6,6 @@ on:
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
concurrency:
|
||||
group: "codestyle-${{ github.event.pull_request.number }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "clang-format-always-success"
|
||||
@@ -31,4 +27,4 @@ jobs:
|
||||
# Check if there are any formatting changes
|
||||
git diff --exit-code
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
continue-on-error: true
|
||||
4
.github/workflows/core_build.yml
vendored
4
.github/workflows/core_build.yml
vendored
@@ -6,10 +6,6 @@ on:
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
concurrency:
|
||||
group: "core-build-${{ github.event.pull_request.number }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
|
||||
8
.github/workflows/macos_build.yml
vendored
8
.github/workflows/macos_build.yml
vendored
@@ -5,9 +5,9 @@ on:
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
concurrency:
|
||||
group: "macos-build-${{ github.event.pull_request.number }}"
|
||||
cancel-in-progress: true
|
||||
# concurrency:
|
||||
# group: ${{ github.head_ref }} || concat(${{ github.ref }}, ${{ github.workflow }})
|
||||
# cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
macos-build:
|
||||
@@ -42,4 +42,4 @@ jobs:
|
||||
- name: Configure OS
|
||||
run: source ./acore.sh install-deps
|
||||
- name: Build
|
||||
run: source ./apps/ci/mac/ci-compile.sh
|
||||
run: source ./apps/ci/mac/ci-compile.sh
|
||||
4
.github/workflows/windows_build.yml
vendored
4
.github/workflows/windows_build.yml
vendored
@@ -5,10 +5,6 @@ on:
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
concurrency:
|
||||
group: "windows-build-${{ github.event.pull_request.number }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
windows-build:
|
||||
strategy:
|
||||
|
||||
110
README.md
110
README.md
@@ -1,38 +1,20 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
|
||||
|
|
||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
|
||||
</p>
|
||||
|
||||
|
||||
<div align="center">
|
||||
<img src="icon.png" alt="Playerbots Icon" width="700px">
|
||||
</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">
|
||||
</div>
|
||||
[English](README.md) | [Español](README_ES.md) | [中文](README_CN.md)
|
||||
|
||||
# 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). Features include:
|
||||
|
||||
- Bots that utilize real player data, allowing players to interact with their other characters, form parties, level up, and more;
|
||||
- Random bots that wander through the world and behave like players, simulating the MMO experience;
|
||||
- Bots capable of running raids and battlegrounds;
|
||||
- Highly configurable settings to define how bots behave;
|
||||
- Excellent performance, even when running thousands of bots.
|
||||
Welcome to the Playerbots Module for AzerothCore, a work in progress project based on the IKE3 Playerbots. These Playerbots utilize actual player data, allowing you to interact with your own alts, form parties, level up characters, and much more.
|
||||
|
||||
**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.
|
||||
If you encounter any errors or experience crashes, we kindly request that you report them as GitHub issues. Your valuable feedback will help us improve and enhance this project collaboratively.
|
||||
|
||||
**Playerbots Module** has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project.
|
||||
You can also get more information in our [discord](https://discord.gg/NQm5QShwf9).
|
||||
|
||||
## Installation
|
||||
|
||||
### Classic Installation
|
||||
Please note that this module requires specific custom changes to AzerothCore. To ensure compatibility, you must compile it with a custom branch from my fork, which can be found here: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot).
|
||||
|
||||
`mod-playerbots` requires a custom branch of AzerothCore to work: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
|
||||
To install this module, please refer to the AzerothCore Wiki for detailed instructions: [AzerothCore Installation Guide](https://www.azerothcore.org/wiki/installation).
|
||||
|
||||
We've provided a simple method to clone the module:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||
@@ -40,69 +22,57 @@ cd azerothcore-wotlk/modules
|
||||
git clone https://github.com/liyunfan1223/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.
|
||||
## Quick Start & Documentation
|
||||
|
||||
### Docker Installation
|
||||
For a quick start and an extensive overview of available addons, commands, and recommended configuration please refer to the [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki).
|
||||
|
||||
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
|
||||
Please be aware that documentation for some newly added commands is currently lacking as the project is still under development.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||
cd azerothcore-wotlk/modules
|
||||
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
|
||||
```
|
||||
## Progress
|
||||
|
||||
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:
|
||||
The module primarily emphasizes the following key features, and we have implemented improvements in these areas:
|
||||
|
||||
```yml
|
||||
services:
|
||||
ac-worldserver:
|
||||
volumes:
|
||||
- ./modules:/azerothcore/modules:ro
|
||||
```
|
||||
- **Bots in World (Random bot):** We have enhanced the behavior of random bots to make them mimic real players more closely, creating a more authentic player server environment.
|
||||
|
||||
Additionally, this override file can be used to set custom configuration settings for `ac-worldserver` and any modules you install as environment variables:
|
||||
- **Bots in Raid:** We've empowered bots to conquer challenging raid content by implementing specific strategies for various bosses, making raid encounters more engaging. Additionally, we have enhanced bots' capabilities in various roles such as DPS, healing, and tanking, ensuring they contribute effectively to the success of raid groups.
|
||||
|
||||
```yml
|
||||
services:
|
||||
ac-worldserver:
|
||||
environment:
|
||||
AC_RATE_XP_KILL: "1"
|
||||
AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "1"
|
||||
volumes:
|
||||
- ./modules:/azerothcore/modules:ro
|
||||
```
|
||||
- **Bots in Battleground:** Bots are now capable of actively participating in battlegrounds alongside real players, adding depth and excitement to these PvP scenarios.
|
||||
|
||||
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.
|
||||
- **Interation with Bots:** We have improved the interaction between real players and bots, enabling players to complete quests and level up with multiple characters while collaborating with the bot companions.
|
||||
|
||||
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.
|
||||
- **Player Progression Path:** We have designed an improved progression path for players, complemented by bots, to offer an alternative and engaging gameplay experience.
|
||||
|
||||
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.
|
||||
- **Stability:** Our efforts have focused on enhancing the overall stability of AzerothCore when using the Playerbots module. These improvements aim to prevent server crashes and ensure a smoother experience for all users.
|
||||
|
||||
## Documentation
|
||||
- **Configuration:** We have introduced a range of configurable options to cater to players with varying requirements, allowing for a more personalized experience.
|
||||
|
||||
The [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki) contains an extensive overview of addons, commands, and recommended configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
|
||||
It's essential to note that there is still a significant amount of work to be done as we continue to enhance the project. We welcome everyone to contribute in various ways.
|
||||
|
||||
## 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 [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.
|
||||
|
||||
## Addons
|
||||
|
||||
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:
|
||||
## Addon
|
||||
|
||||
For enhanced control over the bots and to simplify command usage, you can also make use of available Playerbots addons:
|
||||
- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio)
|
||||
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan)
|
||||
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision)
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Bots can't cast spells**
|
||||
|
||||
- Please make sure that the necessary English DBC file (enUS) is present.
|
||||
|
||||
**Compilation error**
|
||||
|
||||
- We support for Ubuntu, Windows, and macOS.
|
||||
|
||||
- Continuous integration workflows have been established. You can review the build status in [GitHub Actions](https://github.com/liyunfan1223/mod-playerbots/actions).
|
||||
|
||||
- If the latest build status fails, please revert to the previous commit. We will address the issue ASAP.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
`mod-playerbots` is is based off [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for the continued efforts in maintaining the module.
|
||||
The code for this module is ported from [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to @ZhengPeiRu21 and @celguar for the continued efforts in maintaining the module.
|
||||
|
||||
We also want to express our sincere appreciation to all individuals who have contributed to playerbot development. Your dedication and efforts have been instrumental in shaping this project, and we are thankful for your contributions.
|
||||
|
||||
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>
|
||||
|
||||
41
README_example.md
Normal file
41
README_example.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# MY_NEW_MODULE
|
||||
|
||||
## Description
|
||||
|
||||
This module allows to do this and this.
|
||||
|
||||
|
||||
## How to use ingame
|
||||
|
||||
Do this and that.
|
||||
|
||||

|
||||
|
||||
<!-- Video example - We can't embed videos on github, only on github.io pages. If you can, make an animated gif of your video instead (but it's not required) -->
|
||||
[](https://www.youtube.com/watch?v=T6UEX47mPeE)
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
My_new_module requires:
|
||||
|
||||
- AzerothCore v4.0.0+
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
1) Simply `git clone` the module under the `modules` directory of your AzerothCore source or copy paste it manually.
|
||||
2) Import the SQL manually to the right Database (auth, world or characters) or with the `db_assembler.sh` (if `include.sh` provided).
|
||||
3) Re-run cmake and launch a clean build of AzerothCore.
|
||||
```
|
||||
|
||||
## Edit the module's configuration (optional)
|
||||
|
||||
If you need to change the module configuration, go to your server configuration directory (where your `worldserver` or `worldserver.exe` is), copy `my_module.conf.dist` to `my_module.conf` and edit that new file.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Me](https://github.com/YOUR_GITHUB_NAME) (author of the module): Check out my soundcloud - Join my discord
|
||||
* AzerothCore: [repository](https://github.com/azerothcore) - [website](http://azerothcore.org/) - [discord chat community](https://discord.gg/PaqQRkd)
|
||||
@@ -5,59 +5,57 @@
|
||||
###################################################################################################
|
||||
# SECTION INDEX
|
||||
# GENERAL SETTINGS
|
||||
# PLAYERBOT SETTINGS
|
||||
# GENERAL
|
||||
# SUMMON OPTIONS
|
||||
# MOUNT
|
||||
# GEAR
|
||||
# LOOTING
|
||||
# TIMERS
|
||||
# DISTANCES
|
||||
# THRESHOLDS
|
||||
# QUESTS
|
||||
# COMBAT
|
||||
# CHEATS
|
||||
# SPELLS
|
||||
# PLAYERBOT RNDBOT SPECIFIC SETTINGS
|
||||
# GENERAL
|
||||
# LEVELS
|
||||
# GEAR
|
||||
# QUESTS
|
||||
# ACTIVITIES
|
||||
# SPELLS
|
||||
# STRATEGIES
|
||||
# TELEPORTS
|
||||
# BATTLEGROUND & ARENA & PVP
|
||||
# INTERVALS
|
||||
# PREMADE SPECS
|
||||
# INFORMATION
|
||||
# WARRIOR
|
||||
# PALADIN
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
# DRUID
|
||||
# RANDOM BOT DEFAULT TALENT SPEC
|
||||
# WARRIOR
|
||||
# PALADIN
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
# DRUID
|
||||
# PLAYERBOT SYSTEM SETTINGS
|
||||
# DATABASE & CONNECTIONS
|
||||
# DEBUG
|
||||
# CHAT SETTINGS
|
||||
# LOGS
|
||||
# DEPRECIATED (TEMPORARY)
|
||||
# PLAYERBOT SETTINGS
|
||||
# GENERAL
|
||||
# SUMMON OPTIONS
|
||||
# GEAR
|
||||
# LOOTING
|
||||
# TIMERS
|
||||
# DISTANCES
|
||||
# THRESHOLDS
|
||||
# QUESTS
|
||||
# COMBAT
|
||||
# CHEATS
|
||||
# SPELLS
|
||||
# PLAYERBOT RNDBOT SPECIFIC SETTINGS
|
||||
# GENERAL
|
||||
# LEVELS
|
||||
# GEAR
|
||||
# QUESTS
|
||||
# SPELLS
|
||||
# STRATEGIES
|
||||
# TELEPORTS
|
||||
# BATTLEGROUND & ARENA & PVP
|
||||
# INTERVALS
|
||||
# PREMADE SPECS
|
||||
# INFORMATION
|
||||
# WARRIOR
|
||||
# PALADIN
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
# DRUID
|
||||
# RANDOM BOT DEFAULT TALENT SPEC
|
||||
# WARRIOR
|
||||
# PALADIN
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
# DRUID
|
||||
# PLAYERBOT SYSTEM SETTINGS
|
||||
# DATABASE & CONNECTIONS
|
||||
# DEBUG
|
||||
# CHAT SETTINGS
|
||||
# LOGS
|
||||
# DEPRECIATED (TEMPORARY)
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -81,15 +79,13 @@ AiPlayerbot.Enabled = 1
|
||||
# Enable random bot system
|
||||
AiPlayerbot.RandomBotAutologin = 1
|
||||
|
||||
# Random bot count
|
||||
AiPlayerbot.MinRandomBots = 500
|
||||
AiPlayerbot.MaxRandomBots = 500
|
||||
# Random bot account
|
||||
# Please ensure that RandomBotAccountCount is greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||
AiPlayerbot.RandomBotAccountCount = 200
|
||||
|
||||
# Random bot accounts
|
||||
# If you are not using any expansion at all, you may have to set this manually, then
|
||||
# please ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||
# default: 0 - Automatic
|
||||
AiPlayerbot.RandomBotAccountCount = 0
|
||||
# Random bot count
|
||||
AiPlayerbot.MinRandomBots = 50
|
||||
AiPlayerbot.MaxRandomBots = 50
|
||||
|
||||
# Delete all random bot accounts (reset randombots)
|
||||
AiPlayerbot.DeleteRandomBotAccounts = 0
|
||||
@@ -109,14 +105,18 @@ AiPlayerbot.DeleteRandomBotAccounts = 0
|
||||
#
|
||||
#
|
||||
|
||||
# The maximum number of bots that a player can control simultaneously
|
||||
# Maximum number of bots added by one account
|
||||
AiPlayerbot.MaxAddedBots = 40
|
||||
|
||||
# Maximum number of bots per class added by one account
|
||||
AiPlayerbot.MaxAddedBotsPerClass = 40
|
||||
|
||||
# Enable/Disable create bot by addclass command (0 = GM only, 1 = enable)
|
||||
# default: 1 (enable)
|
||||
AiPlayerbot.AddClassCommand = 1
|
||||
|
||||
# Set the addclass command account pool size
|
||||
# Addclass command uses a subset of accounts from RandomBotAccountCount
|
||||
AiPlayerbot.AddClassAccountPoolSize = 50
|
||||
|
||||
# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept)
|
||||
@@ -126,21 +126,13 @@ AiPlayerbot.GroupInvitationPermission = 1
|
||||
# auto-login all player alts as bots on player login
|
||||
AiPlayerbot.BotAutologin = 0
|
||||
|
||||
# Allow/deny bots from the player's account
|
||||
AiPlayerbot.AllowAccountBots = 1
|
||||
# Allow login other players' characters as bots
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.AllowPlayerBots = 0
|
||||
|
||||
# Allow/deny bots in the player's guild
|
||||
# Allow/deny bots from your guild
|
||||
AiPlayerbot.AllowGuildBots = 1
|
||||
|
||||
# Allow linking accounts for shared alt-bot control
|
||||
AiPlayerbot.AllowTrustedAccountBots = 1
|
||||
|
||||
# Random bot guild count
|
||||
AiPlayerbot.RandomBotGuildCount = 20
|
||||
|
||||
# Delete all random bot guilds
|
||||
AiPlayerbot.DeleteRandomBotGuilds = 0
|
||||
|
||||
# Randombots will invite nearby bots to guilds
|
||||
AiPlayerbot.RandomBotGuildNearby = 0
|
||||
|
||||
@@ -207,36 +199,6 @@ AiPlayerbot.BotRepairWhenSummon = 1
|
||||
#
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# MOUNT
|
||||
#
|
||||
#
|
||||
|
||||
# Defines at what level a bot will naturally use its 60% ground mount
|
||||
# note: was level 20 during WotLK, 30 during TBC, 40 during Vanilla
|
||||
# default: 20
|
||||
AiPlayerbot.UseGroundMountAtMinLevel = 20
|
||||
|
||||
# Defines at what level a bot will naturally use its 100% fast ground mount
|
||||
# note: was level 40 during WotLK, 60 during Vanilla
|
||||
# default: 40
|
||||
AiPlayerbot.UseFastGroundMountAtMinLevel = 40
|
||||
|
||||
# Defines at what level a bot will naturally use its 150% fly mount
|
||||
# note: was level 60 during WotLK, 70 during TBC
|
||||
# default: 60
|
||||
AiPlayerbot.UseFlyMountAtMinLevel = 60
|
||||
|
||||
# Defines at what level a bot will naturally use its 280% fast fly mount
|
||||
# note: was level 70 during WotLK and TBC
|
||||
# default: 70
|
||||
AiPlayerbot.UseFastFlyMountAtMinLevel = 70
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# GEAR
|
||||
#
|
||||
@@ -372,9 +334,9 @@ AiPlayerbot.AggroDistance = 22
|
||||
#
|
||||
#
|
||||
|
||||
# Set XP rate for bots (default: 1.0)
|
||||
# Server XP Rate * AiPlayerbot.PlayerbotsXPRate
|
||||
AiPlayerbot.PlayerbotsXPRate = 1.0
|
||||
# Set kill XP rate for bots (default: 1)
|
||||
# Server XP Rate * AiPlayerbot.KillXPRate
|
||||
AiPlayerbot.KillXPRate = 1
|
||||
|
||||
# Health/Mana levels
|
||||
AiPlayerbot.CriticalHealth = 25
|
||||
@@ -512,6 +474,14 @@ AiPlayerbot.RandomBotRandomPassword = 0
|
||||
# Accounts to create for random bots
|
||||
AiPlayerbot.RandomBotAccountPrefix = "rndbot"
|
||||
|
||||
# Enable/Disable rotation of bots (randomly select a bot from the bots pool to go online and rotate them periodically)
|
||||
# Need to reset rndbot after changing the setting (.playerbot rndbot reset)
|
||||
# default: 0 (disable, the online bots are fixed)
|
||||
AiPlayerbot.EnableRotation = 0
|
||||
|
||||
# Bots pool size for rotation (should be less than RandomBotAccountCount * 10)
|
||||
AiPlayerbot.RotationPoolSize = 500
|
||||
|
||||
AiPlayerbot.RandomBotMinLevel = 1
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
|
||||
@@ -525,31 +495,10 @@ AiPlayerbot.PreQuests = 0
|
||||
# Enable LFG for random bots
|
||||
AiPlayerbot.RandomBotJoinLfg = 1
|
||||
|
||||
# Enable/Disable periodic online - offline to mimic the real-world scenario where not all players are online simultaneously
|
||||
# When enabled, bots are randomly selected to go online or offline periodically from a larger set
|
||||
# Default: 0 (disabled, the set of online bots remains fixed)
|
||||
AiPlayerbot.EnablePeriodicOnlineOffline = 0
|
||||
|
||||
# Defines the ratio between the total number of bots (including offline ones) and the number of bots currently online (MaxRandomBots)
|
||||
# This setting must greater than 1.0 and only applies when EnablePeriodicOnlineOffline
|
||||
# Default: 2.0 (total number of bots is twice the number of MaxRandomBots)
|
||||
AiPlayerbot.PeriodicOnlineOfflineRatio = 2.0
|
||||
|
||||
# Percentage ratio of alliance and horde
|
||||
AiPlayerbot.RandomBotAllianceRatio = 50
|
||||
AiPlayerbot.RandomBotHordeRatio = 50
|
||||
|
||||
# Disable death knight for bots login
|
||||
# Need to reset rndbot after changing the setting (.playerbot rndbot reset)
|
||||
AiPlayerbot.DisableDeathKnightLogin = 0
|
||||
|
||||
# Enable expansion limitation for talents and glyphs - ie: level <= 60 bot only uses talents
|
||||
# available in vanilla, level <= 70 bot only uses talents available in TBC)
|
||||
# Default: 0
|
||||
AiPlayerbot.LimitTalentsExpansion = 0
|
||||
|
||||
# Configure random bots and addClass bot trading (0: Disabled, 1: Enabled, 2: Only buy, 3: Only Sell)
|
||||
# Default: 1 (Enabled)
|
||||
AiPlayerbot.EnableRandomBotTrading = 1
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -565,13 +514,10 @@ AiPlayerbot.EnableRandomBotTrading = 1
|
||||
AiPlayerbot.DisableRandomLevels = 0
|
||||
|
||||
# Set randombots starting level here if "AiPlayerbot.DisableRandomLevels" enabled
|
||||
AiPlayerbot.RandombotStartingLevel = 1
|
||||
AiPlayerbot.RandombotStartingLevel = 5
|
||||
|
||||
# Chance random bot has min level on first randomize (default 0.1)
|
||||
AiPlayerbot.RandomBotMinLevelChance = 0.1
|
||||
|
||||
# Chance random bot has max level on first randomize (default 0.1)
|
||||
AiPlayerbot.RandomBotMaxLevelChance = 0.1
|
||||
# Chance random bot has max level on first randomize (default 0.15)
|
||||
AiPlayerbot.RandomBotMaxLevelChance = 0.15
|
||||
|
||||
# Fix the level of random bot (won't level up by grinding)
|
||||
# Default: 0 (disable)
|
||||
@@ -630,50 +576,6 @@ AiPlayerbot.EquipmentPersistenceLevel = 80
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoUpgradeEquip = 1
|
||||
|
||||
# Only set wolf pets for hunters to have higher damage (0 = disabled, 1 = enabled only for max-level, 2 = enabled)
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.HunterWolfPet = 0
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
####################################################################################################
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# ACTIVITIES
|
||||
#
|
||||
#
|
||||
# Specify percent of active bots
|
||||
# The default is 100%, but would be automatically adjusted if botActiveAloneSmartScale
|
||||
# is enabled. Regardless, this value is only applied to inactive areas where no real-players
|
||||
# are detected. When real-players are nearby, friend, group, guild, BGs, instances etc,
|
||||
# the value is always enforced to 100%
|
||||
AiPlayerbot.BotActiveAlone = 100
|
||||
|
||||
# Force botActiveAlone when bot is ... of real player
|
||||
AiPlayerbot.BotActiveAloneForceWhenInRadius = 150
|
||||
AiPlayerbot.BotActiveAloneForceWhenInZone = 1
|
||||
AiPlayerbot.BotActiveAloneForceWhenInMap = 0
|
||||
AiPlayerbot.BotActiveAloneForceWhenIsFriend = 1
|
||||
AiPlayerbot.BotActiveAloneForceWhenInGuild = 1
|
||||
|
||||
# SmartScale is enabled or not.
|
||||
# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value.
|
||||
# (The scaling will be overruled by the BotActiveAloneForceWhen...rules)
|
||||
#
|
||||
# Limitfloor - when DIFF (latency) above floor, activity scaling is applied starting with 90%
|
||||
# LimitCeiling - when DIFF (latency) above ceiling, activity is 0%;
|
||||
#
|
||||
# MinLevel - only apply scaling when level is above or equal to min(bot)Level
|
||||
# MaxLevel - only apply scaling when level is lower or equal of max(bot)Level
|
||||
#
|
||||
AiPlayerbot.botActiveAloneSmartScale = 1
|
||||
AiPlayerbot.botActiveAloneSmartScaleDiffLimitfloor = 50
|
||||
AiPlayerbot.botActiveAloneSmartScaleDiffLimitCeiling = 200
|
||||
AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1
|
||||
AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -696,8 +598,8 @@ AiPlayerbot.AutoDoQuests = 1
|
||||
|
||||
# Random Bots will behave more like real players (exprimental)
|
||||
# This option will override AutoDoQuests
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.EnableNewRpgStrategy = 1
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.EnableNewRpgStrategy = 0
|
||||
|
||||
# Quest items to leave (do not destroy)
|
||||
AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11000"
|
||||
@@ -785,56 +687,18 @@ AiPlayerbot.AutoTeleportForLevel = 1
|
||||
# Enable BG/Arena for random Bots
|
||||
AiPlayerbot.RandomBotJoinBG = 1
|
||||
|
||||
# Enable Auto join BG - have bots start BG's and Arenas on their own
|
||||
# Enable Auto join BG - bots randomly join WSG and 2v2 Arena if server is not lagging
|
||||
AiPlayerbot.RandomBotAutoJoinBG = 0
|
||||
|
||||
# Required Configuration for RandomBotAutoJoinBG
|
||||
#
|
||||
# Known issue: When enabling a lot of brackats in combination with multiple instances,
|
||||
# can lead to more instances created by bots than intended (over-queuing).
|
||||
#
|
||||
# This section controls the level brackets and
|
||||
# automatic bot participation in battlegrounds and arenas.
|
||||
#
|
||||
# Brackets:
|
||||
# - Specify the level ranges for bots to auto-join:
|
||||
# - Warsong Gulch (WS): 0 = 10-19, 1 = 20-29, 2 = 30-39, ..., 7 = 80 (default: 7)
|
||||
# - Rated Arena: 0 = 10-14, 1 = 15-19, ..., 14 = 80-84 (default: 14)
|
||||
# - Multiple brackets can be specified as a comma-separated list (e.g., "0,2,5").
|
||||
#
|
||||
# Counts:
|
||||
# - Specify the number of Battlegrounds to auto-fill per bracket.
|
||||
# - For battlegrounds, Count is the number of bots per bracket. For example:
|
||||
# - Warsong Gulch Count = 1 adds 20 bots (10 per team).
|
||||
# - Ensure there are enough eligible bots to meet the specified counts.
|
||||
#
|
||||
# Arena Considerations:
|
||||
# - Rated Arena brackets default to level 80-84 (bracket 14).
|
||||
# - Custom code changes are required for lower-level arena brackets to function properly.
|
||||
#
|
||||
# Battleground bracket range possibilities:
|
||||
# AiPlayerbot.RandomBotAutoJoinICBrackets = 0,1
|
||||
# AiPlayerbot.RandomBotAutoJoinEYBrackets = 0,1,2
|
||||
# AiPlayerbot.RandomBotAutoJoinAVBrackets = 0,1,2,3
|
||||
# AiPlayerbot.RandomBotAutoJoinABBrackets = 0,1,2,3,4,5,6
|
||||
# AiPlayerbot.RandomBotAutoJoinWSBrackets = 0,1,2,3,4,5,6,7
|
||||
|
||||
AiPlayerbot.RandomBotAutoJoinICBrackets = 1
|
||||
AiPlayerbot.RandomBotAutoJoinEYBrackets = 2
|
||||
AiPlayerbot.RandomBotAutoJoinAVBrackets = 3
|
||||
AiPlayerbot.RandomBotAutoJoinABBrackets = 6
|
||||
AiPlayerbot.RandomBotAutoJoinWSBrackets = 7
|
||||
|
||||
# Battlegrounds count (per bracket!):
|
||||
AiPlayerbot.RandomBotAutoJoinBGICCount = 0
|
||||
AiPlayerbot.RandomBotAutoJoinBGEYCount = 1
|
||||
AiPlayerbot.RandomBotAutoJoinBGAVCount = 0
|
||||
AiPlayerbot.RandomBotAutoJoinBGABCount = 1
|
||||
AiPlayerbot.RandomBotAutoJoinBGWSCount = 1
|
||||
|
||||
# Arena configuration:
|
||||
# Required for RandomBotAutoJoinBG
|
||||
# Currently you can select 1 bracket (level range) for bots to auto join.
|
||||
# Brackets for warsong 0:10-19, 1:20-29, 2:30-39, 3:40-49 etc. Default: 7 (level 80)
|
||||
# Brackets for rated arena: 0:10-14, 1:15-19 etc. Default: 14 (80-84)
|
||||
# For lower level ranges to work in Rated Arena, custom code changes need to be made.
|
||||
# Count = amount of games Bots auto fill. (Count 1 for WSG = 20 bots).
|
||||
AiPlayerbot.RandomBotAutoJoinWarsongBracket = 7
|
||||
AiPlayerbot.RandomBotAutoJoinArenaBracket = 14
|
||||
|
||||
AiPlayerbot.RandomBotAutoJoinBGWarsongCount = 0
|
||||
AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count = 0
|
||||
AiPlayerbot.RandomBotAutoJoinBGRatedArena3v3Count = 0
|
||||
AiPlayerbot.RandomBotAutoJoinBGRatedArena5v5Count = 0
|
||||
@@ -861,7 +725,7 @@ AiPlayerbot.DeleteRandomBotArenaTeams = 0
|
||||
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,139"
|
||||
|
||||
# PvP Restricted Areas (bots don't pvp)
|
||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312"
|
||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268"
|
||||
|
||||
# Improve react speed in battleground and arena (may cause lag)
|
||||
AiPlayerbot.FastReactInBG = 1
|
||||
@@ -880,8 +744,8 @@ AiPlayerbot.FastReactInBG = 1
|
||||
AiPlayerbot.RandomBotUpdateInterval = 20
|
||||
AiPlayerbot.RandomBotCountChangeMinInterval = 1800
|
||||
AiPlayerbot.RandomBotCountChangeMaxInterval = 7200
|
||||
AiPlayerbot.MinRandomBotInWorldTime = 600
|
||||
AiPlayerbot.MaxRandomBotInWorldTime = 28800
|
||||
AiPlayerbot.MinRandomBotInWorldTime = 3600
|
||||
AiPlayerbot.MaxRandomBotInWorldTime = 1209600
|
||||
AiPlayerbot.MinRandomBotRandomizeTime = 7200
|
||||
AiPlayerbot.MaxRandomBotRandomizeTime = 1209600
|
||||
AiPlayerbot.RandomBotsPerInterval = 60
|
||||
@@ -889,7 +753,7 @@ AiPlayerbot.MinRandomBotReviveTime = 60
|
||||
AiPlayerbot.MaxRandomBotReviveTime = 300
|
||||
AiPlayerbot.MinRandomBotTeleportInterval = 3600
|
||||
AiPlayerbot.MaxRandomBotTeleportInterval = 18000
|
||||
AiPlayerbot.PermanantlyInWorldTime = 31104000
|
||||
AiPlayerbot.RandomBotInWorldWithRotationDisabled = 31104000
|
||||
|
||||
#
|
||||
#
|
||||
@@ -972,8 +836,8 @@ AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
|
||||
|
||||
AiPlayerbot.PremadeSpecName.3.0 = bm pve
|
||||
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243110531051
|
||||
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243120531251-025305101
|
||||
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351
|
||||
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021
|
||||
AiPlayerbot.PremadeSpecName.3.1 = mm pve
|
||||
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051
|
||||
@@ -1007,16 +871,16 @@ AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221
|
||||
|
||||
AiPlayerbot.PremadeSpecName.4.0 = as pve
|
||||
AiPlayerbot.PremadeSpecGlyph.4.0 = 45768,43379,45761,43380,43378,45766
|
||||
AiPlayerbot.PremadeSpecLink.4.0.60 = 005303104352100520103331051
|
||||
AiPlayerbot.PremadeSpecLink.4.0.80 = 005303104352100520103331051-005005005003-2
|
||||
AiPlayerbot.PremadeSpecLink.4.0.60 = 005323005350100520103331051
|
||||
AiPlayerbot.PremadeSpecLink.4.0.80 = 005323005350100520103331051-005005005003-2
|
||||
AiPlayerbot.PremadeSpecName.4.1 = combat pve
|
||||
AiPlayerbot.PremadeSpecGlyph.4.1 = 42962,43379,45762,43380,43378,42969
|
||||
AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251
|
||||
AiPlayerbot.PremadeSpecLink.4.1.80 = 00532000523-0252051000035015223100501251
|
||||
AiPlayerbot.PremadeSpecName.4.2 = subtlety pve
|
||||
AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767
|
||||
AiPlayerbot.PremadeSpecLink.4.2.60 = --5022012030321121350115031151
|
||||
AiPlayerbot.PremadeSpecLink.4.2.80 = 30532010114--5022012030321121350115031151
|
||||
AiPlayerbot.PremadeSpecLink.4.2.60 = --5120122030321121050135031241
|
||||
AiPlayerbot.PremadeSpecLink.4.2.80 = 0053231-2-5120222030321121050135231251
|
||||
|
||||
#
|
||||
#
|
||||
@@ -1060,7 +924,7 @@ AiPlayerbot.PremadeSpecGlyph.6.1 = 45805,43673,43547,43544,43672,43543
|
||||
AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351
|
||||
AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003
|
||||
AiPlayerbot.PremadeSpecName.6.2 = unholy pve
|
||||
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,43546,43544,43672,43549
|
||||
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549
|
||||
AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151
|
||||
AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151
|
||||
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
|
||||
@@ -1113,10 +977,6 @@ AiPlayerbot.PremadeSpecName.8.2 = frost pve
|
||||
AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751
|
||||
AiPlayerbot.PremadeSpecLink.8.2.60 = --0533030313203100030152231151
|
||||
AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--0533030313203100030152231351
|
||||
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
|
||||
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
|
||||
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
|
||||
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120311351-023303031
|
||||
|
||||
#
|
||||
#
|
||||
@@ -1177,50 +1037,48 @@ AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
|
||||
|
||||
###################################
|
||||
# #
|
||||
# WORLD BUFFS #
|
||||
# WORLD BUFFS #
|
||||
# #
|
||||
###################################
|
||||
|
||||
####################################################################################################
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# Applies a permanent buff to all bots when not in combat simulating flasks, food, rune etc.
|
||||
# WorldBuff.Faction.Class.Spec.MinLevel.MaxLevel
|
||||
|
||||
AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS
|
||||
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY
|
||||
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY
|
||||
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION
|
||||
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST
|
||||
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP
|
||||
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL
|
||||
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION
|
||||
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT
|
||||
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY
|
||||
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE
|
||||
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY
|
||||
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW
|
||||
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD
|
||||
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST
|
||||
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY
|
||||
AiPlayerbot.WorldBuff.0.6.3.80.80 = 53760,57371 #DEATH KNIGHT BLOOD DPS
|
||||
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL
|
||||
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT
|
||||
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION
|
||||
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE
|
||||
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE
|
||||
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST
|
||||
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION
|
||||
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY
|
||||
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION
|
||||
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE
|
||||
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL BEAR
|
||||
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION
|
||||
AiPlayerbot.WorldBuff.0.11.3.80.80 = 53760,57358 #DRUID FERAL CAT
|
||||
AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS
|
||||
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY
|
||||
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY
|
||||
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION
|
||||
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST
|
||||
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP
|
||||
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL
|
||||
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION
|
||||
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT
|
||||
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY
|
||||
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE
|
||||
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY
|
||||
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW
|
||||
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD
|
||||
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST
|
||||
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY
|
||||
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL
|
||||
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT
|
||||
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION
|
||||
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE
|
||||
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE
|
||||
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST
|
||||
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION
|
||||
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY
|
||||
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION
|
||||
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE
|
||||
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL
|
||||
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION
|
||||
|
||||
#
|
||||
#
|
||||
@@ -1234,7 +1092,7 @@ AiPlayerbot.WorldBuff.0.11.3.80.80 = 53760,57358 #DRUID FERAL
|
||||
###################################
|
||||
|
||||
####################################################################################################
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
@@ -1640,6 +1498,12 @@ AiPlayerbot.EnableGuildTasks = 0
|
||||
# Enable dungeon suggestions in lower case randomly
|
||||
AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0
|
||||
|
||||
# Random bot guild count
|
||||
AiPlayerbot.RandomBotGuildCount = 20
|
||||
|
||||
# Delete all random bot guilds
|
||||
AiPlayerbot.DeleteRandomBotGuilds = 0
|
||||
|
||||
# Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding
|
||||
AiPlayerbot.RandomBotRpgChance = 0.20
|
||||
|
||||
@@ -1649,6 +1513,27 @@ AiPlayerbot.RandombotsWalkingRPG = 0
|
||||
# Set randombots movement speed to walking only inside buildings
|
||||
AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
|
||||
|
||||
# Specify percent of active bots
|
||||
# The default is 10. With 10% of all bots going active or inactive each minute. Regardless
|
||||
# This value is only applied to inactive areas where no real-players are detected, when
|
||||
# real-players are nearby, friend, group, guild, bg, instances etc the value is always
|
||||
# enforced to 100%
|
||||
AiPlayerbot.BotActiveAlone = 100
|
||||
|
||||
# Force botActiveAlone when bot is ... of real player
|
||||
AiPlayerbot.BotActiveAloneForceWhenInRadius = 150
|
||||
AiPlayerbot.BotActiveAloneForceWhenInZone = 1
|
||||
AiPlayerbot.BotActiveAloneForceWhenInMap = 0
|
||||
AiPlayerbot.BotActiveAloneForceWhenIsFriend = 1
|
||||
AiPlayerbot.BotActiveAloneForceWhenInGuild = 1
|
||||
|
||||
# Specify smart scaling is enabled or not.
|
||||
# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value.
|
||||
# Only when botLevel is between WhenMinLevel and WhenMaxLevel.
|
||||
AiPlayerbot.botActiveAloneSmartScale = 1
|
||||
AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1
|
||||
AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
|
||||
|
||||
# Premade spell to avoid (undetected spells)
|
||||
# spellid-radius, ...
|
||||
AiPlayerbot.PremadeAvoidAoe = 62234-4
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
|
||||
CREATE TABLE `playerbot_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -1,9 +0,0 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
|
||||
CREATE TABLE `playerbot_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -1,4 +0,0 @@
|
||||
-- Update max_level for TBC Heroic dungeons in `playerbots_dungeon_suggestion_definition`
|
||||
UPDATE `playerbots_dungeon_suggestion_definition`
|
||||
SET `max_level` = 73
|
||||
WHERE `id` IN (40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56);
|
||||
@@ -1,22 +0,0 @@
|
||||
-- Delete existing entries from playerbots_travelnode, playerbots_travelnode_path, and playerbots_travelnode_link tables
|
||||
DELETE FROM `playerbots_travelnode_link` WHERE `node_id` = 3780;
|
||||
DELETE FROM `playerbots_travelnode_path` WHERE `node_id` = 3780;
|
||||
DELETE FROM `playerbots_travelnode` WHERE `id` = 3780;
|
||||
|
||||
-- Insert new entries into playerbots_travelnode
|
||||
INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`)
|
||||
VALUES (3780, 'Highlord Mograine', 533, 2524.32, -2951.28, 245.633, 1);
|
||||
|
||||
-- Insert new entries into playerbots_travelnode_path
|
||||
INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`)
|
||||
VALUES
|
||||
(3780, 472, 0, 533, 2524.32, -2951.28, 245.633),
|
||||
(3780, 472, 1, 533, 2528.79, -2948.58, 245.633),
|
||||
(3780, 757, 0, 533, 2524.32, -2951.28, 245.633),
|
||||
(3780, 757, 1, 533, 2517.62, -2959.38, 245.636);
|
||||
|
||||
-- Insert new entries into playerbots_travelnode_link
|
||||
INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, `extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`)
|
||||
VALUES
|
||||
(3780, 472, 1, 0, 5.3221, 0, 0, 1, 83, 0, 0),
|
||||
(3780, 757, 1, 0, 10.6118, 0, 0, 1, 83, 0, 0);
|
||||
@@ -1,25 +0,0 @@
|
||||
-- ##########################################################
|
||||
-- # Playerbots RandomBots Performance Update
|
||||
-- # Add missing index to reduce Deadlocks
|
||||
-- # Author: Raz0r1337 aka St0ny
|
||||
-- # Date: 2025-04-26
|
||||
-- ##########################################################
|
||||
|
||||
-- Check if the index already exists
|
||||
SET @index_exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'playerbots_random_bots'
|
||||
AND INDEX_NAME = 'idx_owner_bot_event'
|
||||
);
|
||||
|
||||
-- Conditionally create the index only if it doesn't exist
|
||||
SET @ddl := IF(@index_exists = 0,
|
||||
'ALTER TABLE `playerbots_random_bots` ADD INDEX `idx_owner_bot_event` (`owner`, `bot`, `event`);',
|
||||
'SELECT "Index idx_owner_bot_event already exists.";'
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
@@ -1,17 +0,0 @@
|
||||
DROP TABLE IF EXISTS `playerbot_account_links`;
|
||||
|
||||
CREATE TABLE `playerbot_account_links` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`account_id` INT NOT NULL,
|
||||
`linked_account_id` INT NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
|
||||
DROP TABLE IF EXISTS `playerbot_account_keys`;
|
||||
|
||||
CREATE TABLE `playerbot_account_keys` (
|
||||
`account_id` INT PRIMARY KEY,
|
||||
`security_key` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=INNODB DEFAULT CHARSET=latin1;
|
||||
@@ -275,7 +275,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
|
||||
if (!player->InBattleground())
|
||||
{
|
||||
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr);
|
||||
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
|
||||
}
|
||||
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
|
||||
{
|
||||
@@ -304,16 +304,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (tab == 0)
|
||||
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
|
||||
else if (tab == 1)
|
||||
{
|
||||
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
|
||||
{
|
||||
engine->addStrategiesNoInit("frostfire", "frostfire aoe", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
|
||||
}
|
||||
}
|
||||
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
|
||||
|
||||
@@ -321,8 +312,8 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == 2)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
|
||||
else if (tab == 0 || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", "mark rti", nullptr);
|
||||
else if (player->GetLevel() < 36 || tab == 0)
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
@@ -356,7 +347,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr);
|
||||
else
|
||||
{
|
||||
if (player->HasSpell(768) /*cat form*/&& !player->HasAura(16931) /*thick hide*/)
|
||||
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
|
||||
{
|
||||
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
|
||||
}
|
||||
@@ -369,19 +360,15 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
case CLASS_HUNTER:
|
||||
engine->addStrategiesNoInit("dps", "aoe", "bdps", "dps assist", nullptr);
|
||||
engine->addStrategy("dps debuff", false);
|
||||
// if (tab == HUNTER_TAB_SURVIVAL)
|
||||
// {
|
||||
// engine->addStrategy("trap weave", false);
|
||||
// }
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
|
||||
if (tab == ROGUE_TAB_ASSASSINATION)
|
||||
{
|
||||
engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr);
|
||||
engine->addStrategiesNoInit("melee", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr);
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
}
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
@@ -613,15 +600,14 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
|
||||
if (!player->InBattleground())
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("nc", "food", "chat", "follow", "default", "quest", "loot",
|
||||
"gather", "duel", "pvp", "buff", "mount", "emote", nullptr);
|
||||
nonCombatEngine->addStrategiesNoInit("nc", "food", "chat", "follow", "default", "quest", "loot", "gather", "duel",
|
||||
"buff", "mount", "emote", nullptr);
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true))
|
||||
if (sPlayerbotAIConfig->autoSaveMana)
|
||||
{
|
||||
nonCombatEngine->addStrategy("save mana", false);
|
||||
}
|
||||
|
||||
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
|
||||
{
|
||||
Player* master = facade->GetMaster();
|
||||
@@ -639,11 +625,11 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
// if (!urand(0, 3))
|
||||
// nonCombatEngine->addStrategy("attack tagged");
|
||||
|
||||
// nonCombatEngine->addStrategy("pvp", false);
|
||||
nonCombatEngine->addStrategy("pvp", false);
|
||||
// nonCombatEngine->addStrategy("collision");
|
||||
nonCombatEngine->addStrategy("grind", false);
|
||||
// nonCombatEngine->addStrategy("group");
|
||||
// nonCombatEngine->addStrategy("guild");
|
||||
nonCombatEngine->addStrategy("grind", false);
|
||||
|
||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||
{
|
||||
@@ -676,7 +662,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
|
||||
if (masterBotAI || sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
{
|
||||
// nonCombatEngine->addStrategy("pvp", false);
|
||||
nonCombatEngine->addStrategy("pvp", false);
|
||||
// nonCombatEngine->addStrategy("collision");
|
||||
// nonCombatEngine->addStrategy("group");
|
||||
// nonCombatEngine->addStrategy("guild");
|
||||
@@ -696,7 +682,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
}
|
||||
else
|
||||
{
|
||||
// nonCombatEngine->addStrategy("pvp", false);
|
||||
nonCombatEngine->addStrategy("pvp", false);
|
||||
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ uint8 BroadcastHelper::GetLocale()
|
||||
return locale;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* /* bot */)
|
||||
bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* bot)
|
||||
{
|
||||
//return something to ignore the logic
|
||||
return false;
|
||||
@@ -609,7 +609,7 @@ bool BroadcastHelper::BroadcastLevelup(PlayerbotAI* ai, Player* bot)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* /* bot */, Player* player)
|
||||
bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* bot, Player* player)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
@@ -627,7 +627,7 @@ bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* /*
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* /* bot */, Player* player)
|
||||
bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* bot, Player* player)
|
||||
{
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceGuildManagement)
|
||||
{
|
||||
@@ -643,7 +643,7 @@ bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* /* b
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* /* bot */, Player* player, Group* group)
|
||||
bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* bot, Player* player, Group* group)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
return message;
|
||||
|
||||
bool found = false;
|
||||
//bool isRti = false; //not used, shadowed by the next declaration, line marked for removal.
|
||||
bool isRti = false;
|
||||
for (std::vector<std::string>::iterator i = rtis.begin(); i != rtis.end(); i++)
|
||||
{
|
||||
std::string const rti = *i;
|
||||
@@ -213,7 +213,7 @@ public:
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
bool found = false;
|
||||
//bool isClass = false; //not used, shadowed by the next declaration, line marked for removal.
|
||||
bool isClass = false;
|
||||
for (std::map<std::string, uint8>::iterator i = classNames.begin(); i != classNames.end(); i++)
|
||||
{
|
||||
bool isClass = message.find(i->first) == 0;
|
||||
@@ -6,9 +6,6 @@
|
||||
#include "ChatHelper.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "Common.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
@@ -313,16 +310,7 @@ std::string const ChatHelper::FormatQuest(Quest const* quest)
|
||||
}
|
||||
|
||||
std::ostringstream out;
|
||||
QuestLocale const* locale = sObjectMgr->GetQuestLocale(quest->GetQuestId());
|
||||
std::string questTitle;
|
||||
|
||||
if (locale && locale->Title.size() > sWorld->GetDefaultDbcLocale())
|
||||
questTitle = locale->Title[sWorld->GetDefaultDbcLocale()];
|
||||
|
||||
if (questTitle.empty())
|
||||
questTitle = quest->GetTitle();
|
||||
|
||||
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
|
||||
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle() << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@@ -330,7 +318,7 @@ std::string const ChatHelper::FormatGameobject(GameObject* go)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":"
|
||||
<< "|h[" << go->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()) << "]|h|r";
|
||||
<< "|h[" << go->GetNameForLocaleIdx(LOCALE_enUS) << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@@ -339,8 +327,8 @@ std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":"
|
||||
<< "|h[";
|
||||
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())
|
||||
: wo->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()))
|
||||
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(LOCALE_enUS)
|
||||
: wo->GetNameForLocaleIdx(LOCALE_enUS))
|
||||
<< "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
@@ -373,9 +361,7 @@ std::string const ChatHelper::FormatWorldEntry(int32 entry)
|
||||
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
|
||||
{
|
||||
std::ostringstream out;
|
||||
std::string spellName = spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] ?
|
||||
spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] : spellInfo->SpellName[LOCALE_enUS];
|
||||
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellName << "]|h|r";
|
||||
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[LOCALE_enUS] << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@@ -384,18 +370,11 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
|
||||
char color[32];
|
||||
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
|
||||
|
||||
std::string itemName;
|
||||
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
|
||||
|
||||
if (locale && locale->Name.size() > sWorld->GetDefaultDbcLocale())
|
||||
itemName = locale->Name[sWorld->GetDefaultDbcLocale()];
|
||||
|
||||
if (itemName.empty())
|
||||
itemName = proto->Name1;
|
||||
// const std::string &name = sObjectMgr->GetItemLocale(proto->ItemId)->Name[LOCALE_enUS];
|
||||
|
||||
std::ostringstream out;
|
||||
out << "|c" << color << "|Hitem:" << proto->ItemId << ":0:0:0:0:0:0:0"
|
||||
<< "|h[" << itemName << "]|h|r";
|
||||
<< "|h[" << proto->Name1 << "]|h|r";
|
||||
|
||||
if (count > 1)
|
||||
out << "x" << count;
|
||||
@@ -483,7 +462,7 @@ GuidVector ChatHelper::parseGameobjects(std::string const text)
|
||||
break;
|
||||
|
||||
std::string const entryC = text.substr(pos, endPos - pos); // get std::string const within window i.e entry
|
||||
//uint32 entry = atol(entryC.c_str()); // convert ascii to float
|
||||
uint32 entry = atol(entryC.c_str()); // convert ascii to float
|
||||
|
||||
ObjectGuid lootCurrent = ObjectGuid(guid);
|
||||
|
||||
@@ -18,7 +18,7 @@ class FleePoint
|
||||
{
|
||||
public:
|
||||
FleePoint(PlayerbotAI* botAI, float x, float y, float z)
|
||||
: x(x), y(y), z(z), sumDistance(0.0f), minDistance(0.0f), botAI(botAI)
|
||||
: botAI(botAI), sumDistance(0.0f), minDistance(0.0f), x(x), y(y), z(z)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public:
|
||||
|
||||
bool Apply(ItemTemplate const* proto) override
|
||||
{
|
||||
//uint32* tradeSkills = PlayerbotFactory::tradeSkills;
|
||||
uint32* tradeSkills = PlayerbotFactory::tradeSkills;
|
||||
|
||||
for (uint32 i = 0; i < 13; ++i)
|
||||
{
|
||||
@@ -546,7 +546,7 @@ bool GuildTaskMgr::IsGuildTaskItem(uint32 itemId, uint32 guildId)
|
||||
}
|
||||
|
||||
std::map<uint32, uint32> GuildTaskMgr::GetTaskValues(uint32 owner, std::string const type,
|
||||
[[maybe_unused]] uint32* validIn /* = nullptr */)
|
||||
uint32* validIn /* = nullptr */)
|
||||
{
|
||||
std::map<uint32, uint32> results;
|
||||
|
||||
@@ -571,10 +571,10 @@ std::map<uint32, uint32> GuildTaskMgr::GetTaskValues(uint32 owner, std::string c
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
return results;
|
||||
return std::move(results);
|
||||
}
|
||||
|
||||
uint32 GuildTaskMgr::GetTaskValue(uint32 owner, uint32 guildId, std::string const type, [[maybe_unused]] uint32* validIn /* = nullptr */)
|
||||
uint32 GuildTaskMgr::GetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32* validIn /* = nullptr */)
|
||||
{
|
||||
uint32 value = 0;
|
||||
|
||||
@@ -622,7 +622,7 @@ uint32 GuildTaskMgr::SetTaskValue(uint32 owner, uint32 guildId, std::string cons
|
||||
return value;
|
||||
}
|
||||
|
||||
bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* args)
|
||||
bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->guildTaskEnabled)
|
||||
{
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#define MAX_LOOT_OBJECT_COUNT 200
|
||||
#define MAX_LOOT_OBJECT_COUNT 10
|
||||
|
||||
LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) {}
|
||||
|
||||
@@ -81,8 +81,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
GameObject* go = botAI->GetGameObject(lootGUID);
|
||||
if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY)
|
||||
{
|
||||
bool onlyHasQuestItems = true;
|
||||
bool hasAnyQuestItems = false;
|
||||
bool isQuestItemOnly = false;
|
||||
|
||||
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
|
||||
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
||||
@@ -90,88 +89,19 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
if (!items || i >= items->size())
|
||||
break;
|
||||
|
||||
uint32 itemId = uint32((*items)[i]);
|
||||
if (!itemId)
|
||||
continue;
|
||||
|
||||
hasAnyQuestItems = true;
|
||||
auto itemId = uint32((*items)[i]);
|
||||
|
||||
if (IsNeededForQuest(bot, itemId))
|
||||
{
|
||||
this->guid = lootGUID;
|
||||
return;
|
||||
}
|
||||
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
}
|
||||
isQuestItemOnly |= itemId > 0;
|
||||
}
|
||||
|
||||
// Retrieve the correct loot table entry
|
||||
uint32 lootEntry = go->GetGOInfo()->GetLootId();
|
||||
if (lootEntry == 0)
|
||||
if (isQuestItemOnly)
|
||||
return;
|
||||
|
||||
// Check the main loot template
|
||||
if (const LootTemplate* lootTemplate = LootTemplates_Gameobject.GetLootFor(lootEntry))
|
||||
{
|
||||
Loot loot;
|
||||
lootTemplate->Process(loot, LootTemplates_Gameobject, 1, bot);
|
||||
|
||||
for (const LootItem& item : loot.items)
|
||||
{
|
||||
uint32 itemId = item.itemid;
|
||||
if (!itemId)
|
||||
continue;
|
||||
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If this item references another loot table, process it
|
||||
if (const LootTemplate* refLootTemplate = LootTemplates_Reference.GetLootFor(itemId))
|
||||
{
|
||||
Loot refLoot;
|
||||
refLootTemplate->Process(refLoot, LootTemplates_Reference, 1, bot);
|
||||
|
||||
for (const LootItem& refItem : refLoot.items)
|
||||
{
|
||||
uint32 refItemId = refItem.itemid;
|
||||
if (!refItemId)
|
||||
continue;
|
||||
|
||||
const ItemTemplate* refProto = sObjectMgr->GetItemTemplate(refItemId);
|
||||
if (!refProto)
|
||||
continue;
|
||||
|
||||
if (refProto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If gameobject has only quest items that bot doesn’t need, skip it.
|
||||
if (hasAnyQuestItems && onlyHasQuestItems)
|
||||
return;
|
||||
|
||||
// Otherwise, loot it.
|
||||
guid = lootGUID;
|
||||
|
||||
uint32 goId = go->GetEntry();
|
||||
uint32 lockId = go->GetGOInfo()->GetLockId();
|
||||
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
||||
@@ -189,7 +119,6 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
guid = lootGUID;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCK_KEY_SKILL:
|
||||
if (goId == 13891 || goId == 19535) // Serpentbloom
|
||||
{
|
||||
@@ -202,7 +131,6 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
guid = lootGUID;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCK_KEY_NONE:
|
||||
guid = lootGUID;
|
||||
break;
|
||||
@@ -272,11 +200,7 @@ LootObject::LootObject(LootObject const& other)
|
||||
|
||||
bool LootObject::IsLootPossible(Player* bot)
|
||||
{
|
||||
if (IsEmpty() || !bot)
|
||||
return false;
|
||||
|
||||
WorldObject* worldObj = GetWorldObject(bot); // Store result to avoid multiple calls
|
||||
if (!worldObj)
|
||||
if (IsEmpty() || !GetWorldObject(bot))
|
||||
return false;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
@@ -287,7 +211,7 @@ bool LootObject::IsLootPossible(Player* bot)
|
||||
if (reqItem && !bot->HasItemCount(reqItem, 1))
|
||||
return false;
|
||||
|
||||
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE -2.0f)
|
||||
if (abs(GetWorldObject(bot)->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE)
|
||||
return false;
|
||||
|
||||
Creature* creature = botAI->GetCreature(guid);
|
||||
@@ -312,32 +236,25 @@ bool LootObject::IsLootPossible(Player* bot)
|
||||
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
|
||||
if (reqSkillValue > skillValue)
|
||||
return false;
|
||||
|
||||
if (skillId == SKILL_MINING &&
|
||||
!bot->HasItemCount(756, 1) &&
|
||||
!bot->HasItemCount(778, 1) &&
|
||||
!bot->HasItemCount(1819, 1) &&
|
||||
!bot->HasItemCount(1893, 1) &&
|
||||
!bot->HasItemCount(1959, 1) &&
|
||||
!bot->HasItemCount(2901, 1) &&
|
||||
!bot->HasItemCount(9465, 1) &&
|
||||
!bot->HasItemCount(20723, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40892, 1) &&
|
||||
!bot->HasItemCount(40893, 1))
|
||||
{
|
||||
return false; // Bot is missing a mining pick
|
||||
}
|
||||
|
||||
if (skillId == SKILL_SKINNING &&
|
||||
!bot->HasItemCount(7005, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40893, 1) &&
|
||||
!bot->HasItemCount(12709, 1) &&
|
||||
!bot->HasItemCount(19901, 1))
|
||||
{
|
||||
return false; // Bot is missing a skinning knife
|
||||
}
|
||||
|
||||
if (skillId == SKILL_MINING && !bot->HasItemCount(756, 1) &&
|
||||
!bot->HasItemCount(778, 1) &&
|
||||
!bot->HasItemCount(1819, 1) &&
|
||||
!bot->HasItemCount(1893, 1) &&
|
||||
!bot->HasItemCount(1959, 1) &&
|
||||
!bot->HasItemCount(2901, 1) &&
|
||||
!bot->HasItemCount(9465, 1) &&
|
||||
!bot->HasItemCount(20723, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40892, 1) &&
|
||||
!bot->HasItemCount(40893, 1) )
|
||||
|
||||
if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40893, 1) &&
|
||||
!bot->HasItemCount(12709, 1) &&
|
||||
!bot->HasItemCount(19901, 1) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -380,6 +297,7 @@ LootObject LootObjectStack::GetLoot(float maxDistance)
|
||||
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
|
||||
return ordered.empty() ? LootObject() : *ordered.begin();
|
||||
}
|
||||
|
||||
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
||||
{
|
||||
availableLoot.shrink(time(nullptr) - 30);
|
||||
@@ -390,23 +308,17 @@ std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
||||
{
|
||||
ObjectGuid guid = i->guid;
|
||||
LootObject lootObject(bot, guid);
|
||||
if (!lootObject.IsLootPossible(bot)) // Ensure loot object is valid
|
||||
if (!lootObject.IsLootPossible(bot))
|
||||
continue;
|
||||
|
||||
WorldObject* worldObj = lootObject.GetWorldObject(bot);
|
||||
if (!worldObj) // Prevent null pointer dereference
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float distance = bot->GetDistance(worldObj);
|
||||
float distance = bot->GetDistance(lootObject.GetWorldObject(bot));
|
||||
if (!maxDistance || distance <= maxDistance)
|
||||
sortedMap[distance] = lootObject;
|
||||
}
|
||||
|
||||
std::vector<LootObject> result;
|
||||
for (auto& [_, lootObject] : sortedMap)
|
||||
result.push_back(lootObject);
|
||||
for (std::map<float, LootObject>::iterator i = sortedMap.begin(); i != sortedMap.end(); i++)
|
||||
result.push_back(i->second);
|
||||
|
||||
return result;
|
||||
}
|
||||
1341
src/PlayerbotAI.cpp
1341
src/PlayerbotAI.cpp
File diff suppressed because it is too large
Load Diff
@@ -13,18 +13,15 @@
|
||||
#include "ChatFilter.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "Common.h"
|
||||
#include "CreatureData.h"
|
||||
#include "Event.h"
|
||||
#include "Item.h"
|
||||
#include "NewRpgInfo.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "PlayerbotAIBase.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotSecurity.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "Util.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
|
||||
class AiObjectContext;
|
||||
class Creature;
|
||||
@@ -392,8 +389,6 @@ public:
|
||||
void HandleMasterOutgoingPacket(WorldPacket const& packet);
|
||||
void HandleTeleportAck();
|
||||
void ChangeEngine(BotState type);
|
||||
void ChangeEngineOnCombat();
|
||||
void ChangeEngineOnNonCombat();
|
||||
void DoNextAction(bool minimal = false);
|
||||
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false,
|
||||
std::string const qualifier = "");
|
||||
@@ -413,8 +408,8 @@ public:
|
||||
static bool IsRanged(Player* player, bool bySpec = false);
|
||||
static bool IsMelee(Player* player, bool bySpec = false);
|
||||
static bool IsCaster(Player* player, bool bySpec = false);
|
||||
static bool IsCombo(Player* player, bool bySpec = false);
|
||||
static bool IsRangedDps(Player* player, bool bySpec = false);
|
||||
static bool IsCombo(Player* player);
|
||||
static bool IsMainTank(Player* player);
|
||||
static uint32 GetGroupTankNum(Player* player);
|
||||
bool IsAssistTank(Player* player);
|
||||
@@ -424,7 +419,7 @@ public:
|
||||
bool HasAggro(Unit* unit);
|
||||
int32 GetGroupSlotIndex(Player* player);
|
||||
int32 GetRangedIndex(Player* player);
|
||||
int32 GetClassIndex(Player* player, uint8 cls);
|
||||
int32 GetClassIndex(Player* player, uint8_t cls);
|
||||
int32 GetRangedDpsIndex(Player* player);
|
||||
int32 GetMeleeIndex(Player* player);
|
||||
|
||||
@@ -439,8 +434,7 @@ public:
|
||||
const AreaTableEntry* GetCurrentArea();
|
||||
const AreaTableEntry* GetCurrentZone();
|
||||
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
||||
static std::string GetLocalizedCreatureName(uint32 entry);
|
||||
static std::string GetLocalizedGameObjectName(uint32 entry);
|
||||
|
||||
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||
bool TellMasterNoFacing(std::ostringstream& stream,
|
||||
@@ -467,10 +461,7 @@ public:
|
||||
bool PlayEmote(uint32 emote);
|
||||
void Ping(float x, float y);
|
||||
Item* FindPoison() const;
|
||||
Item* FindAmmo() const;
|
||||
Item* FindBandage() const;
|
||||
Item* FindOpenableItem() const;
|
||||
Item* FindLockedItem() const;
|
||||
Item* FindConsumable(uint32 displayId) const;
|
||||
Item* FindStoneFor(Item* weapon) const;
|
||||
Item* FindOilFor(Item* weapon) const;
|
||||
@@ -492,8 +483,8 @@ public:
|
||||
virtual bool HasAuraToDispel(Unit* player, uint32 dispelType);
|
||||
bool CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell = true, Item* itemTarget = nullptr,
|
||||
Item* castItem = nullptr);
|
||||
bool CanCastSpell(uint32 spellid, GameObject* goTarget, bool checkHasSpell = true);
|
||||
bool CanCastSpell(uint32 spellid, float x, float y, float z, bool checkHasSpell = true,
|
||||
bool CanCastSpell(uint32 spellid, GameObject* goTarget, uint8 effectMask, bool checkHasSpell = true);
|
||||
bool CanCastSpell(uint32 spellid, float x, float y, float z, uint8 effectMask, bool checkHasSpell = true,
|
||||
Item* itemTarget = nullptr);
|
||||
|
||||
bool HasAura(uint32 spellId, Unit const* player);
|
||||
@@ -509,8 +500,7 @@ public:
|
||||
bool IsInVehicle(bool canControl = false, bool canCast = false, bool canAttack = false, bool canTurn = false,
|
||||
bool fixed = false);
|
||||
|
||||
uint32 GetEquipGearScore(Player* player);
|
||||
//uint32 GetEquipGearScore(Player* player, bool withBags, bool withBank);
|
||||
uint32 GetEquipGearScore(Player* player, bool withBags, bool withBank);
|
||||
static uint32 GetMixedGearScore(Player* player, bool withBags, bool withBank, uint32 topN = 0);
|
||||
bool HasSkill(SkillType skill);
|
||||
bool IsAllowedCommand(std::string const text);
|
||||
@@ -530,7 +520,7 @@ public:
|
||||
bool IsAlt();
|
||||
Player* GetGroupMaster();
|
||||
// Returns a semi-random (cycling) number that is fixed for each bot.
|
||||
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
|
||||
uint32 GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum = 100, float cyclePerMin = 1);
|
||||
GrouperType GetGrouperType();
|
||||
GuilderType GetGuilderType();
|
||||
bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance);
|
||||
@@ -565,7 +555,6 @@ public:
|
||||
void ResetJumpDestination() { jumpDestination = Position(); }
|
||||
|
||||
bool CanMove();
|
||||
static bool IsRealGuild(uint32 guildId);
|
||||
bool IsInRealGuild();
|
||||
static std::vector<std::string> dispel_whitelist;
|
||||
bool EqualLowercaseName(std::string s1, std::string s2);
|
||||
@@ -584,18 +573,13 @@ public:
|
||||
std::set<uint32> GetCurrentIncompleteQuestIds();
|
||||
void PetFollow();
|
||||
static float GetItemScoreMultiplier(ItemQualities quality);
|
||||
static bool IsHealingSpell(uint32 spellFamilyName, flag96 spelFalimyFlags);
|
||||
static SpellFamilyNames Class2SpellFamilyName(uint8 cls);
|
||||
NewRpgInfo rpgInfo;
|
||||
NewRpgStatistic rpgStatistic;
|
||||
std::unordered_set<uint32> lowPriorityQuest;
|
||||
|
||||
private:
|
||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||
bool mixed = false);
|
||||
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||
void UpdateAIGroupMembership();
|
||||
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
||||
|
||||
void HandleCommands();
|
||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||
bool _isBotInitializing = false;
|
||||
|
||||
@@ -120,18 +120,16 @@ bool PlayerbotAIConfig::Initialize()
|
||||
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
|
||||
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
|
||||
|
||||
randomBotMinLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMinLevelChance", 0.1f);
|
||||
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.1f);
|
||||
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.15f);
|
||||
randomBotRpgChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotRpgChance", 0.20f);
|
||||
|
||||
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);
|
||||
|
||||
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
|
||||
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
|
||||
allowTrustedAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowTrustedAccountBots", true);
|
||||
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
|
||||
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
|
||||
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
|
||||
allowPlayerBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowPlayerBots", false);
|
||||
|
||||
randomBotMapsAsString = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotMaps", "0,1,530,571");
|
||||
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
|
||||
@@ -157,7 +155,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
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);
|
||||
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 200);
|
||||
randomBotUpdateInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotUpdateInterval", 20);
|
||||
randomBotCountChangeMinInterval =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE);
|
||||
@@ -175,8 +173,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
maxRandomBotReviveTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotReviveTime", 5 * MINUTE);
|
||||
minRandomBotTeleportInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotTeleportInterval", 1 * HOUR);
|
||||
maxRandomBotTeleportInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotTeleportInterval", 5 * HOUR);
|
||||
permanantlyInWorldTime =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.PermanantlyInWorldTime", 1 * YEAR);
|
||||
randomBotInWorldWithRotationDisabled =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotInWorldWithRotationDisabled", 1 * YEAR);
|
||||
randomBotTeleportDistance = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleportDistance", 100);
|
||||
randomBotsPerInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotsPerInterval", 60);
|
||||
minRandomBotsPriceChangeInterval =
|
||||
@@ -280,21 +278,9 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true);
|
||||
randomBotAutoJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutoJoinBG", false);
|
||||
|
||||
randomBotAutoJoinWarsongBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinWarsongBracket", 14);
|
||||
randomBotAutoJoinArenaBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinArenaBracket", 7);
|
||||
|
||||
randomBotAutoJoinICBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinICBrackets", "0,1");
|
||||
randomBotAutoJoinEYBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinEYBrackets", "0,1,2");
|
||||
randomBotAutoJoinAVBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinAVBrackets", "0,1,2,3");
|
||||
randomBotAutoJoinABBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinABBrackets", "0,1,2,3,4,5,6");
|
||||
randomBotAutoJoinWSBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinWSBrackets", "0,1,2,3,4,5,6,7");
|
||||
|
||||
randomBotAutoJoinBGICCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGICCount", 0);
|
||||
randomBotAutoJoinBGEYCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGEYCount", 0);
|
||||
randomBotAutoJoinBGAVCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGAVCount", 0);
|
||||
randomBotAutoJoinBGABCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGABCount", 0);
|
||||
randomBotAutoJoinBGWSCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWSCount", 0);
|
||||
|
||||
randomBotAutoJoinBGWarsongCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWarsongCount", 0);
|
||||
randomBotAutoJoinBGRatedArena2v2Count =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count", 0);
|
||||
randomBotAutoJoinBGRatedArena3v3Count =
|
||||
@@ -326,12 +312,9 @@ bool PlayerbotAIConfig::Initialize()
|
||||
commandServerPort = sConfigMgr->GetOption<int32>("AiPlayerbot.CommandServerPort", 8888);
|
||||
perfMonEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.PerfMonEnabled", false);
|
||||
|
||||
useGroundMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseGroundMountAtMinLevel", 20);
|
||||
useFastGroundMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastGroundMountAtMinLevel", 40);
|
||||
useFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFlyMountAtMinLevel", 60);
|
||||
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
|
||||
|
||||
LOG_INFO("server.loading", "Loading TalentSpecs...");
|
||||
LOG_INFO("server.loading", "---------------------------------------");
|
||||
LOG_INFO("server.loading", " Loading TalentSpecs ");
|
||||
LOG_INFO("server.loading", "---------------------------------------");
|
||||
|
||||
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
@@ -421,22 +404,21 @@ bool PlayerbotAIConfig::Initialize()
|
||||
{
|
||||
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
|
||||
{
|
||||
for (uint32 specId = 0; specId <= MAX_WORLDBUFF_SPECNO; specId++)
|
||||
for (uint32 specId = 0; specId < MAX_SPECNO; specId++)
|
||||
{
|
||||
for (uint32 minLevel = 0; minLevel <= randomBotMaxLevel; minLevel++)
|
||||
for (uint32 minLevel = 0; minLevel < MAX_LEVEL; minLevel++)
|
||||
{
|
||||
for (uint32 maxLevel = minLevel; maxLevel <= randomBotMaxLevel; maxLevel++)
|
||||
for (uint32 maxLevel = 0; maxLevel < MAX_LEVEL; maxLevel++)
|
||||
{
|
||||
loadWorldBuff(factionId, classId, specId, minLevel, maxLevel);
|
||||
loadWorldBuf(factionId, classId, specId, minLevel, maxLevel);
|
||||
}
|
||||
loadWorldBuff(factionId, classId, specId, minLevel, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
randomBotAccountPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAccountPrefix", "rndbot");
|
||||
randomBotAccountCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAccountCount", 0);
|
||||
randomBotAccountCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAccountCount", 200);
|
||||
deleteRandomBotAccounts = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotAccounts", false);
|
||||
randomBotGuildCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildCount", 20);
|
||||
deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false);
|
||||
@@ -445,7 +427,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
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);
|
||||
maxGuildTaskAdvertisementTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskAdvertisementTime", 12 * 3600);
|
||||
maxGuildTaskAdvertisementTime =
|
||||
sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskAdvertisementTime", 12 * 3600);
|
||||
minGuildTaskRewardTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskRewardTime", 300);
|
||||
maxGuildTaskRewardTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskRewardTime", 3600);
|
||||
guildTaskAdvertCleanupTime = sConfigMgr->GetOption<int32>("AiPlayerbot.GuildTaskAdvertCleanupTime", 300);
|
||||
@@ -475,6 +458,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoInitEquipLevelLimitRatio = sConfigMgr->GetOption<float>("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0);
|
||||
|
||||
maxAddedBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBots", 40);
|
||||
maxAddedBotsPerClass = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBotsPerClass", 10);
|
||||
addClassCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassCommand", 1);
|
||||
addClassAccountPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassAccountPoolSize", 50);
|
||||
maintenanceCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.MaintenanceCommand", 1);
|
||||
@@ -483,11 +467,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
|
||||
autoGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearScoreLimit", 0);
|
||||
|
||||
playerbotsXPrate = sConfigMgr->GetOption<float>("AiPlayerbot.PlayerbotsXPRate", 1.0);
|
||||
randomBotAllianceRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAllianceRatio", 50);
|
||||
randomBotHordeRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotHordeRatio", 50);
|
||||
playerbotsXPrate = sConfigMgr->GetOption<int32>("AiPlayerbot.KillXPRate", 1);
|
||||
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);
|
||||
limitTalentsExpansion = sConfigMgr->GetOption<bool>("AiPlayerbot.LimitTalentsExpansion", 0);
|
||||
botActiveAlone = sConfigMgr->GetOption<int32>("AiPlayerbot.BotActiveAlone", 100);
|
||||
BotActiveAloneForceWhenInRadius = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotActiveAloneForceWhenInRadius", 150);
|
||||
BotActiveAloneForceWhenInZone = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenInZone", 1);
|
||||
@@ -495,10 +476,10 @@ bool PlayerbotAIConfig::Initialize()
|
||||
BotActiveAloneForceWhenIsFriend = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenIsFriend", 1);
|
||||
BotActiveAloneForceWhenInGuild = sConfigMgr->GetOption<bool>("AiPlayerbot.BotActiveAloneForceWhenInGuild", 1);
|
||||
botActiveAloneSmartScale = sConfigMgr->GetOption<bool>("AiPlayerbot.botActiveAloneSmartScale", 1);
|
||||
botActiveAloneSmartScaleDiffLimitfloor = sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleDiffLimitfloor", 50);
|
||||
botActiveAloneSmartScaleDiffLimitCeiling = sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleDiffLimitCeiling", 200);
|
||||
botActiveAloneSmartScaleWhenMinLevel = sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1);
|
||||
botActiveAloneSmartScaleWhenMaxLevel = sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80);
|
||||
botActiveAloneSmartScaleWhenMinLevel =
|
||||
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1);
|
||||
botActiveAloneSmartScaleWhenMaxLevel =
|
||||
sConfigMgr->GetOption<uint32>("AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel", 80);
|
||||
|
||||
randombotsWalkingRPG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG", false);
|
||||
randombotsWalkingRPGInDoors = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG.InDoors", false);
|
||||
@@ -506,9 +487,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 1);
|
||||
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 1);
|
||||
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 5);
|
||||
enablePeriodicOnlineOffline = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePeriodicOnlineOffline", false);
|
||||
enableRandomBotTrading = sConfigMgr->GetOption<int32>("AiPlayerbot.EnableRandomBotTrading", 1);
|
||||
periodicOnlineOfflineRatio = sConfigMgr->GetOption<float>("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0);
|
||||
enableRotation = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableRotation", false);
|
||||
rotationPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.RotationPoolSize", 500);
|
||||
gearscorecheck = sConfigMgr->GetOption<bool>("AiPlayerbot.GearScoreCheck", false);
|
||||
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", true);
|
||||
|
||||
@@ -525,7 +505,6 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
|
||||
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
|
||||
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
|
||||
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
|
||||
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
|
||||
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
|
||||
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
|
||||
@@ -550,11 +529,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->enabled)
|
||||
{
|
||||
sRandomPlayerbotMgr->Init();
|
||||
}
|
||||
if (sPlayerbotAIConfig->addClassCommand)
|
||||
sRandomPlayerbotMgr->PrepareAddclassCache();
|
||||
|
||||
sRandomItemMgr->Init();
|
||||
sRandomItemMgr->InitAfterAhBot();
|
||||
@@ -568,6 +544,8 @@ bool PlayerbotAIConfig::Initialize()
|
||||
sTravelMgr->LoadQuestTravelTable();
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotJoinBG)
|
||||
sRandomPlayerbotMgr->LoadBattleMastersCache();
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
|
||||
{
|
||||
@@ -678,7 +656,7 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void PlayerbotAIConfig::loadWorldBuff(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
|
||||
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
|
||||
{
|
||||
std::vector<uint32> buffs;
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ enum class HealingManaEfficiency : uint8
|
||||
};
|
||||
|
||||
#define MAX_SPECNO 20
|
||||
#define MAX_WORLDBUFF_SPECNO 3
|
||||
|
||||
class PlayerbotAIConfig
|
||||
{
|
||||
@@ -55,7 +54,7 @@ public:
|
||||
bool IsInPvpProhibitedArea(uint32 id);
|
||||
|
||||
bool enabled;
|
||||
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
|
||||
bool allowGuildBots, allowPlayerBots;
|
||||
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
|
||||
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
|
||||
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
|
||||
@@ -86,7 +85,7 @@ public:
|
||||
float randomGearLoweringChance;
|
||||
int32 randomGearQualityLimit;
|
||||
int32 randomGearScoreLimit;
|
||||
float randomBotMinLevelChance, randomBotMaxLevelChance;
|
||||
float randomBotMaxLevelChance;
|
||||
float randomBotRpgChance;
|
||||
uint32 minRandomBots, maxRandomBots;
|
||||
uint32 randomBotUpdateInterval, randomBotCountChangeMinInterval, randomBotCountChangeMaxInterval;
|
||||
@@ -95,7 +94,7 @@ public:
|
||||
uint32 minRandomBotChangeStrategyTime, maxRandomBotChangeStrategyTime;
|
||||
uint32 minRandomBotReviveTime, maxRandomBotReviveTime;
|
||||
uint32 minRandomBotTeleportInterval, maxRandomBotTeleportInterval;
|
||||
uint32 permanantlyInWorldTime;
|
||||
uint32 randomBotInWorldWithRotationDisabled;
|
||||
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
|
||||
uint32 randomBotsPerInterval;
|
||||
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
|
||||
@@ -174,25 +173,12 @@ public:
|
||||
|
||||
bool randomBotJoinBG;
|
||||
bool randomBotAutoJoinBG;
|
||||
|
||||
std::string randomBotAutoJoinICBrackets;
|
||||
std::string randomBotAutoJoinEYBrackets;
|
||||
std::string randomBotAutoJoinAVBrackets;
|
||||
std::string randomBotAutoJoinABBrackets;
|
||||
std::string randomBotAutoJoinWSBrackets;
|
||||
|
||||
uint32 randomBotAutoJoinBGICCount;
|
||||
uint32 randomBotAutoJoinBGEYCount;
|
||||
uint32 randomBotAutoJoinBGAVCount;
|
||||
uint32 randomBotAutoJoinBGABCount;
|
||||
uint32 randomBotAutoJoinBGWSCount;
|
||||
|
||||
uint32 randomBotAutoJoinWarsongBracket;
|
||||
uint32 randomBotAutoJoinArenaBracket;
|
||||
|
||||
uint32 randomBotAutoJoinBGWarsongCount;
|
||||
uint32 randomBotAutoJoinBGRatedArena2v2Count;
|
||||
uint32 randomBotAutoJoinBGRatedArena3v3Count;
|
||||
uint32 randomBotAutoJoinBGRatedArena5v5Count;
|
||||
|
||||
bool randomBotLoginAtStartup;
|
||||
uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel;
|
||||
bool logInGroupOnly, logValuesPerTick;
|
||||
@@ -235,8 +221,8 @@ public:
|
||||
uint32 limitEnchantExpansion;
|
||||
uint32 limitGearExpansion;
|
||||
uint32 randombotStartingLevel;
|
||||
bool enablePeriodicOnlineOffline;
|
||||
float periodicOnlineOfflineRatio;
|
||||
bool enableRotation;
|
||||
uint32 rotationPoolSize;
|
||||
bool gearscorecheck;
|
||||
bool randomBotPreQuests;
|
||||
|
||||
@@ -275,11 +261,8 @@ public:
|
||||
bool randomBotShowCloak;
|
||||
bool randomBotFixedLevel;
|
||||
bool disableRandomLevels;
|
||||
float playerbotsXPrate;
|
||||
uint32 randomBotAllianceRatio;
|
||||
uint32 randomBotHordeRatio;
|
||||
uint32 playerbotsXPrate;
|
||||
bool disableDeathKnightLogin;
|
||||
bool limitTalentsExpansion;
|
||||
uint32 botActiveAlone;
|
||||
uint32 BotActiveAloneForceWhenInRadius;
|
||||
bool BotActiveAloneForceWhenInZone;
|
||||
@@ -287,8 +270,6 @@ public:
|
||||
bool BotActiveAloneForceWhenIsFriend;
|
||||
bool BotActiveAloneForceWhenInGuild;
|
||||
bool botActiveAloneSmartScale;
|
||||
uint32 botActiveAloneSmartScaleDiffLimitfloor;
|
||||
uint32 botActiveAloneSmartScaleDiffLimitCeiling;
|
||||
uint32 botActiveAloneSmartScaleWhenMinLevel;
|
||||
uint32 botActiveAloneSmartScaleWhenMaxLevel;
|
||||
|
||||
@@ -304,7 +285,6 @@ public:
|
||||
std::string autoTrainSpells;
|
||||
bool autoPickTalents;
|
||||
bool autoUpgradeEquip;
|
||||
int32 hunterWolfPet;
|
||||
bool autoLearnTrainerSpells;
|
||||
bool autoDoQuests;
|
||||
bool enableNewRpgStrategy;
|
||||
@@ -313,7 +293,6 @@ public:
|
||||
bool autoLearnQuestSpells;
|
||||
bool autoTeleportForLevel;
|
||||
bool randomBotGroupNearby;
|
||||
int32 enableRandomBotTrading;
|
||||
uint32 tweakValue; // Debugging config
|
||||
|
||||
uint32 randomBotArenaTeamCount;
|
||||
@@ -337,17 +316,12 @@ public:
|
||||
bool botRepairWhenSummon;
|
||||
bool autoInitOnly;
|
||||
float autoInitEquipLevelLimitRatio;
|
||||
int32 maxAddedBots;
|
||||
int32 maxAddedBots, maxAddedBotsPerClass;
|
||||
int32 addClassCommand;
|
||||
int32 addClassAccountPoolSize;
|
||||
int32 maintenanceCommand;
|
||||
int32 autoGearCommand, autoGearCommandAltBots, autoGearQualityLimit, autoGearScoreLimit;
|
||||
|
||||
uint32 useGroundMountAtMinLevel;
|
||||
uint32 useFastGroundMountAtMinLevel;
|
||||
uint32 useFlyMountAtMinLevel;
|
||||
uint32 useFastFlyMountAtMinLevel;
|
||||
|
||||
std::string const GetTimestampStr();
|
||||
bool hasLog(std::string const fileName)
|
||||
{
|
||||
@@ -361,7 +335,7 @@ public:
|
||||
}
|
||||
void log(std::string const fileName, const char* str, ...);
|
||||
|
||||
void loadWorldBuff(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
|
||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
|
||||
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
|
||||
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,11 @@ void PlayerbotDbStore::Load(PlayerbotAI* botAI)
|
||||
stmt->SetData(0, guid);
|
||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||
{
|
||||
botAI->ClearStrategies(BOT_STATE_COMBAT);
|
||||
botAI->ClearStrategies(BOT_STATE_NON_COMBAT);
|
||||
botAI->ChangeStrategy("+chat", BOT_STATE_COMBAT);
|
||||
botAI->ChangeStrategy("+chat", BOT_STATE_NON_COMBAT);
|
||||
|
||||
std::vector<std::string> values;
|
||||
do
|
||||
{
|
||||
@@ -27,17 +32,9 @@ void PlayerbotDbStore::Load(PlayerbotAI* botAI)
|
||||
if (key == "value")
|
||||
values.push_back(value);
|
||||
else if (key == "co")
|
||||
{
|
||||
botAI->ClearStrategies(BOT_STATE_COMBAT);
|
||||
botAI->ChangeStrategy("+chat", BOT_STATE_COMBAT);
|
||||
botAI->ChangeStrategy(value, BOT_STATE_COMBAT);
|
||||
}
|
||||
else if (key == "nc")
|
||||
{
|
||||
botAI->ClearStrategies(BOT_STATE_NON_COMBAT);
|
||||
botAI->ChangeStrategy("+chat", BOT_STATE_NON_COMBAT);
|
||||
botAI->ChangeStrategy(value, BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
else if (key == "dead")
|
||||
botAI->ChangeStrategy(value, BOT_STATE_DEAD);
|
||||
} while (result->NextRow());
|
||||
@@ -52,11 +49,6 @@ void PlayerbotDbStore::Save(PlayerbotAI* botAI)
|
||||
|
||||
Reset(botAI);
|
||||
|
||||
PlayerbotsDatabasePreparedStatement* deleteStatement =
|
||||
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_DB_STORE);
|
||||
deleteStatement->SetData(0, guid);
|
||||
PlayerbotsDatabase.Execute(deleteStatement);
|
||||
|
||||
std::vector<std::string> data = botAI->GetAiObjectContext()->Save();
|
||||
for (std::vector<std::string>::iterator i = data.begin(); i != data.end(); ++i)
|
||||
{
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <openssl/sha.h>
|
||||
#include <unordered_set>
|
||||
#include <iomanip>
|
||||
|
||||
#include "ChannelMgr.h"
|
||||
#include "CharacterCache.h"
|
||||
@@ -20,7 +17,6 @@
|
||||
#include "Define.h"
|
||||
#include "Group.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ObjectMgr.h"
|
||||
@@ -34,36 +30,6 @@
|
||||
#include "WorldSession.h"
|
||||
#include "ChannelMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
class BotInitGuard
|
||||
{
|
||||
public:
|
||||
BotInitGuard(ObjectGuid guid) : guid(guid), active(false)
|
||||
{
|
||||
if (!botsBeingInitialized.contains(guid))
|
||||
{
|
||||
botsBeingInitialized.insert(guid);
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
|
||||
~BotInitGuard()
|
||||
{
|
||||
if (active)
|
||||
botsBeingInitialized.erase(guid);
|
||||
}
|
||||
|
||||
bool IsLocked() const { return !active; }
|
||||
|
||||
private:
|
||||
ObjectGuid guid;
|
||||
bool active;
|
||||
static std::unordered_set<ObjectGuid> botsBeingInitialized;
|
||||
};
|
||||
|
||||
std::unordered_set<ObjectGuid> BotInitGuard::botsBeingInitialized;
|
||||
|
||||
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
|
||||
class PlayerbotLoginQueryHolder : public LoginQueryHolder
|
||||
@@ -71,6 +37,7 @@ class PlayerbotLoginQueryHolder : public LoginQueryHolder
|
||||
private:
|
||||
uint32 masterAccountId;
|
||||
PlayerbotHolder* playerbotHolder;
|
||||
|
||||
public:
|
||||
PlayerbotLoginQueryHolder(PlayerbotHolder* playerbotHolder, uint32 masterAccount, uint32 accountId, ObjectGuid guid)
|
||||
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount), playerbotHolder(playerbotHolder)
|
||||
@@ -96,49 +63,6 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
if (!accountId)
|
||||
return;
|
||||
|
||||
WorldSession* masterSession = masterAccountId ? sWorldSessionMgr->FindSession(masterAccountId) : nullptr;
|
||||
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
|
||||
|
||||
bool isRndbot = !masterAccountId;
|
||||
bool sameAccount = sPlayerbotAIConfig->allowAccountBots && accountId == masterAccountId;
|
||||
Guild* guild = masterPlayer ? sGuildMgr->GetGuildById(masterPlayer->GetGuildId()) : nullptr;
|
||||
bool sameGuild = sPlayerbotAIConfig->allowGuildBots && guild && guild->GetMember(playerGuid);
|
||||
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(playerGuid.GetCounter());
|
||||
bool linkedAccount = sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId);
|
||||
|
||||
bool allowed = true;
|
||||
std::ostringstream out;
|
||||
std::string botName;
|
||||
sCharacterCache->GetCharacterNameByGuid(playerGuid, botName);
|
||||
if (!isRndbot && !sameAccount && !sameGuild && !addClassBot && !linkedAccount)
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You are not allowed to control bot " << botName.c_str();
|
||||
}
|
||||
if (masterAccountId && masterPlayer)
|
||||
{
|
||||
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer);
|
||||
if (!mgr)
|
||||
{
|
||||
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
||||
return;
|
||||
}
|
||||
uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
|
||||
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You have added too many bots (more than " << sPlayerbotAIConfig->maxAddedBots << ")";
|
||||
}
|
||||
}
|
||||
if (!allowed)
|
||||
{
|
||||
if (masterSession)
|
||||
{
|
||||
ChatHandler ch(masterSession);
|
||||
ch.SendSysMessage(out.str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<PlayerbotLoginQueryHolder> holder =
|
||||
std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
|
||||
if (!holder->Initialize())
|
||||
@@ -147,23 +71,25 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
}
|
||||
|
||||
botLoading.insert(playerGuid);
|
||||
|
||||
// Always login in with world session to avoid race condition
|
||||
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||
.AfterComplete([this](SQLQueryHolderBase const& holder)
|
||||
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
|
||||
}
|
||||
|
||||
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
|
||||
{
|
||||
QueryResult result = PlayerbotsDatabase.Query(
|
||||
"SELECT 1 FROM playerbot_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
|
||||
return result != nullptr;
|
||||
|
||||
if (WorldSession* masterSession = sWorld->FindSession(masterAccountId))
|
||||
{
|
||||
masterSession->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||
.AfterComplete([this](SQLQueryHolderBase const& holder)
|
||||
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
|
||||
}
|
||||
else
|
||||
{
|
||||
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||
.AfterComplete([this](SQLQueryHolderBase const& holder)
|
||||
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder)
|
||||
{
|
||||
uint32 botAccountId = holder.GetAccountId();
|
||||
|
||||
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
|
||||
// allows channels to work as intended)
|
||||
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
|
||||
@@ -183,7 +109,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
||||
}
|
||||
|
||||
uint32 masterAccount = holder.GetMasterAccountId();
|
||||
WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr;
|
||||
WorldSession* masterSession = masterAccount ? sWorld->FindSession(masterAccount) : nullptr;
|
||||
|
||||
// Check if masterSession->GetPlayer() is valid
|
||||
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
|
||||
@@ -192,9 +118,64 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
||||
LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount);
|
||||
}
|
||||
|
||||
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
||||
OnBotLogin(bot);
|
||||
std::ostringstream out;
|
||||
bool allowed = false;
|
||||
if (botAccountId == masterAccount)
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
else if (masterSession && sPlayerbotAIConfig->allowGuildBots && bot->GetGuildId() != 0 &&
|
||||
bot->GetGuildId() == masterPlayer->GetGuildId())
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
else if (sPlayerbotAIConfig->IsInRandomAccountList(botAccountId))
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You are not allowed to control bot " << bot->GetName().c_str();
|
||||
}
|
||||
|
||||
if (allowed && masterSession && masterPlayer)
|
||||
{
|
||||
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer);
|
||||
if (!mgr)
|
||||
{
|
||||
LOG_DEBUG("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
||||
}
|
||||
|
||||
uint32 count = mgr->GetPlayerbotsCount();
|
||||
uint32 cls_count = mgr->GetPlayerbotsCountByClass(bot->getClass());
|
||||
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You have added too many bots";
|
||||
}
|
||||
else if (cls_count >= sPlayerbotAIConfig->maxAddedBotsPerClass)
|
||||
{
|
||||
allowed = false;
|
||||
out << "Failure: You have added too many bots for this class";
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed)
|
||||
{
|
||||
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
||||
OnBotLogin(bot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (masterSession)
|
||||
{
|
||||
ChatHandler ch(masterSession);
|
||||
ch.SendSysMessage(out.str());
|
||||
}
|
||||
botSession->LogoutPlayer(true);
|
||||
delete botSession;
|
||||
}
|
||||
botLoading.erase(holder.GetGuid());
|
||||
}
|
||||
|
||||
@@ -314,7 +295,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
||||
sPlayerbotDbStore->Save(botAI);
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str());
|
||||
LOG_INFO("playerbots", "Bot {} logging out", bot->GetName().c_str());
|
||||
bot->SaveToDB(false, false);
|
||||
|
||||
WorldSession* botWorldSessionPtr = bot->GetSession();
|
||||
@@ -371,7 +352,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
||||
botWorldSessionPtr->HandleLogoutRequestOpcode(data);
|
||||
if (!bot)
|
||||
{
|
||||
RemoveFromPlayerbotsMap(guid);
|
||||
playerBots.erase(guid);
|
||||
delete botWorldSessionPtr;
|
||||
if (target)
|
||||
delete target;
|
||||
@@ -380,7 +361,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
playerBots.erase(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
||||
if (target)
|
||||
delete target;
|
||||
@@ -390,7 +371,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
||||
else if (bot && (logout || !botWorldSessionPtr->isLogingOut()))
|
||||
{
|
||||
botAI->TellMaster("Goodbye!");
|
||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
playerBots.erase(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object
|
||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
||||
}
|
||||
@@ -427,17 +408,12 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid)
|
||||
delete target;
|
||||
}
|
||||
|
||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
playerBots.erase(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
|
||||
delete botAI;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotHolder::RemoveFromPlayerbotsMap(ObjectGuid guid)
|
||||
{
|
||||
playerBots.erase(guid);
|
||||
}
|
||||
|
||||
Player* PlayerbotHolder::GetPlayerBot(ObjectGuid playerGuid) const
|
||||
{
|
||||
PlayerBotMap::const_iterator it = playerBots.find(playerGuid);
|
||||
@@ -461,7 +437,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
|
||||
sPlayerbotsMgr->AddPlayerbotData(bot, true);
|
||||
playerBots[bot->GetGUID()] = bot;
|
||||
|
||||
OnBotLoginInternal(bot);
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
@@ -509,7 +484,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
|
||||
if (!groupValid)
|
||||
{
|
||||
bot->RemoveFromGroup();
|
||||
WorldPacket p;
|
||||
std::string const member = bot->GetName();
|
||||
p << uint32(PARTY_OP_LEAVE) << member << uint32(0);
|
||||
bot->GetSession()->HandleGroupDisbandOpcode(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,9 +498,9 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
}
|
||||
else
|
||||
{
|
||||
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
|
||||
// botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
|
||||
botAI->ResetStrategies();
|
||||
}
|
||||
sPlayerbotDbStore->Load(botAI);
|
||||
|
||||
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||
{
|
||||
@@ -581,16 +559,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
}
|
||||
|
||||
bot->SaveToDB(false, false);
|
||||
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(bot->GetGUID().GetCounter());
|
||||
if (addClassBot && master && isRandomAccount && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
|
||||
if (master && isRandomAccount && master->GetLevel() < bot->GetLevel())
|
||||
{
|
||||
// PlayerbotFactory factory(bot, master->GetLevel());
|
||||
// factory.Randomize(false);
|
||||
uint32 mixedGearScore =
|
||||
PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
// work around: distinguish from 0 if no gear
|
||||
if (mixedGearScore == 0)
|
||||
mixedGearScore = 1;
|
||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||
factory.Randomize(false);
|
||||
}
|
||||
@@ -668,28 +642,25 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
return "bot system is disabled";
|
||||
|
||||
uint32 botAccount = sCharacterCache->GetCharacterAccountIdByGuid(guid);
|
||||
//bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(guid.GetCounter()); //not used, line marked for removal.
|
||||
//bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount); //not used, shadowed, line marked for removal.
|
||||
//bool isMasterAccount = (masterAccountId == botAccount); //not used, line marked for removal.
|
||||
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(guid.GetCounter());
|
||||
bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount);
|
||||
bool isMasterAccount = (masterAccountId == botAccount);
|
||||
|
||||
if (cmd == "add" || cmd == "addaccount" || cmd == "login")
|
||||
if (!isRandomAccount && !isMasterAccount && !admin && masterguid)
|
||||
{
|
||||
Player* master = ObjectAccessor::FindConnectedPlayer(masterguid);
|
||||
if (master && (!sPlayerbotAIConfig->allowGuildBots || !masterGuildId ||
|
||||
(masterGuildId && sCharacterCache->GetCharacterGuildIdByGuid(guid) != masterGuildId)))
|
||||
return "not in your guild or account";
|
||||
}
|
||||
|
||||
if (cmd == "add" || cmd == "login")
|
||||
{
|
||||
if (ObjectAccessor::FindPlayer(guid))
|
||||
return "player already logged in";
|
||||
|
||||
// For addaccount command, verify it's an account name
|
||||
if (cmd == "addaccount")
|
||||
{
|
||||
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid);
|
||||
if (!accountId)
|
||||
return "character not found";
|
||||
|
||||
if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId &&
|
||||
!(sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId)))
|
||||
{
|
||||
return "you can only add bots from your own account or linked accounts";
|
||||
}
|
||||
}
|
||||
if (!sPlayerbotAIConfig->allowPlayerBots && !isRandomAccount && !isMasterAccount)
|
||||
return "You cannot login another player's character as bot.";
|
||||
|
||||
AddPlayerBot(guid, masterAccountId);
|
||||
return "ok";
|
||||
@@ -715,10 +686,10 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
if (!bot)
|
||||
return "bot not found";
|
||||
|
||||
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(guid.GetCounter());
|
||||
|
||||
if (!addClassBot)
|
||||
return "ERROR: You can not use this command on non-addclass bot.";
|
||||
if (!isRandomAccount || isRandomBot)
|
||||
{
|
||||
return "ERROR: You can not use this command on non-summoned random bot.";
|
||||
}
|
||||
|
||||
if (!admin)
|
||||
{
|
||||
@@ -738,14 +709,6 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
{
|
||||
return "The command is not allowed, use init=auto instead.";
|
||||
}
|
||||
|
||||
// Use boot guard
|
||||
BotInitGuard guard(bot->GetGUID());
|
||||
if (guard.IsLocked())
|
||||
{
|
||||
return "Initialization already in progress, please wait.";
|
||||
}
|
||||
|
||||
int gs;
|
||||
if (cmd == "init=white" || cmd == "init=common")
|
||||
{
|
||||
@@ -781,9 +744,6 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
{
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) *
|
||||
sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
// work around: distinguish from 0 if no gear
|
||||
if (mixedGearScore == 0)
|
||||
mixedGearScore = 1;
|
||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||
factory.Randomize(false);
|
||||
return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) +
|
||||
@@ -875,7 +835,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
|
||||
if (!*args)
|
||||
{
|
||||
messages.push_back("usage: list/reload/tweak/self or add/addaccount/init/remove PLAYERNAME\n");
|
||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME\n");
|
||||
messages.push_back("usage: addclass CLASSNAME");
|
||||
return messages;
|
||||
}
|
||||
@@ -1107,22 +1067,15 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
messages.push_back("Error: Invalid Class. Try again.");
|
||||
return messages;
|
||||
}
|
||||
if (claz == 6 && master->GetLevel() < sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL))
|
||||
{
|
||||
messages.push_back("Your level is too low to summon Deathknight");
|
||||
return messages;
|
||||
}
|
||||
uint8 teamId = master->GetTeamId(true);
|
||||
const std::unordered_set<ObjectGuid> &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)];
|
||||
for (const ObjectGuid &guid: guidCache)
|
||||
std::vector<ObjectGuid> &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)];
|
||||
for (size_t i = 0; i < guidCache.size(); i++)
|
||||
{
|
||||
ObjectGuid guid = guidCache[i];
|
||||
if (botLoading.find(guid) != botLoading.end())
|
||||
continue;
|
||||
if (ObjectAccessor::FindConnectedPlayer(guid))
|
||||
continue;
|
||||
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
|
||||
if (guildId && PlayerbotAI::IsRealGuild(guildId))
|
||||
continue;
|
||||
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
||||
messages.push_back("Add class " + std::string(charname));
|
||||
return messages;
|
||||
@@ -1194,48 +1147,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
{
|
||||
std::string const s = *i;
|
||||
|
||||
if (!strcmp(cmd, "addaccount"))
|
||||
uint32 accountId = GetAccountId(s);
|
||||
if (!accountId)
|
||||
{
|
||||
// When using addaccount, first try to get account ID directly
|
||||
uint32 accountId = GetAccountId(s);
|
||||
if (!accountId)
|
||||
{
|
||||
// If not found, try to get account ID from character name
|
||||
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
|
||||
if (!charGuid)
|
||||
{
|
||||
messages.push_back("Neither account nor character '" + s + "' found");
|
||||
continue;
|
||||
}
|
||||
accountId = sCharacterCache->GetCharacterAccountIdByGuid(charGuid);
|
||||
if (!accountId)
|
||||
{
|
||||
messages.push_back("Could not find account for character '" + s + "'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
|
||||
if (results)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
std::string const charName = fields[0].Get<std::string>();
|
||||
bots.insert(charName);
|
||||
} while (results->NextRow());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For regular add command, only add the specific character
|
||||
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
|
||||
if (!charGuid)
|
||||
{
|
||||
messages.push_back("Character '" + s + "' not found");
|
||||
continue;
|
||||
}
|
||||
bots.insert(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
|
||||
if (results)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
std::string const charName = fields[0].Get<std::string>();
|
||||
bots.insert(charName);
|
||||
} while (results->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1718,121 +1645,3 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string& key)
|
||||
{
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
|
||||
// Hash the security key using SHA-256
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)key.c_str(), key.size(), hash);
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
std::ostringstream hashedKey;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
|
||||
hashedKey << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
||||
|
||||
// Store the hashed key in the database
|
||||
PlayerbotsDatabase.Execute(
|
||||
"REPLACE INTO playerbot_account_keys (account_id, security_key) VALUES ({}, '{}')",
|
||||
accountId, hashedKey.str());
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully.");
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key)
|
||||
{
|
||||
QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
|
||||
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbot_account_keys WHERE account_id = {}", linkedAccountId);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Hash the provided key
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)key.c_str(), key.size(), hash);
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
std::ostringstream hashedKey;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
|
||||
hashedKey << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
||||
|
||||
// Compare the hashed key with the stored hashed key
|
||||
std::string storedKey = result->Fetch()->Get<std::string>();
|
||||
if (hashedKey.str() != storedKey)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
accountId, linkedAccountId);
|
||||
PlayerbotsDatabase.Execute(
|
||||
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
|
||||
linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account linked successfully.");
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player)
|
||||
{
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbot_account_links WHERE account_id = {}", accountId);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("No linked accounts.");
|
||||
return;
|
||||
}
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Linked accounts:");
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
|
||||
QueryResult accountResult = LoginDatabase.Query("SELECT username FROM account WHERE id = {}", linkedAccountId);
|
||||
if (accountResult)
|
||||
{
|
||||
Field* accountFields = accountResult->Fetch();
|
||||
std::string username = accountFields[0].Get<std::string>();
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("- {}", username.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("- Unknown account");
|
||||
}
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string& accountName)
|
||||
{
|
||||
QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName);
|
||||
if (!result)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 linkedAccountId = fields[0].Get<uint32>();
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbot_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
|
||||
accountId, linkedAccountId, linkedAccountId, accountId);
|
||||
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("Account unlinked successfully.");
|
||||
}
|
||||
|
||||
@@ -28,12 +28,10 @@ public:
|
||||
virtual ~PlayerbotHolder(){};
|
||||
|
||||
void AddPlayerBot(ObjectGuid guid, uint32 masterAccountId);
|
||||
bool IsAccountLinked(uint32 accountId, uint32 masterAccountId);
|
||||
void HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder);
|
||||
|
||||
void LogoutPlayerBot(ObjectGuid guid);
|
||||
void DisablePlayerBot(ObjectGuid guid);
|
||||
void RemoveFromPlayerbotsMap(ObjectGuid guid);
|
||||
Player* GetPlayerBot(ObjectGuid guid) const;
|
||||
Player* GetPlayerBot(ObjectGuid::LowType lowGuid) const;
|
||||
PlayerBotMap::const_iterator GetPlayerBotsBegin() const { return playerBots.begin(); }
|
||||
@@ -83,11 +81,6 @@ public:
|
||||
|
||||
void SaveToDB();
|
||||
|
||||
void HandleSetSecurityKeyCommand(Player* player, const std::string& key);
|
||||
void HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key);
|
||||
void HandleViewLinkedAccountsCommand(Player* player);
|
||||
void HandleUnlinkAccountCommand(Player* player, const std::string& accountName);
|
||||
|
||||
protected:
|
||||
void OnBotLoginInternal(Player* const bot) override;
|
||||
void CheckTellErrors(uint32 elapsed);
|
||||
|
||||
@@ -86,8 +86,8 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
}
|
||||
}
|
||||
|
||||
int32 botGS = (int32)botAI->GetEquipGearScore(bot/*, false, false*/);
|
||||
int32 fromGS = (int32)botAI->GetEquipGearScore(from/*, false, false*/);
|
||||
int32 botGS = (int32)botAI->GetEquipGearScore(bot, false, false);
|
||||
int32 fromGS = (int32)botAI->GetEquipGearScore(from, false, false);
|
||||
if (sPlayerbotAIConfig->gearscorecheck)
|
||||
{
|
||||
if (botGS && bot->GetLevel() > 15 && botGS > fromGS &&
|
||||
@@ -139,7 +139,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
return PLAYERBOT_SECURITY_INVITE;
|
||||
}
|
||||
|
||||
if (!ignoreGroup && group->IsFull())
|
||||
if (group->IsFull())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_FULL_GROUP;
|
||||
@@ -147,7 +147,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
|
||||
if (!ignoreGroup && group->GetLeaderGUID() != bot->GetGUID())
|
||||
if (group->GetLeaderGUID() != bot->GetGUID())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_NOT_LEADER;
|
||||
@@ -211,8 +211,8 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
break;
|
||||
case PLAYERBOT_DENY_GEARSCORE:
|
||||
{
|
||||
int botGS = (int)botAI->GetEquipGearScore(bot/*, false, false*/);
|
||||
int fromGS = (int)botAI->GetEquipGearScore(from/*, false, false*/);
|
||||
int botGS = (int)botAI->GetEquipGearScore(bot, false, false);
|
||||
int fromGS = (int)botAI->GetEquipGearScore(from, false, false);
|
||||
int diff = (100 * (botGS - fromGS) / botGS);
|
||||
int req = 12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel();
|
||||
out << "Your gearscore is too low: |cffff0000" << fromGS << "|cffffffff/|cff00ff00" << botGS
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "PlayerbotTextMgr.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
void PlayerbotTextMgr::replaceAll(std::string& str, const std::string& from, const std::string& to)
|
||||
{
|
||||
@@ -185,7 +184,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority()
|
||||
uint32 topLocale = 0;
|
||||
|
||||
// if no real players online, reset top locale
|
||||
if (!sWorldSessionMgr->GetActiveSessionCount())
|
||||
if (!sWorld->GetActiveSessionCount())
|
||||
{
|
||||
ResetLocalePriority();
|
||||
return 0;
|
||||
@@ -23,8 +23,6 @@
|
||||
#include "DatabaseLoader.h"
|
||||
#include "GuildTaskMgr.h"
|
||||
#include "Metric.h"
|
||||
#include "PlayerScript.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "cs_playerbots.h"
|
||||
@@ -74,53 +72,45 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class PlayerbotsMetricScript : public MetricScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsMetricScript() : MetricScript("PlayerbotsMetricScript") {}
|
||||
|
||||
void OnMetricLogging() override
|
||||
{
|
||||
if (sMetric->IsEnabled())
|
||||
{
|
||||
sMetric->LogValue("db_queue_playerbots", uint64(PlayerbotsDatabase.QueueSize()), {});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PlayerbotsPlayerScript : public PlayerScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
|
||||
PLAYERHOOK_ON_LOGIN,
|
||||
PLAYERHOOK_ON_AFTER_UPDATE,
|
||||
PLAYERHOOK_ON_CHAT,
|
||||
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
|
||||
PLAYERHOOK_ON_CHAT_WITH_GROUP,
|
||||
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
|
||||
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
||||
PLAYERHOOK_ON_GIVE_EXP
|
||||
}) {}
|
||||
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript") {}
|
||||
|
||||
void OnPlayerLogin(Player* player) override
|
||||
void OnLogin(Player* player) override
|
||||
{
|
||||
if (!player->GetSession()->IsBot())
|
||||
{
|
||||
sPlayerbotsMgr->AddPlayerbotData(player, false);
|
||||
sRandomPlayerbotMgr->OnPlayerLogin(player);
|
||||
|
||||
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
|
||||
// especially if you are distributing a repack or hosting a public server
|
||||
// e.g. you can replace the URL with your own repository,
|
||||
// but it should be publicly accessible and include all modifications you've made
|
||||
if (sPlayerbotAIConfig->enabled)
|
||||
{
|
||||
ChatHandler(player->GetSession()).SendSysMessage(
|
||||
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
|
||||
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
|
||||
{
|
||||
std::string roundedTime =
|
||||
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
|
||||
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.13 / 60) * 10) / 10.0);
|
||||
roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2);
|
||||
|
||||
ChatHandler(player->GetSession()).SendSysMessage(
|
||||
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
|
||||
+ roundedTime + "' minutes.");
|
||||
"Playerbots: bot initialization at server startup will require '" + roundedTime + "' minutes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
|
||||
void OnAfterUpdate(Player* player, uint32 diff) override
|
||||
{
|
||||
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
|
||||
{
|
||||
@@ -133,7 +123,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
|
||||
bool CanPlayerUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
|
||||
{
|
||||
if (type == CHAT_MSG_WHISPER)
|
||||
{
|
||||
@@ -148,7 +138,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
@@ -162,7 +152,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
|
||||
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
|
||||
{
|
||||
if (type == CHAT_MSG_GUILD)
|
||||
{
|
||||
@@ -183,7 +173,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||
{
|
||||
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
||||
{
|
||||
@@ -196,7 +186,7 @@ public:
|
||||
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
|
||||
}
|
||||
|
||||
bool OnPlayerBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* /*criteria*/) override
|
||||
bool OnBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* /*criteria*/) override
|
||||
{
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
{
|
||||
@@ -205,7 +195,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* /*achievement*/) override
|
||||
bool OnBeforeAchiComplete(Player* player, AchievementEntry const* /*achievement*/) override
|
||||
{
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
{
|
||||
@@ -213,17 +203,6 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
|
||||
{
|
||||
if (!player->GetSession()->IsBot())
|
||||
return;
|
||||
|
||||
if (sPlayerbotAIConfig->playerbotsXPrate != 1.0)
|
||||
{
|
||||
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->playerbotsXPrate));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PlayerbotsMiscScript : public MiscScript
|
||||
@@ -248,9 +227,7 @@ public:
|
||||
class PlayerbotsServerScript : public ServerScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsServerScript() : ServerScript("PlayerbotsServerScript", {
|
||||
SERVERHOOK_CAN_PACKET_RECEIVE
|
||||
}) {}
|
||||
PlayerbotsServerScript() : ServerScript("PlayerbotsServerScript") {}
|
||||
|
||||
void OnPacketReceived(WorldSession* session, WorldPacket const& packet) override
|
||||
{
|
||||
@@ -263,29 +240,12 @@ public:
|
||||
class PlayerbotsWorldScript : public WorldScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript", {
|
||||
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED
|
||||
}) {}
|
||||
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript") {}
|
||||
|
||||
void OnBeforeWorldInitialized() override
|
||||
{
|
||||
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
|
||||
// especially if you are distributing a repack or hosting a public server
|
||||
// e.g. you can replace the URL with your own repository,
|
||||
// but it should be publicly accessible and include all modifications you've made
|
||||
LOG_INFO("server.loading", "╔══════════════════════════════════════════════════════════╗");
|
||||
LOG_INFO("server.loading", "║ ║");
|
||||
LOG_INFO("server.loading", "║ AzerothCore Playerbots Module ║");
|
||||
LOG_INFO("server.loading", "║ ║");
|
||||
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
|
||||
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", "╚══════════════════════════════════════════════════════════╝");
|
||||
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
|
||||
LOG_INFO("server.loading", " ");
|
||||
LOG_INFO("server.loading", "Load Playerbots Config...");
|
||||
|
||||
@@ -379,16 +339,13 @@ public:
|
||||
sRandomPlayerbotMgr->OnPlayerLogout(player);
|
||||
}
|
||||
|
||||
void OnPlayerbotLogoutBots() override
|
||||
{
|
||||
LOG_INFO("playerbots", "Logging out all bots...");
|
||||
sRandomPlayerbotMgr->LogoutAllBots();
|
||||
}
|
||||
void OnPlayerbotLogoutBots() override { sRandomPlayerbotMgr->LogoutAllBots(); }
|
||||
};
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
new PlayerbotsDatabaseScript();
|
||||
new PlayerbotsMetricScript();
|
||||
new PlayerbotsPlayerScript();
|
||||
new PlayerbotsMiscScript();
|
||||
new PlayerbotsServerScript();
|
||||
|
||||
@@ -833,7 +833,7 @@ bool RandomItemMgr::CanEquipWeapon(uint8 clazz, ItemTemplate const* proto)
|
||||
|
||||
void RandomItemMgr::BuildItemInfoCache()
|
||||
{
|
||||
//uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); //not used, line marked for removal.
|
||||
uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
|
||||
// load weightscales
|
||||
LOG_INFO("playerbots", "Loading weightscales info");
|
||||
@@ -1347,7 +1347,7 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTem
|
||||
// check weapon dps
|
||||
if (proto->IsWeaponVellum())
|
||||
{
|
||||
//WeaponAttackType attType = BASE_ATTACK; //not used, line marked for removal.
|
||||
WeaponAttackType attType = BASE_ATTACK;
|
||||
|
||||
uint32 dps = 0;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; i++)
|
||||
@@ -2206,7 +2206,7 @@ void RandomItemMgr::BuildEquipCacheNew()
|
||||
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
||||
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
||||
{
|
||||
//uint32 questId = i->first; //not used in this scope, line marked for removal.
|
||||
uint32 questId = i->first;
|
||||
Quest const* quest = i->second;
|
||||
|
||||
if (quest->IsRepeatable())
|
||||
@@ -2313,9 +2313,8 @@ void RandomItemMgr::BuildAmmoCache()
|
||||
for (uint32 subClass = ITEM_SUBCLASS_ARROW; subClass <= ITEM_SUBCLASS_BULLET; subClass++)
|
||||
{
|
||||
QueryResult results = WorldDatabase.Query(
|
||||
"SELECT entry FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} AND duration = 0 "
|
||||
"AND (Flags & 16) = 0 AND dmg_min1 != 0 AND RequiredLevel != 0 "
|
||||
"ORDER BY stackable DESC, ItemLevel DESC",
|
||||
"SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} and duration = 0 "
|
||||
"ORDER BY stackable DESC, RequiredLevel DESC",
|
||||
ITEM_CLASS_PROJECTILE, subClass, level);
|
||||
if (!results)
|
||||
continue;
|
||||
@@ -2323,27 +2322,35 @@ void RandomItemMgr::BuildAmmoCache()
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
uint32 entry = fields[0].Get<uint32>();
|
||||
ammoCache[level][subClass].push_back(entry);
|
||||
uint32 flags = fields[1].Get<uint32>();
|
||||
if (flags & ITEM_FLAG_DEPRECATED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ammoCache[level][subClass] = entry;
|
||||
++counter;
|
||||
break;
|
||||
} while (results->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", "Cached {} ammo", counter); // TEST
|
||||
LOG_INFO("server.loading", "Cached {} types of ammo", counter); // TEST
|
||||
}
|
||||
|
||||
std::vector<uint32> RandomItemMgr::GetAmmo(uint32 level, uint32 subClass) { return ammoCache[level][subClass]; }
|
||||
uint32 RandomItemMgr::GetAmmo(uint32 level, uint32 subClass) { return ammoCache[level][subClass]; }
|
||||
|
||||
void RandomItemMgr::BuildPotionCache()
|
||||
{
|
||||
uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
// if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
// maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
|
||||
LOG_INFO("playerbots", "Building potion cache for {} levels", maxLevel);
|
||||
LOG_INFO("server.loading", "Building potion cache for {} levels", maxLevel);
|
||||
|
||||
ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore();
|
||||
|
||||
uint32 counter = 0;
|
||||
for (uint32 level = 1; level <= maxLevel; level++)
|
||||
for (uint32 level = 1; level <= maxLevel + 1; level += 10)
|
||||
{
|
||||
uint32 effects[] = {SPELL_EFFECT_HEAL, SPELL_EFFECT_ENERGIZE};
|
||||
for (uint8 i = 0; i < 2; ++i)
|
||||
@@ -2360,9 +2367,8 @@ void RandomItemMgr::BuildPotionCache()
|
||||
(proto->SubClass != ITEM_SUBCLASS_POTION && proto->SubClass != ITEM_SUBCLASS_FLASK) ||
|
||||
proto->Bonding != NO_BIND)
|
||||
continue;
|
||||
|
||||
uint32 requiredLevel = proto->RequiredLevel;
|
||||
if (requiredLevel > level || (level > 13 && requiredLevel < level - 13))
|
||||
|
||||
if (proto->RequiredLevel && (proto->RequiredLevel > level || proto->RequiredLevel < level - 10))
|
||||
continue;
|
||||
|
||||
if (proto->RequiredSkill)
|
||||
@@ -2374,44 +2380,39 @@ void RandomItemMgr::BuildPotionCache()
|
||||
if (proto->Duration & 0x80000000)
|
||||
continue;
|
||||
|
||||
|
||||
if (proto->AllowableClass != -1)
|
||||
continue;
|
||||
|
||||
bool hybrid = false;
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[0].SpellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
// do not accept hybrid potion
|
||||
for (uint8 i = 1; i < 3; i++)
|
||||
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
|
||||
{
|
||||
if (spellInfo->Effects[i].Effect != 0)
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
hybrid = true;
|
||||
break;
|
||||
if (spellInfo->Effects[i].Effect == effect)
|
||||
{
|
||||
potionCache[level / 10][effect].push_back(itr.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hybrid)
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[0].Effect == effect)
|
||||
potionCache[level][effect].push_back(itr.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 level = 1; level <= maxLevel; level++)
|
||||
for (uint32 level = 1; level <= maxLevel + 1; level += 10)
|
||||
{
|
||||
uint32 effects[] = {SPELL_EFFECT_HEAL, SPELL_EFFECT_ENERGIZE};
|
||||
for (uint8 i = 0; i < 2; ++i)
|
||||
{
|
||||
uint32 effect = effects[i];
|
||||
uint32 size = potionCache[level][effect].size();
|
||||
counter += size;
|
||||
uint32 size = potionCache[level / 10][effect].size();
|
||||
++counter;
|
||||
|
||||
LOG_DEBUG("server.loading", "Potion cache for level={}, effect={}: {} items", level, effect, size);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Cached {} potions", counter);
|
||||
LOG_INFO("server.loading", "Cached {} types of potions", counter); // TEST
|
||||
}
|
||||
|
||||
void RandomItemMgr::BuildFoodCache()
|
||||
@@ -2477,7 +2478,7 @@ void RandomItemMgr::BuildFoodCache()
|
||||
|
||||
uint32 RandomItemMgr::GetRandomPotion(uint32 level, uint32 effect)
|
||||
{
|
||||
const std::vector<uint32> &potions = potionCache[level][effect];
|
||||
std::vector<uint32> potions = potionCache[(level - 1) / 10][effect];
|
||||
if (potions.empty())
|
||||
return 0;
|
||||
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
uint32 GetStatWeight(Player* player, uint32 itemId);
|
||||
uint32 GetLiveStatWeight(Player* player, uint32 itemId);
|
||||
uint32 GetRandomItem(uint32 level, RandomItemType type, RandomItemPredicate* predicate = nullptr);
|
||||
std::vector<uint32> GetAmmo(uint32 level, uint32 subClass);
|
||||
uint32 GetAmmo(uint32 level, uint32 subClass);
|
||||
uint32 GetRandomPotion(uint32 level, uint32 effect);
|
||||
uint32 GetRandomFood(uint32 level, uint32 category);
|
||||
uint32 GetFood(uint32 level, uint32 category);
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
std::map<RandomItemType, RandomItemPredicate*> predicates;
|
||||
BotEquipCache equipCache;
|
||||
std::map<EquipmentSlots, std::set<InventoryType>> viableSlots;
|
||||
std::map<uint32, std::map<uint32, std::vector<uint32>>> ammoCache;
|
||||
std::map<uint32, std::map<uint32, uint32>> ammoCache;
|
||||
std::map<uint32, std::map<uint32, std::vector<uint32>>> potionCache;
|
||||
std::map<uint32, std::map<uint32, std::vector<uint32>>> foodCache;
|
||||
std::map<uint32, std::vector<uint32>> tradeCache;
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RandomPlayerbotFactory.h"
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SocialMgr.h"
|
||||
#include "Timer.h"
|
||||
|
||||
std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces;
|
||||
|
||||
constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 gender,
|
||||
uint8 race)
|
||||
uint8 race)
|
||||
{
|
||||
switch (race)
|
||||
{
|
||||
@@ -79,7 +78,6 @@ RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(acc
|
||||
availableRaces[CLASS_ROGUE].push_back(RACE_NIGHTELF);
|
||||
availableRaces[CLASS_ROGUE].push_back(RACE_GNOME);
|
||||
availableRaces[CLASS_ROGUE].push_back(RACE_ORC);
|
||||
availableRaces[CLASS_ROGUE].push_back(RACE_UNDEAD_PLAYER);
|
||||
availableRaces[CLASS_ROGUE].push_back(RACE_TROLL);
|
||||
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
|
||||
{
|
||||
@@ -225,7 +223,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
|
||||
}
|
||||
}
|
||||
|
||||
//uint8 skinColor = skinColors[urand(0, skinColors.size() - 1)]; //not used, line marked for removal.
|
||||
uint8 skinColor = skinColors[urand(0, skinColors.size() - 1)];
|
||||
std::pair<uint8, uint8> face = faces[urand(0, faces.size() - 1)];
|
||||
std::pair<uint8, uint8> hair = hairs[urand(0, hairs.size() - 1)];
|
||||
|
||||
@@ -244,7 +242,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
|
||||
delete player;
|
||||
|
||||
LOG_ERROR("playerbots", "Unable to create random bot for account {} - name: \"{}\"; race: {}; class: {}",
|
||||
accountId, name.c_str(), race, cls);
|
||||
accountId, name.c_str(), race, cls);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -256,7 +254,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
|
||||
player->learnSpell(50977, false);
|
||||
}
|
||||
LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId,
|
||||
name.c_str(), race, cls);
|
||||
name.c_str(), race, cls);
|
||||
|
||||
return player;
|
||||
}
|
||||
@@ -278,19 +276,13 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
botName = fields[0].Get<std::string>();
|
||||
if (ObjectMgr::CheckPlayerName(botName) == CHAR_NAME_SUCCESS) // Checks for reservation & profanity, too
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
|
||||
stmt->SetData(0, botName);
|
||||
|
||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
||||
continue;
|
||||
|
||||
return botName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CONLANG NAME GENERATION
|
||||
@@ -353,14 +345,6 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
|
||||
botName.clear();
|
||||
continue;
|
||||
}
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
|
||||
stmt->SetData(0, botName);
|
||||
|
||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
||||
{
|
||||
botName.clear();
|
||||
continue;
|
||||
}
|
||||
return std::move(botName);
|
||||
}
|
||||
|
||||
@@ -378,14 +362,6 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
|
||||
botName.clear();
|
||||
continue;
|
||||
}
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
|
||||
stmt->SetData(0, botName);
|
||||
|
||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
||||
{
|
||||
botName.clear();
|
||||
continue;
|
||||
}
|
||||
return std::move(botName);
|
||||
}
|
||||
LOG_ERROR("playerbots", "Random name generation failed.");
|
||||
@@ -393,76 +369,15 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
|
||||
return std::move(botName);
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
|
||||
{
|
||||
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
|
||||
// or determining it dynamically based on the WOTLK condition, max random bots, rotation pool size,
|
||||
// and additional class account pool size.
|
||||
|
||||
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
|
||||
if (sPlayerbotAIConfig->randomBotAccountCount > 0)
|
||||
return sPlayerbotAIConfig->randomBotAccountCount;
|
||||
|
||||
// Avoid creating accounts if both maxRandom & ClassBots are set to zero.
|
||||
if (sPlayerbotAIConfig->maxRandomBots == 0 &&
|
||||
sPlayerbotAIConfig->addClassAccountPoolSize == 0)
|
||||
return 0;
|
||||
|
||||
//bool isWOTLK = sWorld->getIntConfig(CONFIG_EXPANSION) == EXPANSION_WRATH_OF_THE_LICH_KING; //not used, line marked for removal.
|
||||
|
||||
// Determine divisor based on WOTLK condition
|
||||
int divisor = CalculateAvailableCharsPerAccount();
|
||||
|
||||
// Calculate max bots
|
||||
int maxBots = sPlayerbotAIConfig->maxRandomBots;
|
||||
// Take perodic online - offline into account
|
||||
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
||||
{
|
||||
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
|
||||
}
|
||||
|
||||
// Calculate base accounts, add class account pool size, and add 1 as a fixed offset
|
||||
uint32 baseAccounts = maxBots / divisor;
|
||||
return baseAccounts + sPlayerbotAIConfig->addClassAccountPoolSize + 1;
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
|
||||
{
|
||||
bool noDK = sPlayerbotAIConfig->disableDeathKnightLogin || sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING;
|
||||
|
||||
uint32 availableChars = noDK ? 9 : 10;
|
||||
|
||||
uint32 hordeRatio = sPlayerbotAIConfig->randomBotHordeRatio;
|
||||
uint32 allianceRatio = sPlayerbotAIConfig->randomBotAllianceRatio;
|
||||
|
||||
// horde : alliance = 50 : 50 -> 0%
|
||||
// horde : alliance = 0 : 50 -> 50%
|
||||
// horde : alliance = 10 : 50 -> 40%
|
||||
float unavailableRatio = static_cast<float>((std::max(hordeRatio, allianceRatio) - std::min(hordeRatio, allianceRatio))) /
|
||||
(std::max(hordeRatio, allianceRatio) * 2);
|
||||
|
||||
if (unavailableRatio != 0)
|
||||
{
|
||||
// conservative floor to ensure enough chars (may result in more accounts than needed)
|
||||
availableChars = availableChars - availableChars * unavailableRatio;
|
||||
}
|
||||
|
||||
return availableChars;
|
||||
}
|
||||
|
||||
void RandomPlayerbotFactory::CreateRandomBots()
|
||||
{
|
||||
/* multi-thread here is meaningless? since the async db operations */
|
||||
|
||||
if (sPlayerbotAIConfig->deleteRandomBotAccounts)
|
||||
{
|
||||
std::vector<uint32> botAccounts;
|
||||
std::vector<uint32> botFriends;
|
||||
|
||||
// Calculates the total number of required accounts.
|
||||
uint32 totalAccountCount = CalculateTotalAccountCount();
|
||||
|
||||
for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber)
|
||||
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
|
||||
@@ -472,79 +387,9 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
botAccounts.push_back(accountId);
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Deleting all random bot characters and accounts...");
|
||||
|
||||
// First execute all the cleanup SQL commands
|
||||
// Clear playerbots_random_bots table
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots");
|
||||
|
||||
// Get the database names dynamically
|
||||
std::string loginDBName = LoginDatabase.GetConnectionInfo()->database;
|
||||
std::string characterDBName = CharacterDatabase.GetConnectionInfo()->database;
|
||||
|
||||
// Delete all characters from bot accounts
|
||||
CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')",
|
||||
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||
|
||||
// Clean up orphaned entries in playerbots_guild_tasks
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)");
|
||||
|
||||
// Clean up orphaned entries in playerbots_db_store
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_db_store WHERE guid NOT IN (SELECT guid FROM " + characterDBName + ".characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username NOT LIKE '{}%%'))",
|
||||
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||
|
||||
// Clean up orphaned records in character-related tables
|
||||
CharacterDatabase.Execute("DELETE FROM arena_team_member WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM arena_team WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team_member)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_account_data WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
|
||||
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Clean up pet data
|
||||
CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
|
||||
// Clean up character data
|
||||
CharacterDatabase.Execute("DELETE FROM character_queststatus WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_queststatus_rewarded WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_reputation WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_skills WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_social WHERE friend NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_spell WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_spell_cooldown WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_talent WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM corpse WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Clean up group data
|
||||
CharacterDatabase.Execute("DELETE FROM `groups` WHERE leaderGuid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM group_member WHERE memberGuid NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Clean up mail
|
||||
CharacterDatabase.Execute("DELETE FROM mail WHERE receiver NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM mail_items WHERE receiver NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Clean up guild data
|
||||
CharacterDatabase.Execute("DELETE FROM guild WHERE leaderguid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM guild_bank_eventlog WHERE guildid NOT IN (SELECT guildid FROM guild)");
|
||||
CharacterDatabase.Execute("DELETE FROM guild_member WHERE guildid NOT IN (SELECT guildid FROM guild) OR guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM guild_rank WHERE guildid NOT IN (SELECT guildid FROM guild)");
|
||||
|
||||
// Clean up petition data
|
||||
CharacterDatabase.Execute("DELETE FROM petition WHERE ownerguid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM petition_sign WHERE ownerguid NOT IN (SELECT guid FROM characters) OR playerguid NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Finally, delete the bot accounts themselves
|
||||
LOG_INFO("playerbots", "Deleting random bot accounts...");
|
||||
LOG_INFO("playerbots", "Deleting all random bot characters, {} accounts collected...", botAccounts.size());
|
||||
QueryResult results = LoginDatabase.Query("SELECT id FROM account WHERE username LIKE '{}%%'",
|
||||
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||
int32 deletion_count = 0;
|
||||
if (results)
|
||||
{
|
||||
@@ -552,19 +397,16 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
uint32 accId = fields[0].Get<uint32>();
|
||||
LOG_DEBUG("playerbots", "Deleting account accID: {}({})...", accId, ++deletion_count);
|
||||
LOG_INFO("playerbots", "Deleting account accID: {}({})...", accId, ++deletion_count);
|
||||
AccountMgr::DeleteAccount(accId);
|
||||
} while (results->NextRow());
|
||||
}
|
||||
|
||||
uint32 timer = getMSTime();
|
||||
|
||||
// Wait for all pending database operations to complete
|
||||
while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer));
|
||||
PlayerbotsDatabase.Execute(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_RANDOM_BOTS));
|
||||
/* TODO(yunfan): we need to sleep here to wait for async account deleted, or the newly account won't be created
|
||||
correctly the better way is turning the async db operation to sync db operation */
|
||||
std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount);
|
||||
LOG_INFO("playerbots", "Random bot characters deleted.");
|
||||
LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server...");
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
return;
|
||||
@@ -572,14 +414,28 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
|
||||
LOG_INFO("playerbots", "Creating random bot accounts...");
|
||||
std::unordered_map<NameRaceAndGender, std::vector<std::string>> nameCache;
|
||||
uint32 totalAccCount = sPlayerbotAIConfig->randomBotAccountCount;
|
||||
std::vector<std::future<void>> account_creations;
|
||||
int account_creation = 0;
|
||||
|
||||
// Calculates the total number of required accounts.
|
||||
uint32 totalAccountCount = CalculateTotalAccountCount();
|
||||
uint32 timer = getMSTime();
|
||||
LOG_INFO("playerbots", "Creating cache for names per gender and race.");
|
||||
QueryResult result = CharacterDatabase.Query("SELECT name, gender FROM playerbots_names");
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No more unused names left");
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
std::string name = fields[0].Get<std::string>();
|
||||
NameRaceAndGender raceAndGender = static_cast<NameRaceAndGender>(fields[1].Get<uint8>());
|
||||
if (sObjectMgr->CheckPlayerName(name) == CHAR_NAME_SUCCESS)
|
||||
nameCache[raceAndGender].push_back(name);
|
||||
|
||||
for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber)
|
||||
} while (result->NextRow());
|
||||
|
||||
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
|
||||
@@ -608,26 +464,22 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
|
||||
LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str());
|
||||
}
|
||||
|
||||
if (account_creation)
|
||||
{
|
||||
LOG_INFO("playerbots", "Waiting for {} accounts loading into database ({} queries)...", account_creation, LoginDatabase.QueueSize());
|
||||
/* wait for async accounts create to make character create correctly */
|
||||
|
||||
while (LoginDatabase.QueueSize())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
LOG_INFO("playerbots", ">> {} Accounts loaded into database in {} ms", account_creation, GetMSTimeDiffToNow(timer));
|
||||
/* wait for async accounts create to make character create correctly, same as account delete */
|
||||
LOG_INFO("playerbots", "Waiting for {} accounts loading into database...", account_creation);
|
||||
std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount);
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Creating random bot characters...");
|
||||
uint32 totalRandomBotChars = 0;
|
||||
uint32 totalCharCount = sPlayerbotAIConfig->randomBotAccountCount * 10;
|
||||
std::vector<std::pair<Player*, uint32>> playerBots;
|
||||
std::vector<WorldSession*> sessionBots;
|
||||
int bot_creation = 0;
|
||||
timer = getMSTime();
|
||||
bool nameCached = false;
|
||||
for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber)
|
||||
|
||||
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
|
||||
@@ -649,41 +501,12 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!nameCached)
|
||||
{
|
||||
nameCached = true;
|
||||
LOG_INFO("playerbots", "Creating cache for names per gender and race...");
|
||||
QueryResult result = CharacterDatabase.Query("SELECT name, gender FROM playerbots_names");
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No more unused names left");
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
std::string name = fields[0].Get<std::string>();
|
||||
NameRaceAndGender raceAndGender = static_cast<NameRaceAndGender>(fields[1].Get<uint8>());
|
||||
if (sObjectMgr->CheckPlayerName(name) == CHAR_NAME_SUCCESS)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
|
||||
stmt->SetData(0, name);
|
||||
|
||||
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
||||
continue;
|
||||
|
||||
nameCache[raceAndGender].push_back(name);
|
||||
}
|
||||
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, totalAccountCount);
|
||||
LOG_INFO("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1,
|
||||
sPlayerbotAIConfig->randomBotAccountCount);
|
||||
RandomPlayerbotFactory factory(accountId);
|
||||
|
||||
WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
|
||||
time_t(0), LOCALE_enUS, 0, false, false, 0, true);
|
||||
time_t(0), LOCALE_enUS, 0, false, false, 0, true);
|
||||
sessionBots.push_back(session);
|
||||
|
||||
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES - count; ++cls)
|
||||
@@ -720,13 +543,9 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
|
||||
if (bot_creation)
|
||||
{
|
||||
LOG_INFO("playerbots", "Waiting for {} characters loading into database ({} queries)...", bot_creation, CharacterDatabase.QueueSize());
|
||||
LOG_INFO("playerbots", "Waiting for {} characters loading into database...", bot_creation);
|
||||
/* wait for characters load into database, or characters will fail to loggin */
|
||||
while (CharacterDatabase.QueueSize())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
LOG_INFO("playerbots", ">> {} Characters loaded into database in {} ms", bot_creation, GetMSTimeDiffToNow(timer));
|
||||
std::this_thread::sleep_for(5s + bot_creation * 5ms);
|
||||
}
|
||||
|
||||
for (WorldSession* session : sessionBots)
|
||||
@@ -738,7 +557,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", ">> {} random bot accounts with {} characters available",
|
||||
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
|
||||
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
|
||||
}
|
||||
|
||||
void RandomPlayerbotFactory::CreateRandomGuilds()
|
||||
@@ -808,7 +627,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds()
|
||||
if (!player)
|
||||
{
|
||||
LOG_ERROR("playerbots", "ObjectAccessor Cannot find player to set leader for guild {} . Skipped...",
|
||||
guildName.c_str());
|
||||
guildName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -819,7 +638,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds()
|
||||
if (!guild->Create(player, guildName))
|
||||
{
|
||||
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName.c_str(),
|
||||
player->GetName().c_str());
|
||||
player->GetName().c_str());
|
||||
delete guild;
|
||||
continue;
|
||||
}
|
||||
@@ -981,7 +800,7 @@ void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count
|
||||
sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId());
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots", "{} random bot {}vs{} arena teams available", arenaTeamNumber, type, type);
|
||||
LOG_INFO("playerbots", "{} random bot arena teams available", arenaTeamNumber);
|
||||
}
|
||||
|
||||
std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName()
|
||||
|
||||
@@ -54,8 +54,6 @@ public:
|
||||
static void CreateRandomGuilds();
|
||||
static void CreateRandomArenaTeams(ArenaType slot, uint32 count);
|
||||
static std::string const CreateRandomGuildName();
|
||||
static uint32 CalculateTotalAccountCount();
|
||||
static uint32 CalculateAvailableCharsPerAccount();
|
||||
|
||||
private:
|
||||
std::string const CreateRandomBotName(NameRaceAndGender raceAndGender);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@
|
||||
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
||||
#define _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
||||
|
||||
#include "NewRpgInfo.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
|
||||
@@ -112,8 +111,6 @@ public:
|
||||
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
|
||||
bool IsRandomBot(Player* bot);
|
||||
bool IsRandomBot(ObjectGuid::LowType bot);
|
||||
bool IsAddclassBot(Player* bot);
|
||||
bool IsAddclassBot(ObjectGuid::LowType bot);
|
||||
void Randomize(Player* bot);
|
||||
void Clear(Player* bot);
|
||||
void RandomizeFirst(Player* bot);
|
||||
@@ -173,19 +170,10 @@ public:
|
||||
static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; }
|
||||
|
||||
void PrepareAddclassCache();
|
||||
void PrepareZone2LevelBracket();
|
||||
void PrepareTeleportCache();
|
||||
void Init();
|
||||
std::map<uint8, std::unordered_set<ObjectGuid>> addclassCache;
|
||||
std::map<uint8, std::vector<ObjectGuid>> addclassCache;
|
||||
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
|
||||
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
|
||||
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
|
||||
struct LevelBracket {
|
||||
uint32 low;
|
||||
uint32 high;
|
||||
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
|
||||
};
|
||||
std::map<uint32, LevelBracket> zone2LevelBracket;
|
||||
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
||||
protected:
|
||||
void OnBotLoginInternal(Player* const bot) override;
|
||||
@@ -195,8 +183,6 @@ private:
|
||||
botPID pid = botPID(1, 50, -50, 0, 0, 0);
|
||||
float activityMod = 0.25;
|
||||
bool _isBotInitializing = true;
|
||||
bool _isBotLogging = true;
|
||||
NewRpgStatistic rpgStasticTotal;
|
||||
uint32 GetEventValue(uint32 bot, std::string const event);
|
||||
std::string const GetEventData(uint32 bot, std::string const event);
|
||||
uint32 SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn,
|
||||
@@ -213,6 +199,7 @@ private:
|
||||
void RandomTeleport(Player* bot);
|
||||
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
|
||||
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
|
||||
void PrepareTeleportCache();
|
||||
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
|
||||
|
||||
std::vector<Player*> players;
|
||||
|
||||
@@ -260,7 +260,7 @@ void TalentSpec::ReadTalents(Player* bot)
|
||||
// Set the talent ranks to the ranks of the link.
|
||||
void TalentSpec::ReadTalents(std::string const link)
|
||||
{
|
||||
//uint32 rank = 0; //not used, line marked for removal.
|
||||
uint32 rank = 0;
|
||||
uint32 pos = 0;
|
||||
uint32 tab = 0;
|
||||
std::string chr;
|
||||
@@ -397,7 +397,7 @@ uint32 TalentSpec::highestTree()
|
||||
|
||||
std::string const TalentSpec::FormatSpec(Player* bot)
|
||||
{
|
||||
// uint8 cls = bot->getClass(); //not used, (used in lined 403), line marked for removal.
|
||||
uint8 cls = bot->getClass();
|
||||
|
||||
std::ostringstream out;
|
||||
// out << chathelper:: specs[cls][highestTree()] << " (";
|
||||
@@ -468,7 +468,7 @@ bool TalentSpec::isEarlierVersionOf(TalentSpec& newSpec)
|
||||
// Modifies current talents towards new talents up to a maxium of points.
|
||||
void TalentSpec::ShiftTalents(TalentSpec* currentSpec, uint32 level)
|
||||
{
|
||||
//uint32 currentPoints = currentSpec->GetTalentPoints(); //not used, line marked for removal.
|
||||
uint32 currentPoints = currentSpec->GetTalentPoints();
|
||||
if (points >= LeveltoPoints(level)) // We have no more points to spend. Better reset and crop
|
||||
{
|
||||
CropTalents(level);
|
||||
@@ -22,26 +22,12 @@
|
||||
|
||||
WorldPosition::WorldPosition(std::string const str)
|
||||
{
|
||||
std::vector<std::string> tokens = split(str, '|');
|
||||
if (tokens.size() == 5)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_mapId = std::stoi(tokens[0]);
|
||||
m_positionX = std::stof(tokens[1]);
|
||||
m_positionY = std::stof(tokens[2]);
|
||||
m_positionZ = std::stof(tokens[3]);
|
||||
m_orientation = std::stof(tokens[4]);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
m_mapId = 0;
|
||||
m_positionX = 0.0f;
|
||||
m_positionY = 0.0f;
|
||||
m_positionZ = 0.0f;
|
||||
m_orientation = 0.0f;
|
||||
}
|
||||
}
|
||||
std::stringstream out(str);
|
||||
out >> this->m_mapId;
|
||||
out >> this->m_positionX;
|
||||
out >> this->m_positionY;
|
||||
out >> this->m_positionZ;
|
||||
out >> this->m_orientation;
|
||||
}
|
||||
|
||||
WorldPosition::WorldPosition(uint32 mapId, const Position& pos)
|
||||
@@ -375,25 +361,13 @@ std::string const WorldPosition::print()
|
||||
std::string const WorldPosition::to_string()
|
||||
{
|
||||
std::stringstream out;
|
||||
out << m_mapId << '|';
|
||||
out << m_positionX << '|';
|
||||
out << m_positionY << '|';
|
||||
out << m_positionZ << '|';
|
||||
out << m_orientation;
|
||||
out << GetMapId();
|
||||
out << GetPositionX();
|
||||
out << GetPositionY();
|
||||
out << GetPositionZ();
|
||||
out << GetOrientation();
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::vector<std::string> WorldPosition::split(const std::string& s, char delimiter)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, token, delimiter))
|
||||
{
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
};
|
||||
|
||||
void WorldPosition::printWKT(std::vector<WorldPosition> points, std::ostringstream& out, uint32 dim, bool loop)
|
||||
{
|
||||
@@ -644,7 +618,7 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y)
|
||||
{
|
||||
// load VMAPs for current map/grid...
|
||||
const MapEntry* i_mapEntry = sMapStore.LookupEntry(mapId);
|
||||
//const char* mapName = i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; //not used, (usage are commented out below), line marked for removal.
|
||||
const char* mapName = i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
|
||||
|
||||
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap(
|
||||
(sWorld->GetDataPath() + "vmaps").c_str(), mapId, x, y);
|
||||
@@ -1103,7 +1077,7 @@ bool QuestRelationTravelDestination::isActive(Player* bot)
|
||||
if (!bot->GetMap()->GetEntry()->IsWorldMap() || !bot->CanTakeQuest(questTemplate, false))
|
||||
return false;
|
||||
|
||||
//uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate); //not used, shadowed by the next declaration, line marked for removal.
|
||||
uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate);
|
||||
|
||||
if (AI_VALUE(bool, "can fight equal"))
|
||||
{
|
||||
@@ -131,7 +131,6 @@ public:
|
||||
std::string const print();
|
||||
|
||||
std::string const to_string();
|
||||
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||
|
||||
void printWKT(std::vector<WorldPosition> points, std::ostringstream& out, uint32 dim = 0, bool loop = false);
|
||||
void printWKT(std::ostringstream& out) { printWKT({*this}, out); }
|
||||
@@ -594,7 +594,7 @@ bool TravelNode::isEqual(TravelNode* compareNode)
|
||||
|
||||
void TravelNode::print([[maybe_unused]] bool printFailed)
|
||||
{
|
||||
// WorldPosition* startPosition = getPosition(); //not used, line marked for removal.
|
||||
WorldPosition* startPosition = getPosition();
|
||||
|
||||
uint32 mapSize = getNodeMap(true).size();
|
||||
|
||||
@@ -1167,9 +1167,9 @@ std::vector<TravelNode*> TravelNodeMap::getNodes(WorldPosition pos, float range)
|
||||
TravelNode* TravelNodeMap::getNode(WorldPosition pos, [[maybe_unused]] std::vector<WorldPosition>& ppath, Unit* bot,
|
||||
float range)
|
||||
{
|
||||
//float x = pos.getX(); //not used, line marked for removal.
|
||||
//float y = pos.getY(); //not used, line marked for removal.
|
||||
//float z = pos.getZ(); //not used, line marked for removal.
|
||||
float x = pos.getX();
|
||||
float y = pos.getY();
|
||||
float z = pos.getZ();
|
||||
|
||||
if (bot && !bot->GetMap())
|
||||
return nullptr;
|
||||
@@ -1348,9 +1348,6 @@ TravelNodeRoute TravelNodeMap::getRoute(WorldPosition startPos, WorldPosition en
|
||||
std::vector<WorldPosition> newStartPath;
|
||||
std::vector<TravelNode*> startNodes = m_nodes, endNodes = m_nodes;
|
||||
|
||||
if(!startNodes.size() || !endNodes.size())
|
||||
return TravelNodeRoute();
|
||||
|
||||
// Partial sort to get the closest 5 nodes at the begin of the array.
|
||||
std::partial_sort(startNodes.begin(), startNodes.begin() + 5, startNodes.end(),
|
||||
[startPos](TravelNode* i, TravelNode* j) { return i->fDist(startPos) < j->fDist(startPos); });
|
||||
@@ -1498,7 +1495,7 @@ TravelNode* TravelNodeMap::addZoneLinkNode(TravelNode* startNode)
|
||||
{
|
||||
for (auto& path : *startNode->getPaths())
|
||||
{
|
||||
//TravelNode* endNode = path.first; //not used, line marked for removal.
|
||||
TravelNode* endNode = path.first;
|
||||
|
||||
std::string zoneName = startNode->getPosition()->getAreaName(true, true);
|
||||
for (auto& pos : path.second.getPath())
|
||||
@@ -1641,7 +1638,7 @@ void TravelNodeMap::generateNpcNodes()
|
||||
else if (cInfo->npcflag & UNIT_NPC_FLAG_SPIRITGUIDE)
|
||||
nodeName += " spiritguide";
|
||||
|
||||
/*TravelNode* node = */ sTravelNodeMap->addNode(guidP, nodeName, true, true); //node not used, fragment marked for removal.
|
||||
TravelNode* node = sTravelNodeMap->addNode(guidP, nodeName, true, true);
|
||||
}
|
||||
else if (cInfo->rank == 3)
|
||||
{
|
||||
@@ -1757,7 +1754,7 @@ void TravelNodeMap::generateAreaTriggerNodes()
|
||||
else
|
||||
nodeName = inPos.getAreaName(false) + " portal";
|
||||
|
||||
//TravelNode* entryNode = sTravelNodeMap->getNode(outPos, nullptr, 20.0f); // Entry side, portal exit. //not used, line marked for removal.
|
||||
TravelNode* entryNode = sTravelNodeMap->getNode(outPos, nullptr, 20.0f); // Entry side, portal exit.
|
||||
|
||||
TravelNode* outNode = sTravelNodeMap->addNode(outPos, nodeName, true, true); // Exit size, portal exit.
|
||||
|
||||
@@ -1979,7 +1976,7 @@ void TravelNodeMap::generateZoneMeanNodes()
|
||||
|
||||
WorldPosition pos = WorldPosition(points, WP_MEAN_CENTROID);
|
||||
|
||||
/*TravelNode* node = */sTravelNodeMap->addNode(pos, pos.getAreaName(), true, true, false); //node not used, but addNode as side effect, fragment marked for removal.
|
||||
TravelNode* node = sTravelNodeMap->addNode(pos, pos.getAreaName(), true, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2120,7 +2117,7 @@ void TravelNodeMap::removeUselessPaths()
|
||||
if (path.second.getComplete() && startNode->hasLinkTo(path.first))
|
||||
ASSERT(true);
|
||||
}
|
||||
uint32 it = 0/*, rem = 0*/; //rem not used in this scope, (shadowing) fragment marked for removal.
|
||||
uint32 it = 0, rem = 0;
|
||||
while (true)
|
||||
{
|
||||
uint32 rem = 0;
|
||||
@@ -2210,7 +2207,7 @@ void TravelNodeMap::printMap()
|
||||
|
||||
std::vector<TravelNode*> anodes = getNodes();
|
||||
|
||||
//uint32 nr = 0; //not used, line marked for removal.
|
||||
uint32 nr = 0;
|
||||
|
||||
for (auto& node : anodes)
|
||||
{
|
||||
@@ -33,21 +33,12 @@ public:
|
||||
static ChatCommandTable playerbotsDebugCommandTable = {
|
||||
{"bg", HandleDebugBGCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
};
|
||||
|
||||
static ChatCommandTable playerbotsAccountCommandTable = {
|
||||
{"setKey", HandleSetSecurityKeyCommand, SEC_PLAYER, Console::No},
|
||||
{"link", HandleLinkAccountCommand, SEC_PLAYER, Console::No},
|
||||
{"linkedAccounts", HandleViewLinkedAccountsCommand, SEC_PLAYER, Console::No},
|
||||
{"unlink", HandleUnlinkAccountCommand, SEC_PLAYER, Console::No},
|
||||
};
|
||||
|
||||
static ChatCommandTable playerbotsCommandTable = {
|
||||
{"bot", HandlePlayerbotCommand, SEC_PLAYER, Console::No},
|
||||
{"gtask", HandleGuildTaskCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"pmon", HandlePerfMonCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"rndbot", HandleRandomPlayerbotCommand, SEC_GAMEMASTER, Console::Yes},
|
||||
{"debug", playerbotsDebugCommandTable},
|
||||
{"account", playerbotsAccountCommandTable},
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable = {
|
||||
@@ -110,103 +101,6 @@ public:
|
||||
{
|
||||
return BGTactics::HandleConsoleCommand(handler, args);
|
||||
}
|
||||
|
||||
static bool HandleSetSecurityKeyCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account setKey <securityKey>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
std::string key = args;
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleSetSecurityKeyCommand(player, key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleLinkAccountCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
return false;
|
||||
|
||||
char* accountName = strtok((char*)args, " ");
|
||||
char* key = strtok(nullptr, " ");
|
||||
|
||||
if (!accountName || !key)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account link <accountName> <securityKey>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleLinkAccountCommand(player, accountName, key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleViewLinkedAccountsCommand(ChatHandler* handler, char const* /*args*/)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleViewLinkedAccountsCommand(player);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleUnlinkAccountCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!args || !*args)
|
||||
return false;
|
||||
|
||||
char* accountName = strtok((char*)args, " ");
|
||||
if (!accountName)
|
||||
{
|
||||
handler->PSendSysMessage("Usage: .playerbots account unlink <accountName>");
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
|
||||
if (mgr)
|
||||
{
|
||||
mgr->HandleUnlinkAccountCommand(player, accountName);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_playerbots_commandscript() { new playerbots_commandscript(); }
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Queue.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Log.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
|
||||
void Queue::Push(ActionBasket* action)
|
||||
{
|
||||
if (!action)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (action->getAction()->getName() == basket->getAction()->getName())
|
||||
{
|
||||
updateExistingBasket(basket, action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
ActionNode* Queue::Pop()
|
||||
{
|
||||
ActionBasket* highestRelevanceBasket = findHighestRelevanceBasket();
|
||||
if (!highestRelevanceBasket)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return extractAndDeleteBasket(highestRelevanceBasket);
|
||||
}
|
||||
|
||||
ActionBasket* Queue::Peek()
|
||||
{
|
||||
return findHighestRelevanceBasket();
|
||||
}
|
||||
|
||||
uint32 Queue::Size()
|
||||
{
|
||||
return actions.size();
|
||||
}
|
||||
|
||||
void Queue::RemoveExpired()
|
||||
{
|
||||
if (!sPlayerbotAIConfig->expireActionTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::list<ActionBasket*> expiredBaskets;
|
||||
collectExpiredBaskets(expiredBaskets);
|
||||
removeAndDeleteBaskets(expiredBaskets);
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
void Queue::updateExistingBasket(ActionBasket* existing, ActionBasket* newBasket)
|
||||
{
|
||||
if (existing->getRelevance() < newBasket->getRelevance())
|
||||
{
|
||||
existing->setRelevance(newBasket->getRelevance());
|
||||
}
|
||||
|
||||
if (ActionNode* actionNode = newBasket->getAction())
|
||||
{
|
||||
delete actionNode;
|
||||
}
|
||||
|
||||
delete newBasket;
|
||||
}
|
||||
|
||||
ActionBasket* Queue::findHighestRelevanceBasket() const
|
||||
{
|
||||
if (actions.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float maxRelevance = -1.0f;
|
||||
ActionBasket* selection = nullptr;
|
||||
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (basket->getRelevance() > maxRelevance)
|
||||
{
|
||||
maxRelevance = basket->getRelevance();
|
||||
selection = basket;
|
||||
}
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
ActionNode* Queue::extractAndDeleteBasket(ActionBasket* basket)
|
||||
{
|
||||
ActionNode* action = basket->getAction();
|
||||
actions.remove(basket);
|
||||
delete basket;
|
||||
return action;
|
||||
}
|
||||
|
||||
void Queue::collectExpiredBaskets(std::list<ActionBasket*>& expiredBaskets)
|
||||
{
|
||||
uint32 expiryTime = sPlayerbotAIConfig->expireActionTime;
|
||||
for (ActionBasket* basket : actions)
|
||||
{
|
||||
if (basket->isExpired(expiryTime))
|
||||
{
|
||||
expiredBaskets.push_back(basket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Queue::removeAndDeleteBaskets(std::list<ActionBasket*>& basketsToRemove)
|
||||
{
|
||||
for (ActionBasket* basket : basketsToRemove)
|
||||
{
|
||||
actions.remove(basket);
|
||||
|
||||
if (ActionNode* action = basket->getAction())
|
||||
{
|
||||
delete action;
|
||||
}
|
||||
|
||||
delete basket;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef PLAYERBOT_QUEUE_H
|
||||
#define PLAYERBOT_QUEUE_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "Common.h"
|
||||
|
||||
/**
|
||||
* @class Queue
|
||||
* @brief Manages a priority queue of actions for the playerbot system
|
||||
*
|
||||
* This queue maintains a list of ActionBasket objects, each containing an action
|
||||
* and its relevance score. Actions with higher relevance scores are prioritized.
|
||||
*/
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
Queue() = default;
|
||||
~Queue() = default;
|
||||
|
||||
/**
|
||||
* @brief Adds an action to the queue or updates existing action's relevance
|
||||
* @param action Pointer to the ActionBasket to be added
|
||||
*
|
||||
* If an action with the same name exists, updates its relevance if the new
|
||||
* relevance is higher, then deletes the new action. Otherwise, adds the new
|
||||
* action to the queue.
|
||||
*/
|
||||
void Push(ActionBasket* action);
|
||||
|
||||
/**
|
||||
* @brief Removes and returns the action with highest relevance
|
||||
* @return Pointer to the highest relevance ActionNode, or nullptr if queue is empty
|
||||
*
|
||||
* Ownership of the returned ActionNode is transferred to the caller.
|
||||
* The associated ActionBasket is deleted.
|
||||
*/
|
||||
ActionNode* Pop();
|
||||
|
||||
/**
|
||||
* @brief Returns the action with highest relevance without removing it
|
||||
* @return Pointer to the ActionBasket with highest relevance, or nullptr if queue is empty
|
||||
*/
|
||||
ActionBasket* Peek();
|
||||
|
||||
/**
|
||||
* @brief Returns the current size of the queue
|
||||
* @return Number of actions in the queue
|
||||
*/
|
||||
uint32 Size();
|
||||
|
||||
/**
|
||||
* @brief Removes and deletes expired actions from the queue
|
||||
*
|
||||
* Uses sPlayerbotAIConfig->expireActionTime to determine if actions have expired.
|
||||
* Both the ActionNode and ActionBasket are deleted for expired actions.
|
||||
*/
|
||||
void RemoveExpired();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Updates existing basket with new relevance and cleans up new basket
|
||||
*/
|
||||
void updateExistingBasket(ActionBasket* existing, ActionBasket* newBasket);
|
||||
|
||||
/**
|
||||
* @brief Finds the basket with the highest relevance score
|
||||
* @return Pointer to the highest relevance basket, or nullptr if queue is empty
|
||||
*/
|
||||
ActionBasket* findHighestRelevanceBasket() const;
|
||||
|
||||
/**
|
||||
* @brief Extracts action from basket and handles basket cleanup
|
||||
*/
|
||||
ActionNode* extractAndDeleteBasket(ActionBasket* basket);
|
||||
|
||||
/**
|
||||
* @brief Collects all expired baskets into the provided list
|
||||
*/
|
||||
void collectExpiredBaskets(std::list<ActionBasket*>& expiredBaskets);
|
||||
|
||||
/**
|
||||
* @brief Removes and deletes all baskets in the provided list
|
||||
*/
|
||||
void removeAndDeleteBaskets(std::list<ActionBasket*>& basketsToRemove);
|
||||
|
||||
std::list<ActionBasket*> actions; /**< Container for action baskets */
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -122,7 +122,6 @@ public:
|
||||
static void InitTalentsByParsedSpecLink(Player* bot, std::vector<std::vector<uint32>> parsedSpecLink, bool reset);
|
||||
void InitAvailableSpells();
|
||||
void InitClassSpells();
|
||||
void InitSpecialSpells();
|
||||
void InitEquipment(bool incremental, bool second_chance = false);
|
||||
void InitPet();
|
||||
void InitAmmo();
|
||||
@@ -137,11 +136,7 @@ public:
|
||||
void ApplyEnchantAndGemsNew(bool destoryOld = true);
|
||||
void InitInstanceQuests();
|
||||
void UnbindInstance();
|
||||
void InitKeyring();
|
||||
void InitReputation();
|
||||
void InitAttunementQuests();
|
||||
void InitPotions();
|
||||
|
||||
|
||||
private:
|
||||
void Prepare();
|
||||
// void InitSecondEquipmentSet();
|
||||
@@ -154,12 +149,14 @@ private:
|
||||
void InitSpells();
|
||||
void ClearSpells();
|
||||
void ClearSkills();
|
||||
void InitSpecialSpells();
|
||||
void InitTalents(uint32 specNo);
|
||||
void InitTalentsByTemplate(uint32 specNo);
|
||||
void InitQuests(std::list<uint32>& questMap, bool withRewardItem = true);
|
||||
void InitQuests(std::list<uint32>& questMap);
|
||||
void ClearInventory();
|
||||
void ClearAllItems();
|
||||
void ResetQuests();
|
||||
void InitPotions();
|
||||
|
||||
std::vector<uint32> GetCurrentGemsCount();
|
||||
bool CanEquipArmor(ItemTemplate const* proto);
|
||||
@@ -193,7 +190,7 @@ private:
|
||||
uint32 itemQuality;
|
||||
uint32 gearScoreLimit;
|
||||
static std::list<uint32> specialQuestIds;
|
||||
static std::unordered_map<uint32, std::vector<uint32>> trainerIdCache;
|
||||
std::vector<uint32> trainerIdCache;
|
||||
static std::vector<uint32> enchantSpellIdCache;
|
||||
static std::vector<uint32> enchantGemIdCache;
|
||||
|
||||
|
||||
@@ -5,14 +5,11 @@
|
||||
#include "DBCStores.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIAware.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "UpdateFields.h"
|
||||
#include "Util.h"
|
||||
|
||||
StatsCollector::StatsCollector(CollectorType type, int32 cls) : type_(type), cls_(cls) { Reset(); }
|
||||
|
||||
@@ -55,12 +52,12 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
|
||||
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 0);
|
||||
break;
|
||||
case ITEM_SPELLTRIGGER_CHANCE_ON_HIT:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
if (proto->Spells[j].SpellPPMRate > 0.01f)
|
||||
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / proto->Spells[j].SpellPPMRate);
|
||||
else
|
||||
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / 1.8f); // Default PPM = 1.8
|
||||
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / 1.8f); // Default PPM = 1.8
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -70,7 +67,7 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
|
||||
|
||||
if (proto->socketBonus)
|
||||
{
|
||||
if (const SpellItemEnchantmentEntry* enchant = sSpellItemEnchantmentStore.LookupEntry(proto->socketBonus))
|
||||
if (const SpellItemEnchantmentEntry *enchant = sSpellItemEnchantmentStore.LookupEntry(proto->socketBonus))
|
||||
CollectEnchantStats(enchant);
|
||||
}
|
||||
}
|
||||
@@ -97,50 +94,34 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
|
||||
procFlags = eventEntry->procFlags;
|
||||
else
|
||||
procFlags = spellInfo->ProcFlags;
|
||||
|
||||
|
||||
if (eventEntry && eventEntry->customChance)
|
||||
procChance = eventEntry->customChance;
|
||||
else
|
||||
procChance = spellInfo->ProcChance;
|
||||
bool lowChance = procChance <= 5;
|
||||
|
||||
bool lowChance = procChance <= 5;
|
||||
|
||||
if (lowChance || (procFlags && !CanBeTriggeredByType(spellInfo, procFlags)))
|
||||
canNextTrigger = false;
|
||||
|
||||
if (spellInfo->StackAmount)
|
||||
{
|
||||
// Heuristic multiplier for spell with stackAmount since high stackAmount may not be available
|
||||
if (spellInfo->StackAmount <= 1)
|
||||
multiplier *= spellInfo->StackAmount * 1;
|
||||
else if (spellInfo->StackAmount <= 5)
|
||||
multiplier *= 1 + (spellInfo->StackAmount - 1) * 0.75;
|
||||
else if (spellInfo->StackAmount <= 10)
|
||||
multiplier *= 4 + (spellInfo->StackAmount - 5) * 0.6;
|
||||
if (spellInfo->StackAmount <= 10)
|
||||
multiplier *= spellInfo->StackAmount * 0.6;
|
||||
else if (spellInfo->StackAmount <= 20)
|
||||
multiplier *= 7 + (spellInfo->StackAmount - 10) * 0.4;
|
||||
multiplier *= 6 + (spellInfo->StackAmount - 10) * 0.4;
|
||||
else
|
||||
multiplier *= 11;
|
||||
multiplier *= 10;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
||||
{
|
||||
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
|
||||
if (!effectInfo.Effect)
|
||||
continue;
|
||||
switch (effectInfo.Effect)
|
||||
{
|
||||
case SPELL_EFFECT_APPLY_AURA:
|
||||
{
|
||||
if (spellInfo->SpellFamilyName && /*effectInfo.ApplyAuraName != SPELL_AURA_DUMMY &&*/
|
||||
effectInfo.ApplyAuraName != SPELL_AURA_PROC_TRIGGER_SPELL)
|
||||
{
|
||||
if (!CheckSpellValidation(spellInfo->SpellFamilyName, effectInfo.SpellClassMask))
|
||||
return;
|
||||
|
||||
// Some dummy effects cannot be recognized, make some bonus to identify
|
||||
stats[STATS_TYPE_BONUS] += 1;
|
||||
}
|
||||
|
||||
/// @todo Handle negative spell
|
||||
if (!spellInfo->IsPositive())
|
||||
break;
|
||||
@@ -149,8 +130,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
|
||||
if (spellCooldown <= 2000 || spellInfo->GetDuration() == -1)
|
||||
coverage = 1.0f;
|
||||
else
|
||||
coverage =
|
||||
std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown));
|
||||
coverage = std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown));
|
||||
|
||||
multiplier *= coverage;
|
||||
HandleApplyAura(effectInfo, multiplier, canNextTrigger, triggerCooldown);
|
||||
@@ -187,12 +167,12 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
|
||||
break;
|
||||
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f);
|
||||
int32 val = AverageValue(effectInfo);
|
||||
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
|
||||
if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED)
|
||||
{
|
||||
float transfer_multiplier = 1;
|
||||
stats[STATS_TYPE_ATTACK_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier;
|
||||
}
|
||||
else if (type_ & CollectorType::SPELL_DMG)
|
||||
else if (type_ == CollectorType::SPELL_DMG)
|
||||
{
|
||||
float transfer_multiplier = 0.5;
|
||||
stats[STATS_TYPE_SPELL_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier;
|
||||
@@ -220,7 +200,7 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan
|
||||
{
|
||||
case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
|
||||
{
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
CollectSpellStats(enchant_spell_id, 0.25f);
|
||||
break;
|
||||
}
|
||||
@@ -245,60 +225,55 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan
|
||||
}
|
||||
|
||||
/// @todo Special case for some spell that hard to calculate, like trinket, relic, etc.
|
||||
bool StatsCollector::SpecialSpellFilter(uint32 spellId)
|
||||
{
|
||||
bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
||||
// trinket
|
||||
switch (spellId)
|
||||
{
|
||||
case 60764: // Totem of Splintering
|
||||
if (type_ & (CollectorType::SPELL))
|
||||
return true;
|
||||
break;
|
||||
case 27521: // Insightful Earthstorm Diamond
|
||||
case 27521: // Insightful Earthstorm Diamond
|
||||
stats[STATS_TYPE_MANA_REGENERATION] += 20;
|
||||
return true;
|
||||
case 55381: // Insightful Earthsiege Diamond
|
||||
case 55381: // Insightful Earthsiege Diamond
|
||||
stats[STATS_TYPE_MANA_REGENERATION] += 40;
|
||||
return true;
|
||||
case 39442: // Darkmoon Card: Wrath
|
||||
if (!(type_ & CollectorType::SPELL_HEAL))
|
||||
case 39442: // Darkmoon Card: Wrath
|
||||
if (type_ != CollectorType::SPELL_HEAL)
|
||||
stats[STATS_TYPE_CRIT] += 50;
|
||||
return true;
|
||||
case 59620: // Berserk
|
||||
if (type_ & CollectorType::MELEE)
|
||||
case 59620: // Berserk
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 120;
|
||||
return true;
|
||||
case 67702: // Death's Verdict
|
||||
case 67702: // Death's Verdict
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 225;
|
||||
return true;
|
||||
case 67771: // Death's Verdict (heroic)
|
||||
case 67771: // Death's Verdict (heroic)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 260;
|
||||
return true;
|
||||
case 71406: // Tiny Abomination in a Jar
|
||||
case 71406: // Tiny Abomination in a Jar
|
||||
if (cls_ == CLASS_PALADIN)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 600;
|
||||
else
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 150;
|
||||
return true;
|
||||
case 71545: // Tiny Abomination in a Jar (heroic)
|
||||
case 71545: // Tiny Abomination in a Jar (heroic)
|
||||
if (cls_ == CLASS_PALADIN)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 800;
|
||||
else
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 200;
|
||||
return true;
|
||||
case 71519: // Deathbringer's Will
|
||||
return true;
|
||||
case 71519: // Deathbringer's Will
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 350;
|
||||
return true;
|
||||
case 71562: // Deathbringer's Will (heroic)
|
||||
case 71562: // Deathbringer's Will (heroic)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 400;
|
||||
return true;
|
||||
case 71602: // Dislodged Foreign Object
|
||||
case 71602: // Dislodged Foreign Object
|
||||
/// @todo The item can be triggered by heal spell, which mismatch with it's description
|
||||
/// Noticing that heroic item can not be triggered, probably a bug to report to AC
|
||||
if (type_ & CollectorType::SPELL_HEAL)
|
||||
if (type_ == CollectorType::SPELL_HEAL)
|
||||
return true;
|
||||
break;
|
||||
case 71903: // Shadowmourne
|
||||
case 71903: // Shadowmourne
|
||||
stats[STATS_TYPE_STRENGTH] += 200;
|
||||
return true;
|
||||
default:
|
||||
@@ -320,26 +295,26 @@ bool StatsCollector::SpecialEnchantFilter(uint32 enchantSpellId)
|
||||
switch (enchantSpellId)
|
||||
{
|
||||
case 64440:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
stats[STATS_TYPE_PARRY] += 50;
|
||||
}
|
||||
return true;
|
||||
case 53365: // Rune of the Fallen Crusader
|
||||
if (type_ & CollectorType::MELEE)
|
||||
case 53365: // Rune of the Fallen Crusader
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
stats[STATS_TYPE_STRENGTH] += 75;
|
||||
}
|
||||
return true;
|
||||
case 62157: // Rune of the Stoneskin Gargoyle
|
||||
if (type_ & CollectorType::MELEE)
|
||||
case 62157: // Rune of the Stoneskin Gargoyle
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
stats[STATS_TYPE_DEFENSE] += 25;
|
||||
stats[STATS_TYPE_STAMINA] += 50;
|
||||
}
|
||||
return true;
|
||||
case 64571: // Blood draining
|
||||
if (type_ & CollectorType::MELEE)
|
||||
case 64571: // Blood draining
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
stats[STATS_TYPE_STAMINA] += 50;
|
||||
}
|
||||
@@ -350,34 +325,18 @@ bool StatsCollector::SpecialEnchantFilter(uint32 enchantSpellId)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict)
|
||||
bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags)
|
||||
{
|
||||
const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id);
|
||||
uint32 spellFamilyName = 0;
|
||||
if (eventEntry)
|
||||
{
|
||||
spellFamilyName = eventEntry->spellFamilyName;
|
||||
flag96 spellFamilyMask = eventEntry->spellFamilyMask;
|
||||
if (spellFamilyName != 0)
|
||||
{
|
||||
if (!CheckSpellValidation(spellFamilyName, spellFamilyMask, strict))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint32 spellFamilyName = eventEntry ? eventEntry->spellFamilyName : 0;
|
||||
|
||||
uint32 triggerMask = TAKEN_HIT_PROC_FLAG_MASK; // Generic trigger mask
|
||||
switch (type_)
|
||||
{
|
||||
case CollectorType::MELEE_DMG:
|
||||
{
|
||||
triggerMask |= MELEE_PROC_FLAG_MASK;
|
||||
triggerMask |= SPELL_PROC_FLAG_MASK;
|
||||
triggerMask |= PROC_FLAG_DONE_PERIODIC;
|
||||
if (procFlags & triggerMask)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case CollectorType::MELEE_TANK:
|
||||
if (spellFamilyName != 0)
|
||||
/// @todo Check specific trigger spell by spellFamilyMask
|
||||
return true;
|
||||
|
||||
uint32 triggerMask = TAKEN_HIT_PROC_FLAG_MASK; // Generic trigger mask
|
||||
switch (type_) {
|
||||
case CollectorType::MELEE:
|
||||
{
|
||||
triggerMask |= MELEE_PROC_FLAG_MASK;
|
||||
triggerMask |= SPELL_PROC_FLAG_MASK;
|
||||
@@ -390,7 +349,7 @@ bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 pro
|
||||
{
|
||||
triggerMask |= RANGED_PROC_FLAG_MASK;
|
||||
triggerMask |= SPELL_PROC_FLAG_MASK;
|
||||
triggerMask |= PROC_FLAG_DONE_PERIODIC;
|
||||
triggerMask |= PERIODIC_PROC_FLAG_MASK;
|
||||
if (procFlags & triggerMask)
|
||||
return true;
|
||||
break;
|
||||
@@ -398,7 +357,6 @@ bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 pro
|
||||
case CollectorType::SPELL_DMG:
|
||||
{
|
||||
triggerMask |= SPELL_PROC_FLAG_MASK;
|
||||
triggerMask |= PROC_FLAG_DONE_PERIODIC;
|
||||
// Healing spell cannot trigger
|
||||
triggerMask &= ~PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS;
|
||||
triggerMask &= ~PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS;
|
||||
@@ -409,13 +367,10 @@ bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 pro
|
||||
case CollectorType::SPELL_HEAL:
|
||||
{
|
||||
triggerMask |= SPELL_PROC_FLAG_MASK;
|
||||
triggerMask |= PROC_FLAG_DONE_PERIODIC;
|
||||
// Dmg spell should not trigger
|
||||
triggerMask &= ~PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG;
|
||||
triggerMask &= ~PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
|
||||
if (!spellFamilyName)
|
||||
triggerMask &=
|
||||
~PROC_FLAG_DONE_PERIODIC; // spellFamilyName = 0 and PROC_FLAG_DONE_PERIODIC -> it is a dmg spell
|
||||
triggerMask &= ~PROC_FLAG_DONE_PERIODIC; // spellFamilyName = 0 and PROC_FLAG_DONE_PERIODIC -> it is a dmg spell
|
||||
if (procFlags & triggerMask)
|
||||
return true;
|
||||
break;
|
||||
@@ -464,39 +419,39 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
|
||||
stats[STATS_TYPE_BLOCK_RATING] += val;
|
||||
break;
|
||||
case ITEM_MOD_HIT_MELEE_RATING:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_HIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_HIT_RANGED_RATING:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_HIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_HIT_SPELL_RATING:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_HIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_CRIT_MELEE_RATING:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_CRIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_CRIT_RANGED_RATING:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_CRIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_CRIT_SPELL_RATING:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_CRIT] += val;
|
||||
break;
|
||||
case ITEM_MOD_HASTE_MELEE_RATING:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_HASTE] += val;
|
||||
break;
|
||||
case ITEM_MOD_HASTE_RANGED_RATING:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_HASTE] += val;
|
||||
break;
|
||||
case ITEM_MOD_HASTE_SPELL_RATING:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_HASTE] += val;
|
||||
break;
|
||||
case ITEM_MOD_HIT_RATING:
|
||||
@@ -547,14 +502,13 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
|
||||
}
|
||||
}
|
||||
|
||||
void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
|
||||
uint32 triggerCooldown)
|
||||
void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, uint32 triggerCooldown)
|
||||
{
|
||||
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
|
||||
return;
|
||||
|
||||
|
||||
int32 val = AverageValue(effectInfo);
|
||||
|
||||
|
||||
switch (effectInfo.ApplyAuraName)
|
||||
{
|
||||
case SPELL_AURA_MOD_DAMAGE_DONE:
|
||||
@@ -580,11 +534,11 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_MOD_ATTACK_POWER:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += val * multiplier;
|
||||
break;
|
||||
case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += val * multiplier;
|
||||
break;
|
||||
case SPELL_AURA_MOD_SHIELD_BLOCKVALUE:
|
||||
@@ -610,7 +564,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
case STAT_SPIRIT:
|
||||
stats[STATS_TYPE_SPIRIT] += val * multiplier;
|
||||
break;
|
||||
case -1: // Stat all
|
||||
case -1: // Stat all
|
||||
stats[STATS_TYPE_STRENGTH] += val * multiplier;
|
||||
stats[STATS_TYPE_AGILITY] += val * multiplier;
|
||||
stats[STATS_TYPE_STAMINA] += val * multiplier;
|
||||
@@ -625,7 +579,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
case SPELL_AURA_MOD_RESISTANCE:
|
||||
{
|
||||
int32 statType = effectInfo.MiscValue;
|
||||
if (statType & SPELL_SCHOOL_MASK_NORMAL) // physical
|
||||
if (statType & SPELL_SCHOOL_MASK_NORMAL) // physical
|
||||
stats[STATS_TYPE_ARMOR] += val * multiplier;
|
||||
break;
|
||||
}
|
||||
@@ -650,39 +604,39 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
stats[STATS_TYPE_BLOCK_RATING] += val * multiplier;
|
||||
break;
|
||||
case CR_HIT_MELEE:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_HIT] += val * multiplier;
|
||||
break;
|
||||
case CR_HIT_RANGED:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_HIT] += val * multiplier;
|
||||
break;
|
||||
case CR_HIT_SPELL:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_HIT] += val * multiplier;
|
||||
break;
|
||||
case CR_CRIT_MELEE:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_CRIT] += val * multiplier;
|
||||
break;
|
||||
case CR_CRIT_RANGED:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_CRIT] += val * multiplier;
|
||||
break;
|
||||
case CR_CRIT_SPELL:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_CRIT] += val * multiplier;
|
||||
break;
|
||||
case CR_HASTE_MELEE:
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_HASTE] += val * multiplier;
|
||||
break;
|
||||
case CR_HASTE_RANGED:
|
||||
if (type_ & CollectorType::RANGED)
|
||||
if (type_ == CollectorType::RANGED)
|
||||
stats[STATS_TYPE_HASTE] += val * multiplier;
|
||||
break;
|
||||
case CR_HASTE_SPELL:
|
||||
if (type_ & CollectorType::SPELL)
|
||||
if (type_ == CollectorType::SPELL)
|
||||
stats[STATS_TYPE_HASTE] += val * multiplier;
|
||||
break;
|
||||
case CR_EXPERTISE:
|
||||
@@ -721,18 +675,12 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
CollectSpellStats(effectInfo.TriggerSpell, multiplier, triggerCooldown);
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_ADD_TARGET_TRIGGER:
|
||||
{
|
||||
if (canNextTrigger)
|
||||
CollectSpellStats(effectInfo.TriggerSpell, multiplier, triggerCooldown);
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_MOD_CRIT_DAMAGE_BONUS:
|
||||
{
|
||||
if (type_ != CollectorType::SPELL_HEAL)
|
||||
{
|
||||
int32 statType = effectInfo.MiscValue;
|
||||
if (statType & SPELL_SCHOOL_MASK_NORMAL) // physical
|
||||
if (statType & SPELL_SCHOOL_MASK_NORMAL) // physical
|
||||
stats[STATS_TYPE_CRIT] += 30 * val * multiplier;
|
||||
}
|
||||
break;
|
||||
@@ -744,7 +692,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
|
||||
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
{
|
||||
//float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
|
||||
float basePointsPerLevel = effectInfo.RealPointsPerLevel;
|
||||
int32 basePoints = effectInfo.BasePoints;
|
||||
int32 randomPoints = int32(effectInfo.DieSides);
|
||||
|
||||
@@ -761,73 +709,4 @@ int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
break;
|
||||
}
|
||||
return basePoints;
|
||||
}
|
||||
|
||||
bool StatsCollector::CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict)
|
||||
{
|
||||
if (PlayerbotAI::Class2SpellFamilyName(cls_) != spellFamilyName)
|
||||
return false;
|
||||
|
||||
bool isHealingSpell = PlayerbotAI::IsHealingSpell(spellFamilyName, spelFalimyFlags);
|
||||
// strict to healer
|
||||
if (strict && (type_ & CollectorType::SPELL_HEAL))
|
||||
{
|
||||
return isHealingSpell;
|
||||
}
|
||||
|
||||
if (!(type_ & CollectorType::SPELL_HEAL) && isHealingSpell)
|
||||
return false;
|
||||
|
||||
// spells for caster/melee/tank are ambiguous
|
||||
if (cls_ == CLASS_DRUID && spellFamilyName == SPELLFAMILY_DRUID && (type_ & CollectorType::MELEE))
|
||||
{
|
||||
uint32 castingFlagsA = 0x4 | 0x2 | 0x1 | 0x200000; // starfire | moonfire | wrath | insect swarm
|
||||
uint32 castingFlagsB = 0x0;
|
||||
uint32 castingFlagsC = 0x0;
|
||||
flag96 invalidFlags = {castingFlagsA, castingFlagsB, castingFlagsC};
|
||||
if (spelFalimyFlags & invalidFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls_ == CLASS_PALADIN && spellFamilyName == SPELLFAMILY_PALADIN && (type_ & CollectorType::MELEE_TANK))
|
||||
{
|
||||
uint32 retributionFlagsA = 0x0;
|
||||
uint32 retributionFlagsB = 0x8000; // crusader strike
|
||||
uint32 retributionFlagsC = 0x0;
|
||||
flag96 invalidFlags = {retributionFlagsA, retributionFlagsB, retributionFlagsC};
|
||||
if (spelFalimyFlags & invalidFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls_ == CLASS_PALADIN && spellFamilyName == SPELLFAMILY_PALADIN && (type_ & CollectorType::MELEE_DMG))
|
||||
{
|
||||
uint32 retributionFlagsA = 0x0;
|
||||
uint32 retributionFlagsB = 0x100000 | 0x40; // shield of righteouness | holy shield
|
||||
uint32 retributionFlagsC = 0x0;
|
||||
flag96 invalidFlags = {retributionFlagsA, retributionFlagsB, retributionFlagsC};
|
||||
if (spelFalimyFlags & invalidFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls_ == CLASS_SHAMAN && spellFamilyName == SPELLFAMILY_SHAMAN && (type_ & CollectorType::SPELL_DMG))
|
||||
{
|
||||
uint32 meleeFlagsA = 0x0;
|
||||
uint32 meleeFlagsB = 0x1000010; // stromstrike
|
||||
uint32 meleeFlagsC = 0x4; // lava lash
|
||||
flag96 invalidFlags = {meleeFlagsA, meleeFlagsB, meleeFlagsC};
|
||||
if (spelFalimyFlags & invalidFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls_ == CLASS_SHAMAN && spellFamilyName == SPELLFAMILY_SHAMAN && (type_ & CollectorType::MELEE_DMG))
|
||||
{
|
||||
uint32 casterFlagsA = 0x0;
|
||||
uint32 casterFlagsB = 0x1000; // lava burst
|
||||
uint32 casterFlagsC = 0x0;
|
||||
flag96 invalidFlags = {casterFlagsA, casterFlagsB, casterFlagsC};
|
||||
if (spelFalimyFlags & invalidFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -42,19 +42,15 @@ enum StatsType : uint8
|
||||
// Stats for weapon dps
|
||||
STATS_TYPE_MELEE_DPS,
|
||||
STATS_TYPE_RANGED_DPS,
|
||||
// Bonus for unrecognized stats
|
||||
STATS_TYPE_BONUS,
|
||||
STATS_TYPE_MAX = 26
|
||||
STATS_TYPE_MAX = 25
|
||||
};
|
||||
|
||||
enum CollectorType : uint8
|
||||
{
|
||||
MELEE_DMG = 1,
|
||||
MELEE_TANK = 2,
|
||||
RANGED = 4,
|
||||
SPELL_DMG = 8,
|
||||
SPELL_HEAL = 16,
|
||||
MELEE = MELEE_DMG | MELEE_TANK,
|
||||
MELEE = 1,
|
||||
RANGED = 2,
|
||||
SPELL_DMG = 4,
|
||||
SPELL_HEAL = 8,
|
||||
SPELL = SPELL_DMG | SPELL_HEAL
|
||||
};
|
||||
|
||||
@@ -67,21 +63,19 @@ public:
|
||||
void CollectItemStats(ItemTemplate const* proto);
|
||||
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1);
|
||||
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant);
|
||||
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
|
||||
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
|
||||
|
||||
public:
|
||||
int32 stats[STATS_TYPE_MAX];
|
||||
|
||||
private:
|
||||
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags);
|
||||
void CollectByItemStatType(uint32 itemStatType, int32 val);
|
||||
bool SpecialSpellFilter(uint32 spellId);
|
||||
bool SpecialEnchantFilter(uint32 enchantSpellId);
|
||||
|
||||
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
|
||||
uint32 triggerCooldown);
|
||||
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, uint32 triggerCooldown);
|
||||
int32 AverageValue(const SpellEffectInfo& effectInfo);
|
||||
|
||||
|
||||
private:
|
||||
CollectorType type_;
|
||||
uint32 cls_;
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "StatsCollector.h"
|
||||
#include "Unit.h"
|
||||
|
||||
@@ -25,16 +23,15 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
|
||||
type_ = CollectorType::SPELL_HEAL;
|
||||
else if (PlayerbotAI::IsCaster(player))
|
||||
type_ = CollectorType::SPELL_DMG;
|
||||
else if (PlayerbotAI::IsTank(player))
|
||||
type_ = CollectorType::MELEE_TANK;
|
||||
else if (PlayerbotAI::IsMelee(player))
|
||||
type_ = CollectorType::MELEE_DMG;
|
||||
type_ = CollectorType::MELEE;
|
||||
else
|
||||
type_ = CollectorType::RANGED;
|
||||
cls = player->getClass();
|
||||
tab = AiFactory::GetPlayerSpecTab(player);
|
||||
collector_ = std::make_unique<StatsCollector>(type_, cls);
|
||||
|
||||
|
||||
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)
|
||||
@@ -82,7 +79,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
||||
CalculateItemTypePenalty(proto);
|
||||
|
||||
if (enable_item_set_bonus_)
|
||||
CalculateItemSetMod(player_, proto);
|
||||
CalculateItemSetBonus(player_, proto);
|
||||
|
||||
CalculateSocketBonus(player_, proto);
|
||||
|
||||
@@ -126,10 +123,8 @@ void StatsWeightCalculator::GenerateWeights(Player* player)
|
||||
void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
{
|
||||
// Basic weights
|
||||
stats_weights_[STATS_TYPE_STAMINA] += 0.1f;
|
||||
stats_weights_[STATS_TYPE_STAMINA] += 0.01f;
|
||||
stats_weights_[STATS_TYPE_ARMOR] += 0.001f;
|
||||
stats_weights_[STATS_TYPE_BONUS] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f;
|
||||
|
||||
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
|
||||
{
|
||||
@@ -264,8 +259,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f;
|
||||
}
|
||||
else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
|
||||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
|
||||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
|
||||
@@ -294,24 +289,16 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||
}
|
||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
|
||||
stats_weights_[STATS_TYPE_HEAL_POWER] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_MANA_REGENERATION] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
|
||||
}
|
||||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||
(cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION) || // heal
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.8f;
|
||||
stats_weights_[STATS_TYPE_HEAL_POWER] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_MANA_REGENERATION] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
|
||||
stats_weights_[STATS_TYPE_MANA_REGENERATION] += 1.2f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.7f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
||||
}
|
||||
else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
@@ -324,7 +311,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_DEFENSE] += 2.5f;
|
||||
stats_weights_[STATS_TYPE_PARRY] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_DODGE] += 2.0f;
|
||||
// stats_weights_[STATS_TYPE_RESILIENCE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_RESILIENCE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_BLOCK_RATING] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_BLOCK_VALUE] += 0.5f;
|
||||
stats_weights_[STATS_TYPE_ARMOR] += 0.15f;
|
||||
@@ -343,7 +330,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_DEFENSE] += 3.5f;
|
||||
stats_weights_[STATS_TYPE_PARRY] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_DODGE] += 2.0f;
|
||||
// stats_weights_[STATS_TYPE_RESILIENCE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_RESILIENCE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_ARMOR] += 0.15f;
|
||||
stats_weights_[STATS_TYPE_HIT] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.5f;
|
||||
@@ -360,7 +347,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_DEFENSE] += 0.3f;
|
||||
stats_weights_[STATS_TYPE_DODGE] += 0.7f;
|
||||
// stats_weights_[STATS_TYPE_RESILIENCE] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_RESILIENCE] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_ARMOR] += 0.15f;
|
||||
stats_weights_[STATS_TYPE_HIT] += 3.0f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 1.3f;
|
||||
@@ -393,7 +380,7 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player)
|
||||
}
|
||||
}
|
||||
|
||||
void StatsWeightCalculator::CalculateItemSetMod(Player* player, ItemTemplate const* proto)
|
||||
void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate const* proto)
|
||||
{
|
||||
uint32 itemSet = proto->ItemSet;
|
||||
if (!itemSet)
|
||||
@@ -479,7 +466,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
|
||||
if (isDoubleHand &&
|
||||
((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab != DEATHKNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
|
||||
@@ -499,20 +486,21 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
// fury with titan's grip
|
||||
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
|
||||
proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) &&
|
||||
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) &&
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip()))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
}
|
||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
|
||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||
if (cls == CLASS_ROGUE && tab == ROGUE_TAB_ASSASSINATION && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||
{
|
||||
weight_ *= 0.5;
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
|
||||
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
|
||||
@@ -524,10 +512,6 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
{
|
||||
weight_ *= 1.1;
|
||||
}
|
||||
if (cls == CLASS_DEATH_KNIGHT && player_->HasAura(50138) && !isDoubleHand)
|
||||
{
|
||||
weight_ *= 1.3;
|
||||
}
|
||||
bool slowDelay = proto->Delay > 2500;
|
||||
if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay)
|
||||
weight_ *= 1.1;
|
||||
@@ -556,24 +540,19 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
{
|
||||
float hit_current, hit_overflow;
|
||||
float validPoints;
|
||||
if (hitOverflowType_ & CollectorType::SPELL)
|
||||
// m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||
// m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE);
|
||||
if (hitOverflowType_ == CollectorType::SPELL)
|
||||
{
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
|
||||
hit_current += player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
|
||||
|
||||
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
|
||||
hit_current += 3;
|
||||
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
|
||||
hit_current += 3;
|
||||
|
||||
hit_overflow = SPELL_HIT_OVERFLOW;
|
||||
if (hit_overflow > hit_current)
|
||||
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_SPELL);
|
||||
else
|
||||
validPoints = 0;
|
||||
}
|
||||
else if (hitOverflowType_ & CollectorType::MELEE)
|
||||
else if (hitOverflowType_ == CollectorType::MELEE)
|
||||
{
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_MELEE);
|
||||
@@ -597,7 +576,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
}
|
||||
|
||||
{
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
float expertise_current, expertise_overflow;
|
||||
expertise_current = player->GetUInt32Value(PLAYER_EXPERTISE);
|
||||
@@ -610,13 +589,12 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_EXPERTISE] =
|
||||
std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
|
||||
collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (type_ & CollectorType::MELEE)
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
float defense_current, defense_overflow;
|
||||
defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL);
|
||||
@@ -633,7 +611,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
}
|
||||
|
||||
{
|
||||
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
|
||||
if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED)
|
||||
{
|
||||
float armor_penetration_current, armor_penetration_overflow;
|
||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||
@@ -641,13 +619,11 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
|
||||
float validPoints;
|
||||
if (armor_penetration_overflow > armor_penetration_current)
|
||||
validPoints = (armor_penetration_overflow - armor_penetration_current) /
|
||||
player->GetRatingMultiplier(CR_ARMOR_PENETRATION);
|
||||
validPoints = (armor_penetration_overflow - armor_penetration_current) / player->GetRatingMultiplier(CR_ARMOR_PENETRATION);
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] =
|
||||
std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
|
||||
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,9 +631,9 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
|
||||
{
|
||||
{
|
||||
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
|
||||
if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED)
|
||||
{
|
||||
float armor_penetration_current/*, armor_penetration_overflow*/; //not used, line marked for removal.
|
||||
float armor_penetration_current, armor_penetration_overflow;
|
||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||
if (armor_penetration_current > 50)
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
void GenerateBasicWeights(Player* player);
|
||||
void GenerateAdditionalWeights(Player* player);
|
||||
|
||||
void CalculateItemSetMod(Player* player, ItemTemplate const* proto);
|
||||
void CalculateItemSetBonus(Player* player, ItemTemplate const* proto);
|
||||
void CalculateSocketBonus(Player* player, ItemTemplate const* proto);
|
||||
|
||||
void CalculateItemTypePenalty(ItemTemplate const* proto);
|
||||
|
||||
@@ -488,7 +488,7 @@ protected:
|
||||
|
||||
// node_name , action, prerequisite
|
||||
#define ACTION_NODE_P(name, spell, pre) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
static ActionNode* name(PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ NextAction::array(0, new NextAction(pre), nullptr), /*A*/ nullptr, \
|
||||
/*C*/ nullptr); \
|
||||
@@ -496,7 +496,7 @@ protected:
|
||||
|
||||
// node_name , action, alternative
|
||||
#define ACTION_NODE_A(name, spell, alt) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
static ActionNode* name(PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ nullptr, /*A*/ NextAction::array(0, new NextAction(alt), nullptr), \
|
||||
/*C*/ nullptr); \
|
||||
@@ -504,7 +504,7 @@ protected:
|
||||
|
||||
// node_name , action, continuer
|
||||
#define ACTION_NODE_C(name, spell, con) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
static ActionNode* name(PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ nullptr, /*A*/ nullptr, \
|
||||
/*C*/ NextAction::array(0, new NextAction(con), nullptr)); \
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "ChatActionContext.h"
|
||||
#include "ChatTriggerContext.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidIccActionContext.h"
|
||||
#include "RaidIccTriggerContext.h"
|
||||
#include "RaidUlduarTriggerContext.h"
|
||||
#include "RaidUlduarActionContext.h"
|
||||
#include "SharedValueContext.h"
|
||||
@@ -17,28 +19,22 @@
|
||||
#include "ValueContext.h"
|
||||
#include "WorldPacketActionContext.h"
|
||||
#include "WorldPacketTriggerContext.h"
|
||||
#include "RaidStrategyContext.h"
|
||||
#include "RaidBwlActionContext.h"
|
||||
#include "RaidBwlTriggerContext.h"
|
||||
#include "RaidNaxxActionContext.h"
|
||||
#include "RaidNaxxTriggerContext.h"
|
||||
#include "RaidIccActionContext.h"
|
||||
#include "RaidIccTriggerContext.h"
|
||||
#include "RaidOsActionContext.h"
|
||||
#include "RaidOsTriggerContext.h"
|
||||
#include "RaidEoEActionContext.h"
|
||||
#include "RaidVoATriggerContext.h"
|
||||
#include "RaidOnyxiaActionContext.h"
|
||||
#include "RaidOnyxiaTriggerContext.h"
|
||||
#include "RaidVoAActionContext.h"
|
||||
#include "RaidEoETriggerContext.h"
|
||||
#include "RaidMcActionContext.h"
|
||||
#include "RaidMcTriggerContext.h"
|
||||
#include "RaidAq20ActionContext.h"
|
||||
#include "RaidAq20TriggerContext.h"
|
||||
#include "DungeonStrategyContext.h"
|
||||
#include "WotlkDungeonActionContext.h"
|
||||
#include "WotlkDungeonTriggerContext.h"
|
||||
#include "raids/RaidStrategyContext.h"
|
||||
#include "raids/blackwinglair/RaidBwlActionContext.h"
|
||||
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsActionContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoEActionContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
|
||||
#include "raids/moltencore/RaidMcActionContext.h"
|
||||
#include "raids/moltencore/RaidMcTriggerContext.h"
|
||||
#include "raids/aq20/RaidAq20ActionContext.h"
|
||||
#include "raids/aq20/RaidAq20TriggerContext.h"
|
||||
#include "dungeons/DungeonStrategyContext.h"
|
||||
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
|
||||
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
|
||||
|
||||
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
{
|
||||
@@ -54,12 +50,10 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WorldPacketActionContext());
|
||||
actionContexts.Add(new RaidMcActionContext());
|
||||
actionContexts.Add(new RaidBwlActionContext());
|
||||
actionContexts.Add(new RaidOnyxiaActionContext());
|
||||
actionContexts.Add(new RaidAq20ActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidIccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||
@@ -75,19 +69,16 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonFoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonToCActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
triggerContexts.Add(new WorldPacketTriggerContext());
|
||||
triggerContexts.Add(new RaidMcTriggerContext());
|
||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||
triggerContexts.Add(new RaidOnyxiaTriggerContext());
|
||||
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidIccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||
@@ -103,7 +94,6 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFosTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
|
||||
@@ -87,23 +87,27 @@ Engine::~Engine(void)
|
||||
void Engine::Reset()
|
||||
{
|
||||
strategyTypeMask = 0;
|
||||
|
||||
ActionNode* action = nullptr;
|
||||
|
||||
while ((action = queue.Pop()) != nullptr)
|
||||
do
|
||||
{
|
||||
action = queue.Pop();
|
||||
if (!action)
|
||||
break;
|
||||
|
||||
delete action;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
for (TriggerNode* trigger : triggers)
|
||||
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
|
||||
{
|
||||
TriggerNode* trigger = *i;
|
||||
delete trigger;
|
||||
}
|
||||
|
||||
triggers.clear();
|
||||
|
||||
for (Multiplier* multiplier : multipliers)
|
||||
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i != multipliers.end(); i++)
|
||||
{
|
||||
Multiplier* multiplier = *i;
|
||||
delete multiplier;
|
||||
}
|
||||
|
||||
@@ -142,100 +146,160 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
|
||||
|
||||
bool actionExecuted = false;
|
||||
ActionBasket* basket = nullptr;
|
||||
time_t currentTime = time(nullptr);
|
||||
|
||||
// Update triggers and push default actions
|
||||
time_t currentTime = time(nullptr);
|
||||
// aiObjectContext->Update();
|
||||
ProcessTriggers(minimal);
|
||||
PushDefaultActions();
|
||||
|
||||
uint32 iterations = 0;
|
||||
uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick);
|
||||
|
||||
while (++iterations <= iterationsPerTick)
|
||||
do
|
||||
{
|
||||
basket = queue.Peek();
|
||||
if (!basket)
|
||||
break;
|
||||
|
||||
float relevance = basket->getRelevance(); // for reference
|
||||
bool skipPrerequisites = basket->isSkipPrerequisites();
|
||||
|
||||
if (minimal && (relevance < 100))
|
||||
continue;
|
||||
|
||||
Event event = basket->getEvent();
|
||||
ActionNode* actionNode = queue.Pop(); // NOTE: Pop() deletes basket
|
||||
Action* action = InitializeAction(actionNode);
|
||||
|
||||
if (!action)
|
||||
if (basket)
|
||||
{
|
||||
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
|
||||
}
|
||||
else if (action->isUseful())
|
||||
{
|
||||
// Apply multipliers early to avoid unnecessary iterations
|
||||
for (Multiplier* multiplier : multipliers)
|
||||
{
|
||||
relevance *= multiplier->GetValue(action);
|
||||
float relevance = basket->getRelevance(); // just for reference
|
||||
bool skipPrerequisites = basket->isSkipPrerequisites();
|
||||
|
||||
if (minimal && (relevance < 100))
|
||||
continue;
|
||||
|
||||
Event event = basket->getEvent();
|
||||
// NOTE: queue.Pop() deletes basket
|
||||
ActionNode* actionNode = queue.Pop();
|
||||
Action* action = InitializeAction(actionNode);
|
||||
|
||||
if (action)
|
||||
action->setRelevance(relevance);
|
||||
|
||||
if (relevance <= 0)
|
||||
{
|
||||
LogAction("Multiplier %s made action %s useless", multiplier->getName().c_str(), action->getName().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (action->isPossible() && relevance > 0)
|
||||
if (!action)
|
||||
{
|
||||
if (!skipPrerequisites)
|
||||
// LOG_ERROR("playerbots", "Action: {} - is UNKNOWN - c:{} l:{}", actionNode->getName().c_str(),
|
||||
// botAI->GetBot()->getClass(), botAI->GetBot()->GetLevel());
|
||||
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
|
||||
}
|
||||
else if (action->isUseful())
|
||||
{
|
||||
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i != multipliers.end(); i++)
|
||||
{
|
||||
LogAction("A:%s - PREREQ", action->getName().c_str());
|
||||
Multiplier* multiplier = *i;
|
||||
relevance *= multiplier->GetValue(action);
|
||||
action->setRelevance(relevance);
|
||||
|
||||
if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.002f, false, event, "prereq"))
|
||||
if (!relevance)
|
||||
{
|
||||
PushAgain(actionNode, relevance + 0.001f, event);
|
||||
continue;
|
||||
LogAction("Multiplier %s made action %s useless", multiplier->getName().c_str(),
|
||||
action->getName().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack);
|
||||
actionExecuted = ListenAndExecute(action, event);
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
|
||||
if (actionExecuted)
|
||||
if (action->isPossible() && relevance)
|
||||
{
|
||||
LogAction("A:%s - OK", action->getName().c_str());
|
||||
MultiplyAndPush(actionNode->getContinuers(), relevance, false, event, "cont");
|
||||
lastRelevance = relevance;
|
||||
delete actionNode; // Safe memory management
|
||||
break;
|
||||
if (!skipPrerequisites)
|
||||
{
|
||||
LogAction("A:%s - PREREQ", action->getName().c_str());
|
||||
|
||||
if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.002f, false, event, "prereq"))
|
||||
{
|
||||
PushAgain(actionNode, relevance + 0.001f, event);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_ACTION, action->getName(),
|
||||
&aiObjectContext->performanceStack);
|
||||
actionExecuted = ListenAndExecute(action, event);
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
|
||||
if (actionExecuted)
|
||||
{
|
||||
LogAction("A:%s - OK", action->getName().c_str());
|
||||
MultiplyAndPush(actionNode->getContinuers(), relevance, false, event, "cont");
|
||||
lastRelevance = relevance;
|
||||
delete actionNode;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAction("A:%s - FAILED", action->getName().c_str());
|
||||
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAction("A:%s - FAILED", action->getName().c_str());
|
||||
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT))
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "do: ";
|
||||
out << action->getName();
|
||||
out << " impossible (";
|
||||
|
||||
out << action->getRelevance() << ")";
|
||||
|
||||
if (!event.GetSource().empty())
|
||||
out << " [" << event.GetSource() << "]";
|
||||
|
||||
botAI->TellMasterNoFacing(out);
|
||||
}
|
||||
LogAction("A:%s - IMPOSSIBLE", action->getName().c_str());
|
||||
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAction("A:%s - IMPOSSIBLE", action->getName().c_str());
|
||||
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.003f, false, event, "alt");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAction("A:%s - USELESS", action->getName().c_str());
|
||||
lastRelevance = relevance;
|
||||
}
|
||||
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT))
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "do: ";
|
||||
out << action->getName();
|
||||
out << " useless (";
|
||||
|
||||
delete actionNode; // Always delete after processing the action node
|
||||
out << action->getRelevance() << ")";
|
||||
|
||||
if (!event.GetSource().empty())
|
||||
out << " [" << event.GetSource() << "]";
|
||||
|
||||
botAI->TellMasterNoFacing(out);
|
||||
}
|
||||
lastRelevance = relevance;
|
||||
LogAction("A:%s - USELESS", action->getName().c_str());
|
||||
}
|
||||
|
||||
delete actionNode;
|
||||
}
|
||||
} while (basket && ++iterations <= iterationsPerTick);
|
||||
|
||||
// if (!basket)
|
||||
// {
|
||||
// lastRelevance = 0.0f;
|
||||
// PushDefaultActions();
|
||||
|
||||
// // prevent the delay after pushing default actions
|
||||
// if (queue.Peek() && depth < 1 && !minimal)
|
||||
// return DoNextAction(unit, depth + 1, minimal);
|
||||
// }
|
||||
|
||||
// MEMORY FIX TEST
|
||||
/*
|
||||
do
|
||||
{
|
||||
basket = queue.Peek();
|
||||
if (basket)
|
||||
{
|
||||
// NOTE: queue.Pop() deletes basket
|
||||
delete queue.Pop();
|
||||
}
|
||||
}
|
||||
while (basket);
|
||||
*/
|
||||
|
||||
if (time(nullptr) - currentTime > 1)
|
||||
{
|
||||
LogAction("Execution time exceeded 1 second");
|
||||
LogAction("too long execution");
|
||||
}
|
||||
|
||||
if (!actionExecuted)
|
||||
@@ -503,7 +567,7 @@ std::string const Engine::ListStrategies()
|
||||
std::string s = "Strategies: ";
|
||||
|
||||
if (strategies.empty())
|
||||
return s;
|
||||
return std::move(s);
|
||||
|
||||
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
|
||||
{
|
||||
104
src/strategy/Queue.cpp
Normal file
104
src/strategy/Queue.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Queue.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Log.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
|
||||
void Queue::Push(ActionBasket* action)
|
||||
{
|
||||
if (action)
|
||||
{
|
||||
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
|
||||
{
|
||||
ActionBasket* basket = *iter;
|
||||
if (action->getAction()->getName() == basket->getAction()->getName())
|
||||
{
|
||||
if (basket->getRelevance() < action->getRelevance())
|
||||
basket->setRelevance(action->getRelevance());
|
||||
|
||||
if (ActionNode* actionNode = action->getAction())
|
||||
delete actionNode;
|
||||
|
||||
delete action;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
actions.push_back(action);
|
||||
}
|
||||
}
|
||||
|
||||
ActionNode* Queue::Pop()
|
||||
{
|
||||
float max = -1;
|
||||
ActionBasket* selection = nullptr;
|
||||
|
||||
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
|
||||
{
|
||||
ActionBasket* basket = *iter;
|
||||
if (basket->getRelevance() > max)
|
||||
{
|
||||
max = basket->getRelevance();
|
||||
selection = basket;
|
||||
}
|
||||
}
|
||||
|
||||
if (selection != nullptr)
|
||||
{
|
||||
ActionNode* action = selection->getAction();
|
||||
actions.remove(selection);
|
||||
delete selection;
|
||||
return action;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ActionBasket* Queue::Peek()
|
||||
{
|
||||
float max = -1;
|
||||
ActionBasket* selection = nullptr;
|
||||
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
|
||||
{
|
||||
ActionBasket* basket = *iter;
|
||||
if (basket->getRelevance() > max)
|
||||
{
|
||||
max = basket->getRelevance();
|
||||
selection = basket;
|
||||
}
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
uint32 Queue::Size() { return actions.size(); }
|
||||
|
||||
void Queue::RemoveExpired()
|
||||
{
|
||||
std::list<ActionBasket*> expired;
|
||||
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
|
||||
{
|
||||
ActionBasket* basket = *iter;
|
||||
if (sPlayerbotAIConfig->expireActionTime && basket->isExpired(sPlayerbotAIConfig->expireActionTime))
|
||||
expired.push_back(basket);
|
||||
}
|
||||
|
||||
for (std::list<ActionBasket*>::iterator iter = expired.begin(); iter != expired.end(); iter++)
|
||||
{
|
||||
ActionBasket* basket = *iter;
|
||||
actions.remove(basket);
|
||||
|
||||
if (ActionNode* action = basket->getAction())
|
||||
{
|
||||
delete action;
|
||||
}
|
||||
|
||||
delete basket;
|
||||
}
|
||||
}
|
||||
28
src/strategy/Queue.h
Normal file
28
src/strategy/Queue.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_QUEUE_H
|
||||
#define _PLAYERBOT_QUEUE_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "Common.h"
|
||||
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
Queue(void) {}
|
||||
~Queue(void) {}
|
||||
|
||||
void Push(ActionBasket* action);
|
||||
ActionNode* Pop();
|
||||
ActionBasket* Peek();
|
||||
uint32 Size();
|
||||
void RemoveExpired();
|
||||
|
||||
private:
|
||||
std::list<ActionBasket*> actions;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -119,7 +119,6 @@ public:
|
||||
creators["move random"] = &StrategyContext::move_random;
|
||||
creators["formation"] = &StrategyContext::combat_formation;
|
||||
creators["move from group"] = &StrategyContext::move_from_group;
|
||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -187,7 +186,6 @@ private:
|
||||
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
|
||||
static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); }
|
||||
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }
|
||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||
};
|
||||
|
||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user