Compare commits

..

1 Commits

Author SHA1 Message Date
Yunfan Li
8d09a08dfd Preparation for project transfer 2025-10-14 00:05:16 +08:00
1264 changed files with 16604 additions and 26579 deletions

View File

@@ -1,40 +0,0 @@
name: C++ Codestyle
on:
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
paths:
- src/**
- "!README.md"
- "!docs/**"
concurrency:
group: "codestyle-cppcheck-${{ github.event.pull_request.number }}"
cancel-in-progress: true
jobs:
triage:
runs-on: ubuntu-latest
name: C++
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v4
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: AzerothCore codestyle
run: python ./apps/codestyle/codestyle-cpp.py
- name: C++ Advanced
run: |
sudo apt update -y
sudo apt install -y cppcheck
cppcheck --force --inline-suppr --suppressions-list=./.suppress.cppcheck src/ --output-file=report.txt
if [ -s report.txt ]; then # if file is not empty
cat report.txt
exit 1 # let github action fails
fi

View File

@@ -25,25 +25,21 @@ jobs:
with:
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
path: 'ac'
- name: Checkout Playerbot Module
uses: actions/checkout@v3
with:
repository: 'mod-playerbots/mod-playerbots'
#path: 'modules/mod-playerbots'
path: ac/modules/mod-playerbots
path: 'modules/mod-playerbots'
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.13
- name: Configure OS
shell: bash
working-directory: ac
env:
CONTINUOUS_INTEGRATION: true
run: |
./acore.sh install-deps
- name: Build
shell: bash
working-directory: ac
run: |
export CTOOLS_BUILD=all
./acore.sh compiler build

3
.gitignore vendored
View File

@@ -48,5 +48,4 @@ local.properties
.loadpath
.project
.cproject
.vscode
.idea
.vscode

View File

@@ -1 +0,0 @@
cppcheckError

View File

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

View File

@@ -1,263 +0,0 @@
import io
import os
import sys
import re
# Get the src directory of the project
src_directory = os.path.join(os.getcwd(), 'src')
# Global variables
error_handler = False
results = {
"Multiple blank lines check": "Passed",
"Trailing whitespace check": "Passed",
"GetCounter() check": "Passed",
"Misc codestyle check": "Passed",
"GetTypeId() check": "Passed",
"NpcFlagHelpers check": "Passed",
"ItemFlagHelpers check": "Passed",
"ItemTemplateFlagHelpers check": "Passed"
}
# Main function to parse all the files of the project
def parsing_file(directory: str) -> None:
print("Starting AzerothCore CPP Codestyle check...")
print(" ")
print("Please read the C++ Code Standards for AzerothCore:")
print("https://www.azerothcore.org/wiki/cpp-code-standards")
print(" ")
for root, _, files in os.walk(directory):
for file in files:
if not file.endswith('.ico'): # Skip .ico files that cannot be read
file_path = os.path.join(root, file)
file_name = file
try:
with open(file_path, 'r', encoding='utf-8') as file:
multiple_blank_lines_check(file, file_path)
trailing_whitespace_check(file, file_path)
get_counter_check(file, file_path)
if not file_name.endswith('.cmake') and file_name != 'CMakeLists.txt':
misc_codestyle_check(file, file_path)
if file_name != 'Object.h':
get_typeid_check(file, file_path)
if file_name != 'Unit.h':
npcflags_helpers_check(file, file_path)
if file_name != 'Item.h':
itemflag_helpers_check(file, file_path)
if file_name != 'ItemTemplate.h':
itemtemplateflag_helpers_check(file, file_path)
except UnicodeDecodeError:
print(f"\nCould not decode file {file_path}")
sys.exit(1)
# Output the results
print("")
for check, result in results.items():
print(f"{check} : {result}")
if error_handler:
print("\nPlease fix the codestyle issues above.")
sys.exit(1)
else:
print(f"\nEverything looks good")
# Codestyle patterns checking for multiple blank lines
def multiple_blank_lines_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
consecutive_blank_lines = 0
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if line.strip() == '':
consecutive_blank_lines += 1
if consecutive_blank_lines > 1:
print(f"Multiple blank lines found in {file_path} at line {line_number - 1}")
check_failed = True
else:
consecutive_blank_lines = 0
# Additional check for the end of the file
if consecutive_blank_lines >= 1:
print(f"Multiple blank lines found at the end of: {file_path}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["Multiple blank lines check"] = "Failed"
# Codestyle patterns checking for whitespace at the end of the lines
def trailing_whitespace_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if line.endswith(' \n'):
print(f"Trailing whitespace found: {file_path} at line {line_number}")
if not error_handler:
error_handler = True
results["Trailing whitespace check"] = "Failed"
# Codestyle patterns checking for ObjectGuid::GetCounter()
def get_counter_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'ObjectGuid::GetCounter()' in line:
print(f"Please use ObjectGuid::ToString().c_str() instead ObjectGuid::GetCounter(): {file_path} at line {line_number}")
if not error_handler:
error_handler = True
results["GetCounter() check"] = "Failed"
# Codestyle patterns checking for GetTypeId()
def get_typeid_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'GetTypeId() == TYPEID_ITEM' in line or 'GetTypeId() != TYPEID_ITEM' in line:
print(f"Please use IsItem() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_UNIT' in line or 'GetTypeId() != TYPEID_UNIT' in line:
print(f"Please use IsCreature() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_PLAYER' in line or 'GetTypeId() != TYPEID_PLAYER' in line:
print(f"Please use IsPlayer() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_GAMEOBJECT' in line or 'GetTypeId() != TYPEID_GAMEOBJECT' in line:
print(f"Please use IsGameObject() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_DYNOBJECT' in line or 'GetTypeId() != TYPEID_DYNOBJECT' in line:
print(f"Please use IsDynamicObject() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["GetTypeId() check"] = "Failed"
# Codestyle patterns checking for NpcFlag helpers
def npcflags_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'GetUInt32Value(UNIT_NPC_FLAGS)' in line:
print(
f"Please use GetNpcFlags() instead of GetUInt32Value(UNIT_NPC_FLAGS): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use HasNpcFlag() instead of HasFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'SetUInt32Value(UNIT_NPC_FLAGS,' in line:
print(
f"Please use ReplaceAllNpcFlags() instead of SetUInt32Value(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'SetFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use SetNpcFlag() instead of SetFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'RemoveFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use RemoveNpcFlag() instead of RemoveFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["NpcFlagHelpers check"] = "Failed"
# Codestyle patterns checking for ItemFlag helpers
def itemflag_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)' in line:
print(
f"Please use IsRefundable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)' in line:
print(
f"Please use IsBOPTradable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)' in line:
print(
f"Please use IsWrapped() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["ItemFlagHelpers check"] = "Failed"
# Codestyle patterns checking for ItemTemplate helpers
def itemtemplateflag_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'Flags & ITEM_FLAG' in line:
print(
f"Please use HasFlag(ItemFlag) instead of 'Flags & ITEM_FLAG_': {file_path} at line {line_number}")
check_failed = True
if 'Flags2 & ITEM_FLAG2' in line:
print(
f"Please use HasFlag2(ItemFlag2) instead of 'Flags2 & ITEM_FLAG2_': {file_path} at line {line_number}")
check_failed = True
if 'FlagsCu & ITEM_FLAGS_CU' in line:
print(
f"Please use HasFlagCu(ItemFlagsCustom) instead of 'FlagsCu & ITEM_FLAGS_CU_': {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["ItemTemplateFlagHelpers check"] = "Failed"
# Codestyle patterns checking for various codestyle issues
def misc_codestyle_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# used to check for "if/else (...) {" "} else" ignores "if/else (...) {...}" "#define ... if/else (...) {"
ifelse_curlyregex = r"^[^#define].*\s+(if|else)(\s*\(.*\))?\s*{[^}]*$|}\s*else(\s*{[^}]*$)"
# used to catch double semicolons ";;" ignores "(;;)"
double_semiregex = r"(?<!\()\s*;;(?!\))"
# used to catch tabs
tab_regex = r"\t"
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'const auto&' in line:
print(
f"Please use the 'auto const&' syntax instead of 'const auto&': {file_path} at line {line_number}")
check_failed = True
if re.search(r'\bconst\s+\w+\s*\*\b', line):
print(
f"Please use the 'Class/ObjectType const*' syntax instead of 'const Class/ObjectType*': {file_path} at line {line_number}")
check_failed = True
if [match for match in [' if(', ' if ( '] if match in line]:
print(
f"Please use the 'if (XXXX)' syntax instead of 'if(XXXX)': {file_path} at line {line_number}")
check_failed = True
if re.match(ifelse_curlyregex, line):
print(
f"Curly brackets are not allowed to be leading or trailing if/else statements. Place it on a new line: {file_path} at line {line_number}")
check_failed = True
if re.search(double_semiregex, line):
print(
f"Double semicolon (;;) found in {file_path} at line {line_number}")
check_failed = True
if re.match(tab_regex, line):
print(
f"Tab found! Replace it to 4 spaces: {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["Misc codestyle check"] = "Failed"
# Main function
parsing_file(src_directory)

2
code_format.sh Normal file → Executable file
View File

@@ -15,4 +15,4 @@ for file in $cpp_files; do
$CLANG_FORMAT_PATH -i $file
done
echo "All .cpp or .h files have been formatted."
echo "All .cpp or .h files have been formatted."

View File

@@ -21,11 +21,10 @@
# THRESHOLDS
# QUESTS
# COMBAT
# GREATER BUFFS STRATEGIES
# PALADIN BUFFS STRATEGIES
# CHEATS
# SPELLS
# FLIGHTPATH
# PROFESSIONS
# RANDOMBOT-SPECIFIC SETTINGS
# GENERAL
# LEVELS
@@ -37,7 +36,7 @@
# RPG STRATEGY
# TELEPORTS
# BATTLEGROUND & ARENA & PVP
# RANDOM BOT TIMING AND BEHAVIOR
# INTERVALS
# PREMADE SPECS
# INFORMATION
# WARRIOR
@@ -45,7 +44,7 @@
# HUNTER
# ROGUE
# PRIEST
# DEATH KNIGHT
# DEATHKNIGHT
# SHAMAN
# MAGE
# WARLOCK
@@ -56,7 +55,7 @@
# HUNTER
# ROGUE
# PRIEST
# DEATH KNIGHT
# DEATHKNIGHT
# SHAMAN
# MAGE
# WARLOCK
@@ -91,20 +90,17 @@ AiPlayerbot.MinRandomBots = 500
AiPlayerbot.MaxRandomBots = 500
# Randombot accounts
# If you are not using any expansion at all, you may have to set this manually, in which case please
# ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
# If you are not using any expansion at all, you may have to set this manually, in which case please ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
# Default: 0 (automatic)
AiPlayerbot.RandomBotAccountCount = 0
# Delete all randombot accounts
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would
# like to recreate randombots, set the number back to 0 and rerun the Worldserver.
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would like to recreate randombots, set the number back to 0 and rerun the Worldserver.
AiPlayerbot.DeleteRandomBotAccounts = 0
# Disable randombots when no real players are logged in
# Default: 0 (randombots will login when server starts)
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will
# log out 300 seconds (default) after all real players log out
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will log out 300 seconds (default) after all real players log out
AiPlayerbot.DisabledWithoutRealPlayer = 0
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
@@ -156,8 +152,7 @@ AiPlayerbot.AllowGuildBots = 1
AiPlayerbot.AllowTrustedAccountBots = 1
# Randombots will create guilds with nearby randombots
# Note: currently, randombots will not invite more bots after a guild is created,
# meaning randombot guilds will have only the 10 initial randombots needed to sign the charter
# Note: currently, randombots will not invite more bots after a guild is created (i.e., randombot guilds will have only the 10 initial randombots needed to sign the charter)
# Default: 0 (disabled)
AiPlayerbot.RandomBotGuildNearby = 0
@@ -191,8 +186,7 @@ AiPlayerbot.AutoInitOnly = 0
# Default: 1.0 (same with the player)
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
# Bot automatically trains spells when talking to trainer
# yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells
# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells)
AiPlayerbot.AutoTrainSpells = yes
#
@@ -269,7 +263,7 @@ AiPlayerbot.UseFastFlyMountAtMinLevel = 70
AiPlayerbot.RandomBotShowHelmet = 1
AiPlayerbot.RandomBotShowCloak = 1
# Randombots and altbots automatically equip any items in their inventory that are sufficient upgrades
# Randombots and altbots automatically equip upgrades (bots will equip any item obtained from looting or a quest if they are sufficient upgrades)
# Default: 1 (enabled)
AiPlayerbot.AutoEquipUpgradeLoot = 1
@@ -290,6 +284,9 @@ AiPlayerbot.TwoRoundsGearInit = 0
#
#
# Bots will say information about items when collecting them
AiPlayerbot.SayWhenCollectingItems = 1
# Bots keep looting when loot system is set to free for all
# Default: 0 (disabled)
AiPlayerbot.FreeMethodLoot = 0
@@ -317,8 +314,7 @@ AiPlayerbot.GlobalCooldown = 500
# Max wait time when moving
AiPlayerbot.MaxWaitForMove = 5000
# Enable/disable use of MoveSplinePath for bot movement
# Disabling will result in more erratic movement but is required for stuns, snares, and roots to work on bots
# Disable use of MoveSplinePath for bot movement, will result in more erratic bot movement but means stun/snare/root/etc will work on bots (they wont reliably work when MoveSplinePath is enabled, though slowing effects still work ok)
# Default: 0 - MoveSplinePath enabled
# 1 - MoveSplinePath disabled in BG/Arena only
# 2 - MoveSplinePath disabled everywhere
@@ -364,15 +360,15 @@ AiPlayerbot.LootDelay = 1000
#
#
# Distances are in yards
AiPlayerbot.FarDistance = 20.0
AiPlayerbot.SightDistance = 100.0
AiPlayerbot.SightDistance = 75.0
AiPlayerbot.SpellDistance = 28.5
AiPlayerbot.ShootDistance = 5.0
AiPlayerbot.ReactDistance = 150.0
AiPlayerbot.GrindDistance = 75.0
AiPlayerbot.HealDistance = 38.5
AiPlayerbot.LootDistance = 15.0
AiPlayerbot.FleeDistance = 5.0
AiPlayerbot.AggroDistance = 22
AiPlayerbot.TooCloseDistance = 5.0
AiPlayerbot.MeleeDistance = 0.75
AiPlayerbot.FollowDistance = 1.5
@@ -380,8 +376,7 @@ AiPlayerbot.WhisperDistance = 6000.0
AiPlayerbot.ContactDistance = 0.45
AiPlayerbot.AoeRadius = 10
AiPlayerbot.RpgDistance = 200
AiPlayerbot.GrindDistance = 75.0
AiPlayerbot.ReactDistance = 150.0
AiPlayerbot.AggroDistance = 22
#
#
@@ -412,11 +407,10 @@ AiPlayerbot.HighMana = 65
#
#
# Bots pick their quest rewards
# yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple
# Bots pick their quest rewards (yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple)
AiPlayerbot.AutoPickReward = yes
# Sync quests with player (bots will complete quests the moment you hand them in and will not loot quest items.)
# Sync quests with player (Bots will complete quests the moment you hand them in and will not loot quest items.)
# Default: 1 (enabled)
AiPlayerbot.SyncQuestWithPlayer = 1
@@ -441,7 +435,7 @@ AiPlayerbot.DropObsoleteQuests = 1
# Auto add dungeon/raid strategies when entering the instance if implemented
AiPlayerbot.ApplyInstanceStrategies = 1
# Enable auto avoid aoe strategy
# Enable auto avoid aoe strategy (experimental)
# Default: 1 (enabled)
AiPlayerbot.AutoAvoidAoe = 1
@@ -468,7 +462,7 @@ AiPlayerbot.FleeingEnabled = 1
####################################################################################################
####################################################################################################
# GREATER BUFFS STRATEGIES
# PALADIN BUFFS STRATEGIES
#
#
@@ -490,14 +484,12 @@ AiPlayerbot.RPWarningCooldown = 30
#
#
# Enable/Disable maintenance command
# Learn all available spells and skills, assign talent points, refresh consumables, repair, enchant equipment, socket gems, etc.
# Applies if bot's level is above AiPlayerbot.MinEnchantingBotLevel
# Enable/Disable maintenance command (learn all available spells and skills, supplement consumables, repair, enchant equipment if bot's level is above AiPlayerbot.MinEnchantingBotLevel)
# Default: 1 (enabled)
AiPlayerbot.MaintenanceCommand = 1
# Enable/Disable specific maintenance command functionality for alt bots
# Disable to prevent players from giving free bags, spells, skill levels, etc. to their alt bots
# Disable to prevent players from giving free bags, spells, skill levels etc to their alt bots
# Default: 1 (enabled)
AiPlayerbot.AltMaintenanceAmmo = 1
AiPlayerbot.AltMaintenanceFood = 1
@@ -509,7 +501,6 @@ AiPlayerbot.AltMaintenanceBags = 1
AiPlayerbot.AltMaintenanceMounts = 1
AiPlayerbot.AltMaintenanceSkills = 1
# "Special Spells" consist of any spells listed in AiPlayerbot.RandomBotSpellIds and Death Gate for Death Knights
AiPlayerbot.AltMaintenanceClassSpells = 1
AiPlayerbot.AltMaintenanceAvailableSpells = 1
AiPlayerbot.AltMaintenanceSpecialSpells = 1
@@ -524,8 +515,8 @@ AiPlayerbot.AltMaintenanceReputation = 1
AiPlayerbot.AltMaintenanceAttunementQuests = 1
AiPlayerbot.AltMaintenanceKeyring = 1
# Enable/Disable autogear command, which automatically upgrades bots' gear
# The quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
# Enable/Disable autogear command, which automatically upgrades bots' gear; the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
# Default: 1 (enabled)
AiPlayerbot.AutoGearCommand = 1
@@ -557,7 +548,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
# "mana" (bots have infinite mana)
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
# "raid" (bots use cheats implemented into raid strategies)
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid"
@@ -589,30 +580,6 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
#
####################################################################################################
####################################################################################################
# PROFESSIONS
# Note: Random bots currently do not get professions
#
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
# Default: 1 (Enabled)
AiPlayerbot.EnableFishingWithMaster = 1
# Distance from itself (in yards) that a bot with a master will search for water to fish
AiPlayerbot.FishingDistanceFromMaster = 10.0
# Distance from itself (in yards) that a bot without a master will search for water to fish
# Currently not relevant since masterless bots will not fish
AiPlayerbot.FishingDistance = 40.0
# Distance from water (in yards) beyond which a bot will remove the 'master fishing' strategy
AiPlayerbot.EndFishingWithMaster = 30.0
#
#
#
####################################################################################################
#######################################
# #
# RANDOMBOT-SPECIFIC SETTINGS #
@@ -639,11 +606,9 @@ AiPlayerbot.RandomBotMaxLevel = 80
AiPlayerbot.SyncLevelWithPlayers = 0
# Mark many quests ≤ bot level as complete (slows down bot creation)
# Default: 0 (disabled)
AiPlayerbot.PreQuests = 0
# Enable LFG for randombots
# Default: 1 (enabled)
AiPlayerbot.RandomBotJoinLfg = 1
# Enable/Disable periodic online - offline of randombots to mimic the real-world scenario where not all players are online simultaneously
@@ -664,8 +629,7 @@ AiPlayerbot.RandomBotHordeRatio = 50
AiPlayerbot.DisableDeathKnightLogin = 0
# Enable simulated expansion limitation for talents and glyphs
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61 and 7 rows plus the middle talent of the 8th row for bots until level 71
# Default: 0 (disabled)
AiPlayerbot.LimitTalentsExpansion = 0
@@ -734,7 +698,7 @@ AiPlayerbot.RandomGearQualityLimit = 3
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
# TBC
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
# Wotlk
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
@@ -745,13 +709,12 @@ AiPlayerbot.RandomGearScoreLimit = 0
# Default: 1 (enabled)
AiPlayerbot.IncrementalGearInit = 1
# Set minimum level of bots that will enchant and socket gems into their equipment with maintenance
# If greater than RandomBotMaxlevel, bots will not automatically enchant equipment or socket gems
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
# Default: 60
AiPlayerbot.MinEnchantingBotLevel = 60
# Enable expansion limitation for bot enchants and gems
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants and gems until level 71
# Enable expansion limitation for bot enchants
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants until level 71
# Default: 1 (enabled)
AiPlayerbot.LimitEnchantExpansion = 1
@@ -847,7 +810,7 @@ AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
#
# Quest that will be completed and rewarded for all randombots
AiPlayerbot.RandomBotQuestIds = "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"
AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,13188,13189,24499,24511,24710,24712"
# Randombots will group with nearby randombots to do shared quests
AiPlayerbot.RandomBotGroupNearby = 0
@@ -898,21 +861,19 @@ AiPlayerbot.OpenGoSpell = 6477
#
# Additional randombot strategies
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies.
# These rules are processed after the defaults.
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
# Example: "+threat,-potions"
AiPlayerbot.RandomBotCombatStrategies = ""
AiPlayerbot.RandomBotNonCombatStrategies = ""
# Additional altbot strategies
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies.
# These rules are processed after the defaults.
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
AiPlayerbot.CombatStrategies = ""
AiPlayerbot.NonCombatStrategies = ""
# Remove "healer dps" strategy on the maps specified below.
# Default: 1 (enabled)
AiPlayerbot.HealerDPSMapRestriction = 1
# Remove "healer dps" strategy on specified maps.
# Default: 0 (disabled)
AiPlayerbot.HealerDPSMapRestriction = 0
# List of Map IDs where "healer dps" strategy will be removed if AiPlayerbot.HealerDPSMapRestriction is enabled
# Default: (Dungeon and Raid maps) "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
@@ -1096,8 +1057,7 @@ AiPlayerbot.ZoneBracket.4197 = 79,80
#
#
# Map IDs where bots can be teleported to
# Defaults: 0 = Eastern Kingdoms, 1 = Kalimdor, 530 = Outland, 571 = Northrend
# Maps where bots can be teleported to
AiPlayerbot.RandomBotMaps = 0,1,530,571
# Probabilty bots teleport to banker (city)
@@ -1222,7 +1182,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,3951"
# PvP Restricted Areas (bots don't pvp)
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
AiPlayerbot.FastReactInBG = 1
@@ -1233,46 +1193,24 @@ AiPlayerbot.FastReactInBG = 1
####################################################################################################
####################################################################################################
# RANDOM BOT TIMING AND BEHAVIOR
# INTERVALS
#
#
# How often (in seconds) the random bot manager runs its main update loop
# Default: 20
# All in seconds
AiPlayerbot.RandomBotUpdateInterval = 20
# Minimum and maximum seconds before the manager re-evaluates and adjusts total random bot count
# Defaults: 1800 (min), 7200 (max)
AiPlayerbot.RandomBotCountChangeMinInterval = 1800
AiPlayerbot.RandomBotCountChangeMaxInterval = 7200
# Minimum and maximum seconds a random bot will stay online before logging out
# Defaults: 600 (min), 28800 (max)
AiPlayerbot.MinRandomBotInWorldTime = 600
AiPlayerbot.MaxRandomBotInWorldTime = 28800
# Minimum and maximum seconds before a bot is eligible for re-randomization (gear, class, talents, etc.)
# Defaults: 7200 (min), 1209600 (max)
AiPlayerbot.MinRandomBotRandomizeTime = 7200
AiPlayerbot.MaxRandomBotRandomizeTime = 1209600
# Number of bots processed (login, logout, update) per manager update cycle
# Default: 60
AiPlayerbot.RandomBotsPerInterval = 60
# Minimum and maximum seconds after death before a bot revives
# Defaults: 60 (min), 300 (max)
AiPlayerbot.MinRandomBotReviveTime = 60
AiPlayerbot.MaxRandomBotReviveTime = 300
# Minimum and maximum seconds between bot teleports to new areas or zones
# Defaults: 3600 (min), 18000 (max)
AiPlayerbot.MinRandomBotTeleportInterval = 3600
AiPlayerbot.MaxRandomBotTeleportInterval = 18000
# Number of seconds bots flagged as permanent stay in the world (31,104,000 ≈ 1 year)
# Default: 31104000
AiPlayerbot.PermanentlyInWorldTime = 31104000
AiPlayerbot.PermanantlyInWorldTime = 31104000
#
#
@@ -1492,7 +1430,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
####################################################################################################
####################################################################################################
# DEATH KNIGHT
# DEATHKNIGHT
#
#
@@ -1815,7 +1753,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
####################################################################################################
####################################################################################################
# DEATH KNIGHT
# DEATHKNIGHT
#
#

View File

@@ -1,255 +0,0 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'pet_usage_error',
'pet_no_pet_error',
'pet_stance_report',
'pet_no_target_error',
'pet_target_dead_error',
'pet_invalid_target_error',
'pet_pvp_prohibited_error',
'pet_attack_success',
'pet_attack_failed',
'pet_follow_success',
'pet_stay_success',
'pet_unknown_command_error',
'pet_stance_set_success',
'pet_type_pet',
'pet_type_guardian',
'pet_stance_aggressive',
'pet_stance_defensive',
'pet_stance_passive',
'pet_stance_unknown'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'pet_usage_error',
'pet_no_pet_error',
'pet_stance_report',
'pet_no_target_error',
'pet_target_dead_error',
'pet_invalid_target_error',
'pet_pvp_prohibited_error',
'pet_attack_success',
'pet_attack_failed',
'pet_follow_success',
'pet_stay_success',
'pet_unknown_command_error',
'pet_stance_set_success',
'pet_type_pet',
'pet_type_guardian',
'pet_stance_aggressive',
'pet_stance_defensive',
'pet_stance_passive',
'pet_stance_unknown'
);
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
(1717, 'pet_usage_error', "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
"사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
(1718, 'pet_no_pet_error', "You have no pet or guardian pet.", 0, 0,
"펫이나 수호자 펫이 없습니다.",
"Vous n'avez pas de familier ou gardien.",
"Du hast kein Tier oder Wächter.",
"你没有宠物或守护者宠物。",
"你沒有寵物或守護者寵物。",
"No tienes mascota o mascota guardián.",
"No tienes mascota o mascota guardián.",
"У вас нет питомца или защитника."),
(1719, 'pet_stance_report', "Current stance of %type \"%name\": %stance.", 0, 0,
"%type \"%name\"의 현재 태세: %stance.",
"Position actuelle du %type \"%name\": %stance.",
"Aktuelle Haltung des %type \"%name\": %stance.",
"%type \"%name\" 的当前姿态: %stance。",
"%type \"%name\" 的當前姿態: %stance。",
"Postura actual del %type \"%name\": %stance.",
"Postura actual del %type \"%name\": %stance.",
"Текущая позиция %type \"%name\": %stance."),
(1720, 'pet_no_target_error', "No valid target selected by master.", 0, 0,
"주인이 유효한 대상을 선택하지 않았습니다.",
"Aucune cible valide sélectionnée par le maître.",
"Kein gültiges Ziel vom Meister ausgewählt.",
"主人未选择有效目标。",
"主人未選擇有效目標。",
"No hay objetivo válido seleccionado por el maestro.",
"No hay objetivo válido seleccionado por el maestro.",
"Хозяин не выбрал действительную цель."),
(1721, 'pet_target_dead_error', "Target is not alive.", 0, 0,
"대상이 살아있지 않습니다.",
"La cible n'est pas vivante.",
"Das Ziel ist nicht am Leben.",
"目标未存活。",
"目標未存活。",
"El objetivo no está vivo.",
"El objetivo no está vivo.",
"Цель не жива."),
(1722, 'pet_invalid_target_error', "Target is not a valid attack target for the bot.", 0, 0,
"대상이 봇에게 유효한 공격 대상이 아닙니다.",
"La cible n'est pas une cible d'attaque valide pour le bot.",
"Das Ziel ist kein gültiges Angriffsziel für den Bot.",
"目标不是机器人的有效攻击目标。",
"目標不是機器人的有效攻擊目標。",
"El objetivo no es un objetivo de ataque válido para el bot.",
"El objetivo no es un objetivo de ataque válido para el bot.",
"Цель не является допустимой целью атаки для бота."),
(1723, 'pet_pvp_prohibited_error', "I cannot command my pet to attack players in PvP prohibited areas.", 0, 0,
"PvP 금지 지역에서는 펫에게 플레이어 공격 명령을 내릴 수 없습니다.",
"Je ne peux pas commander à mon familier d'attaquer des joueurs dans les zones où le PvP est interdit.",
"Ich kann meinem Tier nicht befehlen, Spieler in PvP-verbotenen Gebieten anzugreifen.",
"我不能命令我的宠物在禁止PvP的区域攻击玩家。",
"我不能命令我的寵物在禁止PvP的區域攻擊玩家。",
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
"Я не могу приказать своему питомцу атаковать игроков в зонах, где PvP запрещено."),
(1724, 'pet_attack_success', "Pet commanded to attack your target.", 0, 0,
"펫이 당신의 대상을 공격하도록 명령했습니다.",
"Le familier a reçu l'ordre d'attaquer votre cible.",
"Tier wurde befohlen, dein Ziel anzugreifen.",
"宠物已命令攻击你的目标。",
"寵物已命令攻擊你的目標。",
"Mascota ordenada a atacar tu objetivo.",
"Mascota ordenada a atacar tu objetivo.",
"Питомцу приказано атаковать вашу цель."),
(1725, 'pet_attack_failed', "Pet did not attack. (Already attacking or unable to attack target)", 0, 0,
"펫이 공격하지 않았습니다. (이미 공격 중이거나 대상 공격 불가)",
"Le familier n'a pas attaqué. (Attaque déjà en cours ou impossible d'attaquer la cible)",
"Tier hat nicht angegriffen. (Greift bereits an oder kann Ziel nicht angreifen)",
"宠物未攻击。(已在攻击或无法攻击目标)",
"寵物未攻擊。(已在攻擊或無法攻擊目標)",
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
"Питомец не атаковал. (Уже атакует или не может атаковать цель)"),
(1726, 'pet_follow_success', "Pet commanded to follow.", 0, 0,
"펫이 따라오도록 명령했습니다.",
"Le familier a reçu l'ordre de suivre.",
"Tier wurde befohlen zu folgen.",
"宠物已命令跟随。",
"寵物已命令跟隨。",
"Mascota ordenada a seguir.",
"Mascota ordenada a seguir.",
"Питомцу приказано следовать."),
(1727, 'pet_stay_success', "Pet commanded to stay.", 0, 0,
"펫이 머물도록 명령했습니다.",
"Le familier a reçu l'ordre de rester.",
"Tier wurde befohlen zu bleiben.",
"宠物已命令停留。",
"寵物已命令停留。",
"Mascota ordenada a quedarse.",
"Mascota ordenada a quedarse.",
"Питомцу приказано остаться."),
(1728, 'pet_unknown_command_error', "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
"알 수 없는 펫 명령: %param. 사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Commande de familier inconnue: %param. Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Unbekannter Tierbefehl: %param. Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"未知宠物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"未知寵物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Неизвестная команда питомца: %param. Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
(1729, 'pet_stance_set_success', "Pet stance set to %stance.", 0, 0,
"펫 태세가 %stance(으)로 설정되었습니다.",
"Position du familier définie sur %stance.",
"Tierhaltung auf %stance gesetzt.",
"宠物姿态设置为 %stance。",
"寵物姿態設置為 %stance。",
"Postura de mascota establecida en %stance.",
"Postura de mascota establecida en %stance.",
"Позиция питомца установлена на %stance."),
(1730, 'pet_type_pet', "pet", 0, 0,
"",
"familier",
"Tier",
"宠物",
"寵物",
"mascota",
"mascota",
"питомец"),
(1731, 'pet_type_guardian', "guardian", 0, 0,
"수호자",
"gardien",
"Wächter",
"守护者",
"守護者",
"guardián",
"guardián",
"защитник"),
(1732, 'pet_stance_aggressive', "aggressive", 0, 0,
"공격적",
"agressif",
"aggressiv",
"进攻",
"進攻",
"agresivo",
"agresivo",
"агрессивная"),
(1733, 'pet_stance_defensive', "defensive", 0, 0,
"방어적",
"défensif",
"defensiv",
"防御",
"防禦",
"defensivo",
"defensivo",
"защитная"),
(1734, 'pet_stance_passive', "passive", 0, 0,
"수동적",
"passif",
"passiv",
"被动",
"被動",
"pasivo",
"pasivo",
"пассивная"),
(1735, 'pet_stance_unknown', "unknown", 0, 0,
"알 수 없음",
"inconnu",
"unbekannt",
"未知",
"未知",
"desconocido",
"desconocido",
"неизвестная");
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('pet_usage_error', 100),
('pet_no_pet_error', 100),
('pet_stance_report', 100),
('pet_no_target_error', 100),
('pet_target_dead_error', 100),
('pet_invalid_target_error', 100),
('pet_pvp_prohibited_error', 100),
('pet_attack_success', 100),
('pet_attack_failed', 100),
('pet_follow_success', 100),
('pet_stay_success', 100),
('pet_unknown_command_error', 100),
('pet_stance_set_success', 100),
('pet_type_pet', 100),
('pet_type_guardian', 100),
('pet_stance_aggressive', 100),
('pet_stance_defensive', 100),
('pet_stance_passive', 100),
('pet_stance_unknown', 100);

View File

@@ -1,15 +0,0 @@
DELETE FROM ai_playerbot_texts WHERE name IN ('no_fishing_pole_error');
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('no_fishing_pole_error');
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
(1736, 'no_fishing_pole_error', "I don't have a Fishing Pole", 0, 0,
"낚싯대가 없습니다",
"Je nai pas de canne à pêche",
"Ich habe keine Angelrute",
"我沒有釣魚竿",
"我没有钓鱼竿",
"No tengo una caña de pescar",
"No tengo una caña de pescar",
"У меня нет удочки");
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('no_fishing_pole_error', 100);

View File

@@ -140,37 +140,37 @@ BotRoles AiFactory::GetPlayerRoles(Player* player)
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
role = BOT_ROLE_DPS;
else
role = BOT_ROLE_HEALER;
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_RESTORATION)
if (tab == 2)
role = BOT_ROLE_HEALER;
else
role = BOT_ROLE_DPS;
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
role = BOT_ROLE_TANK;
else
role = BOT_ROLE_DPS;
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_HOLY)
if (tab == 0)
role = BOT_ROLE_HEALER;
else if (tab == PALADIN_TAB_PROTECTION)
else if (tab == 1)
role = BOT_ROLE_TANK;
else if (tab == PALADIN_TAB_RETRIBUTION)
else if (tab == 2)
role = BOT_ROLE_DPS;
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
role = BOT_ROLE_DPS;
else if (tab == DRUID_TAB_FERAL)
else if (tab == 1)
role = (BotRoles)(BOT_ROLE_TANK | BOT_ROLE_DPS);
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
role = BOT_ROLE_HEALER;
break;
default:
@@ -188,83 +188,84 @@ std::string AiFactory::GetPlayerSpecName(Player* player)
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
specName = "shadow";
else if (tab == PRIEST_TAB_HOLY)
else if (tab == 1)
specName = "holy";
else
specName = "disc";
;
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_RESTORATION)
if (tab == 2)
specName = "resto";
else if (tab == SHAMAN_TAB_ENHANCEMENT)
else if (tab == 1)
specName = "enhance";
else
specName = "elem";
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
specName = "prot";
else if (tab == WARRIOR_TAB_FURY)
else if (tab == 1)
specName = "fury";
else
specName = "arms";
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_HOLY)
if (tab == 0)
specName = "holy";
else if (tab == PALADIN_TAB_PROTECTION)
else if (tab == 1)
specName = "prot";
else if (tab == PALADIN_TAB_RETRIBUTION)
else if (tab == 2)
specName = "retrib";
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
specName = "balance";
else if (tab == DRUID_TAB_FERAL)
else if (tab == 1)
specName = "feraldps";
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
specName = "resto";
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION)
if (tab == 0)
specName = "assas";
else if (tab == ROGUE_TAB_COMBAT)
else if (tab == 1)
specName = "combat";
else if (tab == ROGUE_TAB_SUBTLETY)
else if (tab == 2)
specName = "subtle";
break;
case CLASS_HUNTER:
if (tab == HUNTER_TAB_BEAST_MASTERY)
if (tab == 0)
specName = "beast";
else if (tab == HUNTER_TAB_MARKSMANSHIP)
else if (tab == 1)
specName = "marks";
else if (tab == HUNTER_TAB_SURVIVAL)
else if (tab == 2)
specName = "surv";
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
specName = "blooddps";
else if (tab == DEATH_KNIGHT_TAB_FROST)
else if (tab == 1)
specName = "frostdps";
else if (tab == DEATH_KNIGHT_TAB_UNHOLY)
else if (tab == 2)
specName = "unholydps";
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE)
if (tab == 0)
specName = "arcane";
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1)
specName = "fire";
else if (tab == MAGE_TAB_FROST)
else if (tab == 2)
specName = "frost";
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
if (tab == 0)
specName = "afflic";
else if (tab == WARLOCK_TAB_DEMONOLOGY)
else if (tab == 1)
specName = "demo";
else if (tab == WARLOCK_TAB_DESTRUCTION)
else if (tab == 2)
specName = "destro";
break;
default:
@@ -279,124 +280,145 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
uint8 tab = GetPlayerSpecTab(player);
if (!player->InBattleground())
{
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr);
}
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
{
engine->addStrategy("avoid aoe", false);
}
engine->addStrategy("formation", false);
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == PRIEST_TAB_SHADOW)
if (tab == 2)
{
engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr);
}
else if (tab == PRIEST_TAB_DISCIPLINE)
{
engine->addStrategiesNoInit("heal", nullptr);
}
else
{
engine->addStrategiesNoInit("holy heal", nullptr);
}
engine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE)
if (tab == 0) // Arcane
engine->addStrategiesNoInit("arcane", nullptr);
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1) // Fire
{
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
{
engine->addStrategiesNoInit("frostfire", nullptr);
}
else
{
engine->addStrategiesNoInit("fire", nullptr);
}
}
else
else // Frost
engine->addStrategiesNoInit("frost", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
else if (tab == 0 || !player->HasSpell(1680)) // Whirlwind
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
else
engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr);
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_ELEMENTAL)
if (tab == 0) // Elemental
engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr);
else if (tab == SHAMAN_TAB_RESTORATION)
else if (tab == 2) // Restoration
engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr);
else
else // Enhancement
engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr);
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_PROTECTION)
if (tab == 1)
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
else if (tab == PALADIN_TAB_HOLY)
else if (tab == 0)
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
else
engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr);
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_BALANCE)
if (tab == 0)
{
engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr);
engine->addStrategy("caster debuff", false);
}
else if (tab == DRUID_TAB_RESTORATION)
else if (tab == 2)
engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr);
else
{
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
if (player->HasSpell(768) /*cat form*/&& !player->HasAura(16931) /*thick hide*/)
{
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
}
else
{
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
}
}
break;
case CLASS_HUNTER:
if (tab == HUNTER_TAB_BEAST_MASTERY)
if (tab == 0) // Beast Mastery
engine->addStrategiesNoInit("bm", nullptr);
else if (tab == HUNTER_TAB_MARKSMANSHIP)
else if (tab == 1) // Marksmanship
engine->addStrategiesNoInit("mm", nullptr);
else
else if (tab == 2) // Survival
engine->addStrategiesNoInit("surv", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
{
engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr);
}
else
{
engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr);
}
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
if (tab == 0) // Affliction
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
else if (tab == WARLOCK_TAB_DEMONOLOGY)
else if (tab == 1) // Demonology
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
else
else if (tab == 2) // Destruction
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
else if (tab == DEATH_KNIGHT_TAB_FROST)
else if (tab == 1)
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
else
engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr);
break;
}
if (PlayerbotAI::IsTank(player, true))
if (PlayerbotAI::IsTank(player, true)) {
engine->addStrategy("tank face", false);
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true))
}
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) {
engine->addStrategy("behind", false);
}
if (PlayerbotAI::IsHeal(player, true))
{
if (sPlayerbotAIConfig->autoSaveMana)
@@ -404,7 +426,6 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId()))
engine->addStrategy("healer dps", false);
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
if (!player->GetGroup())
@@ -413,13 +434,15 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
engine->addStrategy("boost", false);
engine->addStrategy("dps assist", false);
engine->removeStrategy("threat", false);
// engine-
switch (player->getClass())
{
case CLASS_PRIEST:
{
if (tab != PRIEST_TAB_SHADOW)
{
engine->addStrategiesNoInit("holy dps", "shadow debuff", "shadow aoe", nullptr);
}
break;
}
case CLASS_DRUID:
@@ -434,13 +457,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
case CLASS_SHAMAN:
{
if (tab == SHAMAN_TAB_RESTORATION)
{
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
}
break;
}
case CLASS_PALADIN:
{
if (tab == PALADIN_TAB_HOLY)
{
engine->addStrategiesNoInit("dps", "dps assist", "baoe", nullptr);
}
break;
}
default:
@@ -449,9 +476,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
}
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
}
else
{
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
}
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
@@ -478,15 +509,23 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (player->InArena())
{
engine->addStrategy("arena", false);
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "cast time", "dps assist", nullptr);
}
else
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist", nullptr);
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist",
nullptr);
engine->removeStrategy("custom::say", false);
engine->removeStrategy("flee", false);
engine->removeStrategy("threat", false);
engine->addStrategy("boost", false);
// if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
// engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
// if (player->getClass() == CLASS_DRUID && tab == 1)
// engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
// if (player->getClass() == CLASS_ROGUE)
// engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
}
}
@@ -508,15 +547,19 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_PALADIN:
if (tab == PALADIN_TAB_PROTECTION)
if (tab == 1)
{
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20)
{
nonCombatEngine->addStrategy("bhealth", false);
}
else
{
nonCombatEngine->addStrategy("bdps", false);
}
}
else if (tab == PALADIN_TAB_HOLY)
else if (tab == 0)
nonCombatEngine->addStrategiesNoInit("dps assist", "bmana", "bcast", nullptr);
else
nonCombatEngine->addStrategiesNoInit("dps assist", "bdps", "baoe", nullptr);
@@ -527,7 +570,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr);
break;
case CLASS_SHAMAN:
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
if (tab == 0 || tab == 2)
nonCombatEngine->addStrategy("bmana", false);
else
nonCombatEngine->addStrategy("bdps", false);
@@ -543,34 +586,43 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_DRUID:
if (tab == DRUID_TAB_FERAL)
if (tab == 1)
{
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
{
nonCombatEngine->addStrategy("dps assist", false);
}
else
{
nonCombatEngine->addStrategy("tank assist", false);
}
}
else
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == WARRIOR_TAB_PROTECTION)
if (tab == 2)
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist", false);
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICTION)
{
nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DEMONOLOGY)
{
nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DESTRUCTION)
{
nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr);
}
nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == DEATH_KNIGHT_TAB_BLOOD)
if (tab == 0)
nonCombatEngine->addStrategy("tank assist", false);
else
nonCombatEngine->addStrategy("dps assist", false);
@@ -587,7 +639,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
}
if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true))
{
nonCombatEngine->addStrategy("save mana", false);
}
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{
@@ -613,14 +667,18 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategy("grind", false);
if (sPlayerbotAIConfig->enableNewRpgStrategy)
{
nonCombatEngine->addStrategy("new rpg", false);
}
else if (sPlayerbotAIConfig->autoDoQuests)
{
// nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg", false);
}
else
{
nonCombatEngine->addStrategy("move random", false);
}
if (sPlayerbotAIConfig->randomBotJoinBG)
nonCombatEngine->addStrategy("bg", false);
@@ -648,9 +706,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// {
// // nonCombatEngine->addStrategy("travel");
// nonCombatEngine->addStrategy("rpg");
// }
// else
// {
// } else {
// nonCombatEngine->addStrategy("move random");
// }
@@ -669,8 +725,11 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
}
}
else
{
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
// nonCombatEngine->addStrategy("battleground");
// nonCombatEngine->addStrategy("warsong");
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
@@ -727,7 +786,9 @@ void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const faca
deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr);
if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup())
{
deadEngine->removeStrategy("follow", false);
}
}
Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext)

View File

@@ -1,510 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FishingAction.h"
#include "FishValues.h"
#include "Event.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "ItemPackets.h"
#include "LastMovementValue.h"
#include "Map.h"
#include "MovementActions.h"
#include "Object.h"
#include "PlayerbotAI.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "Position.h"
uint32 const FISHING_SPELL = 7620;
uint32 const FISHING_POLE = 6256;
uint32 const FISHING_BOBBER = 35591;
float const MIN_DISTANCE_TO_WATER = 10.0f; // Minimum spell distance
float const MAX_DISTANCE_TO_WATER = 20.0f; // Maximum spell distance
float const HEIGHT_ABOVE_WATER_TOLERANCE = 1.0f; // Can stand in up to 1 unit of water and still fish.
float const SEARCH_INCREMENT = 2.5f;
float const HEIGHT_SEARCH_BUFFER = 10.0f; // Height buffer to prevent potentially missing the model the bot is standing on.
float const SEARCH_LAND_BUFFER = 0.5f;
uint32 const FISHING_LOCATION_TIMEOUT = 180000; //Three minutes
static bool IsFishingPole(Item* const item)
{
if (!item)
return false;
const ItemTemplate* proto = item->GetTemplate();
return proto && proto->Class == ITEM_CLASS_WEAPON &&
proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE;
}
float HasFishableWaterOrLand(float x, float y, float z, Map* map, uint32 phaseMask, bool checkForLand=false)
{
if (!map)
return INVALID_HEIGHT;
LiquidData const& liq = map->GetLiquidData(phaseMask, x, y, z+HEIGHT_ABOVE_WATER_TOLERANCE, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
float ground = map->GetHeight(phaseMask, x, y, z + HEIGHT_SEARCH_BUFFER, true);
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER)
{
if (checkForLand)
return ground;
return INVALID_HEIGHT;
}
if (checkForLand)
{
if (ground > liq.Level - HEIGHT_ABOVE_WATER_TOLERANCE)
return ground;
return INVALID_HEIGHT;
}
if (liq.Level + HEIGHT_ABOVE_WATER_TOLERANCE > ground)
{
if (abs(liq.DepthLevel) < 0.5f) // too shallow to fish in.
return INVALID_HEIGHT;
return liq.Level;
}
return INVALID_HEIGHT;
}
bool HasLosToWater(Player* bot, float wx, float wy, float waterZ)
{
float z = bot->GetCollisionHeight() + bot->GetPositionZ();
return bot->GetMap()->isInLineOfSight(
bot->GetPositionX(), bot->GetPositionY(), z,
wx, wy, waterZ,
bot->GetPhaseMask(),
LINEOFSIGHT_ALL_CHECKS,
VMAP::ModelIgnoreFlags::Nothing);
}
WorldPosition FindLandFromPosition(PlayerbotAI* botAI, float startDistance, float endDistance, float increment, float orientation, WorldPosition targetPos, float fishingSearchWindow, bool checkLOS = true)
{
Player* bot = botAI->GetBot();
Map* map = bot->GetMap();
uint32 phaseMask = bot->GetPhaseMask();
Player* master = botAI->GetMaster();
float targetX = targetPos.GetPositionX();
float targetY = targetPos.GetPositionY();
float targetZ = targetPos.GetPositionZ();
for (float dist = startDistance; dist <= endDistance; dist += increment)
{
//step backwards from position to bot to find edge of shore.
float checkX = targetX - dist * cos(orientation);
float checkY = targetY - dist * sin(orientation);
float groundZ = map->GetHeight(phaseMask, checkX, checkY, targetZ + HEIGHT_SEARCH_BUFFER, true);
if (groundZ == INVALID_HEIGHT)
continue;
LiquidData const& liq = map->GetLiquidData(phaseMask, checkX, checkY, targetZ, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER || groundZ > liq.DepthLevel + HEIGHT_ABOVE_WATER_TOLERANCE)
{
if (checkLOS)
{
bool hasLOS = map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
if (!hasLOS)
continue;
}
// Add a distance check for the position to prevent the bot from moving out of range to the master.
if (master && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
continue;
return WorldPosition(bot->GetMapId(), checkX, checkY, groundZ);
}
}
return WorldPosition();
}
WorldPosition FindLandRadialFromPosition (PlayerbotAI* botAI, WorldPosition targetPos, float startDistance, float endDistance, float increment, float fishingSearchWindow, int angles = 16)
{
Player* bot = botAI->GetBot();
const int numDirections = angles;
std::vector<WorldPosition> boundaryPoints;
Player* master = botAI->GetMaster();
if (!master)
return WorldPosition();
Map* map = bot->GetMap();
uint32 phaseMask = bot->GetPhaseMask();
float targetX = targetPos.GetPositionX();
float targetY = targetPos.GetPositionY();
float targetZ = targetPos.GetPositionZ();
for (float dist = startDistance; dist <= endDistance; dist += increment)
{
for (int i = 0; i < numDirections; ++i)
{
float angle = (2.0f * M_PI * i) / numDirections;
float checkX = targetX - cos(angle) * dist;
float checkY = targetY - sin(angle) * dist;
float groundZ = HasFishableWaterOrLand(checkX, checkY, targetZ, map, phaseMask, true);
if (groundZ == INVALID_HEIGHT)
continue;
if (map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
continue;
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, groundZ));
}
if (!boundaryPoints.empty())
break;
}
if (boundaryPoints.empty())
return WorldPosition();
if (boundaryPoints.size() == 1)
return boundaryPoints[0];
float minDistance = FLT_MAX;
WorldLocation closestPoint = WorldPosition();
for (auto const& pos : boundaryPoints)
{
float distance = bot->GetExactDist2d(&pos);
if (distance < minDistance)
{
minDistance = distance;
closestPoint = pos;
}
}
return closestPoint;
}
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS, int numDirections)
{
std::vector<WorldPosition> boundaryPoints;
float dist = minDistance;
while (dist <= maxDistance)
{
for (int i = 0; i < numDirections; ++i)
{
float angle = (2.0f * M_PI * i) / numDirections;
float checkX = x + cos(angle) * dist;
float checkY = y + sin(angle) * dist;
float waterZ = HasFishableWaterOrLand(checkX, checkY, z, map, phaseMask);
if (waterZ == INVALID_HEIGHT)
continue;
if (checkLOS && !HasLosToWater(bot, checkX, checkY, waterZ))
continue;
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, waterZ));
}
if (!boundaryPoints.empty())
break;
dist += increment;
}
if (boundaryPoints.empty())
return WorldPosition();
if (boundaryPoints.size() == 1)
return boundaryPoints[0];
// return the central point in the identified positions in to try to be perpendicular to the shore.
return boundaryPoints[boundaryPoints.size() / 2];
}
WorldPosition FindFishingHole(PlayerbotAI* botAI)
{
Player* player = botAI->GetBot();
GuidVector gos = PAI_VALUE(GuidVector, "nearest game objects no los");
GameObject* nearestFishingHole = nullptr;
float minDist = std::numeric_limits<float>::max();
for (auto const& guid : gos)
{
GameObject* go = botAI->GetGameObject(guid);
if (!go)
continue;
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
{
float dist = player->GetDistance2d(go);
if (dist < minDist)
{
minDist = dist;
nearestFishingHole = go;
}
}
}
if (nearestFishingHole)
return WorldPosition(nearestFishingHole->GetMapId(), nearestFishingHole->GetPositionX(), nearestFishingHole->GetPositionY(), nearestFishingHole->GetPositionZ());
return WorldPosition();
}
bool MoveNearWaterAction::Execute(Event event)
{
WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot");
if (landSpot.IsValid())
return MoveTo(landSpot.GetMapId(), landSpot.GetPositionX(), landSpot.GetPositionY(), landSpot.GetPositionZ());
return false;
}
bool MoveNearWaterAction::isUseful()
{
if (!AI_VALUE(bool, "can fish"))
return false;
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) ||
bot->GetExactDist(&pos) < 0.1f;
}
bool MoveNearWaterAction::isPossible()
{
Player* master = botAI->GetMaster();
float fishingSearchWindow;
if (master)
fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster;
else
fishingSearchWindow = sPlayerbotAIConfig->fishingDistance;
WorldPosition fishingHole = FindFishingHole(botAI);
if (fishingHole.IsValid())
{
float distance = bot->GetExactDist2d(&fishingHole);
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
// Water spot is in range, and we have LOS to it. Set bot position to fishing spot and do not move
if (distance >= MIN_DISTANCE_TO_WATER &&
distance <= MAX_DISTANCE_TO_WATER && hasLOS)
{
SET_AI_VALUE(WorldPosition, "fishing spot", WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())));
return false;
}
// Water spot is out of range, lets look for a spot to move to for the fishing hole.
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
{
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
if (landSpot.IsValid())
{
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
return true;
}
}
}
// Can the bot fish from current position?
WorldPosition waterAtCurrentPos =
FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMap(),
bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true);
if (waterAtCurrentPos.IsValid())
{
SET_AI_VALUE(WorldPosition, "fishing spot",
WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ())));
return false;
}
// Lets find some water where we can fish.
WorldPosition water = FindWaterRadial(
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
bot->GetMap(), bot->GetPhaseMask(),
MIN_DISTANCE_TO_WATER,
fishingSearchWindow + MAX_DISTANCE_TO_WATER,
SEARCH_INCREMENT, false);
if (!water.IsValid())
return false;
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
WorldPosition landSpot =
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);
if (landSpot.IsValid())
{
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
return true;
}
return false;
}
bool EquipFishingPoleAction::Execute(Event event)
{
if (!_pole)
return false;
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
eqPacket << _pole->GetGUID() << uint8(EQUIPMENT_SLOT_MAINHAND);
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
nicePacket.Read();
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
return true;
}
bool EquipFishingPoleAction::isUseful()
{
Item* mainHand = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
if (IsFishingPole(mainHand))
return false;
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
{
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
if (IsFishingPole(item))
{
_pole = item;
return true;
}
}
}
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag* pBag = bot->GetBagByPos(bag))
{
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
{
if (Item* item = pBag->GetItemByPos(j))
{
if (IsFishingPole(item))
{
_pole = item;
return true;
}
}
}
}
}
if (sRandomPlayerbotMgr->IsRandomBot(bot))
{
bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole
return true;
}
Player* master = botAI->GetMaster();
if (!master)
return false;
std::string masterName = master->GetName();
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
"no_fishing_pole_error", "I don't have a Fishing Pole",{});
botAI->Whisper(text, masterName);
return false;
}
bool FishingAction::Execute(Event event)
{
WorldPosition target = WorldPosition();
WorldPosition fishingHole = FindFishingHole(botAI);
if (fishingHole.IsValid())
{
Position pos = fishingHole;
float distance = bot->GetExactDist2d(&pos);
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
if (distance < MAX_DISTANCE_TO_WATER &&
distance > MIN_DISTANCE_TO_WATER && hasLOS)
target = fishingHole;
}
if (!target.IsValid())
{
target = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), bot->GetMap(), bot->GetPhaseMask(),
MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true, 32);
if (!target.IsValid())
return false;
}
Position pos = target;
if (!bot->HasInArc(1.0, &pos, 1.0))
{
float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY());
bot->SetOrientation(angle);
if (!bot->IsRooted())
bot->SendMovementFlagUpdate();
}
EquipFishingPoleAction equipAction(botAI);
if (equipAction.isUseful())
return equipAction.Execute(event);
botAI->CastSpell(FISHING_SPELL, bot);
botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT);
return true;
}
bool FishingAction::isUseful()
{
if (!AI_VALUE(bool, "can fish"))
return false;
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
if (!pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT))
return false;
return bot->GetExactDist(&pos) < 0.1f;
}
bool UseBobberAction::isUseful()
{
return AI_VALUE(bool, "can use fishing bobber");
}
bool UseBobberAction::Execute(Event event)
{
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
for (auto const& guid : gos)
{
if (GameObject* go = botAI->GetGameObject(guid))
{
if (go->GetEntry() != FISHING_BOBBER)
continue;
if (go->GetOwnerGUID() != bot->GetGUID())
continue;
if (go->getLootState() == GO_READY)
{
go->Use(bot);
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
return true;
}
}
}
return false;
}
bool EndMasterFishingAction::Execute(Event event)
{
botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT);
return true;
}
bool EndMasterFishingAction::isUseful()
{
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get();
if (pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition())
return false;
WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f);
return !nearWater.IsValid();
}
bool RemoveBobberStrategyAction::Execute(Event event)
{
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
return true;
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FISHINGACTION_H
#define _PLAYERBOT_FISHINGACTION_H
#include "Action.h"
#include "MovementActions.h"
#include "Event.h"
#include "Playerbots.h"
extern const uint32 FISHING_SPELL;
extern const uint32 FISHING_POLE;
extern const uint32 FISHING_BOBBER;
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
class PlayerbotAI;
class FishingAction : public Action
{
public:
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
bool Execute(Event event) override;
bool isUseful() override;
};
class EquipFishingPoleAction : public Action
{
public:
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
bool Execute(Event event) override;
bool isUseful() override;
private:
Item* _pole = nullptr;
};
class MoveNearWaterAction : public MovementAction
{
public:
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
};
class UseBobberAction : public Action
{
public:
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
bool Execute(Event event) override;
bool isUseful() override;
};
class EndMasterFishingAction : public Action
{
public:
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
bool Execute(Event event) override;
bool isUseful() override;
};
class RemoveBobberStrategyAction : public Action
{
public:
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FISHING_TRIGGER_H
#define _PLAYERBOT_FISHING_TRIGGER_H
#include "GenericTriggers.h"
class CanFishTrigger : public Trigger
{
public:
CanFishTrigger(PlayerbotAI* ai) : Trigger(ai, "can fish") {};
bool IsActive() override;
};
class CanUseFishingBobberTrigger : public Trigger
{
public:
CanUseFishingBobberTrigger(PlayerbotAI* ai) : Trigger(ai, "can use fishing bobber") {};
bool IsActive() override;
};
#endif

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RtiTriggers.h"
#include "Playerbots.h"
bool NoRtiTrigger::IsActive()
{
// Do not auto-react to raid icons while out of combat.
// Out-of-combat RTI usage (explicit chat commands) is handled by chat triggers,
// not by this generic trigger.
if (!bot->IsInCombat())
return false;
Unit* target = AI_VALUE(Unit*, "rti target");
return target != nullptr;
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FishValues.h"
#include "PlayerbotAI.h"
#include "RandomPlayerbotMgr.h"
#include "Map.h"
#include "Spell.h"
#include "FishingAction.h"
bool CanFishValue::Calculate()
{
int32 SkillFishing = bot->GetSkillValue(SKILL_FISHING);
if (SkillFishing == 0)
return false;
if (bot->isSwimming())
return false;
if (bot->IsInCombat())
return false;
return true;
}
bool CanUseFishingBobberValue::Calculate()
{
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
for (auto const& guid : gos)
{
if (GameObject* go = botAI->GetGameObject(guid))
{
if (go->GetEntry() != FISHING_BOBBER)
continue;
if (go->GetOwnerGUID() != bot->GetGUID())
continue;
if (go->getLootState() == GO_READY)
return true;
// Not ready yet → delay next check
time_t bobberActiveTime = go->GetRespawnTime() - FISHING_BOBBER_READY_TIME;
if (bobberActiveTime > time(0))
botAI->SetNextCheckDelay((bobberActiveTime - time(0)) * IN_MILLISECONDS + 500);
else
botAI->SetNextCheckDelay(1000);
return false;
}
}
return false;
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FISHVALUES_H
#define _PLAYERBOT_FISHVALUES_H
#include "Value.h"
#include "TravelMgr.h"
#include "NamedObjectContext.h"
class PlayerbotAI;
class CanFishValue : public BoolCalculatedValue
{
public:
CanFishValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fish") {};
bool Calculate() override;
};
class CanUseFishingBobberValue : public BoolCalculatedValue
{
public:
CanUseFishingBobberValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can use fishing bobber") {};
bool Calculate() override;
};
class FishingSpotValue : public ManualSetValue<WorldPosition>
{
public:
FishingSpotValue(PlayerbotAI* botAI, WorldPosition const& pos = WorldPosition(), std::string const& name = "fishing spot")
: ManualSetValue<WorldPosition>(botAI, pos, name) {}
void Set(WorldPosition val) override
{
value = val;
_setTime = getMSTime();
}
uint32 lastUpdated() const {return _setTime;}
bool IsStale(uint32 maxDuration) const { return getMSTime() - _setTime > maxDuration; }
private:
uint32 _setTime = 0;
};
#endif

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Action.h"
#include "Playerbots.h"
#include "Timer.h"
Value<Unit*>* Action::GetTargetValue() { return context->GetValue<Unit*>(GetTargetName()); }
Unit* Action::GetTarget() { return GetTargetValue()->Get(); }
ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event)
: action(action), relevance(relevance), skipPrerequisites(skipPrerequisites), event(event), created(getMSTime())
{
}
bool ActionBasket::isExpired(uint32_t msecs) { return getMSTime() - created >= msecs; }

View File

@@ -1,145 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Strategy.h"
#include "Playerbots.h"
class ActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
{
public:
ActionNodeFactoryInternal()
{
creators["melee"] = &melee;
creators["healthstone"] = &healthstone;
creators["be near"] = &follow_master_random;
creators["attack anything"] = &attack_anything;
creators["move random"] = &move_random;
creators["move to loot"] = &move_to_loot;
creators["food"] = &food;
creators["drink"] = &drink;
creators["mana potion"] = &mana_potion;
creators["healing potion"] = &healing_potion;
creators["flee"] = &flee;
}
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healthstone",
/*P*/ {},
/*A*/ { NextAction("healing potion") },
/*C*/ {}
);
}
static ActionNode* follow_master_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"be near",
/*P*/ {},
/*A*/ { NextAction("follow") },
/*C*/ {}
);
}
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"attack anything",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* move_random([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move random",
/*P*/ {},
/*A*/ { NextAction("stay line") },
/*C*/ {}
);
}
static ActionNode* move_to_loot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"move to loot",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"food",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"drink",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mana potion",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing potion",
/*P*/ {},
/*A*/ { NextAction("food") },
/*C*/ {}
);
}
static ActionNode* flee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"flee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
};
Strategy::Strategy(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
actionNodeFactories.Add(new ActionNodeFactoryInternal());
}
ActionNode* Strategy::GetAction(std::string const name) { return actionNodeFactories.GetContextObject(name, botAI); }

View File

@@ -70,7 +70,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s
return false;
}
for (auto const& pair : toChannels)
for (const auto& pair : toChannels)
{
uint32 roll = urand(1, 100);
uint32 chance = pair.second;
@@ -166,7 +166,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s
return false;
}
bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTemplate const* proto)
bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, const ItemTemplate *proto)
{
if (!sPlayerbotAIConfig->enableBroadcasts)
return false;
@@ -410,6 +410,7 @@ bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot,
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_complete", placeholders),

View File

@@ -373,7 +373,7 @@ private:
bool ParseSpecPrefix(const std::string& message, std::string& specPrefix, std::string& rest)
{
std::string msgLower = ToLower(message);
for (auto const& entry : specTabNames)
for (const auto& entry : specTabNames)
{
std::string prefix = "@" + entry.second;
if (msgLower.find(ToLower(prefix)) == 0)
@@ -555,7 +555,7 @@ public:
const float radius = 100.0f;
GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool match = false;
for (auto const& guid : npcs)
for (const auto& guid : npcs)
{
Creature* c = botAI->GetCreature(guid);
if (!c)

View File

@@ -329,11 +329,9 @@ ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const
while (currentPos < text.length()) {
size_t nextColon = text.find(':', currentPos);
if (nextColon == std::string::npos)
{
if (nextColon == std::string::npos) {
size_t hTag = text.find("|h", currentPos);
if (hTag != std::string::npos)
{
if (hTag != std::string::npos) {
params.push_back(text.substr(currentPos, hTag - currentPos));
}
break;
@@ -343,8 +341,7 @@ ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const
currentPos = nextColon + 1;
}
if (params.size() >= 6)
{
if (params.size() >= 6) {
res.randomPropertyId = atoi(params[5].c_str());
}

View File

@@ -1,46 +0,0 @@
#include "PlayerbotSpellRepository.h"
// caches the result set
void PlayerbotSpellRepository::Initialize()
{
LOG_INFO("playerbots", "Playerbots: ListSpellsAction caches initialized");
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
if (SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j))
skillSpells[skillLine->Spell] = skillLine;
}
// Fill the vendorItems cache once from the world database.
QueryResult results = WorldDatabase.Query("SELECT item FROM npc_vendor WHERE maxcount = 0");
if (results)
{
do
{
Field* fields = results->Fetch();
int32 entry = fields[0].Get<int32>();
if (entry <= 0)
continue;
vendorItems.insert(static_cast<uint32>(entry));
}
while (results->NextRow());
}
LOG_DEBUG("playerbots",
"ListSpellsAction: initialized caches (skillSpells={}, vendorItems={}).",
skillSpells.size(), vendorItems.size());
}
SkillLineAbilityEntry const* PlayerbotSpellRepository::GetSkillLine(uint32 spellId) const
{
auto itr = skillSpells.find(spellId);
if (itr != skillSpells.end())
return itr->second;
return nullptr;
}
bool PlayerbotSpellRepository::IsItemBuyable(uint32 itemId) const
{
return vendorItems.find(itemId) != vendorItems.end();
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H
#define _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H
#include "Playerbots.h"
class PlayerbotSpellRepository
{
public:
static PlayerbotSpellRepository* Instance()
{
static PlayerbotSpellRepository instance;
return &instance;
}
void Initialize(); // call once on startup
SkillLineAbilityEntry const* GetSkillLine(uint32 spellId) const;
bool IsItemBuyable(uint32 itemId) const;
private:
PlayerbotSpellRepository() = default;
std::map<uint32, SkillLineAbilityEntry const*> skillSpells;
std::set<uint32> vendorItems;
};
#define sPlayerbotSpellRepository PlayerbotSpellRepository::Instance()
#endif

View File

@@ -1067,7 +1067,7 @@ void GuildTaskMgr::SendCompletionMessage(Player* player, std::string const verb)
void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim)
{
ObjectGuid::LowType owner = player->GetGUID().GetCounter();
if (!victim->IsCreature())
if (victim->GetTypeId() != TYPEID_UNIT)
return;
Creature* creature = reinterpret_cast<Creature*>(victim);

50
src/Helpers.cpp Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Helpers.h"
char* strstri(char const* haystack, char const* needle)
{
if (!*needle)
{
return (char*)haystack;
}
for (; *haystack; ++haystack)
{
if (tolower(*haystack) == tolower(*needle))
{
char const *h = haystack, *n = needle;
for (; *h && *n; ++h, ++n)
{
if (tolower(*h) != tolower(*n))
{
break;
}
}
if (!*n)
{
return (char*)haystack;
}
}
}
return 0;
}
std::string& ltrim(std::string& s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) { return !std::isspace(c); }));
return s;
}
std::string& rtrim(std::string& s)
{
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) { return !std::isspace(c); }).base(), s.end());
return s;
}
std::string& trim(std::string& s) { return ltrim(rtrim(s)); }

55
src/Helpers.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_HELPERS_H
#define _PLAYERBOT_HELPERS_H
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cctype>
#include <functional>
#include <locale>
#include <map>
#include <sstream>
#include <vector>
#include "Common.h"
void split(std::vector<std::string>& dest, std::string const str, char const* delim)
{
char* pTempStr = strdup(str.c_str());
char* pWord = strtok(pTempStr, delim);
while (pWord != nullptr)
{
dest.push_back(pWord);
pWord = strtok(nullptr, delim);
}
free(pTempStr);
}
std::vector<std::string>& split(std::string const s, char delim, std::vector<std::string>& elems)
{
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(std::string const s, char delim)
{
std::vector<std::string> elems;
return split(s, delim, elems);
}
#endif

39
src/LazyCalculatedValue.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_LAZYCALCULATEDVALUE_H
#define _PLAYERBOT_LAZYCALCULATEDVALUE_H
template <class TValue, class TOwner>
class LazyCalculatedValue
{
public:
typedef TValue (TOwner::*Calculator)();
public:
LazyCalculatedValue(TOwner* owner, Calculator calculator) : calculator(calculator), owner(owner) { Reset(); }
public:
TValue GetValue()
{
if (!calculated)
{
value = (owner->*calculator)();
calculated = true;
}
return value;
}
void Reset() { calculated = false; }
protected:
Calculator calculator;
TOwner* owner;
bool calculated;
TValue value;
};
#endif

View File

@@ -1,322 +0,0 @@
#include "PlayerbotGuildMgr.h"
#include "Player.h"
#include "PlayerbotAIConfig.h"
#include "DatabaseEnv.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
PlayerbotGuildMgr::PlayerbotGuildMgr(){}
void PlayerbotGuildMgr::Init()
{
_guildCache.clear();
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
DeleteBotGuilds();
LoadGuildNames();
ValidateGuildCache();
}
bool PlayerbotGuildMgr::CreateGuild(Player* player, std::string guildName)
{
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
{
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName,
player->GetName());
delete guild;
return false;
}
sGuildMgr->AddGuild(guild);
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName);
SetGuildEmblem(guild->GetId());
GuildCache entry;
entry.name = guildName;
entry.memberCount = 1;
entry.status = 1;
entry.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
entry.faction = player->GetTeamId();
_guildCache[guild->GetId()] = entry;
return true;
}
bool PlayerbotGuildMgr::SetGuildEmblem(uint32 guildId)
{
Guild* guild = sGuildMgr->GetGuildById(guildId);
if (!guild)
return false;
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
bc = urand(0, 17);
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
LOG_DEBUG("playerbots",
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), st, cl, br, bc, bg);
// populate guild table with a random tabard design
CharacterDatabase.Execute(
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
"WHERE guildid={}",
st, cl, br, bc, bg, guild->GetId());
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
// Immediate reading for log
if (QueryResult qr = CharacterDatabase.Query(
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
guild->GetId()))
{
Field* f = qr->Fetch();
LOG_DEBUG("playerbots",
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
}
return true;
}
std::string PlayerbotGuildMgr::AssignToGuild(Player* player)
{
if (!player)
return "";
uint8_t playerFaction = player->GetTeamId();
std::vector<GuildCache*> partiallyfilledguilds;
partiallyfilledguilds.reserve(_guildCache.size());
for (auto& keyValue : _guildCache)
{
GuildCache& cached = keyValue.second;
if (!cached.hasRealPlayer && cached.status == 1 && cached.faction == playerFaction)
partiallyfilledguilds.push_back(&cached);
}
if (!partiallyfilledguilds.empty())
{
size_t idx = static_cast<size_t>(urand(0, static_cast<int>(partiallyfilledguilds.size()) - 1));
return (partiallyfilledguilds[idx]->name);
}
size_t count = std::count_if(
_guildCache.begin(), _guildCache.end(),
[](const std::pair<const uint32, GuildCache>& pair)
{
return !pair.second.hasRealPlayer;
}
);
if (count < sPlayerbotAIConfig->randomBotGuildCount)
{
for (auto& key : _shuffled_guild_keys)
{
if (_guildNames[key])
{
LOG_INFO("playerbots","Assigning player [{}] to guild [{}]", player->GetName(), key);
return key;
}
}
LOG_ERROR("playerbots","No available guild names left.");
}
return "";
}
void PlayerbotGuildMgr::OnGuildUpdate(Guild* guild)
{
auto it = _guildCache.find(guild->GetId());
if (it == _guildCache.end())
return;
GuildCache& entry = it->second;
entry.memberCount = guild->GetMemberCount();
if (entry.memberCount < entry.maxMembers)
entry.status = 1;
else if (entry.memberCount >= entry.maxMembers)
entry.status = 2; // Full
std::string guildName = guild->GetName();
for (auto& it : _guildNames)
{
if (it.first == guildName)
{
it.second = false;
break;
}
}
}
void PlayerbotGuildMgr::ResetGuildCache()
{
for (auto it = _guildCache.begin(); it != _guildCache.end();)
{
GuildCache& cached = it->second;
cached.memberCount = 0;
cached.faction = 2;
cached.status = 0;
}
}
void PlayerbotGuildMgr::LoadGuildNames()
{
LOG_INFO("playerbots", "Loading guild names from playerbots_guild_names...");
QueryResult result = CharacterDatabase.Query("SELECT name_id, name FROM playerbots_guild_names");
if (!result)
{
LOG_ERROR("playerbots", "No entries found in playerbots_guild_names. List is empty.");
return;
}
do
{
Field* fields = result->Fetch();
_guildNames[fields[1].Get<std::string>()] = true;
} while (result->NextRow());
for (auto& pair : _guildNames)
_shuffled_guild_keys.push_back(pair.first);
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(_shuffled_guild_keys.begin(), _shuffled_guild_keys.end(), g);
LOG_INFO("playerbots", "Loaded {} guild entries from playerbots_guild_names table.", _guildNames.size());
}
void PlayerbotGuildMgr::ValidateGuildCache()
{
QueryResult result = CharacterDatabase.Query("SELECT guildid, name FROM guild");
if (!result)
{
LOG_ERROR("playerbots", "No guilds found in database, resetting guild cache");
ResetGuildCache();
return;
}
std::unordered_map<uint32, std::string> dbGuilds;
do
{
Field* fields = result->Fetch();
uint32 guildId = fields[0].Get<uint32>();
std::string guildName = fields[1].Get<std::string>();
dbGuilds[guildId] = guildName;
} while (result->NextRow());
for (auto it = dbGuilds.begin(); it != dbGuilds.end(); it++)
{
uint32 guildId = it->first;
GuildCache cache;
cache.name = it->second;
cache.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
Guild* guild = sGuildMgr ->GetGuildById(guildId);
if (!guild)
continue;
cache.memberCount = guild->GetMemberCount();
ObjectGuid leaderGuid = guild->GetLeaderGUID();
CharacterCacheEntry const* leaderEntry = sCharacterCache->GetCharacterCacheByGuid(leaderGuid);
uint32 leaderAccount = leaderEntry->AccountId;
cache.hasRealPlayer = !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
cache.faction = Player::TeamIdForRace(leaderEntry->Race);
if (cache.memberCount == 0)
cache.status = 0; // empty
else if (cache.memberCount < cache.maxMembers)
cache.status = 1; // partially filled
else
cache.status = 2; // full
_guildCache.insert_or_assign(guildId, cache);
for (auto& it : _guildNames)
{
if (it.first == cache.name)
{
it.second = false;
break;
}
}
}
}
void PlayerbotGuildMgr::DeleteBotGuilds()
{
LOG_INFO("playerbots", "Deleting random bot guilds...");
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
} while (result->NextRow());
}
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
guild->Disband();
}
LOG_INFO("playerbots", "Random bot guilds deleted");
}
bool PlayerbotGuildMgr::IsRealGuild(Player* bot)
{
if (!bot)
return false;
uint32 guildId = bot->GetGuildId();
if (!guildId)
return false;
return IsRealGuild(guildId);
}
bool PlayerbotGuildMgr::IsRealGuild(uint32 guildId)
{
if (!guildId)
return false;
auto it = _guildCache.find(guildId);
if (it == _guildCache.end())
return false;
return it->second.hasRealPlayer;
}
class BotGuildCacheWorldScript : public WorldScript
{
public:
BotGuildCacheWorldScript() : WorldScript("BotGuildCacheWorldScript"), _validateTimer(0){}
void OnUpdate(uint32 diff) override
{
_validateTimer += diff;
if (_validateTimer >= _validateInterval) // Validate every hour
{
_validateTimer = 0;
sPlayerbotGuildMgr->ValidateGuildCache();
LOG_INFO("playerbots", "Scheduled guild cache validation");
}
}
private:
uint32 _validateInterval = HOUR*IN_MILLISECONDS;
uint32 _validateTimer;
};
void PlayerBotsGuildValidationScript()
{
new BotGuildCacheWorldScript();
}

View File

@@ -1,52 +0,0 @@
#ifndef _PLAYERBOT_PLAYERBOTGUILDMGR_H
#define _PLAYERBOT_PLAYERBOTGUILDMGR_H
#include "Guild.h"
#include "Player.h"
#include "PlayerbotAI.h"
class PlayerbotAI;
class PlayerbotGuildMgr
{
public:
static PlayerbotGuildMgr* instance()
{
static PlayerbotGuildMgr instance;
return &instance;
}
void Init();
std::string AssignToGuild(Player* player);
void LoadGuildNames();
void ValidateGuildCache();
void ResetGuildCache();
bool CreateGuild(Player* player, std::string guildName);
void OnGuildUpdate (Guild* guild);
bool SetGuildEmblem(uint32 guildId);
void DeleteBotGuilds();
bool IsRealGuild(uint32 guildId);
bool IsRealGuild(Player* bot);
private:
PlayerbotGuildMgr();
std::unordered_map<std::string, bool> _guildNames;
struct GuildCache
{
std::string name;
uint8 status;
uint32 maxMembers = 0;
uint32 memberCount = 0;
uint8 faction = 0;
bool hasRealPlayer = false;
};
std::unordered_map<uint32 , GuildCache> _guildCache;
std::vector<std::string> _shuffled_guild_keys;
};
void PlayerBotsGuildValidationScript();
#define sPlayerbotGuildMgr PlayerbotGuildMgr::instance()
#endif

View File

@@ -3,11 +3,11 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PerfMonitor.h"
#include "PerformanceMonitor.h"
#include "Playerbots.h"
PerfMonitorOperation* PerfMonitor::start(PerformanceMetric metric, std::string const name,
PerformanceMonitorOperation* PerformanceMonitor::start(PerformanceMetric metric, std::string const name,
PerformanceStack* stack)
{
if (!sPlayerbotAIConfig->perfMonEnabled)
@@ -45,10 +45,10 @@ PerfMonitorOperation* PerfMonitor::start(PerformanceMetric metric, std::string c
data[metric][stackName] = pd;
}
return new PerfMonitorOperation(pd, name, stack);
return new PerformanceMonitorOperation(pd, name, stack);
}
void PerfMonitor::PrintStats(bool perTick, bool fullStack)
void PerformanceMonitor::PrintStats(bool perTick, bool fullStack)
{
if (data.empty())
return;
@@ -247,7 +247,7 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack)
}
}
void PerfMonitor::Reset()
void PerformanceMonitor::Reset()
{
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin();
i != data.end(); ++i)
@@ -265,7 +265,7 @@ void PerfMonitor::Reset()
}
}
PerfMonitorOperation::PerfMonitorOperation(PerformanceData* data, std::string const name,
PerformanceMonitorOperation::PerformanceMonitorOperation(PerformanceData* data, std::string const name,
PerformanceStack* stack)
: data(data), name(name), stack(stack)
{
@@ -273,7 +273,7 @@ PerfMonitorOperation::PerfMonitorOperation(PerformanceData* data, std::string co
.time_since_epoch();
}
void PerfMonitorOperation::finish()
void PerformanceMonitorOperation::finish()
{
std::chrono::microseconds finished =
(std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()))

View File

@@ -34,10 +34,10 @@ enum PerformanceMetric
PERF_MON_TOTAL
};
class PerfMonitorOperation
class PerformanceMonitorOperation
{
public:
PerfMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
PerformanceMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
void finish();
private:
@@ -47,19 +47,19 @@ private:
std::chrono::microseconds started;
};
class PerfMonitor
class PerformanceMonitor
{
public:
PerfMonitor(){};
virtual ~PerfMonitor(){};
static PerfMonitor* instance()
PerformanceMonitor(){};
virtual ~PerformanceMonitor(){};
static PerformanceMonitor* instance()
{
static PerfMonitor instance;
static PerformanceMonitor instance;
return &instance;
}
public:
PerfMonitorOperation* start(PerformanceMetric metric, std::string const name,
PerformanceMonitorOperation* start(PerformanceMetric metric, std::string const name,
PerformanceStack* stack = nullptr);
void PrintStats(bool perTick = false, bool fullStack = false);
void Reset();
@@ -69,6 +69,6 @@ private:
std::mutex lock;
};
#define sPerfMonitor PerfMonitor::instance()
#define sPerformanceMonitor PerformanceMonitor::instance()
#endif

View File

@@ -10,7 +10,7 @@
#include "Common.h"
#include "Player.h"
#include "PlayerbotDungeonRepository.h"
#include "PlayerbotDungeonSuggestionMgr.h"
typedef std::map<std::string, std::string> PlaceholderMap;

File diff suppressed because it is too large Load Diff

View File

@@ -276,7 +276,7 @@ enum BotRoles : uint8
enum HUNTER_TABS
{
HUNTER_TAB_BEAST_MASTERY,
HUNTER_TAB_BEASTMASTERY,
HUNTER_TAB_MARKSMANSHIP,
HUNTER_TAB_SURVIVAL,
};
@@ -295,11 +295,11 @@ enum PRIEST_TABS
PRIEST_TAB_SHADOW,
};
enum DEATH_KNIGHT_TABS
enum DEATHKNIGHT_TABS
{
DEATH_KNIGHT_TAB_BLOOD,
DEATH_KNIGHT_TAB_FROST,
DEATH_KNIGHT_TAB_UNHOLY,
DEATHKNIGHT_TAB_BLOOD,
DEATHKNIGHT_TAB_FROST,
DEATHKNIGHT_TAB_UNHOLY,
};
enum DRUID_TABS
@@ -428,7 +428,7 @@ public:
static bool IsMainTank(Player* player);
static uint32 GetGroupTankNum(Player* player);
static bool IsAssistTank(Player* player);
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
static bool IsAssistTankOfIndex(Player* player, int index);
static bool IsHealAssistantOfIndex(Player* player, int index);
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
bool HasAggro(Unit* unit);
@@ -529,7 +529,6 @@ public:
Player* GetBot() { return bot; }
Player* GetMaster() { return master; }
Player* FindNewMaster();
// Checks if the bot is really a player. Players always have themselves as master.
bool IsRealPlayer() { return master ? (master == bot) : false; }
@@ -540,7 +539,7 @@ public:
// Get the group leader or the master of the bot.
// Checks if the bot is summoned as alt of a player
bool IsAlt();
Player* GetGroupLeader();
Player* GetGroupMaster();
// Returns a semi-random (cycling) number that is fixed for each bot.
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
GrouperType GetGrouperType();
@@ -579,6 +578,7 @@ 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);
@@ -611,20 +611,12 @@ 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 UpdateAIGroupMaster();
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;
inline bool IsValidUnit(const Unit* unit) const
{
return unit && unit->IsInWorld() && !unit->IsDuringRemoveFromWorld();
}
inline bool IsValidPlayer(const Player* player) const
{
return player && player->GetSession() && player->IsInWorld() && !player->IsDuringRemoveFromWorld() &&
!player->IsBeingTeleported();
}
protected:
Player* bot;
Player* master;

View File

@@ -14,7 +14,7 @@ void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal)
if (totalPmo)
totalPmo->finish();
totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick");
totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick");
if (nextAICheckDelay > elapsed)
nextAICheckDelay -= elapsed;

View File

@@ -25,7 +25,7 @@ public:
protected:
uint32 nextAICheckDelay;
class PerfMonitorOperation* totalPmo = nullptr;
class PerformanceMonitorOperation* totalPmo = nullptr;
private:
bool _isBotAI;

View File

@@ -7,10 +7,9 @@
#include <iostream>
#include "Config.h"
#include "NewRpgInfo.h"
#include "PlayerbotDungeonRepository.h"
#include "PlayerbotDungeonSuggestionMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "RandomItemMgr.h"
#include "RandomPlayerbotFactory.h"
#include "RandomPlayerbotMgr.h"
@@ -58,54 +57,55 @@ void LoadListString(std::string const value, T& list)
bool PlayerbotAIConfig::Initialize()
{
LOG_INFO("server.loading", "Initializing mod-playerbots, based on AI Playerbots by ike3 and the original Playerbots by blueboy");
LOG_INFO("server.loading", "Initializing AI Playerbots by ike3, based on the original Playerbots by blueboy");
enabled = sConfigMgr->GetOption<bool>("AiPlayerbot.Enabled", true);
if (!enabled)
{
LOG_INFO("server.loading", "Playerbots Module is disabled in playerbots.conf");
LOG_INFO("server.loading", "AI Playerbots is Disabled in aiplayerbot.conf");
return false;
}
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 500);
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 1500);
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 700);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000);
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
dynamicReactDelay = sConfigMgr->GetOption<bool>("AiPlayerbot.DynamicReactDelay", true);
passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 100);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
rpgDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgDelay", 10000);
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 20000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 2000);
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
// Buff system
minBotsForGreaterBuff = sConfigMgr->GetOption<int32>("AiPlayerbot.MinBotsForGreaterBuff", 3);
rpWarningCooldown = sConfigMgr->GetOption<int32>("AiPlayerbot.RPWarningCooldown", 30);
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 100.0f);
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 28.5f);
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 5.0f);
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 38.5f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 25.0f);
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 25.0f);
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 25.0f);
lootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.LootDistance", 15.0f);
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 5.0f);
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f);
aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f);
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.0f);
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 0.75f);
followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.45f);
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 10.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f);
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 5.0f);
rpgDistance = sConfigMgr->GetOption<float>("AiPlayerbot.RpgDistance", 200.0f);
grindDistance = sConfigMgr->GetOption<float>("AiPlayerbot.GrindDistance", 75.0f);
reactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ReactDistance", 150.0f);
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 25);
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 20);
lowHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.LowHealth", 45);
mediumHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumHealth", 65);
almostFullHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.AlmostFullHealth", 85);
@@ -116,7 +116,7 @@ bool PlayerbotAIConfig::Initialize()
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
maxAoeAvoidRadius = sConfigMgr->GetOption<float>("AiPlayerbot.MaxAoeAvoidRadius", 15.0f);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810,29946"),
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810"),
aoeAvoidSpellWhitelist);
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
@@ -129,7 +129,7 @@ bool PlayerbotAIConfig::Initialize()
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.1f);
randomBotRpgChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotRpgChance", 0.20f);
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 10);
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
@@ -143,11 +143,11 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
probTeleToBankers = sConfigMgr->GetOption<float>("AiPlayerbot.ProbTeleToBankers", 0.25f);
enableWeightTeleToCityBankers = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableWeightTeleToCityBankers", false);
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 2);
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 1);
weightTeleToIronforge = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToIronforgeWeight", 1);
weightTeleToDarnassus = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDarnassusWeight", 1);
weightTeleToExodar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToExodarWeight", 1);
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 2);
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 1);
weightTeleToUndercity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToUndercityWeight", 1);
weightTeleToThunderBluff = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToThunderBluffWeight", 1);
weightTeleToSilvermoonCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToSilvermoonCityWeight", 1);
@@ -155,7 +155,7 @@ bool PlayerbotAIConfig::Initialize()
weightTeleToDalaran = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDalaranWeight", 1);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestItems",
"5175,5176,5177,5178,6948,11000,12382,13704,16309"),
"6948,5175,5176,5177,5178,16309,12382,13704,11000"),
randomBotQuestItems);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotSpellIds", "54197"),
randomBotSpellIds);
@@ -166,11 +166,11 @@ bool PlayerbotAIConfig::Initialize()
pvpProhibitedZoneIds);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"),
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"),
pvpProhibitedAreaIds);
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
randomBotQuestIds);
LoadSet<std::set<uint32>>(
@@ -181,8 +181,8 @@ bool PlayerbotAIConfig::Initialize()
disallowedGameObjects);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 500);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 50);
randomBotUpdateInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotUpdateInterval", 20);
randomBotCountChangeMinInterval =
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE);
@@ -200,8 +200,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);
permanentlyInWorldTime =
sConfigMgr->GetOption<int32>("AiPlayerbot.PermanentlyInWorldTime", 1 * YEAR);
permanantlyInWorldTime =
sConfigMgr->GetOption<int32>("AiPlayerbot.PermanantlyInWorldTime", 1 * YEAR);
randomBotTeleportDistance = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleportDistance", 100);
randomBotsPerInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotsPerInterval", 60);
minRandomBotsPriceChangeInterval =
@@ -223,11 +223,6 @@ bool PlayerbotAIConfig::Initialize()
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
//////////////////////////// Professions
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
enableFishingWithMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableFishingWithMaster", true);
//////////////////////////// CHAT
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
@@ -396,8 +391,8 @@ bool PlayerbotAIConfig::Initialize()
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "");
randomBotNonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotNonCombatStrategies", "");
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "");
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "");
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "+custom::say");
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
applyInstanceStrategies = sConfigMgr->GetOption<bool>("AiPlayerbot.ApplyInstanceStrategies", true);
commandPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.CommandPrefix", "");
@@ -517,7 +512,7 @@ bool PlayerbotAIConfig::Initialize()
randomBotGuildSizeMax = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildSizeMax", 15);
deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false);
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", false);
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", true);
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);
@@ -603,12 +598,12 @@ bool PlayerbotAIConfig::Initialize()
minEnchantingBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.MinEnchantingBotLevel", 60);
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 1);
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 1);
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 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);
gearscorecheck = sConfigMgr->GetOption<bool>("AiPlayerbot.GearScoreCheck", false);
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", false);
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", true);
// SPP automation
freeMethodLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeMethodLoot", false);
@@ -641,7 +636,7 @@ bool PlayerbotAIConfig::Initialize()
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", false);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
// arena
randomBotArenaTeam2v2Count = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotArenaTeam2v2Count", 10);
@@ -667,7 +662,6 @@ bool PlayerbotAIConfig::Initialize()
sRandomPlayerbotMgr->Init();
}
sPlayerbotGuildMgr->Init();
sRandomItemMgr->Init();
sRandomItemMgr->InitAfterAhBot();
sPlayerbotTextMgr->LoadBotTexts();
@@ -678,14 +672,14 @@ bool PlayerbotAIConfig::Initialize()
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
{
sPlayerbotDungeonRepository->LoadDungeonSuggestions();
sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions();
}
excludedHunterPetFamilies.clear();
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.ExcludedHunterPetFamilies", ""), excludedHunterPetFamilies);
LOG_INFO("server.loading", "---------------------------------------");
LOG_INFO("server.loading", " mod-playerbots initialized ");
LOG_INFO("server.loading", " AI Playerbots initialized ");
LOG_INFO("server.loading", "---------------------------------------");
return true;
@@ -732,8 +726,8 @@ std::string const PlayerbotAIConfig::GetTimestampStr()
// HH hour (2 digits 00-23)
// MM minutes (2 digits 00-59)
// SS seconds (2 digits 00-59)
char buf[32];
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour,
char buf[20];
snprintf(buf, 20, "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour,
aTm->tm_min, aTm->tm_sec);
return std::string(buf);
}

View File

@@ -132,7 +132,7 @@ public:
uint32 minRandomBotChangeStrategyTime, maxRandomBotChangeStrategyTime;
uint32 minRandomBotReviveTime, maxRandomBotReviveTime;
uint32 minRandomBotTeleportInterval, maxRandomBotTeleportInterval;
uint32 permanentlyInWorldTime;
uint32 permanantlyInWorldTime;
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
@@ -145,10 +145,6 @@ public:
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
int32 rpWarningCooldown;
// Professions
bool enableFishingWithMaster;
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
// chat
bool randomBotTalk;
bool randomBotEmote;
@@ -273,6 +269,7 @@ public:
bool deleteRandomBotAccounts;
uint32 randomBotGuildCount, randomBotGuildSizeMax;
bool deleteRandomBotGuilds;
std::vector<uint32> randomBotGuilds;
std::vector<uint32> pvpProhibitedZoneIds;
std::vector<uint32> pvpProhibitedAreaIds;
bool fastReactInBG;

View File

@@ -3,13 +3,13 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotRepository.h"
#include "PlayerbotDbStore.h"
#include <iostream>
#include "Playerbots.h"
void PlayerbotRepository::Load(PlayerbotAI* botAI)
void PlayerbotDbStore::Load(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
@@ -46,7 +46,7 @@ void PlayerbotRepository::Load(PlayerbotAI* botAI)
}
}
void PlayerbotRepository::Save(PlayerbotAI* botAI)
void PlayerbotDbStore::Save(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
@@ -68,7 +68,7 @@ void PlayerbotRepository::Save(PlayerbotAI* botAI)
SaveValue(guid, "dead", FormatStrategies("dead", botAI->GetStrategies(BOT_STATE_DEAD)));
}
std::string const PlayerbotRepository::FormatStrategies(std::string const type, std::vector<std::string> strategies)
std::string const PlayerbotDbStore::FormatStrategies(std::string const type, std::vector<std::string> strategies)
{
std::ostringstream out;
for (std::vector<std::string>::iterator i = strategies.begin(); i != strategies.end(); ++i)
@@ -78,7 +78,7 @@ std::string const PlayerbotRepository::FormatStrategies(std::string const type,
return res.substr(0, res.size() - 1);
}
void PlayerbotRepository::Reset(PlayerbotAI* botAI)
void PlayerbotDbStore::Reset(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
@@ -87,7 +87,7 @@ void PlayerbotRepository::Reset(PlayerbotAI* botAI)
PlayerbotsDatabase.Execute(stmt);
}
void PlayerbotRepository::SaveValue(uint32 guid, std::string const key, std::string const value)
void PlayerbotDbStore::SaveValue(uint32 guid, std::string const key, std::string const value)
{
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_DB_STORE);
stmt->SetData(0, guid);

View File

@@ -3,8 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTREPOSITORY_H
#define _PLAYERBOT_PLAYERBOTREPOSITORY_H
#ifndef _PLAYERBOT_PLAYERBOTDBSTORE_H
#define _PLAYERBOT_PLAYERBOTDBSTORE_H
#include <vector>
@@ -12,14 +12,14 @@
class PlayerbotAI;
class PlayerbotRepository
class PlayerbotDbStore
{
public:
PlayerbotRepository() {}
virtual ~PlayerbotRepository() {}
static PlayerbotRepository* instance()
PlayerbotDbStore() {}
virtual ~PlayerbotDbStore() {}
static PlayerbotDbStore* instance()
{
static PlayerbotRepository instance;
static PlayerbotDbStore instance;
return &instance;
}
@@ -32,6 +32,6 @@ private:
std::string const FormatStrategies(std::string const type, std::vector<std::string> strategies);
};
#define sPlayerbotRepository PlayerbotRepository::instance()
#define sPlayerbotDbStore PlayerbotDbStore::instance()
#endif

View File

@@ -3,16 +3,16 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotDungeonRepository.h"
#include "PlayerbotDungeonSuggestionMgr.h"
#include "Playerbots.h"
std::vector<DungeonSuggestion> const PlayerbotDungeonRepository::GetDungeonSuggestions()
std::vector<DungeonSuggestion> const PlayerbotDungeonSuggestionMgr::GetDungeonSuggestions()
{
return m_dungeonSuggestions;
}
void PlayerbotDungeonRepository::LoadDungeonSuggestions()
void PlayerbotDungeonSuggestionMgr::LoadDungeonSuggestions()
{
LOG_INFO("server.loading", "Loading playerbots dungeon suggestions...");
uint32 oldMSTime = getMSTime();

View File

@@ -3,8 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H
#define _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
#define _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
#include <map>
#include <vector>
@@ -22,14 +22,14 @@ struct DungeonSuggestion
std::string strategy;
};
class PlayerbotDungeonRepository
class PlayerbotDungeonSuggestionMgr
{
public:
PlayerbotDungeonRepository(){};
~PlayerbotDungeonRepository(){};
static PlayerbotDungeonRepository* instance()
PlayerbotDungeonSuggestionMgr(){};
~PlayerbotDungeonSuggestionMgr(){};
static PlayerbotDungeonSuggestionMgr* instance()
{
static PlayerbotDungeonRepository instance;
static PlayerbotDungeonSuggestionMgr instance;
return &instance;
}
@@ -40,6 +40,6 @@ private:
std::vector<DungeonSuggestion> m_dungeonSuggestions;
};
#define sPlayerbotDungeonRepository PlayerbotDungeonRepository::instance()
#define sPlayerbotDungeonSuggestionMgr PlayerbotDungeonSuggestionMgr::instance()
#endif

View File

@@ -9,10 +9,9 @@
#include <cstring>
#include <istream>
#include <string>
#include <unordered_set>
#include <openssl/sha.h>
#include <unordered_set>
#include <iomanip>
#include <algorithm>
#include "ChannelMgr.h"
#include "CharacterCache.h"
@@ -26,19 +25,19 @@
#include "ObjectGuid.h"
#include "ObjectMgr.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotRepository.h"
#include "PlayerbotDbStore.h"
#include "PlayerbotFactory.h"
#include "PlayerbotOperations.h"
#include "PlayerbotSecurity.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "RandomPlayerbotMgr.h"
#include "SharedDefines.h"
#include "WorldSession.h"
#include "ChannelMgr.h"
#include "BroadcastHelper.h"
#include "PlayerbotDbStore.h"
#include "WorldSessionMgr.h"
#include "DatabaseEnv.h"
#include "DatabaseEnv.h" // Added for gender choice
#include <algorithm> // Added for gender choice
class BotInitGuard
{
@@ -67,7 +66,6 @@ private:
};
std::unordered_set<ObjectGuid> BotInitGuard::botsBeingInitialized;
std::unordered_set<ObjectGuid> PlayerbotHolder::botLoading;
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
class PlayerbotLoginQueryHolder : public LoginQueryHolder
@@ -76,16 +74,18 @@ private:
uint32 masterAccountId;
PlayerbotHolder* playerbotHolder;
public:
PlayerbotLoginQueryHolder(uint32 masterAccount, uint32 accountId, ObjectGuid guid)
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount)
PlayerbotLoginQueryHolder(PlayerbotHolder* playerbotHolder, uint32 masterAccount, uint32 accountId, ObjectGuid guid)
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount), playerbotHolder(playerbotHolder)
{
}
uint32 GetMasterAccountId() const { return masterAccountId; }
PlayerbotHolder* GetPlayerbotHolder() { return playerbotHolder; }
};
void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId)
{
// bot is loading
if (botLoading.find(playerGuid) != botLoading.end())
return;
@@ -142,7 +142,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
return;
}
std::shared_ptr<PlayerbotLoginQueryHolder> holder =
std::make_shared<PlayerbotLoginQueryHolder>(masterAccountId, accountId, playerGuid);
std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
if (!holder->Initialize())
{
return;
@@ -152,27 +152,8 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
// Always login in with world session to avoid race condition
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
.AfterComplete(
[](SQLQueryHolderBase const& queryHolder)
{
PlayerbotLoginQueryHolder const& holder = static_cast<PlayerbotLoginQueryHolder const&>(queryHolder);
PlayerbotHolder* mgr = sRandomPlayerbotMgr; // could be null
uint32 masterAccountId = holder.GetMasterAccountId();
if (masterAccountId)
{
// verify and find current world session of master
WorldSession* masterSession = sWorldSessionMgr->FindSession(masterAccountId);
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
if (masterPlayer)
mgr = GET_PLAYERBOT_MGR(masterPlayer);
}
if (mgr)
mgr->HandlePlayerBotLoginCallback(holder);
else
PlayerbotHolder::botLoading.erase(holder.GetGuid());
});
.AfterComplete([this](SQLQueryHolderBase const& holder)
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
}
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
@@ -187,9 +168,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
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, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
@@ -200,27 +180,24 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId);
botSession->LogoutPlayer(true);
delete botSession;
PlayerbotHolder::botLoading.erase(holder.GetGuid());
botLoading.erase(holder.GetGuid());
return;
}
uint32 masterAccountId = holder.GetMasterAccountId();
WorldSession* masterSession = masterAccountId ? sWorldSessionMgr->FindSession(masterAccountId) : nullptr;
uint32 masterAccount = holder.GetMasterAccountId();
WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr;
// Check if masterSession->GetPlayer() is valid
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
if (masterSession && !masterPlayer)
{
LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}",
masterAccountId);
LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount);
}
sRandomPlayerbotMgr->OnPlayerLogin(bot);
auto op = std::make_unique<OnBotLoginOperation>(bot->GetGUID(), masterAccountId);
sPlayerbotWorldProcessor->QueueOperation(std::move(op));
OnBotLogin(bot);
PlayerbotHolder::botLoading.erase(holder.GetGuid());
botLoading.erase(holder.GetGuid());
}
void PlayerbotHolder::UpdateSessions()
@@ -339,9 +316,11 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
if (!botAI)
return;
// Queue group cleanup operation for world thread
auto cleanupOp = std::make_unique<BotLogoutGroupCleanupOperation>(guid);
sPlayerbotWorldProcessor->QueueOperation(std::move(cleanupOp));
Group* group = bot->GetGroup();
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
{
sPlayerbotDbStore->Save(botAI);
}
LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str());
bot->SaveToDB(false, false);
@@ -442,7 +421,7 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid)
Group* group = bot->GetGroup();
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
{
sPlayerbotRepository->Save(botAI);
sPlayerbotDbStore->Save(botAI);
}
LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str());
@@ -554,7 +533,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
{
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
}
sPlayerbotRepository->Load(botAI);
sPlayerbotDbStore->Load(botAI);
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
@@ -570,7 +549,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
botAI->TellMaster("Hello!", PLAYERBOT_SECURITY_TALK);
// Queue group operations for world thread
if (master && master->GetGroup() && !group)
{
Group* mgroup = master->GetGroup();
@@ -578,29 +556,24 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
{
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
{
// Queue ConvertToRaid operation
auto convertOp = std::make_unique<GroupConvertToRaidOperation>(master->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp));
mgroup->ConvertToRaid();
}
if (mgroup->isRaidGroup())
{
// Queue AddMember operation
auto addOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(addOp));
mgroup->AddMember(bot);
}
}
else
{
// Queue AddMember operation
auto addOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(addOp));
mgroup->AddMember(bot);
}
}
else if (master && !group)
{
// Queue group creation and AddMember operation
auto inviteOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(inviteOp));
Group* newGroup = new Group();
newGroup->Create(master);
sGroupMgr->AddGroup(newGroup);
newGroup->AddMember(bot);
}
// if (master)
// {
@@ -1194,7 +1167,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (ObjectAccessor::FindConnectedPlayer(guid))
continue;
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
if (guildId && sPlayerbotGuildMgr->IsRealGuild(guildId))
if (guildId && PlayerbotAI::IsRealGuild(guildId))
continue;
AddPlayerBot(guid, master->GetSession()->GetAccountId());
messages.push_back("Add class " + std::string(charname));
@@ -1629,26 +1602,8 @@ void PlayerbotMgr::OnBotLoginInternal(Player* const bot)
void PlayerbotMgr::OnPlayerLogin(Player* player)
{
if (!player)
return;
WorldSession* session = player->GetSession();
if (!session)
{
LOG_WARN("playerbots", "Unable to register locale priority for player {} because the session is missing", player->GetName());
return;
}
// DB locale (source of bot text translation)
LocaleConstant const databaseLocale = session->GetSessionDbLocaleIndex();
// For bot texts (DB-driven), prefer the database locale with a safe fallback.
LocaleConstant usedLocale = databaseLocale;
if (usedLocale >= MAX_LOCALES)
usedLocale = LOCALE_enUS; // fallback
// set locale priority for bot texts
sPlayerbotTextMgr->AddLocalePriority(usedLocale);
sPlayerbotTextMgr->AddLocalePriority(player->GetSession()->GetSessionDbcLocale());
if (sPlayerbotAIConfig->selfBotLevel > 2)
HandlePlayerbotCommand("self", player);
@@ -1656,7 +1611,7 @@ void PlayerbotMgr::OnPlayerLogin(Player* player)
if (!sPlayerbotAIConfig->botAutologin)
return;
uint32 accountId = session->GetAccountId();
uint32 accountId = player->GetSession()->GetAccountId();
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
if (results)
{
@@ -1781,8 +1736,7 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
{
return nullptr;
}
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld())
// {
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// return nullptr;
// }
auto itr = _playerbotsAIMap.find(player->GetGUID());

View File

@@ -60,7 +60,7 @@ protected:
virtual void OnBotLoginInternal(Player* const bot) = 0;
PlayerBotMap playerBots;
static std::unordered_set<ObjectGuid> botLoading;
std::unordered_set<ObjectGuid> botLoading;
};
class PlayerbotMgr : public PlayerbotHolder

View File

@@ -17,28 +17,14 @@ PlayerbotSecurity::PlayerbotSecurity(Player* const bot) : bot(bot)
PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* reason, bool ignoreGroup)
{
// Basic pointer validity checks
if (!bot || !from || !from->GetSession())
{
if (reason)
*reason = PLAYERBOT_DENY_NONE;
return PLAYERBOT_SECURITY_DENY_ALL;
}
// GMs always have full access
if (from->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
return PLAYERBOT_SECURITY_ALLOW_ALL;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
{
if (reason)
*reason = PLAYERBOT_DENY_NONE;
return PLAYERBOT_SECURITY_DENY_ALL;
}
if (botAI->IsOpposing(from))
{
if (reason)
@@ -49,7 +35,6 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
if (sPlayerbotAIConfig->IsInRandomAccountList(account))
{
// (duplicate check in case of faction change)
if (botAI->IsOpposing(from))
{
if (reason)
@@ -58,17 +43,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
return PLAYERBOT_SECURITY_DENY_ALL;
}
Group* fromGroup = from->GetGroup();
Group* botGroup = bot->GetGroup();
// if (sLFGMgr->GetState(bot->GetGUID()) != lfg::LFG_STATE_NONE)
// {
// if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
// {
// if (reason)
// *reason = PLAYERBOT_DENY_LFG;
if (fromGroup && botGroup && fromGroup == botGroup && !ignoreGroup)
// return PLAYERBOT_SECURITY_TALK;
// }
// }
Group* group = from->GetGroup();
if (group && group == bot->GetGroup() && !ignoreGroup && botAI->GetMaster() == from)
{
if (botAI->GetMaster() == from)
return PLAYERBOT_SECURITY_ALLOW_ALL;
return PLAYERBOT_SECURITY_ALLOW_ALL;
}
if (group && group == bot->GetGroup() && !ignoreGroup && botAI->GetMaster() != from)
{
if (reason)
*reason = PLAYERBOT_DENY_NOT_YOURS;
return PLAYERBOT_SECURITY_TALK;
}
@@ -80,34 +75,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
return PLAYERBOT_SECURITY_TALK;
}
if (sPlayerbotAIConfig->groupInvitationPermission <= 1)
if (sPlayerbotAIConfig->groupInvitationPermission <= 1 && (int32)bot->GetLevel() - (int8)from->GetLevel() > 5)
{
int32 levelDiff = int32(bot->GetLevel()) - int32(from->GetLevel());
if (levelDiff > 5)
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_LOW_LEVEL;
if (reason)
*reason = PLAYERBOT_DENY_LOW_LEVEL;
return PLAYERBOT_SECURITY_TALK;
}
return PLAYERBOT_SECURITY_TALK;
}
}
int32 botGS = static_cast<int32>(botAI->GetEquipGearScore(bot));
int32 fromGS = static_cast<int32>(botAI->GetEquipGearScore(from));
if (sPlayerbotAIConfig->gearscorecheck && botGS && bot->GetLevel() > 15 && botGS > fromGS)
int32 botGS = (int32)botAI->GetEquipGearScore(bot/*, false, false*/);
int32 fromGS = (int32)botAI->GetEquipGearScore(from/*, false, false*/);
if (sPlayerbotAIConfig->gearscorecheck)
{
uint32 diffPct = uint32(100 * (botGS - fromGS) / botGS);
uint32 reqPct = uint32(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel());
if (diffPct >= reqPct)
if (botGS && bot->GetLevel() > 15 && botGS > fromGS &&
static_cast<float>(100 * (botGS - fromGS) / botGS) >=
static_cast<float>(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel()))
{
if (reason)
*reason = PLAYERBOT_DENY_GEARSCORE;
return PLAYERBOT_SECURITY_TALK;
}
}
@@ -123,17 +111,35 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
}
}
// If the bot is not in the group, we offer an invite
botGroup = bot->GetGroup();
if (!botGroup)
/*if (bot->isDead())
{
if (reason)
*reason = PLAYERBOT_DENY_DEAD;
return PLAYERBOT_SECURITY_TALK;
}*/
group = bot->GetGroup();
if (!group)
{
/*if (bot->GetMapId() != from->GetMapId() || bot->GetDistance(from) > sPlayerbotAIConfig->whisperDistance)
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_FAR;
return PLAYERBOT_SECURITY_TALK;
}
}*/
if (reason)
*reason = PLAYERBOT_DENY_INVITE;
return PLAYERBOT_SECURITY_INVITE;
}
if (!ignoreGroup && botGroup->IsFull())
if (!ignoreGroup && group->IsFull())
{
if (reason)
*reason = PLAYERBOT_DENY_FULL_GROUP;
@@ -141,22 +147,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
return PLAYERBOT_SECURITY_TALK;
}
if (!ignoreGroup && botGroup->GetLeaderGUID() != bot->GetGUID())
if (!ignoreGroup && group->GetLeaderGUID() != bot->GetGUID())
{
if (reason)
*reason = PLAYERBOT_DENY_NOT_LEADER;
return PLAYERBOT_SECURITY_TALK;
}
else
{
if (reason)
*reason = PLAYERBOT_DENY_IS_LEADER;
return PLAYERBOT_SECURITY_INVITE;
}
// The bot is the group leader, you can invite the initiator
if (reason)
*reason = PLAYERBOT_DENY_IS_LEADER;
*reason = PLAYERBOT_DENY_INVITE;
return PLAYERBOT_SECURITY_INVITE;
}
// Non-random bots: only their master has full access
if (botAI->GetMaster() == from)
return PLAYERBOT_SECURITY_ALLOW_ALL;
@@ -168,13 +179,8 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, Player* from, bool ignoreGroup)
{
// If something is wrong with the pointers, we silently refuse
if (!bot || !from || !from->GetSession())
return false;
DenyReason reason = PLAYERBOT_DENY_NONE;
PlayerbotSecurityLevel realLevel = LevelFor(from, &reason, ignoreGroup);
if (realLevel >= level || from == bot)
return true;
@@ -183,17 +189,11 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
Player* master = botAI->GetMaster();
if (master && botAI && botAI->IsOpposing(master) && master->GetSession()->GetSecurity() < SEC_GAMEMASTER)
return false;
Player* master = botAI->GetMaster();
if (master && botAI->IsOpposing(master))
if (WorldSession* session = master->GetSession())
if (session->GetSecurity() < SEC_GAMEMASTER)
return false;
std::ostringstream out;
switch (realLevel)
{
case PLAYERBOT_SECURITY_DENY_ALL:
@@ -206,20 +206,19 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
out << "I'll do it later";
break;
case PLAYERBOT_DENY_LOW_LEVEL:
out << "You are too low level: |cffff0000" << uint32(from->GetLevel()) << "|cffffffff/|cff00ff00"
<< uint32(bot->GetLevel());
out << "You are too low level: |cffff0000" << (uint32)from->GetLevel() << "|cffffffff/|cff00ff00"
<< (uint32)bot->GetLevel();
break;
case PLAYERBOT_DENY_GEARSCORE:
{
int botGS = int(botAI->GetEquipGearScore(bot));
int fromGS = int(botAI->GetEquipGearScore(from));
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
<< " |cffff0000" << diff << "%|cffffffff/|cff00ff00" << req << "%";
break;
}
break;
case PLAYERBOT_DENY_NOT_YOURS:
out << "I have a master already";
break;
@@ -238,10 +237,13 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
case PLAYERBOT_DENY_FAR:
{
out << "You must be closer to invite me to your group. I am in ";
if (AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetAreaId()))
{
out << " |cffffffff(|cffff0000" << entry->area_name[0] << "|cffffffff)";
break;
}
}
break;
case PLAYERBOT_DENY_FULL_GROUP:
out << "I am in a full group. Will do it later";
break;
@@ -249,10 +251,15 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
out << "I am currently leading a group. I can invite you if you want.";
break;
case PLAYERBOT_DENY_NOT_LEADER:
if (Player* leader = botAI->GetGroupLeader())
out << "I am in a group with " << leader->GetName() << ". You can ask him for invite.";
if (botAI->GetGroupMaster())
{
out << "I am in a group with " << botAI->GetGroupMaster()->GetName()
<< ". You can ask him for invite.";
}
else
{
out << "I am in a group with someone else. You can ask him for invite.";
}
break;
case PLAYERBOT_DENY_BG:
out << "I am in a queue for BG. Will do it later";
@@ -276,14 +283,10 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
std::string const text = out.str();
ObjectGuid guid = from->GetGUID();
time_t lastSaid = whispers[guid][text];
if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000)
{
whispers[guid][text] = time(nullptr);
// Additional protection against crashes during logout
if (bot->IsInWorld() && from->IsInWorld())
bot->Whisper(text, LANG_UNIVERSAL, from);
bot->Whisper(text, LANG_UNIVERSAL, from);
}
return false;

View File

@@ -190,29 +190,26 @@ bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map<
void PlayerbotTextMgr::AddLocalePriority(uint32 locale)
{
if (locale >= MAX_LOCALES)
{
LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds MAX_LOCALES ({})", locale, MAX_LOCALES - 1);
if (!locale)
return;
}
botTextLocalePriority[locale]++;
}
uint32 PlayerbotTextMgr::GetLocalePriority()
{
uint32 topLocale = 0;
// if no real players online, reset top locale
uint32 const activeSessions = sWorldSessionMgr->GetActiveSessionCount();
if (!activeSessions)
if (!sWorldSessionMgr->GetActiveSessionCount())
{
ResetLocalePriority();
return 0;
}
uint32 topLocale = 0;
for (uint8 i = 0; i < MAX_LOCALES; ++i)
{
if (botTextLocalePriority[i] > botTextLocalePriority[topLocale])
if (botTextLocalePriority[i] > topLocale)
topLocale = i;
}

View File

@@ -25,12 +25,9 @@
#include "Metric.h"
#include "PlayerScript.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotGuildMgr.h"
#include "PlayerbotSpellRepository.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
#include "PlayerbotCommandScript.h"
#include "cs_playerbots.h"
#include "cmath"
#include "BattleGroundTactics.h"
@@ -84,12 +81,12 @@ 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_CAN_PLAYER_USE_GROUP_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT,
PLAYERHOOK_ON_GIVE_EXP,
PLAYERHOOK_ON_BEFORE_TELEPORT
}) {}
@@ -125,49 +122,24 @@ public:
}
}
bool OnPlayerBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
bool OnPlayerBeforeTeleport(Player* player, uint32 mapid, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
{
/* for now commmented out until proven its actually required
* havent seen any proof CleanVisibilityReferences() is needed
// If the player is not safe to touch, do nothing
if (!player)
// Only apply to bots to prevent affecting real players
if (!player || !player->GetSession()->IsBot())
return true;
// If same map or not in world do nothing
if (!player->IsInWorld() || player->GetMapId() == mapid)
return true;
// If changing maps, proactively clean visibility references to prevent
// stale pointers in other players' visibility maps during the teleport.
// This fixes a race condition where:
// 1. Bot A teleports and its visible objects start getting cleaned up
// 2. Bot B is simultaneously updating visibility and tries to access objects in Bot A's old visibility map
// 3. Those objects may already be freed, causing a segmentation fault
if (player->GetMapId() != mapid && player->IsInWorld())
{
player->GetObjectVisibilityContainer().CleanVisibilityReferences();
}
// If real player do nothing
PlayerbotAI* ai = GET_PLAYERBOT_AI(player);
if (!ai || ai->IsRealPlayer())
return true;
// Cross-map bot teleport: defer visibility reference cleanup.
// CleanVisibilityReferences() erases this bot's GUID from other objects' visibility containers.
// This is intentionally done via the event queue (instead of directly here) because erasing
// from other players' visibility maps inside the teleport call stack can hit unsafe re-entrancy
// or iterator invalidation while visibility updates are in progress
ObjectGuid guid = player->GetGUID();
player->m_Events.AddEventAtOffset(
[guid, mapid]()
{
// do nothing, if the player is not safe to touch
Player* p = ObjectAccessor::FindPlayer(guid);
if (!p || !p->IsInWorld() || p->IsDuringRemoveFromWorld())
return;
// do nothing if we are already on the target map
if (p->GetMapId() == mapid)
return;
p->GetObjectVisibilityContainer().CleanVisibilityReferences();
},
Milliseconds(0));
*/
return true;
return true; // Allow teleport to continue
}
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
@@ -191,17 +163,14 @@ public:
{
botAI->HandleCommand(type, msg, player);
// hotfix; otherwise the server will crash when whispering logout
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
// TODO: find the root cause and solve it. (does not happen in party chat)
if (msg == "logout")
return false;
return false;
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
@@ -213,10 +182,9 @@ public:
}
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
{
if (type == CHAT_MSG_GUILD)
{
@@ -235,10 +203,9 @@ public:
}
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
@@ -249,13 +216,11 @@ public:
}
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
return true;
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
{
if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) &&
(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)))
if (sRandomPlayerbotMgr->IsRandomBot(player) && (achievement->flags == 256 || achievement->flags == 768))
{
return false;
}
@@ -334,8 +299,7 @@ class PlayerbotsWorldScript : public WorldScript
{
public:
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript", {
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED,
WORLDHOOK_ON_UPDATE
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED
}) {}
void OnBeforeWorldInitialized() override
@@ -364,16 +328,6 @@ public:
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
sPlayerbotSpellRepository->Initialize();
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
}
void OnUpdate(uint32 diff) override
{
sPlayerbotWorldProcessor->Update(diff);
sRandomPlayerbotMgr->UpdateAI(diff); // World thread only
}
};
@@ -435,7 +389,8 @@ public:
void OnPlayerbotUpdate(uint32 diff) override
{
sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only
sRandomPlayerbotMgr->UpdateAI(diff);
sRandomPlayerbotMgr->UpdateSessions();
}
void OnPlayerbotUpdateSessions(Player* player) override
@@ -503,8 +458,6 @@ public:
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
};
void AddPlayerbotsSecureLoginScripts();
void AddPlayerbotsScripts()
{
new PlayerbotsDatabaseScript();
@@ -514,7 +467,6 @@ void AddPlayerbotsScripts()
new PlayerbotsWorldScript();
new PlayerbotsScript();
new PlayerBotsBGScript();
AddPlayerbotsSecureLoginScripts();
AddPlayerbotsCommandscripts();
PlayerBotsGuildValidationScript();
AddSC_playerbots_commandscript();
}

View File

@@ -982,7 +982,7 @@ void RandomItemMgr::BuildItemInfoCache()
continue;
}
if (proto->HasFlag(ITEM_FLAG_DEPRECATED))
if (proto->Flags & ITEM_FLAG_DEPRECATED)
{
itemForTest.insert(proto->ItemId);
continue;
@@ -1072,10 +1072,10 @@ void RandomItemMgr::BuildItemInfoCache()
// cacheInfo.team = TEAM_NEUTRAL;
// // check faction
// if (proto->HasFlag2(ITEM_FLAG2_FACTION_HORDE))
// if (proto->Flags2 & ITEM_FLAG2_FACTION_HORDE)
// cacheInfo.team = TEAM_HORDE;
// if (proto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE))
// if (proto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE)
// cacheInfo.team = TEAM_ALLIANCE;
// if (cacheInfo.team == TEAM_NEUTRAL && proto->AllowableRace > 1 && proto->AllowableRace < 8388607)
@@ -1099,7 +1099,7 @@ void RandomItemMgr::BuildItemInfoCache()
// // check item source
// if (proto->HasFlag(ITEM_FLAG_NO_DISENCHANT))
// if (proto->Flags & ITEM_FLAG_NO_DISENCHANT)
// {
// cacheInfo.source = ITEM_SOURCE_PVP;
// LOG_DEBUG("playerbots", "Item: {}, source: PvP Reward", proto->ItemId);
@@ -1367,7 +1367,7 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTem
}
// check item spells
for (auto const& spellData : proto->Spells)
for (const auto& spellData : proto->Spells)
{
// no spell
if (!spellData.SpellId)
@@ -2374,6 +2374,7 @@ void RandomItemMgr::BuildPotionCache()
if (proto->Duration & 0x80000000)
continue;
if (proto->AllowableClass != -1)
continue;
@@ -2834,20 +2835,22 @@ inline bool ContainsInternal(ItemTemplate const* proto, uint32 skillId)
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
if (!trainer)
if (itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
continue;
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill)
uint32 trainerId = itr->second.Entry;
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
if (!trainer_spells)
continue;
for (auto& spell : trainer->GetSpells())
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin();
iter != trainer_spells->spellList.end(); ++iter)
{
if (spell.ReqSkillLine != skillId)
TrainerSpell const* tSpell = &iter->second;
if (!tSpell || tSpell->reqSkill != skillId)
continue;
if (IsCraftedBy(proto, spell.SpellId))
if (IsCraftedBy(proto, tSpell->spell))
return true;
}
}

View File

@@ -11,7 +11,6 @@
#include "GuildMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "PlayerbotGuildMgr.h"
#include "ScriptMgr.h"
#include "SharedDefines.h"
#include "SocialMgr.h"
@@ -20,99 +19,188 @@
#include "Log.h"
#include "GuildMgr.h"
constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 race,
uint8 gender)
std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces;
constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 gender,
uint8 race)
{
NameRaceAndGender baseIndex;
switch (race)
{
case RACE_ORC: baseIndex = NameRaceAndGender::OrcMale; break;
case RACE_DWARF: baseIndex = NameRaceAndGender::DwarfMale; break;
case RACE_NIGHTELF: baseIndex = NameRaceAndGender::NightelfMale; break;
case RACE_TAUREN: baseIndex = NameRaceAndGender::TaurenMale; break;
case RACE_GNOME: baseIndex = NameRaceAndGender::GnomeMale; break;
case RACE_TROLL: baseIndex = NameRaceAndGender::TrollMale; break;
case RACE_BLOODELF: baseIndex = NameRaceAndGender::BloodelfMale; break;
case RACE_DRAENEI: baseIndex = NameRaceAndGender::DraeneiMale; break;
case RACE_HUMAN:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
case RACE_ORC:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::OrcMale) + gender);
case RACE_DWARF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::DwarfMale) + gender);
case RACE_NIGHTELF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::NightelfMale) + gender);
case RACE_UNDEAD_PLAYER:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
case RACE_TAUREN:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::TaurenMale) + gender);
case RACE_GNOME:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GnomeMale) + gender);
case RACE_TROLL:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::TrollMale) + gender);
case RACE_DRAENEI:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::DraeneiMale) + gender);
case RACE_BLOODELF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::BloodelfMale) + gender);
default:
baseIndex = NameRaceAndGender::GenericMale;
break;
LOG_ERROR("playerbots", "The race with ID %d does not have a naming category", race);
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
}
return static_cast<NameRaceAndGender>(static_cast<uint8>(baseIndex) + ((gender >= GENDER_NONE) ? GENDER_MALE : gender));
}
bool RandomPlayerbotFactory::IsValidRaceClassCombination(uint8 race, uint8 cls, uint32 expansion)
RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(accountId)
{
// skip expansion races if not playing with expansion
if (expansion < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
return false;
uint32 const expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
// skip expansion classes if not playing with expansion
if (expansion < EXPANSION_WRATH_OF_THE_LICH_KING && cls == CLASS_DEATH_KNIGHT)
return false;
availableRaces[CLASS_WARRIOR].push_back(RACE_HUMAN);
availableRaces[CLASS_WARRIOR].push_back(RACE_NIGHTELF);
availableRaces[CLASS_WARRIOR].push_back(RACE_GNOME);
availableRaces[CLASS_WARRIOR].push_back(RACE_DWARF);
availableRaces[CLASS_WARRIOR].push_back(RACE_ORC);
availableRaces[CLASS_WARRIOR].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARRIOR].push_back(RACE_TAUREN);
availableRaces[CLASS_WARRIOR].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_WARRIOR].push_back(RACE_DRAENEI);
}
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, cls);
return info != nullptr;
availableRaces[CLASS_PALADIN].push_back(RACE_HUMAN);
availableRaces[CLASS_PALADIN].push_back(RACE_DWARF);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_PALADIN].push_back(RACE_DRAENEI);
availableRaces[CLASS_PALADIN].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_ROGUE].push_back(RACE_HUMAN);
availableRaces[CLASS_ROGUE].push_back(RACE_DWARF);
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)
{
availableRaces[CLASS_ROGUE].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_PRIEST].push_back(RACE_HUMAN);
availableRaces[CLASS_PRIEST].push_back(RACE_DWARF);
availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF);
availableRaces[CLASS_PRIEST].push_back(RACE_TROLL);
availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI);
availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_MAGE].push_back(RACE_HUMAN);
availableRaces[CLASS_MAGE].push_back(RACE_GNOME);
availableRaces[CLASS_MAGE].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_MAGE].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_MAGE].push_back(RACE_DRAENEI);
availableRaces[CLASS_MAGE].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_WARLOCK].push_back(RACE_HUMAN);
availableRaces[CLASS_WARLOCK].push_back(RACE_GNOME);
availableRaces[CLASS_WARLOCK].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARLOCK].push_back(RACE_ORC);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_WARLOCK].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_SHAMAN].push_back(RACE_ORC);
availableRaces[CLASS_SHAMAN].push_back(RACE_TAUREN);
availableRaces[CLASS_SHAMAN].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_SHAMAN].push_back(RACE_DRAENEI);
}
availableRaces[CLASS_HUNTER].push_back(RACE_DWARF);
availableRaces[CLASS_HUNTER].push_back(RACE_NIGHTELF);
availableRaces[CLASS_HUNTER].push_back(RACE_ORC);
availableRaces[CLASS_HUNTER].push_back(RACE_TAUREN);
availableRaces[CLASS_HUNTER].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_HUNTER].push_back(RACE_DRAENEI);
availableRaces[CLASS_HUNTER].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_DRUID].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DRUID].push_back(RACE_TAUREN);
if (expansion == EXPANSION_WRATH_OF_THE_LICH_KING)
{
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TAUREN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_HUMAN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_ORC);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TROLL);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_BLOODELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DRAENEI);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_GNOME);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DWARF);
}
}
Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& nameCache)
{
LOG_DEBUG("playerbots", "Creating a new random bot for class: {}", cls);
const bool alliance = static_cast<bool>(urand(0, 1));
LOG_DEBUG("playerbots", "Creating new random bot for class {}", cls);
uint8 gender = rand() % 2 ? GENDER_MALE : GENDER_FEMALE;
bool alliance = rand() % 2 ? true : false;
std::vector<uint8> raceOptions;
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
for (const auto& race : availableRaces[cls])
{
// skip disabled with config races
if ((1 << (race - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK))
continue;
// Try to get 50/50 faction distribution for random bot population balance.
// Without this check, races from the faction with more class options would dominate.
if (alliance == IsAlliance(race))
{
if (IsValidRaceClassCombination(race, cls, sWorld->getIntConfig(CONFIG_EXPANSION)))
raceOptions.push_back(race);
raceOptions.push_back(race);
}
}
if (raceOptions.empty())
{
LOG_ERROR("playerbots", "No races are available for class: {}", cls);
LOG_ERROR("playerbots", "No races available for class: {}", cls);
return nullptr;
}
const uint8 race = raceOptions[urand(0, raceOptions.size() - 1)];
const uint8 gender = urand(0, 1) ? GENDER_MALE : GENDER_FEMALE;
const auto raceAndGender = CombineRaceAndGender(race, gender);
uint8 race = raceOptions[urand(0, raceOptions.size() - 1)];
const auto raceAndGender = CombineRaceAndGender(gender, race);
std::string name;
if (!nameCache.empty())
if (nameCache.empty())
{
name = CreateRandomBotName(raceAndGender);
}
else
{
if (nameCache[raceAndGender].empty())
{
LOG_ERROR("playerbots", "No names found for the specified race: {} and gender: {}",
race, gender);
LOG_ERROR("playerbots", "No name found for race and gender: {}", raceAndGender);
return nullptr;
}
uint32 i = urand(0, nameCache[raceAndGender].size() - 1);
name = nameCache[raceAndGender][i];
swap(nameCache[raceAndGender][i], nameCache[raceAndGender].back());
nameCache[raceAndGender].pop_back();
}
else
{
name = CreateRandomBotName(raceAndGender);
}
if (name.empty())
{
LOG_ERROR("playerbots", "Failed to get a valid random bot name");
LOG_ERROR("playerbots", "Unable to get random bot name!");
return nullptr;
}
@@ -158,20 +246,19 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
player->CleanupsBeforeDelete();
delete player;
LOG_ERROR("playerbots", "Unable to create random bot - name: \"{}\", race: {}, class: {}",
name.c_str(), race, cls);
LOG_ERROR("playerbots", "Unable to create random bot for account {} - name: \"{}\"; race: {}; class: {}",
accountId, name.c_str(), race, cls);
return nullptr;
}
player->setCinematic(2);
player->SetAtLoginFlag(AT_LOGIN_NONE);
if (cls == CLASS_DEATH_KNIGHT)
if (player->getClass() == CLASS_DEATH_KNIGHT)
{
player->learnSpell(50977, false);
}
LOG_DEBUG("playerbots", "Random bot created - name: \"{}\", race: {}, class: {}",
LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId,
name.c_str(), race, cls);
return player;
@@ -699,7 +786,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
}
LOG_DEBUG("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, totalAccountCount);
RandomPlayerbotFactory factory;
RandomPlayerbotFactory factory(accountId);
WorldSession* session = new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), LOCALE_enUS, 0, false, false, 0, true);
@@ -711,24 +798,29 @@ void RandomPlayerbotFactory::CreateRandomBots()
if (!((1 << (cls - 1)) & CLASSMASK_ALL_PLAYABLE) || !sChrClassesStore.LookupEntry(cls))
continue;
// skip disabled with config classes
if ((1 << (cls - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK))
continue;
Player* playerBot = factory.CreateRandomBot(session, cls, nameCache);
if (!playerBot)
if (bool const isClassDeathKnight = cls == CLASS_DEATH_KNIGHT;
isClassDeathKnight && sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING)
{
LOG_ERROR("playerbots", "Fail to create character for account {}", accountId);
continue;
}
playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(),
playerBot->getClass(), playerBot->GetLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
bot_creation++;
if (cls != 10)
{
if (Player* playerBot = factory.CreateRandomBot(session, cls, nameCache))
{
playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(),
playerBot->getClass(), playerBot->GetLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
bot_creation++;
}
else
{
LOG_ERROR("playerbots", "Fail to create character for account {}", accountId);
}
}
}
}
@@ -755,6 +847,144 @@ void RandomPlayerbotFactory::CreateRandomBots()
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
}
void RandomPlayerbotFactory::CreateRandomGuilds()
{
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
} while (result->NextRow());
}
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
{
LOG_INFO("playerbots", "Deleting random bot guilds...");
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
guild->Disband();
}
LOG_INFO("playerbots", "Random bot guilds deleted");
}
uint32 guildNumber = 0;
GuidVector availableLeaders;
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(*i);
if (Guild* guild = sGuildMgr->GetGuildByLeader(leader))
{
++guildNumber;
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
}
else
{
Player* player = ObjectAccessor::FindPlayer(leader);
if (player && !player->GetGuildId())
availableLeaders.push_back(leader);
}
}
// Create up to randomBotGuildCount by counting only EFFECTIVE creations
uint32 createdThisRun = 0;
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; /* ++guildNumber -> done only if creation */)
{
std::string const guildName = CreateRandomGuildName();
if (guildName.empty())
continue;
if (sGuildMgr->GetGuildByName(guildName))
continue;
if (availableLeaders.empty())
{
LOG_ERROR("playerbots", "No leaders for random guilds available");
break; // no more leaders: we can no longer progress without distorting the counter
}
uint32 index = urand(0, availableLeaders.size() - 1);
ObjectGuid leader = availableLeaders[index];
availableLeaders.erase(availableLeaders.begin() + index); // Removes the chosen leader to avoid re-selecting it repeatedly
Player* player = ObjectAccessor::FindPlayer(leader);
if (!player)
{
LOG_ERROR("playerbots", "ObjectAccessor Cannot find player to set leader for guild {} . Skipped...",
guildName.c_str());
// we will try with other leaders in the next round (guildNumber is not incremented)
continue;
}
if (player->GetGuildId())
{
// leader already in guild -> we don't advance the counter, we move on to the next one
continue;
}
LOG_DEBUG("playerbots", "Creating guild name='{}' leader='{}'...", guildName.c_str(), player->GetName().c_str());
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
{
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName.c_str(),
player->GetName().c_str());
delete guild;
continue;
}
sGuildMgr->AddGuild(guild);
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName.c_str());
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
bc = urand(0, 17);
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
LOG_DEBUG("playerbots",
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), st, cl, br, bc, bg);
// populate guild table with a random tabard design
CharacterDatabase.Execute(
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
"WHERE guildid={}",
st, cl, br, bc, bg, guild->GetId());
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
// Immediate reading for log
if (QueryResult qr = CharacterDatabase.Query(
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
guild->GetId()))
{
Field* f = qr->Fetch();
LOG_DEBUG("playerbots",
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
}
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
// The guild is only counted if it is actually created
++guildNumber;
++createdThisRun;
}
// Shows the true total and how many were created during this run
LOG_INFO("playerbots", "{} random bot guilds available (created this run: {})",
uint32(sPlayerbotAIConfig->randomBotGuilds.size()), createdThisRun);
}
std::string const RandomPlayerbotFactory::CreateRandomGuildName()
{
std::string guildName = "";

View File

@@ -44,22 +44,25 @@ public:
BloodelfFemale
};
static constexpr NameRaceAndGender CombineRaceAndGender(uint8 race, uint8 gender);
static constexpr NameRaceAndGender CombineRaceAndGender(uint8 gender, uint8 race);
RandomPlayerbotFactory() {};
RandomPlayerbotFactory(uint32 accountId);
virtual ~RandomPlayerbotFactory() {}
Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& names);
static void CreateRandomBots();
static void CreateRandomGuilds();
static void CreateRandomArenaTeams(ArenaType slot, uint32 count);
static std::string const CreateRandomGuildName();
static uint32 CalculateTotalAccountCount();
static uint32 CalculateAvailableCharsPerAccount();
private:
static bool IsValidRaceClassCombination(uint8 race, uint8 class_, uint32 expansion);
std::string const CreateRandomBotName(NameRaceAndGender raceAndGender);
static std::string const CreateRandomArenaTeamName();
uint32 accountId;
static std::map<uint8, std::vector<uint8>> availableRaces;
};
#endif

View File

@@ -26,6 +26,7 @@
#include "DatabaseEnv.h"
#include "Define.h"
#include "FleeManager.h"
#include "GameTime.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GuildMgr.h"
@@ -35,7 +36,7 @@
#include "NewRpgInfo.h"
#include "NewRpgStrategy.h"
#include "ObjectGuid.h"
#include "PerfMonitor.h"
#include "PerformanceMonitor.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
@@ -358,7 +359,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
if (totalPmo)
totalPmo->finish();
totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");
totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");
if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled)
return;
@@ -401,7 +402,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval;
SetNextCheckDelay(updateIntervalTurboBoost * (onlineBotFocus + 25) * 10);
PerfMonitorOperation* pmo = sPerfMonitor->start(
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(
PERF_MON_TOTAL,
onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal");
@@ -632,7 +633,7 @@ void RandomPlayerbotMgr::AssignAccountTypes()
uint32 existingRndBotAccounts = 0;
uint32 existingAddClassAccounts = 0;
for (auto const& [accountId, accountType] : currentAssignments)
for (const auto& [accountId, accountType] : currentAssignments)
{
if (accountType == 1) existingRndBotAccounts++;
else if (accountType == 2) existingAddClassAccounts++;
@@ -669,9 +670,9 @@ void RandomPlayerbotMgr::AssignAccountTypes()
uint32 toAssign = neededAddClassAccounts - existingAddClassAccounts;
uint32 assigned = 0;
for (size_t idx = allRandomBotAccounts.size(); idx-- > 0 && assigned < toAssign;)
for (int i = allRandomBotAccounts.size() - 1; i >= 0 && assigned < toAssign; i--)
{
uint32 accountId = allRandomBotAccounts[idx];
uint32 accountId = allRandomBotAccounts[i];
if (currentAssignments[accountId] == 0) // Unassigned
{
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 2, assignment_date = NOW() WHERE account_id = {}", accountId);
@@ -687,7 +688,7 @@ void RandomPlayerbotMgr::AssignAccountTypes()
}
// Populate filtered account lists with ALL accounts of each type
for (auto const& [accountId, accountType] : currentAssignments)
for (const auto& [accountId, accountType] : currentAssignments)
{
if (accountType == 1) rndBotTypeAccounts.push_back(accountId);
else if (accountType == 2) addClassTypeAccounts.push_back(accountId);
@@ -797,7 +798,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
std::vector<CharacterInfo> allianceChars;
std::vector<CharacterInfo> hordeChars;
for (auto const& charInfo : allCharacters)
for (const auto& charInfo : allCharacters)
{
if (IsAlliance(charInfo.rRace))
allianceChars.push_back(charInfo);
@@ -821,7 +822,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline
? urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
sPlayerbotAIConfig->maxRandomBotInWorldTime)
: sPlayerbotAIConfig->permanentlyInWorldTime;
: sPlayerbotAIConfig->permanantlyInWorldTime;
SetEventValue(charInfo.guid, "add", 1, add_time);
SetEventValue(charInfo.guid, "logout", 0, 0);
@@ -831,7 +832,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
};
// PHASE 1: Log-in Alliance bots up to allowedAllianceCount
for (auto const& charInfo : allianceChars)
for (const auto& charInfo : allianceChars)
{
if (!allowedAllianceCount)
break;
@@ -844,7 +845,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
}
// PHASE 2: Log-in Horde bots up to maxAllowedBotCount
for (auto const& charInfo : hordeChars)
for (const auto& charInfo : hordeChars)
{
if (!maxAllowedBotCount)
break;
@@ -854,7 +855,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
}
// PHASE 3: If maxAllowedBotCount wasn't reached, log-in more Alliance bots
for (auto const& charInfo : allianceChars)
for (const auto& charInfo : allianceChars)
{
if (!maxAllowedBotCount)
break;
@@ -1247,7 +1248,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
void RandomPlayerbotMgr::LogBattlegroundInfo()
{
for (auto const& queueTypePair : BattlegroundData)
for (const auto& queueTypePair : BattlegroundData)
{
uint8 queueType = queueTypePair.first;
@@ -1255,7 +1256,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo()
if (uint8 type = BattlegroundMgr::BGArenaType(queueTypeId))
{
for (auto const& bracketIdPair : queueTypePair.second)
for (const auto& bracketIdPair : queueTypePair.second)
{
auto& bgInfo = bracketIdPair.second;
if (bgInfo.minLevel == 0)
@@ -1305,7 +1306,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo()
break;
}
for (auto const& bracketIdPair : queueTypePair.second)
for (const auto& bracketIdPair : queueTypePair.second)
{
auto& bgInfo = bracketIdPair.second;
if (bgInfo.minLevel == 0)
@@ -1424,7 +1425,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
LOG_DEBUG("playerbots", "Bot #{}: log out", bot);
SetEventValue(bot, "add", 0, 0);
currentBots.remove(bot);
currentBots.erase(std::remove(currentBots.begin(), currentBots.end(), bot), currentBots.end());
if (player)
LogoutPlayerBot(botGUID);
@@ -1479,10 +1480,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
if (!sRandomPlayerbotMgr->IsRandomBot(player))
update = false;
if (player->GetGroup() && botAI->GetGroupLeader())
if (player->GetGroup() && botAI->GetGroupMaster())
{
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
{
update = false;
}
@@ -1575,7 +1576,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot)
{
idleBot = true;
}
if (idleBot)
{
// randomize
@@ -1654,10 +1655,6 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
if (bot->IsBeingTeleported() || !bot->IsInWorld())
return;
// no teleport / movement update when rooted.
if (bot->IsRooted())
return;
// ignore when in queue for battle grounds.
if (bot->InBattlegroundQueue())
return;
@@ -1707,7 +1704,8 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
return;
}
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
std::shuffle(std::begin(tlocs), std::end(tlocs), RandomEngine::Instance());
for (uint32 i = 0; i < tlocs.size(); i++)
@@ -2258,7 +2256,7 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
// Pick a weighted city randomly, then a random banker in that city
// then teleport to that banker
CityId selectedCity = weightedCities[urand(0, weightedCities.size() - 1)];
auto const& bankers = cityToBankers.at(selectedCity);
const auto& bankers = cityToBankers.at(selectedCity);
uint32 selectedBankerEntry = bankers[urand(0, bankers.size() - 1)];
auto locIt = bankerEntryToLocation.find(selectedBankerEntry);
if (locIt != bankerEntryToLocation.end())
@@ -2300,7 +2298,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot)
if (bot->InBattleground())
return;
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleport");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleport");
std::vector<WorldLocation> locs;
std::list<Unit*> targets;
@@ -2362,7 +2360,7 @@ void RandomPlayerbotMgr::IncreaseLevel(Player* bot)
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel");
uint32 lastLevel = GetValue(bot, "level");
uint8 level = bot->GetLevel() + 1;
if (level > maxLevel)
@@ -2401,7 +2399,7 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
minLevel = std::max(minLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL));
}
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst");
uint32 level;
@@ -2480,7 +2478,7 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot)
if (!botAI)
return;
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
uint32 level = sPlayerbotAIConfig->randomBotMinLevel;
SetValue(bot, "level", level);
PlayerbotFactory factory(bot, level);
@@ -2569,7 +2567,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
LOG_DEBUG("playerbots", "Refreshing bot {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetName().c_str());
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "Refresh");
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "Refresh");
botAI->Reset();
@@ -2715,73 +2713,69 @@ std::vector<uint32> RandomPlayerbotMgr::GetBgBots(uint32 bracket)
return std::move(BgBots);
}
CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event)
uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const event)
{
BotEventCache& cache = eventCache[bot];
// Load once
if (!cache.loaded)
// load all events at once on first event load
if (eventCache[bot].empty())
{
cache.events.clear();
PlayerbotsDatabasePreparedStatement* stmt =
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT);
stmt->SetData(0, 0);
stmt->SetData(1, bot);
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
std::string const eventName = fields[0].Get<std::string>();
CachedEvent e;
e.value = fields[1].Get<uint32>();
e.lastChangeTime = fields[2].Get<uint32>();
e.validIn = fields[3].Get<uint32>();
e.data = fields[4].Get<std::string>();
cache.events.emplace(fields[0].Get<std::string>(), std::move(e));
eventCache[bot][eventName] = std::move(e);
} while (result->NextRow());
}
cache.loaded = true;
}
auto it = cache.events.find(event);
if (it == cache.events.end())
return nullptr;
CachedEvent& e = it->second;
// remove expired events
if (e.validIn && (NowSeconds() - e.lastChangeTime) >= e.validIn && event != "specNo" && event != "specLink")
CachedEvent& e = eventCache[bot][event];
/*if (e.IsEmpty())
{
cache.events.erase(it);
return nullptr;
QueryResult results = PlayerbotsDatabase.Query("SELECT `value`, `time`, validIn, `data` FROM
playerbots_random_bots WHERE owner = 0 AND bot = {} AND event = {}", bot, event.c_str());
if (results)
{
Field* fields = results->Fetch();
e.value = fields[0].Get<uint32>();
e.lastChangeTime = fields[1].Get<uint32>();
e.validIn = fields[2].Get<uint32>();
e.data = fields[3].Get<std::string>();
}
}
*/
if ((time(0) - e.lastChangeTime) >= e.validIn && event != "specNo" && event != "specLink")
e.value = 0;
return e.value;
}
std::string const RandomPlayerbotMgr::GetEventData(uint32 bot, std::string const event)
{
std::string data = "";
if (GetEventValue(bot, event))
{
CachedEvent e = eventCache[bot][event];
data = e.data;
}
return &e;
return data;
}
uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const& event)
{
if (CachedEvent* e = FindEvent(bot, event))
return e->value;
return 0;
}
std::string RandomPlayerbotMgr::GetEventData(uint32 bot, std::string const& event)
{
if (CachedEvent* e = FindEvent(bot, event))
return e->data;
return "";
}
uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const& event, uint32 value, uint32 validIn,
std::string const& data)
uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn,
std::string const data)
{
PlayerbotsDatabaseTransaction trans = PlayerbotsDatabase.BeginTransaction();
@@ -2797,55 +2791,43 @@ uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const& event, u
stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_RANDOM_BOTS);
stmt->SetData(0, 0);
stmt->SetData(1, bot);
stmt->SetData(2, NowSeconds());
stmt->SetData(2, static_cast<uint32>(GameTime::GetGameTime().count()));
stmt->SetData(3, validIn);
stmt->SetData(4, event.c_str());
stmt->SetData(5, value);
if (!data.empty())
if (data != "")
{
stmt->SetData(6, data.c_str());
}
else
stmt->SetData(6); // NULL
{
stmt->SetData(6);
}
trans->Append(stmt);
}
PlayerbotsDatabase.CommitTransaction(trans);
// Update in-memory cache
BotEventCache& cache = eventCache[bot];
cache.loaded = true;
if (!value)
{
cache.events.erase(event);
return 0;
}
CachedEvent& e = cache.events[event]; // create-on-write is OK here
e.value = value;
e.lastChangeTime = NowSeconds();
e.validIn = validIn;
e.data = data;
CachedEvent e(value, (uint32)time(nullptr), validIn, data);
eventCache[bot][event] = std::move(e);
return value;
}
uint32 RandomPlayerbotMgr::GetValue(uint32 bot, std::string const& type) { return GetEventValue(bot, type); }
uint32 RandomPlayerbotMgr::GetValue(uint32 bot, std::string const type) { return GetEventValue(bot, type); }
uint32 RandomPlayerbotMgr::GetValue(Player* bot, std::string const& type)
uint32 RandomPlayerbotMgr::GetValue(Player* bot, std::string const type)
{
return GetValue(bot->GetGUID().GetCounter(), type);
}
std::string RandomPlayerbotMgr::GetData(uint32 bot, std::string const& type) { return GetEventData(bot, type); }
std::string const RandomPlayerbotMgr::GetData(uint32 bot, std::string const type) { return GetEventData(bot, type); }
void RandomPlayerbotMgr::SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data)
void RandomPlayerbotMgr::SetValue(uint32 bot, std::string const type, uint32 value, std::string const data)
{
SetEventValue(bot, type, value, sPlayerbotAIConfig->maxRandomBotInWorldTime, data);
}
void RandomPlayerbotMgr::SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data)
void RandomPlayerbotMgr::SetValue(Player* bot, std::string const type, uint32 value, std::string const data)
{
SetValue(bot->GetGUID().GetCounter(), type, value, data);
}
@@ -3134,7 +3116,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
void RandomPlayerbotMgr::OnPlayerLoginError(uint32 bot)
{
SetEventValue(bot, "add", 0, 0);
currentBots.remove(bot);
currentBots.erase(std::remove(currentBots.begin(), currentBots.end(), bot), currentBots.end());
}
Player* RandomPlayerbotMgr::GetRandomPlayer()
@@ -3214,12 +3196,6 @@ void RandomPlayerbotMgr::PrintStats()
lvlPerRace[bot->getRace()] += bot->GetLevel();
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
{
LOG_ERROR("playerbots", "Player/Bot {} is registered in sRandomPlayerbotMgr playerBots and has no bot AI!", bot->GetName().c_str());
continue;
}
if (botAI->AllowActivity())
++active;
@@ -3522,8 +3498,7 @@ void RandomPlayerbotMgr::Remove(Player* bot)
stmt->SetData(1, owner.GetCounter());
PlayerbotsDatabase.Execute(stmt);
uint32 botId = owner.GetCounter();
eventCache.erase(botId);
eventCache[owner.GetCounter()].clear();
LogoutPlayerBot(owner);
}
@@ -3540,7 +3515,7 @@ CreatureData const* RandomPlayerbotMgr::GetCreatureDataByEntry(uint32 entry)
return nullptr;
}
ObjectGuid RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId)
ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId)
{
ObjectGuid battleMasterGUID = ObjectGuid::Empty;

View File

@@ -9,7 +9,6 @@
#include "NewRpgInfo.h"
#include "ObjectGuid.h"
#include "PlayerbotMgr.h"
#include "GameTime.h"
struct BattlegroundInfo
{
@@ -43,23 +42,28 @@ struct BattlegroundInfo
};
class ChatHandler;
class PerfMonitorOperation;
class PerformanceMonitorOperation;
class WorldLocation;
struct CachedEvent
class CachedEvent
{
uint32 value = 0;
uint32 lastChangeTime = 0;
uint32 validIn = 0;
public:
CachedEvent() : value(0), lastChangeTime(0), validIn(0), data("") {}
CachedEvent(const CachedEvent& other)
: value(other.value), lastChangeTime(other.lastChangeTime), validIn(other.validIn), data(other.data)
{
}
CachedEvent(uint32 value, uint32 lastChangeTime, uint32 validIn, std::string const data = "")
: value(value), lastChangeTime(lastChangeTime), validIn(validIn), data(data)
{
}
bool IsEmpty() { return !lastChangeTime; }
uint32 value;
uint32 lastChangeTime;
uint32 validIn;
std::string data;
bool IsEmpty() const { return !lastChangeTime; }
};
struct BotEventCache
{
bool loaded = false;
std::unordered_map<std::string, CachedEvent> events;
};
// https://gist.github.com/bradley219/5373998
@@ -135,13 +139,13 @@ public:
void Revive(Player* player);
void ChangeStrategy(Player* player);
void ChangeStrategyOnce(Player* player);
uint32 GetValue(Player* bot, std::string const& type);
uint32 GetValue(uint32 bot, std::string const& type);
std::string GetData(uint32 bot, std::string const& type);
void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = "");
void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = "");
uint32 GetValue(Player* bot, std::string const type);
uint32 GetValue(uint32 bot, std::string const type);
std::string const GetData(uint32 bot, std::string const type);
void SetValue(uint32 bot, std::string const type, uint32 value, std::string const data = "");
void SetValue(Player* bot, std::string const type, uint32 value, std::string const data = "");
void Remove(Player* bot);
ObjectGuid GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
ObjectGuid const GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
CreatureData const* GetCreatureDataByEntry(uint32 entry);
void LoadBattleMastersCache();
std::map<uint32, std::map<uint32, BattlegroundInfo>> BattlegroundData;
@@ -199,11 +203,10 @@ private:
bool _isBotInitializing = true;
bool _isBotLogging = true;
NewRpgStatistic rpgStasticTotal;
CachedEvent* FindEvent(uint32 bot, std::string const& event);
uint32 GetEventValue(uint32 bot, std::string const& event);
std::string GetEventData(uint32 bot, std::string const& event);
uint32 SetEventValue(uint32 bot, std::string const& event, uint32 value, uint32 validIn,
std::string const& data = "");
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,
std::string const data = "");
void GetBots();
std::vector<uint32> GetBgBots(uint32 bracket);
time_t BgCheckTimer;
@@ -225,7 +228,7 @@ private:
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> BattleMastersCache;
std::unordered_map<uint32, BotEventCache> eventCache;
std::map<uint32, std::map<std::string, CachedEvent>> eventCache;
std::list<uint32> currentBots;
uint32 bgBotsCount;
uint32 playersLevel;
@@ -235,7 +238,6 @@ private:
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
//void ScaleBotActivity(); // Deprecated function
static inline uint32 NowSeconds() { return static_cast<uint32>(GameTime::GetGameTime().count()); }
};
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()

View File

@@ -1,165 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "BloodDKStrategy.h"
#include "Playerbots.h"
class BloodDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
BloodDKStrategyActionNodeFactory()
{
creators["rune strike"] = &rune_strike;
creators["heart strike"] = &heart_strike;
creators["death strike"] = &death_strike;
creators["icy touch"] = &icy_touch;
creators["dark command"] = &dark_command;
creators["taunt spell"] = &dark_command;
}
private:
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rune strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"heart strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"death strike",
{
NextAction("frost presence")
},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* dark_command([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"dark command",
{
NextAction("frost presence")
},
/*A*/ {
NextAction("death grip")
},
/*C*/ {}
);
}
};
BloodDKStrategy::BloodDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
{
actionNodeFactories.Add(new BloodDKStrategyActionNodeFactory());
}
std::vector<NextAction> BloodDKStrategy::getDefaultActions()
{
return {
NextAction("rune strike", ACTION_DEFAULT + 0.8f),
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
NextAction("heart strike", ACTION_DEFAULT + 0.6f),
NextAction("blood strike", ACTION_DEFAULT + 0.5f),
NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
NextAction("death coil", ACTION_DEFAULT + 0.3f),
NextAction("plague strike", ACTION_DEFAULT + 0.2f),
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void BloodDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"rune strike",
{
NextAction("rune strike", ACTION_NORMAL + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"blood tap",
{
NextAction("blood tap", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"lose aggro",
{
NextAction("dark command", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"low health",
{
NextAction("army of the dead", ACTION_HIGH + 4),
NextAction("death strike", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"critical health",
{
NextAction("vampiric blood", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"icy touch",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"plague strike",
{
NextAction("plague strike", ACTION_HIGH + 2)
}
)
);
}

View File

@@ -1,169 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FrostDKStrategy.h"
#include "Playerbots.h"
class FrostDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
FrostDKStrategyActionNodeFactory()
{
creators["icy touch"] = &icy_touch;
creators["obliterate"] = &obliterate;
creators["howling blast"] = &howling_blast;
creators["frost strike"] = &frost_strike;
creators["rune strike"] = &rune_strike;
creators["unbreakable armor"] = &unbreakable_armor;
}
private:
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* obliterate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"obliterate",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rune strike",
/*P*/ { NextAction("blood presence") },
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* frost_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"frost strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* howling_blast([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"howling blast",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"unbreakable armor",
/*P*/ { NextAction("blood tap") },
/*A*/ {},
/*C*/ {}
);
}
};
FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
{
actionNodeFactories.Add(new FrostDKStrategyActionNodeFactory());
}
std::vector<NextAction> FrostDKStrategy::getDefaultActions()
{
return {
NextAction("obliterate", ACTION_DEFAULT + 0.7f),
NextAction("frost strike", ACTION_DEFAULT + 0.4f),
NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"unbreakable armor",
{
NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f)
}
)
);
triggers.push_back(
new TriggerNode(
"freezing fog",
{
NextAction("howling blast", ACTION_DEFAULT + 0.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"high blood rune",
{
NextAction("blood strike", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"army of the dead",
{
NextAction("army of the dead", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"icy touch",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"plague strike",
{
NextAction("plague strike", ACTION_HIGH + 2)
}
)
);
}
void FrostDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("howling blast", ACTION_HIGH + 4)
}
)
);
}

View File

@@ -1,192 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "UnholyDKStrategy.h"
#include "Playerbots.h"
class UnholyDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
UnholyDKStrategyActionNodeFactory()
{
creators["death strike"] = &death_strike;
creators["scourge strike"] = &scourge_strike;
creators["ghoul frenzy"] = &ghoul_frenzy;
creators["corpse explosion"] = &corpse_explosion;
creators["icy touch"] = &icy_touch;
}
private:
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"death strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ghoul frenzy",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"corpse explosion",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"scourge strike",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"icy touch",
/*P*/ { NextAction("blood presence") },
/*A*/ {},
/*C*/ {}
);
}
};
UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
{
actionNodeFactories.Add(new UnholyDKStrategyActionNodeFactory());
}
std::vector<NextAction> UnholyDKStrategy::getDefaultActions()
{
return {
NextAction("death and decay", ACTION_HIGH + 5),
NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
NextAction("death coil", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"death and decay cooldown",
{
NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
NextAction("blood strike", ACTION_DEFAULT + 0.6f),
NextAction("plague strike", ACTION_DEFAULT + 0.5f),
}
)
);
triggers.push_back(
new TriggerNode(
"dd cd and no desolation",
{
NextAction("blood strike", ACTION_DEFAULT + 0.75f)
}
)
);
triggers.push_back(
new TriggerNode(
"high frost rune",
{
NextAction("icy touch", ACTION_NORMAL + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"high blood rune",
{
NextAction("blood strike", ACTION_NORMAL + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"high unholy rune",
{
NextAction("plague strike", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode("dd cd and plague strike 3s",
{
NextAction("plague strike", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode("dd cd and icy touch 3s",
{
NextAction("icy touch", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode("no rune",
{
NextAction("empower rune weapon", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"army of the dead",
{
NextAction("army of the dead", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode("bone shield",
{
NextAction("bone shield", ACTION_HIGH + 3)
}
)
);
}
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"loot available",
{
NextAction("corpse explosion", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("death and decay", ACTION_NORMAL + 3),
NextAction("corpse explosion", ACTION_NORMAL + 3)
}
)
);
}

View File

@@ -1,256 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "BearTankDruidStrategy.h"
#include "Playerbots.h"
class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
BearTankDruidStrategyActionNodeFactory()
{
creators["melee"] = &melee;
creators["feral charge - bear"] = &feral_charge_bear;
creators["swipe (bear)"] = &swipe_bear;
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["bear form"] = &bear_form;
creators["dire bear form"] = &dire_bear_form;
creators["mangle (bear)"] = &mangle_bear;
creators["maul"] = &maul;
creators["bash"] = &bash;
creators["swipe"] = &swipe;
creators["lacerate"] = &lacerate;
creators["demoralizing roar"] = &demoralizing_roar;
creators["taunt spell"] = &growl;
}
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* feral_charge_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"feral charge - bear",
/*P*/ {},
/*A*/ { NextAction("reach melee") },
/*C*/ {}
);
}
static ActionNode* swipe_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"swipe (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"bear form",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"dire bear form",
/*P*/ { NextAction("caster form") },
/*A*/ { NextAction("bear form") },
/*C*/ {}
);
}
static ActionNode* mangle_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"maul",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* bash([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"bash",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* swipe([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"swipe",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* lacerate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"lacerate",
/*P*/ {},
/*A*/ { NextAction("maul") },
/*C*/ {}
);
}
static ActionNode* growl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"growl",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* demoralizing_roar([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"demoralizing roar",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
};
BearTankDruidStrategy::BearTankDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
{
actionNodeFactories.Add(new BearTankDruidStrategyActionNodeFactory());
}
std::vector<NextAction> BearTankDruidStrategy::getDefaultActions()
{
return {
NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f),
NextAction("lacerate", ACTION_DEFAULT + 0.3f),
NextAction("maul", ACTION_DEFAULT + 0.2f),
NextAction("enrage", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void BearTankDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - bear", ACTION_NORMAL + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"bear form",
{
NextAction("dire bear form", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"low health",
{
NextAction("frenzied regeneration", ACTION_HIGH + 7)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_HIGH + 7)
}
)
);
triggers.push_back(
new TriggerNode(
"lose aggro",
{
NextAction("growl", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("demoralizing roar", ACTION_HIGH + 6),
NextAction("swipe (bear)", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("swipe (bear)", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"bash",
{
NextAction("bash", ACTION_INTERRUPT + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"bash on enemy healer",
{
NextAction("bash on enemy healer", ACTION_INTERRUPT + 1)
}
)
);
}

View File

@@ -1,254 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "CasterDruidStrategy.h"
#include "AiObjectContext.h"
#include "FeralDruidStrategy.h"
class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
CasterDruidStrategyActionNodeFactory()
{
creators["faerie fire"] = &faerie_fire;
creators["hibernate"] = &hibernate;
creators["entangling roots"] = &entangling_roots;
creators["entangling roots on cc"] = &entangling_roots_on_cc;
creators["wrath"] = &wrath;
creators["starfall"] = &starfall;
creators["insect swarm"] = &insect_swarm;
creators["moonfire"] = &moonfire;
creators["starfire"] = &starfire;
creators["moonkin form"] = &moonkin_form;
}
private:
static ActionNode* faerie_fire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* hibernate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"hibernate",
/*P*/ { NextAction("moonkin form") },
/*A*/ { NextAction("entangling roots") },
/*C*/ {}
);
}
static ActionNode* entangling_roots([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"entangling roots",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"entangling roots on cc",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* wrath([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"wrath",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* starfall([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"starfall",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* insect_swarm([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"insect swarm",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* moonfire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"moonfire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* starfire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"starfire",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* moonkin_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"moonkin form",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {}
);
}
};
CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
{
actionNodeFactories.Add(new CasterDruidStrategyActionNodeFactory());
actionNodeFactories.Add(new ShapeshiftDruidStrategyActionNodeFactory());
}
std::vector<NextAction> CasterDruidStrategy::getDefaultActions()
{
return {
NextAction("starfall", ACTION_HIGH + 1.0f),
NextAction("force of nature", ACTION_DEFAULT + 1.0f),
NextAction("wrath", ACTION_DEFAULT + 0.1f),
};
}
void CasterDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"eclipse (lunar) cooldown",
{
NextAction("starfire", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (solar) cooldown",
{
NextAction("wrath", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"insect swarm",
{
NextAction("insect swarm", ACTION_NORMAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"moonfire",
{
NextAction("moonfire", ACTION_NORMAL + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (solar)",
{
NextAction("wrath", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (lunar)",
{
NextAction("starfire", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"medium mana",
{
NextAction("innervate", ACTION_HIGH + 9)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy too close for spell",
{
NextAction("flee", ACTION_MOVE + 9)
}
)
);
}
void CasterDruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"hurricane channel check",
{
NextAction("cancel channel", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("hurricane", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
NextAction("moonfire on attacker", ACTION_NORMAL + 3)
}
)
);
}
void CasterDruidDebuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"faerie fire",
{
NextAction("faerie fire", ACTION_HIGH)
}
)
);
}

View File

@@ -1,314 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "CatDpsDruidStrategy.h"
#include "AiObjectContext.h"
class CatDpsDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
CatDpsDruidStrategyActionNodeFactory()
{
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["melee"] = &melee;
creators["feral charge - cat"] = &feral_charge_cat;
creators["cat form"] = &cat_form;
creators["claw"] = &claw;
creators["mangle (cat)"] = &mangle_cat;
creators["rake"] = &rake;
creators["ferocious bite"] = &ferocious_bite;
creators["rip"] = &rip;
creators["pounce"] = &pounce;
creators["ravage"] = &ravage;
}
private:
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ { NextAction("feral charge - cat") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"feral charge - cat",
/*P*/ {},
/*A*/ { NextAction("reach melee") },
/*C*/ {}
);
}
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cat form",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"claw",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ { NextAction("rip") },
/*C*/ {}
);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"pounce",
/*P*/ {},
/*A*/ { NextAction("ravage") },
/*C*/ {}
);
}
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ravage",
/*P*/ {},
/*A*/ { NextAction("shred") },
/*C*/ {}
);
}
};
CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
{
actionNodeFactories.Add(new CatDpsDruidStrategyActionNodeFactory());
}
std::vector<NextAction> CatDpsDruidStrategy::getDefaultActions()
{
return {
NextAction("tiger's fury", ACTION_DEFAULT + 0.1f)
};
}
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
// Default priority
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy",
{
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("claw", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy",
{
NextAction("claw", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f)
}
)
);
// Main spell
triggers.push_back(
new TriggerNode(
"cat form", {
NextAction("cat form", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"savage roar", {
NextAction("savage roar", ACTION_HIGH + 7)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points available",
{
NextAction("rip", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"ferocious bite time",
{
NextAction("ferocious bite", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"target with combo points almost dead",
{
NextAction("ferocious bite", ACTION_HIGH + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"mangle (cat)",
{
NextAction("mangle (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"rake",
{
NextAction("rake", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"medium threat",
{
NextAction("cower", ACTION_HIGH + 1)
}
)
);
// AOE
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("swipe (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("rake on attacker", ACTION_HIGH + 2)
}
)
);
// Reach target
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - cat", ACTION_HIGH + 9)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("dash", ACTION_HIGH + 8)
}
)
);
}
void CatAoeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) {}

View File

@@ -1,194 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GenericDruidNonCombatStrategy.h"
#include "Playerbots.h"
#include "AiFactory.h"
class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericDruidNonCombatStrategyActionNodeFactory()
{
creators["thorns"] = &thorns;
creators["thorns on party"] = &thorns_on_party;
creators["mark of the wild"] = &mark_of_the_wild;
creators["mark of the wild on party"] = &mark_of_the_wild_on_party;
// creators["innervate"] = &innervate;
creators["regrowth_on_party"] = &regrowth_on_party;
creators["rejuvenation on party"] = &rejuvenation_on_party;
creators["remove curse on party"] = &remove_curse_on_party;
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["revive"] = &revive;
}
private:
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("thorns",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* thorns_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("thorns on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* mark_of_the_wild([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mark of the wild",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* mark_of_the_wild_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mark of the wild on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* remove_curse_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("remove curse on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* revive([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("revive",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
};
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericDruidNonCombatStrategyActionNodeFactory());
}
void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild", { NextAction("mark of the wild", 14.0f) }));
triggers.push_back(new TriggerNode("party member cure poison", { NextAction("abolish poison on party", 20.0f) }));
triggers.push_back(new TriggerNode("party member dead", { NextAction("revive", ACTION_CRITICAL_HEAL + 10) }));
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
triggers.push_back(
new TriggerNode("party member critical health",
{
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
}));
triggers.push_back(
new TriggerNode("party member low health",
{
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
}));
triggers.push_back(
new TriggerNode("party member medium health",
{ NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
}));
triggers.push_back(
new TriggerNode("party member almost full health",
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2) }));
triggers.push_back(
new TriggerNode("party member remove curse",
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
triggers.push_back(
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
triggers.push_back(new TriggerNode("party member critical health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
}));
triggers.push_back(new TriggerNode("party member low health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
}));
triggers.push_back(new TriggerNode("party member medium health", {
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
}));
triggers.push_back(new TriggerNode("party member almost full health", {
NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
}));
triggers.push_back(new TriggerNode("party member remove curse", {
NextAction("remove curse on party", ACTION_DISPEL + 7),
}));
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
if (specTab == 0 || specTab == 2) // Balance or Restoration
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
if (specTab == 1) // Feral
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
}
GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericDruidNonCombatStrategyActionNodeFactory());
}
void GenericDruidBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild on party", {
NextAction("mark of the wild on party", 13.0f),
}));
triggers.push_back(new TriggerNode("thorns on main tank", {
NextAction("thorns on main tank", 11.0f),
}));
triggers.push_back(new TriggerNode("thorns", {
NextAction("thorns", 10.0f),
}));
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GenericDruidStrategy.h"
#include "Playerbots.h"
class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericDruidStrategyActionNodeFactory()
{
creators["melee"] = &melee;
creators["caster form"] = &caster_form;
creators["cure poison"] = &cure_poison;
creators["cure poison on party"] = &cure_poison_on_party;
creators["abolish poison"] = &abolish_poison;
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["rebirth"] = &rebirth;
creators["entangling roots on cc"] = &entangling_roots_on_cc;
creators["innervate"] = &innervate;
}
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("melee",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* caster_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("caster form",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* rebirth([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rebirth",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("entangling roots on cc",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("innervate",
/*P*/ {},
/*A*/ { NextAction("mana potion") },
/*C*/ {});
}
};
GenericDruidStrategy::GenericDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericDruidStrategyActionNodeFactory());
}
void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) }));
triggers.push_back(new TriggerNode("combat party member dead",
{ NextAction("rebirth", ACTION_HIGH + 9) }));
triggers.push_back(new TriggerNode("being attacked",
{ NextAction("nature's grasp", ACTION_HIGH + 1) }));
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
}
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("party member cure poison",
{ NextAction("abolish poison on party", ACTION_DISPEL + 1) }));
triggers.push_back(
new TriggerNode("party member remove curse",
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
}
void DruidBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"nature's swiftness", { NextAction("nature's swiftness", ACTION_HIGH + 9) }));
}
void DruidCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) }));
triggers.push_back(new TriggerNode(
"entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) }));
triggers.push_back(new TriggerNode(
"hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) }));
}
void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("healer should attack",
{
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
NextAction("wrath", ACTION_DEFAULT + 0.1f),
NextAction("starfire", ACTION_DEFAULT),
}));
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "HealDruidStrategy.h"
#include "Playerbots.h"
class HealDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
HealDruidStrategyActionNodeFactory() {
creators["nourish on party"] = &nourtish_on_party;
}
private:
static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("nourish on party",
/*P*/ {},
/*A*/ { NextAction("healing touch on party") },
/*C*/ {});
}
};
HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
{
actionNodeFactories.Add(new HealDruidStrategyActionNodeFactory());
}
void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"party member to heal out of spell range",
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
// CRITICAL
triggers.push_back(
new TriggerNode("party member critical health",
{
NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f),
NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4),
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3),
NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2),
NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1),
}));
triggers.push_back(
new TriggerNode("party member critical health",
{ NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) }));
triggers.push_back(new TriggerNode(
"group heal setting",
{
NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f),
NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f),
}));
triggers.push_back(
new TriggerNode("medium group heal setting",
{
NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f),
NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f) }));
// LOW
triggers.push_back(
new TriggerNode("party member low health",
{ NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f),
NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2),
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f),
}));
// MEDIUM
triggers.push_back(
new TriggerNode("party member medium health",
{
NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f),
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f),
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f),
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f),
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f) }));
// almost full
triggers.push_back(
new TriggerNode("party member almost full health",
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f),
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f),
NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f) }));
triggers.push_back(
new TriggerNode("medium mana", { NextAction("innervate", ACTION_HIGH + 5) }));
triggers.push_back(new TriggerNode("enemy too close for spell",
{ NextAction("flee", ACTION_MOVE + 9) }));
}

View File

@@ -1,307 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "OffhealDruidCatStrategy.h"
#include "Playerbots.h"
#include "Strategy.h"
class OffhealDruidCatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
OffhealDruidCatStrategyActionNodeFactory()
{
creators["cat form"] = &cat_form;
creators["mangle (cat)"] = &mangle_cat;
creators["shred"] = &shred;
creators["rake"] = &rake;
creators["rip"] = &rip;
creators["ferocious bite"] = &ferocious_bite;
creators["savage roar"] = &savage_roar;
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["healing touch on party"] = &healing_touch_on_party;
creators["regrowth on party"] = &regrowth_on_party;
creators["rejuvenation on party"] = &rejuvenation_on_party;
}
private:
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cat form",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"shred",
/*P*/ {},
/*A*/ { NextAction("claw") },
/*C*/ {}
);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ { NextAction("rip") },
/*C*/ {}
);
}
static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"savage roar",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing touch on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
};
OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
{
actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory());
}
std::vector<NextAction> OffhealDruidCatStrategy::getDefaultActions()
{
return {
NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
NextAction("shred", ACTION_DEFAULT + 0.4f),
NextAction("rake", ACTION_DEFAULT + 0.3f),
NextAction("melee", ACTION_DEFAULT),
NextAction("cat form", ACTION_DEFAULT - 0.1f)
};
}
void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"cat form",
{
NextAction("cat form", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"savage roar",
{
NextAction("savage roar", ACTION_HIGH + 7)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points available",
{
NextAction("rip", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"ferocious bite time",
{
NextAction("ferocious bite", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"target with combo points almost dead",
{
NextAction("ferocious bite", ACTION_HIGH + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"mangle (cat)",
{
NextAction("mangle (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"rake",
{
NextAction("rake", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full",
{
NextAction("shred", ACTION_DEFAULT + 0.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_NORMAL)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy out of melee",
{
NextAction("feral charge - cat", ACTION_HIGH + 9),
NextAction("dash", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("swipe (cat)", ACTION_HIGH + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"low energy",
{
NextAction("tiger's fury", ACTION_NORMAL + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"party member critical health",
{
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"party member low health",
{
NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"party member medium health",
{
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"party member to heal out of spell range",
{
NextAction("reach party member to heal", ACTION_EMERGENCY + 3)
}
)
);
triggers.push_back(
new TriggerNode(
"low mana",
{
NextAction("innervate", ACTION_HIGH + 4)
}
)
);
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "BattlegroundStrategy.h"
#include "Playerbots.h"
void BGStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("often", { NextAction("bg join", relevance)}));
triggers.push_back(new TriggerNode("bg invite active", { NextAction("bg status check", relevance)}));
triggers.push_back(new TriggerNode("timer", { NextAction("bg strategy check", relevance)}));
}
BGStrategy::BGStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
void BattlegroundStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg waiting", { NextAction("bg move to start", ACTION_BG)}));
triggers.push_back(new TriggerNode("bg active", { NextAction("bg move to objective", ACTION_BG)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg check objective", ACTION_BG + 1)}));
triggers.push_back(new TriggerNode("dead", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
}
void WarsongStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY )}));
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f)}));
triggers.push_back(new TriggerNode("team flagcarrier near", { NextAction("bg protect fc", ACTION_RAID)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("player has flag", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
}
void AlteracStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("alliance no snowfall gy", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
}
void ArathiStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
}
void EyeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID)}));
triggers.push_back(new TriggerNode("player has flag",{ NextAction("bg move to objective", ACTION_EMERGENCY)}));
}
//TODO: Do Priorities
void IsleStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_MOVE)}));
triggers.push_back(new TriggerNode("timer", { NextAction("enter vehicle", ACTION_MOVE + 8.0f)}));
triggers.push_back(new TriggerNode("random", { NextAction("leave vehicle", ACTION_MOVE + 7.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("hurl boulder", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("fire cannon", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("napalm", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("enemy is close", { NextAction("steam blast", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("ram", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("enemy is close", { NextAction("ram", ACTION_MOVE + 9.1f)}));
triggers.push_back(new TriggerNode("enemy out of melee", { NextAction("steam rush", ACTION_MOVE + 9.2f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("incendiary rocket", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("rocket blast", ACTION_MOVE + 9.0f)}));
// this is bugged: it doesn't work, and stops glaive throw working (which is needed to take down gate)
// triggers.push_back(new TriggerNode("in vehicle", { NextAction("blade salvo", ACTION_MOVE + 9.0f)}));
triggers.push_back(new TriggerNode("in vehicle", { NextAction("glaive throw", ACTION_MOVE + 9.0f)}));
}
void ArenaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("no possible targets", { NextAction("arena tactics", ACTION_BG)}));
}

View File

@@ -1,193 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ChatCommandHandlerStrategy.h"
#include "Playerbots.h"
class ChatCommandActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
{
public:
ChatCommandActionNodeFactoryInternal() { creators["tank attack chat shortcut"] = &tank_attack_chat_shortcut; }
private:
static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* botAI)
{
return new ActionNode("tank attack chat shortcut",
/*P*/ {},
/*A*/ {},
/*C*/ { NextAction("attack my target", 100.0f) });
}
};
void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
PassTroughStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
NextAction("query item usage", relevance) }));
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
NextAction("loot", relevance) }));
triggers.push_back(new TriggerNode("u", { NextAction("use", relevance) }));
triggers.push_back(new TriggerNode("c", { NextAction("item count", relevance) }));
triggers.push_back(
new TriggerNode("items", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("inv", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("e", { NextAction("equip", relevance) }));
triggers.push_back(new TriggerNode("ue", { NextAction("unequip", relevance) }));
triggers.push_back(new TriggerNode("t", { NextAction("trade", relevance) }));
triggers.push_back(new TriggerNode("nt", { NextAction("trade", relevance) }));
triggers.push_back(new TriggerNode("s", { NextAction("sell", relevance) }));
triggers.push_back(new TriggerNode("b", { NextAction("buy", relevance) }));
triggers.push_back(new TriggerNode("r", { NextAction("reward", relevance) }));
triggers.push_back(
new TriggerNode("attack", { NextAction("attack my target", relevance) }));
triggers.push_back(
new TriggerNode("accept", { NextAction("accept quest", relevance) }));
triggers.push_back(
new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
triggers.push_back(new TriggerNode(
"tank attack", { NextAction("tank attack chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("talk", { NextAction("gossip hello", relevance),
NextAction("talk to quest giver", relevance) }));
triggers.push_back(
new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
triggers.push_back(
new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
triggers.push_back(
new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
triggers.push_back(
new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
triggers.push_back(
new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
triggers.push_back(
new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
triggers.push_back(
new TriggerNode("target", { NextAction("tell target", relevance) }));
triggers.push_back(
new TriggerNode("ready", { NextAction("ready check", relevance) }));
triggers.push_back(
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
triggers.push_back(
new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
triggers.push_back(
new TriggerNode("open items", { NextAction("open items", relevance) }));
triggers.push_back(
new TriggerNode("qi", { NextAction("query item usage", relevance) }));
triggers.push_back(
new TriggerNode("unlock items", { NextAction("unlock items", relevance) }));
triggers.push_back(
new TriggerNode("unlock traded item", { NextAction("unlock traded item", relevance) }));
triggers.push_back(
new TriggerNode("wipe", { NextAction("wipe", relevance) }));
triggers.push_back(new TriggerNode("tame", { NextAction("tame", relevance) }));
triggers.push_back(new TriggerNode("glyphs", { NextAction("glyphs", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("glyph equip", { NextAction("glyph equip", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("pet", { NextAction("pet", relevance) }));
triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) }));
triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) }));
}
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
{
actionNodeFactories.Add(new ChatCommandActionNodeFactoryInternal());
supported.push_back("quests");
supported.push_back("stats");
supported.push_back("leave");
supported.push_back("reputation");
supported.push_back("log");
supported.push_back("los");
supported.push_back("rpg status");
supported.push_back("rpg do quest");
supported.push_back("aura");
supported.push_back("drop");
supported.push_back("share");
supported.push_back("ll");
supported.push_back("ss");
supported.push_back("release");
supported.push_back("teleport");
supported.push_back("taxi");
supported.push_back("repair");
supported.push_back("talents");
supported.push_back("spells");
supported.push_back("co");
supported.push_back("nc");
supported.push_back("de");
supported.push_back("trainer");
supported.push_back("maintenance");
supported.push_back("remove glyph");
supported.push_back("autogear");
supported.push_back("equip upgrade");
supported.push_back("chat");
supported.push_back("home");
supported.push_back("destroy");
supported.push_back("reset botAI");
supported.push_back("emote");
supported.push_back("buff");
supported.push_back("help");
supported.push_back("gb");
supported.push_back("bank");
supported.push_back("invite");
supported.push_back("lfg");
supported.push_back("spell");
supported.push_back("rti");
supported.push_back("position");
supported.push_back("summon");
supported.push_back("who");
supported.push_back("save mana");
supported.push_back("formation");
supported.push_back("stance");
supported.push_back("sendmail");
supported.push_back("mail");
supported.push_back("outfit");
supported.push_back("go");
supported.push_back("debug");
supported.push_back("cdebug");
supported.push_back("cs");
supported.push_back("wts");
supported.push_back("hire");
supported.push_back("craft");
supported.push_back("flag");
supported.push_back("range");
supported.push_back("ra");
supported.push_back("give leader");
supported.push_back("cheat");
supported.push_back("ginvite");
supported.push_back("guild promote");
supported.push_back("guild demote");
supported.push_back("guild remove");
supported.push_back("guild leave");
supported.push_back("rtsc");
supported.push_back("drink");
supported.push_back("calc");
supported.push_back("open items");
supported.push_back("qi");
supported.push_back("unlock items");
supported.push_back("unlock traded item");
supported.push_back("tame");
supported.push_back("glyphs"); // Added for custom Glyphs
supported.push_back("glyph equip"); // Added for custom Glyphs
supported.push_back("pet");
supported.push_back("pet attack");
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "CombatStrategy.h"
#include "Playerbots.h"
#include "Strategy.h"
void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"enemy out of spell",
{
NextAction("reach spell", ACTION_HIGH)
}
)
);
// drop target relevance 99 (lower than Worldpacket triggers)
triggers.push_back(
new TriggerNode(
"invalid target",
{
NextAction("drop target", 99)
}
)
);
triggers.push_back(
new TriggerNode(
"mounted",
{
NextAction("check mount state", 54)
}
)
);
triggers.push_back(
new TriggerNode(
"combat stuck",
{
NextAction("reset", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"not facing target",
{
NextAction("set facing", ACTION_MOVE + 7)
}
)
);
// The pet-attack trigger is commented out because it was forcing the bot's pet to attack, overriding stay and follow commands.
// Pets will automatically attack the bot's enemy if they are in "defensive" or "aggressive"
// stance, or if the master issues an attack command.
}
AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::vector<NextAction> AvoidAoeStrategy::getDefaultActions()
{
return {
NextAction("avoid aoe", ACTION_EMERGENCY)
};
}
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
}
void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
}
TankFaceStrategy::TankFaceStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::vector<NextAction> TankFaceStrategy::getDefaultActions()
{
return {
NextAction("tank face", ACTION_MOVE)
};
}
void TankFaceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
}
std::vector<NextAction> CombatFormationStrategy::getDefaultActions()
{
return {
NextAction("combat formation move", ACTION_NORMAL)
};
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "DeadStrategy.h"
#include "Playerbots.h"
void DeadStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
PassTroughStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("often", { NextAction("auto release", relevance) }));
triggers.push_back(
new TriggerNode("bg active", { NextAction("auto release", relevance) }));
triggers.push_back(
new TriggerNode("dead", { NextAction("find corpse", relevance) }));
triggers.push_back(new TriggerNode(
"corpse near", { NextAction("revive from corpse", relevance - 1.0f) }));
triggers.push_back(new TriggerNode("resurrect request",
{ NextAction("accept resurrect", relevance) }));
triggers.push_back(
new TriggerNode("falling far", { NextAction("repop", relevance + 1.f) }));
triggers.push_back(
new TriggerNode("location stuck", { NextAction("repop", relevance + 1) }));
triggers.push_back(new TriggerNode(
"can self resurrect", { NextAction("self resurrect", relevance + 2.0f) }));
}
DeadStrategy::DeadStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "EmoteStrategy.h"
#include "Playerbots.h"
void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
if (sPlayerbotAIConfig->randomBotEmote)
{
triggers.push_back(new TriggerNode("often", { NextAction("talk", 1.0f) }));
triggers.push_back(new TriggerNode("seldom", { NextAction("emote", 1.0f) }));
triggers.push_back(
new TriggerNode("receive text emote", { NextAction("emote", 10.0f) }));
triggers.push_back(
new TriggerNode("receive emote", { NextAction("emote", 10.0f) }));
}
if (sPlayerbotAIConfig->randomBotTalk)
{
triggers.push_back(new TriggerNode(
"often",
{ NextAction("suggest what to do", 10.0f), NextAction("suggest dungeon", 3.0f),
NextAction("suggest trade", 3.0f) }));
}
if (sPlayerbotAIConfig->enableGreet)
triggers.push_back(
new TriggerNode("new player nearby", { NextAction("greet", 1.0f) }));
triggers.push_back(new TriggerNode("often", { NextAction("rpg mount anim", 1.0f) }));
}

View File

@@ -1,16 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GroupStrategy.h"
#include "Playerbots.h"
void GroupStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("often", { NextAction("invite nearby", 4.0f) }));
triggers.push_back(new TriggerNode("random", { NextAction("invite guild", 4.0f) }));
triggers.push_back(new TriggerNode("random", { NextAction("leave far away", 4.0f) }));
triggers.push_back(new TriggerNode("seldom", { NextAction("reset instances", 1.0f) }));
}

View File

@@ -1,22 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GuildStrategy.h"
#include "Playerbots.h"
void GuildStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("often", { NextAction("offer petition nearby", 4.0f) }));
triggers.push_back(
new TriggerNode("often", { NextAction("guild manage nearby", 4.0f) }));
triggers.push_back(
new TriggerNode("petition signed", { NextAction("turn in petition", 10.0f) }));
triggers.push_back(
new TriggerNode("buy tabard", { NextAction("buy tabard", 10.0f) }));
triggers.push_back(
new TriggerNode("leave large guild", { NextAction("guild leave", 4.0f) }));
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "LootNonCombatStrategy.h"
#include "Playerbots.h"
void LootNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("loot available", { NextAction("loot", 6.0f) }));
triggers.push_back(
new TriggerNode("far from loot target", { NextAction("move to loot", 7.0f) }));
triggers.push_back(new TriggerNode("can loot", { NextAction("open loot", 8.0f) }));
triggers.push_back(new TriggerNode("often", { NextAction("add all loot", 5.0f) }));
}
void GatherStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("timer", { NextAction("add gathering loot", 5.0f) }));
}
void RevealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("often", { NextAction("reveal gathering item", 50.0f) }));
}
void UseBobberStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("can use fishing bobber", { NextAction("use fishing bobber", 20.0f) }));
triggers.push_back(
new TriggerNode("random", { NextAction("remove bobber strategy", 20.0f) }));
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "MaintenanceStrategy.h"
#include "Playerbots.h"
std::vector<NextAction> MaintenanceStrategy::getDefaultActions() { return {}; }
void MaintenanceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("clean quest log", 6.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("use random recipe", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("disenchant random item", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("enchant random item", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("smart destroy item", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"move stuck",
{
NextAction("reset", 1.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("use random quest item", 0.9f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("auto share quest", 0.9f)
}
)
);
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "NonCombatStrategy.h"
#include "Playerbots.h"
void NonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("random", { NextAction("clean quest log", 1.0f) }));
triggers.push_back(new TriggerNode("timer", { NextAction("check mount state", 1.0f) }));
}
void CollisionStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("collision", { NextAction("move out of collision", 2.0f) }));
}
void MountStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
}
void WorldBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"need world buff",
{
NextAction("world buff", 1.0f)
}
)
);
}
void MasterFishingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"very often",
{
NextAction("move near water" , 10.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"very often",
{
NextAction("go fishing" , 10.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"random",
{
NextAction("end master fishing", 12.0f),
NextAction("equip upgrades", 6.0f)
}
)
);
}

View File

@@ -1,171 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RpgStrategy.h"
#include "Playerbots.h"
#include "RpgSubActions.h"
float RpgActionMultiplier::GetValue(Action* action)
{
if (action == nullptr)
return 1.0f;
std::string const nextAction = AI_VALUE(std::string, "next rpg action");
std::string const name = action->getName();
if (!nextAction.empty() && dynamic_cast<RpgEnabled*>(action) && name != nextAction)
return 0.0f;
return 1.0f;
}
RpgStrategy::RpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::vector<NextAction> RpgStrategy::getDefaultActions()
{
return {
NextAction("rpg", 1.0f)
};
}
void RpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"no rpg target",
{
NextAction("choose rpg target", 5.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"often",
{
NextAction("move random", 1.10f)
}
)
);
triggers.push_back(
new TriggerNode(
"far from rpg target",
{
NextAction("move to rpg target", 5.0f)
}
)
);
// Sub actions
triggers.push_back(
new TriggerNode(
"rpg",
{
NextAction("rpg stay", 1.101f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg",
{
NextAction("rpg work", 1.101f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg",
{
NextAction("rpg emote", 1.101f)
}
)
);
triggers.push_back(
new TriggerNode(
"has rpg target",
{
NextAction("rpg cancel", 1.101f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg discover",
{
NextAction("rpg discover", 1.210f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg start quest",
{
NextAction("rpg start quest", 1.180f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg end quest",
{
NextAction("rpg end quest", 1.190f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg buy",
{
NextAction("rpg buy", 1.130f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg repair",
{
NextAction("rpg repair", 1.195f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg heal",
{
NextAction("rpg heal", 1.125f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg home bind",
{
NextAction("rpg home bind", 1.160f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg buy petition",
{
NextAction("rpg buy petition", 1.140f)
}
)
);
triggers.push_back(
new TriggerNode(
"rpg use",
{
NextAction("rpg use", 1.102f)
}
)
);
}
void RpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new RpgActionMultiplier(botAI));
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "SayStrategy.h"
#include "Playerbots.h"
void SayStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("critical health",
{ NextAction("say::critical health", 99.0f) }));
triggers.push_back(
new TriggerNode("low health", { NextAction("say::low health", 99.0f) }));
triggers.push_back(
new TriggerNode("low mana", { NextAction("say::low mana", 99.0f) }));
triggers.push_back(new TriggerNode("tank aoe", { NextAction("say::taunt", 99.0f) }));
triggers.push_back(new TriggerNode("medium aoe", { NextAction("say::aoe", 99.0f) }));
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "StayStrategy.h"
#include "Playerbots.h"
void StayStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"return to stay position",
{
NextAction("return to stay position", ACTION_MOVE)
}
)
);
}
std::vector<NextAction> StayStrategy::getDefaultActions()
{
return {
NextAction("stay", 1.0f)
};
}
void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"sit",
{
NextAction("sit", 1.5f)
}
)
);
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "TravelStrategy.h"
#include "Playerbots.h"
TravelStrategy::TravelStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::vector<NextAction> TravelStrategy::getDefaultActions()
{
return {
NextAction("travel", 1.0f)
};
}
void TravelStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"no travel target",
{
NextAction("choose travel target", 6.f)
}
)
);
triggers.push_back(
new TriggerNode(
"far from travel target",
{
NextAction("move to travel target", 1)
}
)
);
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "WorldPacketHandlerStrategy.h"
void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
PassTroughStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("group invite", { NextAction("accept invitation", relevance) }));
triggers.push_back(
new TriggerNode("uninvite", { NextAction("uninvite", relevance) }));
triggers.push_back(
new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) }));
triggers.push_back(
new TriggerNode("group set leader", { /*NextAction("leader", relevance),*/ }));
triggers.push_back(new TriggerNode(
"not enough money", { NextAction("tell not enough money", relevance) }));
triggers.push_back(
new TriggerNode("not enough reputation",
{ NextAction("tell not enough reputation", relevance) }));
triggers.push_back(
new TriggerNode("cannot equip", { NextAction("tell cannot equip", relevance) }));
triggers.push_back(
new TriggerNode("use game object", { NextAction("add loot", relevance),
NextAction("use meeting stone", relevance) }));
triggers.push_back(
new TriggerNode("gossip hello", { NextAction("trainer", relevance) }));
triggers.push_back(new TriggerNode("activate taxi", { NextAction("remember taxi", relevance),
NextAction("taxi", relevance) }));
triggers.push_back(new TriggerNode("taxi done", { NextAction("taxi", relevance) }));
triggers.push_back(new TriggerNode("trade status", { NextAction("accept trade", relevance), NextAction("equip upgrades", relevance) }));
triggers.push_back(new TriggerNode("trade status extended", { NextAction("trade status extended", relevance) }));
triggers.push_back(new TriggerNode("area trigger", { NextAction("reach area trigger", relevance) }));
triggers.push_back(new TriggerNode("within area trigger", { NextAction("area trigger", relevance) }));
triggers.push_back(new TriggerNode("loot response", { NextAction("store loot", relevance) }));
triggers.push_back(new TriggerNode("item push result", { NextAction("unlock items", relevance),
NextAction("open items", relevance),
NextAction("query item usage", relevance),
NextAction("equip upgrades", relevance) }));
triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) }));
triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) }));
// triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) }));
triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) }));
triggers.push_back(new TriggerNode("petition offer", { NextAction("petition sign", relevance) }));
triggers.push_back(new TriggerNode("lfg proposal", { NextAction("lfg accept", relevance) }));
triggers.push_back(new TriggerNode("lfg proposal active", { NextAction("lfg accept", relevance) }));
triggers.push_back(new TriggerNode("arena team invite", { NextAction("arena team accept", relevance) }));
//triggers.push_back(new TriggerNode("no non bot players around", { NextAction("delay", relevance) }));
triggers.push_back(new TriggerNode("bg status", { NextAction("bg status", relevance) }));
triggers.push_back(new TriggerNode("xpgain", { NextAction("xp gain", relevance) }));
triggers.push_back(
new TriggerNode("levelup", { NextAction("auto maintenance on levelup", relevance + 3) }));
// triggers.push_back(new TriggerNode("group destroyed", { NextAction("reset botAI",
// relevance) }));
triggers.push_back(new TriggerNode("group list", { NextAction("reset botAI", relevance) }));
triggers.push_back(new TriggerNode("see spell", { NextAction("see spell", relevance) }));
triggers.push_back(new TriggerNode("release spirit", { NextAction("release", relevance) }));
triggers.push_back(new TriggerNode("revive from corpse", { NextAction("revive from corpse", relevance) }));
triggers.push_back(new TriggerNode("master loot roll", { NextAction("master loot roll", relevance) }));
// quest ?
//triggers.push_back(new TriggerNode("quest confirm", { NextAction("quest confirm", relevance) }));
triggers.push_back(new TriggerNode("questgiver quest details", { NextAction("turn in query quest", relevance) }));
// loot roll
triggers.push_back(new TriggerNode("very often", { NextAction("loot roll", relevance) }));
}
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
{
supported.push_back("loot roll");
supported.push_back("check mount state");
supported.push_back("party command");
supported.push_back("ready check");
supported.push_back("uninvite");
supported.push_back("lfg role check");
supported.push_back("lfg teleport");
supported.push_back("random bot update");
supported.push_back("inventory change failure");
supported.push_back("bg status");
// quests
supported.push_back("quest update add kill");
// supported.push_back("quest update add item");
supported.push_back("quest update failed");
supported.push_back("quest update failed timer");
supported.push_back("quest update complete");
supported.push_back("confirm quest");
}
void ReadyCheckStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("timer", { NextAction("ready check", relevance) }));
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GenericHunterNonCombatStrategy.h"
#include "Playerbots.h"
class GenericHunterNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericHunterNonCombatStrategyActionNodeFactory()
{
creators["rapid fire"] = &rapid_fire;
creators["boost"] = &rapid_fire;
creators["aspect of the pack"] = &aspect_of_the_pack;
}
private:
static ActionNode* rapid_fire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rapid fire",
/*P*/ {},
/*A*/ { NextAction("readiness")},
/*C*/ {});
}
static ActionNode* aspect_of_the_pack([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aspect of the pack",
/*P*/ {},
/*A*/ { NextAction("aspect of the cheetah")},
/*C*/ {});
}
};
GenericHunterNonCombatStrategy::GenericHunterNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericHunterNonCombatStrategyActionNodeFactory());
}
void GenericHunterNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("trueshot aura", { NextAction("trueshot aura", 2.0f)}));
triggers.push_back(new TriggerNode("often", {
NextAction("apply stone", 1.0f),
NextAction("apply oil", 1.0f),
}));
triggers.push_back(new TriggerNode("low ammo", { NextAction("say::low ammo", ACTION_NORMAL)}));
triggers.push_back(new TriggerNode("no track", { NextAction("track humanoids", ACTION_NORMAL)}));
triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", ACTION_HIGH + 1)}));
}
void HunterPetStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("no pet", { NextAction("call pet", 60.0f)}));
triggers.push_back(new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f)}));
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f)}));
triggers.push_back(new TriggerNode("pet not happy", { NextAction("feed pet", 60.0f)}));
triggers.push_back(new TriggerNode("hunters pet medium health", { NextAction("mend pet", 60.0f)}));
triggers.push_back(new TriggerNode("hunters pet dead", { NextAction("revive pet", 60.0f)}));
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GenericHunterStrategy.h"
#include "Playerbots.h"
#include "Strategy.h"
class GenericHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericHunterStrategyActionNodeFactory()
{
creators["rapid fire"] = &rapid_fire;
creators["boost"] = &rapid_fire;
creators["aspect of the pack"] = &aspect_of_the_pack;
creators["aspect of the dragonhawk"] = &aspect_of_the_dragonhawk;
creators["feign death"] = &feign_death;
creators["wing clip"] = &wing_clip;
creators["mongoose bite"] = &mongoose_bite;
creators["raptor strike"] = &raptor_strike;
creators["explosive trap"] = &explosive_trap;
}
private:
static ActionNode* rapid_fire([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rapid fire",
/*P*/ {},
/*A*/ { NextAction("readiness") },
/*C*/ {});
}
static ActionNode* aspect_of_the_pack([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aspect of the pack",
/*P*/ {},
/*A*/ { NextAction("aspect of the cheetah") },
/*C*/ {});
}
static ActionNode* aspect_of_the_dragonhawk([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aspect of the dragonhawk",
/*P*/ {},
/*A*/ { NextAction("aspect of the hawk") },
/*C*/ {});
}
static ActionNode* feign_death([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("feign death",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* wing_clip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("wing clip",
/*P*/ {},
// /*A*/ { NextAction("mongoose bite") },
{},
/*C*/ {});
}
static ActionNode* mongoose_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mongoose bite",
/*P*/ {},
/*A*/ { NextAction("raptor strike") },
/*C*/ {});
}
static ActionNode* raptor_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("raptor strike",
/*P*/ { NextAction("melee") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* explosive_trap([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("explosive trap",
/*P*/ {},
/*A*/ { NextAction("immolation trap") },
/*C*/ {});
}
};
GenericHunterStrategy::GenericHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
{
actionNodeFactories.Add(new GenericHunterStrategyActionNodeFactory());
}
void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
// Mark/Ammo/Mana Triggers
triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", 30.0f) }));
triggers.push_back(new TriggerNode("hunter's mark", { NextAction("hunter's mark", 29.5f) }));
triggers.push_back(new TriggerNode("rapid fire", { NextAction("rapid fire", 29.0f) }));
triggers.push_back(new TriggerNode("aspect of the viper", { NextAction("aspect of the viper", 28.0f) }));
triggers.push_back(new TriggerNode("aspect of the hawk", { NextAction("aspect of the dragonhawk", 27.5f) }));
// Aggro/Threat/Defensive Triggers
triggers.push_back(new TriggerNode("has aggro", { NextAction("concussive shot", 20.0f) }));
triggers.push_back(new TriggerNode("low tank threat", { NextAction("misdirection on main tank", 27.0f) }));
triggers.push_back(new TriggerNode("low health", { NextAction("deterrence", 35.0f) }));
triggers.push_back(new TriggerNode("concussive shot on snare target", { NextAction("concussive shot", 20.0f) }));
triggers.push_back(new TriggerNode("medium threat", { NextAction("feign death", 35.0f) }));
triggers.push_back(new TriggerNode("hunters pet medium health", { NextAction("mend pet", 22.0f) }));
triggers.push_back(new TriggerNode("hunters pet low health", { NextAction("mend pet", 21.0f) }));
// Dispel Triggers
triggers.push_back(new TriggerNode("tranquilizing shot enrage", { NextAction("tranquilizing shot", 61.0f) }));
triggers.push_back(new TriggerNode("tranquilizing shot magic", { NextAction("tranquilizing shot", 61.0f) }));
// Ranged-based Triggers
triggers.push_back(new TriggerNode("enemy within melee", {
NextAction("explosive trap", 37.0f),
NextAction("mongoose bite", 22.0f),
NextAction("wing clip", 21.0f) }));
triggers.push_back(new TriggerNode("enemy too close for auto shot", {
NextAction("disengage", 35.0f),
NextAction("flee", 34.0f) }));
}
// ===== AoE Strategy, 2/3+ enemies =====
AoEHunterStrategy::AoEHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void AoEHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("volley channel check", { NextAction("cancel channel", 23.0f) }));
triggers.push_back(new TriggerNode("medium aoe", { NextAction("volley", 22.0f) }));
triggers.push_back(new TriggerNode("light aoe", { NextAction("multi-shot", 21.0f) }));
}
void HunterBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
}
void HunterCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("scare beast", { NextAction("scare beast on cc", 23.0f) }));
triggers.push_back(new TriggerNode("freezing trap", { NextAction("freezing trap on cc", 23.0f) }));
}
void HunterTrapWeaveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("immolation trap no cd", { NextAction("reach melee", 23.0f) }));
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "SurvivalHunterStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class SurvivalHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
SurvivalHunterStrategyActionNodeFactory()
{
creators["auto shot"] = &auto_shot;
creators["kill command"] = &kill_command;
creators["kill shot"] = &kill_shot;
creators["explosive shot"] = &explosive_shot;
creators["black arrow"] = &black_arrow;
creators["viper sting"] = &viper_sting;
creators["serpent sting"] = serpent_sting;
creators["aimed shot"] = &aimed_shot;
creators["arcane shot"] = &arcane_shot;
creators["steady shot"] = &steady_shot;
creators["multi-shot"] = &multi_shot;
creators["volley"] = &volley;
}
private:
static ActionNode* auto_shot(PlayerbotAI*) { return new ActionNode("auto shot", {}, {}, {}); }
static ActionNode* kill_command(PlayerbotAI*) { return new ActionNode("kill command", {}, {}, {}); }
static ActionNode* kill_shot(PlayerbotAI*) { return new ActionNode("kill shot", {}, {}, {}); }
static ActionNode* explosive_shot(PlayerbotAI*) { return new ActionNode("explosive shot", {}, {}, {}); }
static ActionNode* black_arrow(PlayerbotAI*) { return new ActionNode("black arrow", {}, {}, {}); }
static ActionNode* viper_sting(PlayerbotAI*) { return new ActionNode("viper sting", {}, {}, {}); }
static ActionNode* serpent_sting(PlayerbotAI*) { return new ActionNode("serpent sting", {}, {}, {}); }
static ActionNode* aimed_shot(PlayerbotAI*) { return new ActionNode("aimed shot", {}, {}, {}); }
static ActionNode* arcane_shot(PlayerbotAI*) { return new ActionNode("arcane shot", {}, {}, {}); }
static ActionNode* steady_shot(PlayerbotAI*) { return new ActionNode("steady shot", {}, {}, {}); }
static ActionNode* multi_shot(PlayerbotAI*) { return new ActionNode("multi shot", {}, {}, {}); }
static ActionNode* volley(PlayerbotAI*) { return new ActionNode("volley", {}, {}, {}); }
};
// ===== Single Target Strategy =====
SurvivalHunterStrategy::SurvivalHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
{
actionNodeFactories.Add(new SurvivalHunterStrategyActionNodeFactory());
}
// ===== Default Actions =====
std::vector<NextAction> SurvivalHunterStrategy::getDefaultActions()
{
return {
NextAction("kill command", 5.9f),
NextAction("kill shot", 5.8f),
NextAction("explosive shot", 5.7f),
NextAction("black arrow", 5.6f),
NextAction("serpent sting", 5.5f),
NextAction("aimed shot", 5.4f),
NextAction("arcane shot", 5.3f),
NextAction("steady shot", 5.2f),
NextAction("auto shot", 5.1f)
};
}
// ===== Trigger Initialization ===
void SurvivalHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericHunterStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"lock and load",
{
NextAction("explosive shot rank 4", 28.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"lock and load",
{
NextAction("explosive shot rank 3", 27.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"lock and load",
{
NextAction("explosive shot rank 2", 27.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"lock and load",
{
NextAction("explosive shot rank 1", 26.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"kill command",
{
NextAction("kill command", 18.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"target critical health",
{
NextAction("kill shot", 18.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"explosive shot",
{
NextAction("explosive shot", 17.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"black arrow",
{
NextAction("black arrow", 16.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"low mana",
{
NextAction("viper sting", 16.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"no stings",
{
NextAction("serpent sting", 15.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"serpent sting on attacker",
{
NextAction("serpent sting on attacker", 15.0f)
}
)
);
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FrostMageStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
FrostMageStrategyActionNodeFactory()
{
creators["cold snap"] = &cold_snap;
creators["ice barrier"] = &ice_barrier;
creators["summon water elemental"] = &summon_water_elemental;
creators["deep freeze"] = &deep_freeze;
creators["icy veins"] = &icy_veins;
creators["frostbolt"] = &frostbolt;
creators["ice lance"] = &ice_lance;
creators["fire blast"] = &fire_blast;
creators["fireball"] = &fireball;
creators["frostfire bolt"] = &frostfire_bolt;
}
private:
static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", {}, {}, {}); }
static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", {}, {}, {}); }
static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", {}, {}, {}); }
static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", {}, {}, {}); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); }
static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", {}, {}, {}); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); }
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, {}, {}); }
};
// ===== Single Target Strategy =====
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
}
// ===== Default Actions =====
std::vector<NextAction> FrostMageStrategy::getDefaultActions()
{
return {
NextAction("frostbolt", 5.4f),
NextAction("ice lance", 5.3f), // cast during movement
NextAction("fire blast", 5.2f), // cast during movement if ice lance is not learned
NextAction("shoot", 5.1f),
NextAction("fireball", 5.0f)
};
}
// ===== Trigger Initialization ===
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericMageStrategy::InitTriggers(triggers);
// Pet/Defensive triggers
triggers.push_back(
new TriggerNode(
"no pet",
{
NextAction("summon water elemental", 30.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"has pet",
{
NextAction("toggle pet spell", 60.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"new pet",
{
NextAction("set pet stance", 60.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"medium health",
{
NextAction("ice barrier", 29.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"being attacked",
{
NextAction("ice barrier", 29.0f)
}
)
);
// Proc/Freeze triggers
triggers.push_back(
new TriggerNode(
"brain freeze",
{
NextAction("frostfire bolt", 19.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"fingers of frost",
{
NextAction("deep freeze", 19.0f),
NextAction("frostbolt", 18.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"frostbite on target",
{
NextAction("deep freeze", 19.0f),
NextAction("frostbolt", 18.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"frost nova on target",
{
NextAction("deep freeze", 19.0f),
NextAction("frostbolt", 18.0f)
}
)
);
}

Some files were not shown because too many files have changed in this diff Show More