mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-18 11:15:43 +00:00
Compare commits
114 Commits
project_tr
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cf7f1aaef | ||
|
|
9f54d7e702 | ||
|
|
aeaaee15da | ||
|
|
a1137dbddc | ||
|
|
2eb98c3233 | ||
|
|
29613e29b7 | ||
|
|
e2c203a35e | ||
|
|
1b1ed18a23 | ||
|
|
2ab73c1fd5 | ||
|
|
6b97c379ba | ||
|
|
965d300203 | ||
|
|
dc55ecfd9c | ||
|
|
59d6eb139e | ||
|
|
00171a8c82 | ||
|
|
02e8465a3b | ||
|
|
f53a8704eb | ||
|
|
e5525958c8 | ||
|
|
9ae457d069 | ||
|
|
c9e98a6b4e | ||
|
|
3d9623f119 | ||
|
|
c9cc4324d3 | ||
|
|
b13fb7d12a | ||
|
|
962fdeb3d1 | ||
|
|
83c6977de5 | ||
|
|
686fe513b2 | ||
|
|
61402e83a1 | ||
|
|
b16789fa54 | ||
|
|
8f638b6a66 | ||
|
|
33f5e733dc | ||
|
|
9917863ca1 | ||
|
|
2317652d72 | ||
|
|
1fcd6c5cda | ||
|
|
88016789ba | ||
|
|
6be860c967 | ||
|
|
9971622093 | ||
|
|
895df9b197 | ||
|
|
467b63b840 | ||
|
|
66f5f597bb | ||
|
|
cafbd4681e | ||
|
|
f5c84ee7ff | ||
|
|
b6f882886d | ||
|
|
c1222da8b0 | ||
|
|
00cb177c86 | ||
|
|
5f697e806e | ||
|
|
934e73ae20 | ||
|
|
f4b4d8967f | ||
|
|
910b8a9c53 | ||
|
|
bb569b4d39 | ||
|
|
dde16674c3 | ||
|
|
e5b2791053 | ||
|
|
353c29dfc4 | ||
|
|
52c3e96641 | ||
|
|
38e2d8584b | ||
|
|
d5dbc4ddd7 | ||
|
|
2424f73bc4 | ||
|
|
cf743a186a | ||
|
|
10213d8381 | ||
|
|
d97870facd | ||
|
|
0c1700c117 | ||
|
|
0b1b0eaecc | ||
|
|
8e03371147 | ||
|
|
27311b734d | ||
|
|
bb5ed37cd3 | ||
|
|
e88c1b779b | ||
|
|
05057ae9b5 | ||
|
|
610a032379 | ||
|
|
ce2a990495 | ||
|
|
6effabfa42 | ||
|
|
cadbcbd447 | ||
|
|
08c739f918 | ||
|
|
0729d14787 | ||
|
|
a37dd2b9ae | ||
|
|
9c8ba42c64 | ||
|
|
e5bc495dbe | ||
|
|
85c7009fe1 | ||
|
|
ce51191e8f | ||
|
|
d02d61e690 | ||
|
|
80dbd22ba1 | ||
|
|
26a135a1ec | ||
|
|
983a55da86 | ||
|
|
e35900f9d0 | ||
|
|
d9f0d5a555 | ||
|
|
7d5c9e3ee0 | ||
|
|
43164e74e1 | ||
|
|
f7fea456ca | ||
|
|
12a5132c33 | ||
|
|
bbbf71d40c | ||
|
|
586c4d9d05 | ||
|
|
cb099bcaf4 | ||
|
|
5d3e64800f | ||
|
|
235f0249b2 | ||
|
|
7237b154e0 | ||
|
|
c3fd97b6c0 | ||
|
|
286213eb8b | ||
|
|
1d19dea974 | ||
|
|
6e1c9114df | ||
|
|
7e810f8174 | ||
|
|
e0df6558f5 | ||
|
|
50ac6e5b95 | ||
|
|
2c5185a7cb | ||
|
|
f874d2c79e | ||
|
|
5a4acbe36c | ||
|
|
e693b208be | ||
|
|
10ce94e065 | ||
|
|
553b8276eb | ||
|
|
f791ab61c4 | ||
|
|
3260ca1429 | ||
|
|
e1fa733aa5 | ||
|
|
3f050a4a77 | ||
|
|
0e4c759e7f | ||
|
|
24f841f728 | ||
|
|
444be2994e | ||
|
|
8a68de4476 | ||
|
|
7d50ceef3d |
40
.github/workflows/codestyle_cpp.yml
vendored
Normal file
40
.github/workflows/codestyle_cpp.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
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
|
||||||
6
.github/workflows/windows_build.yml
vendored
6
.github/workflows/windows_build.yml
vendored
@@ -25,21 +25,25 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||||
ref: 'Playerbot'
|
ref: 'Playerbot'
|
||||||
|
path: 'ac'
|
||||||
- name: Checkout Playerbot Module
|
- name: Checkout Playerbot Module
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: 'mod-playerbots/mod-playerbots'
|
repository: 'mod-playerbots/mod-playerbots'
|
||||||
path: 'modules/mod-playerbots'
|
#path: 'modules/mod-playerbots'
|
||||||
|
path: ac/modules/mod-playerbots
|
||||||
- name: ccache
|
- name: ccache
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||||
- name: Configure OS
|
- name: Configure OS
|
||||||
shell: bash
|
shell: bash
|
||||||
|
working-directory: ac
|
||||||
env:
|
env:
|
||||||
CONTINUOUS_INTEGRATION: true
|
CONTINUOUS_INTEGRATION: true
|
||||||
run: |
|
run: |
|
||||||
./acore.sh install-deps
|
./acore.sh install-deps
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
working-directory: ac
|
||||||
run: |
|
run: |
|
||||||
export CTOOLS_BUILD=all
|
export CTOOLS_BUILD=all
|
||||||
./acore.sh compiler build
|
./acore.sh compiler build
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -49,3 +49,4 @@ local.properties
|
|||||||
.project
|
.project
|
||||||
.cproject
|
.cproject
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
|
|||||||
1
.suppress.cppcheck
Normal file
1
.suppress.cppcheck
Normal file
@@ -0,0 +1 @@
|
|||||||
|
cppcheckError
|
||||||
48
README.md
48
README.md
@@ -3,7 +3,7 @@
|
|||||||
|
|
|
|
||||||
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_CN.md">中文</a>
|
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_CN.md">中文</a>
|
||||||
|
|
|
|
||||||
<a href="https://github.com/brighton-chi/mod-playerbots/blob/readme/README_ES.md">Español</a>
|
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_ES.md">Español</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
@@ -18,25 +18,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Playerbots Module
|
# Playerbots Module
|
||||||
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).
|
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot).
|
||||||
|
|
||||||
Features include:
|
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;
|
- 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;
|
- 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;
|
- Bots capable of running most raids and battlegrounds
|
||||||
- Highly configurable settings to define how bots behave;
|
- Highly configurable settings to define how bots behave
|
||||||
- Excellent performance, even when running thousands of bots.
|
- Excellent performance, even when running thousands of bots
|
||||||
|
|
||||||
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
|
We also have a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
|
||||||
|
|
||||||
`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
|
## Installation
|
||||||
|
|
||||||
### Classic Installation
|
Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support.
|
||||||
|
|
||||||
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
|
**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the `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:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||||
@@ -48,7 +50,7 @@ For more information, refer to the [AzerothCore Installation Guide](https://www.
|
|||||||
|
|
||||||
### Docker Installation
|
### Docker Installation
|
||||||
|
|
||||||
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
|
Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||||
@@ -85,25 +87,23 @@ Use `docker compose up -d --build` to build and run the server. For more informa
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
|
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of AddOns, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections, and contributions are welcome.
|
||||||
|
|
||||||
## Frequently Asked Questions
|
Bots are controlled via chat commands. For larger bot groups, this can be cumbersome. Because of this, community members have developed client AddOns to allow controlling bots through the in-game UI. We recommend you check out their projects listed in the [AddOns and Submodules](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Addons-and-Sub%E2%80%90Modules) page.
|
||||||
|
|
||||||
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
|
## Contributing
|
||||||
- **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.
|
|
||||||
|
|
||||||
## Addons
|
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.
|
||||||
|
|
||||||
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:
|
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.
|
||||||
|
|
||||||
- [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)]
|
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.
|
||||||
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan) [note: no longer under active development]
|
|
||||||
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision) [note: no longer under active development]
|
Please click on the "⭐" button to stay up to date and help us gain more visibility on GitHub!
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
`mod-playerbots` is is based off [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for the continued efforts in maintaining the module.
|
`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.
|
||||||
|
|
||||||
Also, a thank you to the many contributors who've helped build this project:
|
Also, a thank you to the many contributors who've helped build this project:
|
||||||
|
|
||||||
|
|||||||
263
apps/codestyle/codestyle-cpp.py
Normal file
263
apps/codestyle/codestyle-cpp.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
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)
|
||||||
0
code_format.sh
Executable file → Normal file
0
code_format.sh
Executable file → Normal file
@@ -21,10 +21,11 @@
|
|||||||
# THRESHOLDS
|
# THRESHOLDS
|
||||||
# QUESTS
|
# QUESTS
|
||||||
# COMBAT
|
# COMBAT
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
# CHEATS
|
# CHEATS
|
||||||
# SPELLS
|
# SPELLS
|
||||||
# FLIGHTPATH
|
# FLIGHTPATH
|
||||||
|
# PROFESSIONS
|
||||||
# RANDOMBOT-SPECIFIC SETTINGS
|
# RANDOMBOT-SPECIFIC SETTINGS
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# LEVELS
|
# LEVELS
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
# RPG STRATEGY
|
# RPG STRATEGY
|
||||||
# TELEPORTS
|
# TELEPORTS
|
||||||
# BATTLEGROUND & ARENA & PVP
|
# BATTLEGROUND & ARENA & PVP
|
||||||
# INTERVALS
|
# RANDOM BOT TIMING AND BEHAVIOR
|
||||||
# PREMADE SPECS
|
# PREMADE SPECS
|
||||||
# INFORMATION
|
# INFORMATION
|
||||||
# WARRIOR
|
# WARRIOR
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
# HUNTER
|
# HUNTER
|
||||||
# ROGUE
|
# ROGUE
|
||||||
# PRIEST
|
# PRIEST
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
# SHAMAN
|
# SHAMAN
|
||||||
# MAGE
|
# MAGE
|
||||||
# WARLOCK
|
# WARLOCK
|
||||||
@@ -55,7 +56,7 @@
|
|||||||
# HUNTER
|
# HUNTER
|
||||||
# ROGUE
|
# ROGUE
|
||||||
# PRIEST
|
# PRIEST
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
# SHAMAN
|
# SHAMAN
|
||||||
# MAGE
|
# MAGE
|
||||||
# WARLOCK
|
# WARLOCK
|
||||||
@@ -90,17 +91,20 @@ AiPlayerbot.MinRandomBots = 500
|
|||||||
AiPlayerbot.MaxRandomBots = 500
|
AiPlayerbot.MaxRandomBots = 500
|
||||||
|
|
||||||
# Randombot accounts
|
# 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)
|
# Default: 0 (automatic)
|
||||||
AiPlayerbot.RandomBotAccountCount = 0
|
AiPlayerbot.RandomBotAccountCount = 0
|
||||||
|
|
||||||
# Delete all randombot accounts
|
# 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
|
AiPlayerbot.DeleteRandomBotAccounts = 0
|
||||||
|
|
||||||
# Disable randombots when no real players are logged in
|
# Disable randombots when no real players are logged in
|
||||||
# Default: 0 (randombots will login when server starts)
|
# 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.DisabledWithoutRealPlayer = 0
|
||||||
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
|
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
|
||||||
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
|
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
|
||||||
@@ -152,7 +156,8 @@ AiPlayerbot.AllowGuildBots = 1
|
|||||||
AiPlayerbot.AllowTrustedAccountBots = 1
|
AiPlayerbot.AllowTrustedAccountBots = 1
|
||||||
|
|
||||||
# Randombots will create guilds with nearby randombots
|
# Randombots will create guilds with nearby randombots
|
||||||
# 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)
|
# 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
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.RandomBotGuildNearby = 0
|
AiPlayerbot.RandomBotGuildNearby = 0
|
||||||
|
|
||||||
@@ -186,7 +191,8 @@ AiPlayerbot.AutoInitOnly = 0
|
|||||||
# Default: 1.0 (same with the player)
|
# Default: 1.0 (same with the player)
|
||||||
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
|
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
|
AiPlayerbot.AutoTrainSpells = yes
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -263,7 +269,7 @@ AiPlayerbot.UseFastFlyMountAtMinLevel = 70
|
|||||||
AiPlayerbot.RandomBotShowHelmet = 1
|
AiPlayerbot.RandomBotShowHelmet = 1
|
||||||
AiPlayerbot.RandomBotShowCloak = 1
|
AiPlayerbot.RandomBotShowCloak = 1
|
||||||
|
|
||||||
# Randombots and altbots automatically equip upgrades (bots will equip any item obtained from looting or a quest if they are sufficient upgrades)
|
# Randombots and altbots automatically equip any items in their inventory that are sufficient upgrades
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||||
|
|
||||||
@@ -284,9 +290,6 @@ AiPlayerbot.TwoRoundsGearInit = 0
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# Bots will say information about items when collecting them
|
|
||||||
AiPlayerbot.SayWhenCollectingItems = 1
|
|
||||||
|
|
||||||
# Bots keep looting when loot system is set to free for all
|
# Bots keep looting when loot system is set to free for all
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.FreeMethodLoot = 0
|
AiPlayerbot.FreeMethodLoot = 0
|
||||||
@@ -314,7 +317,8 @@ AiPlayerbot.GlobalCooldown = 500
|
|||||||
# Max wait time when moving
|
# Max wait time when moving
|
||||||
AiPlayerbot.MaxWaitForMove = 5000
|
AiPlayerbot.MaxWaitForMove = 5000
|
||||||
|
|
||||||
# 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)
|
# 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
|
||||||
# Default: 0 - MoveSplinePath enabled
|
# Default: 0 - MoveSplinePath enabled
|
||||||
# 1 - MoveSplinePath disabled in BG/Arena only
|
# 1 - MoveSplinePath disabled in BG/Arena only
|
||||||
# 2 - MoveSplinePath disabled everywhere
|
# 2 - MoveSplinePath disabled everywhere
|
||||||
@@ -360,15 +364,15 @@ AiPlayerbot.LootDelay = 1000
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Distances are in yards
|
||||||
AiPlayerbot.FarDistance = 20.0
|
AiPlayerbot.FarDistance = 20.0
|
||||||
AiPlayerbot.SightDistance = 75.0
|
AiPlayerbot.SightDistance = 100.0
|
||||||
AiPlayerbot.SpellDistance = 28.5
|
AiPlayerbot.SpellDistance = 28.5
|
||||||
AiPlayerbot.ShootDistance = 5.0
|
AiPlayerbot.ShootDistance = 5.0
|
||||||
AiPlayerbot.ReactDistance = 150.0
|
|
||||||
AiPlayerbot.GrindDistance = 75.0
|
|
||||||
AiPlayerbot.HealDistance = 38.5
|
AiPlayerbot.HealDistance = 38.5
|
||||||
AiPlayerbot.LootDistance = 15.0
|
AiPlayerbot.LootDistance = 15.0
|
||||||
AiPlayerbot.FleeDistance = 5.0
|
AiPlayerbot.FleeDistance = 5.0
|
||||||
|
AiPlayerbot.AggroDistance = 22
|
||||||
AiPlayerbot.TooCloseDistance = 5.0
|
AiPlayerbot.TooCloseDistance = 5.0
|
||||||
AiPlayerbot.MeleeDistance = 0.75
|
AiPlayerbot.MeleeDistance = 0.75
|
||||||
AiPlayerbot.FollowDistance = 1.5
|
AiPlayerbot.FollowDistance = 1.5
|
||||||
@@ -376,7 +380,8 @@ AiPlayerbot.WhisperDistance = 6000.0
|
|||||||
AiPlayerbot.ContactDistance = 0.45
|
AiPlayerbot.ContactDistance = 0.45
|
||||||
AiPlayerbot.AoeRadius = 10
|
AiPlayerbot.AoeRadius = 10
|
||||||
AiPlayerbot.RpgDistance = 200
|
AiPlayerbot.RpgDistance = 200
|
||||||
AiPlayerbot.AggroDistance = 22
|
AiPlayerbot.GrindDistance = 75.0
|
||||||
|
AiPlayerbot.ReactDistance = 150.0
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -407,10 +412,11 @@ 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
|
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)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.SyncQuestWithPlayer = 1
|
AiPlayerbot.SyncQuestWithPlayer = 1
|
||||||
|
|
||||||
@@ -435,7 +441,7 @@ AiPlayerbot.DropObsoleteQuests = 1
|
|||||||
# Auto add dungeon/raid strategies when entering the instance if implemented
|
# Auto add dungeon/raid strategies when entering the instance if implemented
|
||||||
AiPlayerbot.ApplyInstanceStrategies = 1
|
AiPlayerbot.ApplyInstanceStrategies = 1
|
||||||
|
|
||||||
# Enable auto avoid aoe strategy (experimental)
|
# Enable auto avoid aoe strategy
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoAvoidAoe = 1
|
AiPlayerbot.AutoAvoidAoe = 1
|
||||||
|
|
||||||
@@ -462,7 +468,7 @@ AiPlayerbot.FleeingEnabled = 1
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -484,12 +490,14 @@ AiPlayerbot.RPWarningCooldown = 30
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# Enable/Disable maintenance command (learn all available spells and skills, supplement consumables, repair, enchant equipment if bot's level is above AiPlayerbot.MinEnchantingBotLevel)
|
# Enable/Disable maintenance command
|
||||||
|
# Learn all available spells and skills, assign talent points, refresh consumables, repair, enchant equipment, socket gems, etc.
|
||||||
|
# Applies if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.MaintenanceCommand = 1
|
AiPlayerbot.MaintenanceCommand = 1
|
||||||
|
|
||||||
# Enable/Disable specific maintenance command functionality for alt bots
|
# 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)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AltMaintenanceAmmo = 1
|
AiPlayerbot.AltMaintenanceAmmo = 1
|
||||||
AiPlayerbot.AltMaintenanceFood = 1
|
AiPlayerbot.AltMaintenanceFood = 1
|
||||||
@@ -501,6 +509,7 @@ AiPlayerbot.AltMaintenanceBags = 1
|
|||||||
AiPlayerbot.AltMaintenanceMounts = 1
|
AiPlayerbot.AltMaintenanceMounts = 1
|
||||||
AiPlayerbot.AltMaintenanceSkills = 1
|
AiPlayerbot.AltMaintenanceSkills = 1
|
||||||
|
|
||||||
|
# "Special Spells" consist of any spells listed in AiPlayerbot.RandomBotSpellIds and Death Gate for Death Knights
|
||||||
AiPlayerbot.AltMaintenanceClassSpells = 1
|
AiPlayerbot.AltMaintenanceClassSpells = 1
|
||||||
AiPlayerbot.AltMaintenanceAvailableSpells = 1
|
AiPlayerbot.AltMaintenanceAvailableSpells = 1
|
||||||
AiPlayerbot.AltMaintenanceSpecialSpells = 1
|
AiPlayerbot.AltMaintenanceSpecialSpells = 1
|
||||||
@@ -515,8 +524,8 @@ AiPlayerbot.AltMaintenanceReputation = 1
|
|||||||
AiPlayerbot.AltMaintenanceAttunementQuests = 1
|
AiPlayerbot.AltMaintenanceAttunementQuests = 1
|
||||||
AiPlayerbot.AltMaintenanceKeyring = 1
|
AiPlayerbot.AltMaintenanceKeyring = 1
|
||||||
|
|
||||||
|
# Enable/Disable autogear command, which automatically upgrades bots' gear
|
||||||
# Enable/Disable autogear command, which automatically upgrades bots' gear; the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
# The quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoGearCommand = 1
|
AiPlayerbot.AutoGearCommand = 1
|
||||||
|
|
||||||
@@ -548,7 +557,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
|||||||
# "mana" (bots have infinite mana)
|
# "mana" (bots have infinite mana)
|
||||||
# "power" (bots have infinite energy, rage, and runic power)
|
# "power" (bots have infinite energy, rage, and runic power)
|
||||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||||
# "raid" (bots use cheats implemented into raid strategies)
|
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
|
||||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
# 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
|
# Default: food, taxi, and raid are enabled
|
||||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||||
@@ -580,6 +589,30 @@ 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 #
|
# RANDOMBOT-SPECIFIC SETTINGS #
|
||||||
@@ -606,9 +639,11 @@ AiPlayerbot.RandomBotMaxLevel = 80
|
|||||||
AiPlayerbot.SyncLevelWithPlayers = 0
|
AiPlayerbot.SyncLevelWithPlayers = 0
|
||||||
|
|
||||||
# Mark many quests ≤ bot level as complete (slows down bot creation)
|
# Mark many quests ≤ bot level as complete (slows down bot creation)
|
||||||
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.PreQuests = 0
|
AiPlayerbot.PreQuests = 0
|
||||||
|
|
||||||
# Enable LFG for randombots
|
# Enable LFG for randombots
|
||||||
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.RandomBotJoinLfg = 1
|
AiPlayerbot.RandomBotJoinLfg = 1
|
||||||
|
|
||||||
# Enable/Disable periodic online - offline of randombots to mimic the real-world scenario where not all players are online simultaneously
|
# Enable/Disable periodic online - offline of randombots to mimic the real-world scenario where not all players are online simultaneously
|
||||||
@@ -629,7 +664,8 @@ AiPlayerbot.RandomBotHordeRatio = 50
|
|||||||
AiPlayerbot.DisableDeathKnightLogin = 0
|
AiPlayerbot.DisableDeathKnightLogin = 0
|
||||||
|
|
||||||
# Enable simulated expansion limitation for talents and glyphs
|
# Enable simulated expansion limitation for talents and glyphs
|
||||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61 and 7 rows plus the middle talent of the 8th row for bots until level 71
|
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
||||||
|
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.LimitTalentsExpansion = 0
|
AiPlayerbot.LimitTalentsExpansion = 0
|
||||||
|
|
||||||
@@ -698,7 +734,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
|
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
|
||||||
# TBC
|
# TBC
|
||||||
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
|
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 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
|
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
||||||
# Wotlk
|
# 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 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
|
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
|
||||||
@@ -709,12 +745,13 @@ AiPlayerbot.RandomGearScoreLimit = 0
|
|||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.IncrementalGearInit = 1
|
AiPlayerbot.IncrementalGearInit = 1
|
||||||
|
|
||||||
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
|
# 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
|
||||||
# Default: 60
|
# Default: 60
|
||||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||||
|
|
||||||
# Enable expansion limitation for bot enchants
|
# Enable expansion limitation for bot enchants and gems
|
||||||
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants until level 71
|
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants and gems until level 71
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.LimitEnchantExpansion = 1
|
AiPlayerbot.LimitEnchantExpansion = 1
|
||||||
|
|
||||||
@@ -810,7 +847,7 @@ AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Quest that will be completed and rewarded for all randombots
|
# Quest that will be completed and rewarded for all randombots
|
||||||
AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,13188,13189,24499,24511,24710,24712"
|
AiPlayerbot.RandomBotQuestIds = "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"
|
||||||
|
|
||||||
# Randombots will group with nearby randombots to do shared quests
|
# Randombots will group with nearby randombots to do shared quests
|
||||||
AiPlayerbot.RandomBotGroupNearby = 0
|
AiPlayerbot.RandomBotGroupNearby = 0
|
||||||
@@ -861,19 +898,21 @@ AiPlayerbot.OpenGoSpell = 6477
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Additional randombot strategies
|
# 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"
|
# Example: "+threat,-potions"
|
||||||
AiPlayerbot.RandomBotCombatStrategies = ""
|
AiPlayerbot.RandomBotCombatStrategies = ""
|
||||||
AiPlayerbot.RandomBotNonCombatStrategies = ""
|
AiPlayerbot.RandomBotNonCombatStrategies = ""
|
||||||
|
|
||||||
# Additional altbot strategies
|
# 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.CombatStrategies = ""
|
||||||
AiPlayerbot.NonCombatStrategies = ""
|
AiPlayerbot.NonCombatStrategies = ""
|
||||||
|
|
||||||
# Remove "healer dps" strategy on specified maps.
|
# Remove "healer dps" strategy on the maps specified below.
|
||||||
# Default: 0 (disabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.HealerDPSMapRestriction = 0
|
AiPlayerbot.HealerDPSMapRestriction = 1
|
||||||
|
|
||||||
# List of Map IDs where "healer dps" strategy will be removed if AiPlayerbot.HealerDPSMapRestriction is enabled
|
# 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"
|
# 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"
|
||||||
@@ -1057,7 +1096,8 @@ AiPlayerbot.ZoneBracket.4197 = 79,80
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# Maps where bots can be teleported to
|
# Map IDs where bots can be teleported to
|
||||||
|
# Defaults: 0 = Eastern Kingdoms, 1 = Kalimdor, 530 = Outland, 571 = Northrend
|
||||||
AiPlayerbot.RandomBotMaps = 0,1,530,571
|
AiPlayerbot.RandomBotMaps = 0,1,530,571
|
||||||
|
|
||||||
# Probabilty bots teleport to banker (city)
|
# Probabilty bots teleport to banker (city)
|
||||||
@@ -1182,7 +1222,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"
|
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)
|
# PvP Restricted Areas (bots don't pvp)
|
||||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"
|
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
|
||||||
|
|
||||||
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
||||||
AiPlayerbot.FastReactInBG = 1
|
AiPlayerbot.FastReactInBG = 1
|
||||||
@@ -1193,24 +1233,46 @@ AiPlayerbot.FastReactInBG = 1
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# INTERVALS
|
# RANDOM BOT TIMING AND BEHAVIOR
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# All in seconds
|
# How often (in seconds) the random bot manager runs its main update loop
|
||||||
|
# Default: 20
|
||||||
AiPlayerbot.RandomBotUpdateInterval = 20
|
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.RandomBotCountChangeMinInterval = 1800
|
||||||
AiPlayerbot.RandomBotCountChangeMaxInterval = 7200
|
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.MinRandomBotInWorldTime = 600
|
||||||
AiPlayerbot.MaxRandomBotInWorldTime = 28800
|
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.MinRandomBotRandomizeTime = 7200
|
||||||
AiPlayerbot.MaxRandomBotRandomizeTime = 1209600
|
AiPlayerbot.MaxRandomBotRandomizeTime = 1209600
|
||||||
|
|
||||||
|
# Number of bots processed (login, logout, update) per manager update cycle
|
||||||
|
# Default: 60
|
||||||
AiPlayerbot.RandomBotsPerInterval = 60
|
AiPlayerbot.RandomBotsPerInterval = 60
|
||||||
|
|
||||||
|
# Minimum and maximum seconds after death before a bot revives
|
||||||
|
# Defaults: 60 (min), 300 (max)
|
||||||
AiPlayerbot.MinRandomBotReviveTime = 60
|
AiPlayerbot.MinRandomBotReviveTime = 60
|
||||||
AiPlayerbot.MaxRandomBotReviveTime = 300
|
AiPlayerbot.MaxRandomBotReviveTime = 300
|
||||||
|
|
||||||
|
# Minimum and maximum seconds between bot teleports to new areas or zones
|
||||||
|
# Defaults: 3600 (min), 18000 (max)
|
||||||
AiPlayerbot.MinRandomBotTeleportInterval = 3600
|
AiPlayerbot.MinRandomBotTeleportInterval = 3600
|
||||||
AiPlayerbot.MaxRandomBotTeleportInterval = 18000
|
AiPlayerbot.MaxRandomBotTeleportInterval = 18000
|
||||||
AiPlayerbot.PermanantlyInWorldTime = 31104000
|
|
||||||
|
# Number of seconds bots flagged as permanent stay in the world (31,104,000 ≈ 1 year)
|
||||||
|
# Default: 31104000
|
||||||
|
AiPlayerbot.PermanentlyInWorldTime = 31104000
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -1430,7 +1492,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -1753,7 +1815,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,255 @@
|
|||||||
|
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);
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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 n’ai 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);
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
#include "OutfitAction.h"
|
#include "OutfitAction.h"
|
||||||
#include "PositionAction.h"
|
#include "PositionAction.h"
|
||||||
#include "DropQuestAction.h"
|
#include "DropQuestAction.h"
|
||||||
#include "RaidNaxxActions.h"
|
|
||||||
#include "RandomBotUpdateAction.h"
|
#include "RandomBotUpdateAction.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
#include "ReleaseSpiritAction.h"
|
#include "ReleaseSpiritAction.h"
|
||||||
@@ -64,6 +63,7 @@
|
|||||||
#include "WorldBuffAction.h"
|
#include "WorldBuffAction.h"
|
||||||
#include "XpGainAction.h"
|
#include "XpGainAction.h"
|
||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
|
#include "FishingAction.h"
|
||||||
#include "CancelChannelAction.h"
|
#include "CancelChannelAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@@ -121,7 +121,7 @@ public:
|
|||||||
creators["shoot"] = &ActionContext::shoot;
|
creators["shoot"] = &ActionContext::shoot;
|
||||||
creators["follow"] = &ActionContext::follow;
|
creators["follow"] = &ActionContext::follow;
|
||||||
creators["move from group"] = &ActionContext::move_from_group;
|
creators["move from group"] = &ActionContext::move_from_group;
|
||||||
creators["flee to master"] = &ActionContext::flee_to_master;
|
creators["flee to group leader"] = &ActionContext::flee_to_group_leader;
|
||||||
creators["runaway"] = &ActionContext::runaway;
|
creators["runaway"] = &ActionContext::runaway;
|
||||||
creators["stay"] = &ActionContext::stay;
|
creators["stay"] = &ActionContext::stay;
|
||||||
creators["sit"] = &ActionContext::sit;
|
creators["sit"] = &ActionContext::sit;
|
||||||
@@ -191,6 +191,11 @@ public:
|
|||||||
creators["buy tabard"] = &ActionContext::buy_tabard;
|
creators["buy tabard"] = &ActionContext::buy_tabard;
|
||||||
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
||||||
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
||||||
|
creators["move near water"] = &ActionContext::move_near_water;
|
||||||
|
creators["go fishing"] = &ActionContext::go_fishing;
|
||||||
|
creators["use fishing bobber"] = &ActionContext::use_fishing_bobber;
|
||||||
|
creators["end master fishing"] = &ActionContext::end_master_fishing;
|
||||||
|
creators["remove bobber strategy"] = &ActionContext::remove_bobber_strategy;
|
||||||
creators["roll"] = &ActionContext::roll_action;
|
creators["roll"] = &ActionContext::roll_action;
|
||||||
creators["cancel channel"] = &ActionContext::cancel_channel;
|
creators["cancel channel"] = &ActionContext::cancel_channel;
|
||||||
|
|
||||||
@@ -318,7 +323,7 @@ private:
|
|||||||
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
|
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
|
||||||
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
|
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
|
||||||
static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); }
|
static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); }
|
||||||
static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(botAI); }
|
static Action* flee_to_group_leader(PlayerbotAI* botAI) { return new FleeToGroupLeaderAction(botAI); }
|
||||||
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
|
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
|
||||||
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
|
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
|
||||||
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
|
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
|
||||||
@@ -380,6 +385,11 @@ private:
|
|||||||
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
||||||
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
||||||
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
||||||
|
static Action* move_near_water(PlayerbotAI* botAI) { return new MoveNearWaterAction(botAI); }
|
||||||
|
static Action* go_fishing(PlayerbotAI* botAI) { return new FishingAction(botAI);}
|
||||||
|
static Action* use_fishing_bobber(PlayerbotAI* botAI) { return new UseBobberAction(botAI);}
|
||||||
|
static Action* end_master_fishing(PlayerbotAI* botAI) { return new EndMasterFishingAction(botAI); }
|
||||||
|
static Action* remove_bobber_strategy(PlayerbotAI* botAI) { return new RemoveBobberStrategyAction(botAI); }
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
|
|
||||||
// BG Tactics
|
// BG Tactics
|
||||||
@@ -49,7 +49,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
|||||||
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
botAI->SetMaster(inviter);
|
botAI->SetMaster(inviter);
|
||||||
// else
|
// else
|
||||||
// sPlayerbotDbStore->Save(botAI);
|
// sPlayerbotRepository->Save(botAI);
|
||||||
|
|
||||||
botAI->ResetStrategies();
|
botAI->ResetStrategies();
|
||||||
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
|
||||||
@@ -59,7 +59,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
|||||||
|
|
||||||
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
||||||
{
|
{
|
||||||
Teleport(inviter, bot);
|
Teleport(inviter, bot, true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,5 @@ bool AcceptResurrectAction::Execute(Event event)
|
|||||||
packet << uint8(1); // accept
|
packet << uint8(1); // accept
|
||||||
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
|
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
|
||||||
|
|
||||||
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,14 @@ bool ReachAreaTriggerAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z);
|
bot->GetMotionMaster()->MovePoint(
|
||||||
|
/*id*/ at->map,
|
||||||
|
/*coords*/ at->x, at->y, at->z,
|
||||||
|
/*forcedMovement*/ FORCED_MOVEMENT_NONE,
|
||||||
|
/*speed*/ 0.0f, // default speed (not handled here)
|
||||||
|
/*orientation*/ 0.0f, // keep current orientation of bot
|
||||||
|
/*generatePath*/ true, // true => terrain path (2d mmap); false => straight spline (3d vmap)
|
||||||
|
/*forceDestination*/ false);
|
||||||
|
|
||||||
float distance = bot->GetDistance(at->x, at->y, at->z);
|
float distance = bot->GetDistance(at->x, at->y, at->z);
|
||||||
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay;
|
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay;
|
||||||
@@ -15,20 +15,19 @@
|
|||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
bool AttackAction::Execute(Event event)
|
bool AttackAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!target->IsInWorld())
|
if (!target->IsInWorld())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return Attack(target);
|
return Attack(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackMyTargetAction::Execute(Event event)
|
bool AttackMyTargetAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
if (!master)
|
if (!master)
|
||||||
@@ -51,7 +50,7 @@ bool AttackMyTargetAction::Execute(Event event)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||||
{
|
{
|
||||||
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
||||||
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
||||||
@@ -81,12 +80,15 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
|
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) ||
|
// Check if bot OR target is in prohibited zone/area (skip for duels)
|
||||||
sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId()))
|
if ((target->IsPlayer() || target->IsPet()) &&
|
||||||
&& (target->IsPlayer() || target->IsPet()))
|
(!bot->duel || bot->duel->Opponent != target) &&
|
||||||
|
(sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) ||
|
||||||
|
sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId())))
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
|
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
|
||||||
@@ -98,6 +100,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
|
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +108,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is dead.");
|
botAI->TellError(std::string(target->GetName()) + " is dead.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
|
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +124,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
|
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,25 +159,28 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
bot->StopMoving();
|
bot->StopMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||||
{
|
|
||||||
sServerFacade->SetFacingTo(bot, target);
|
sServerFacade->SetFacingTo(bot, target);
|
||||||
}
|
|
||||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
|
|
||||||
bot->Attack(target, shouldMelee);
|
bot->Attack(target, shouldMelee);
|
||||||
/* prevent pet dead immediately in group */
|
/* prevent pet dead immediately in group */
|
||||||
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) {
|
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
||||||
|
// {
|
||||||
// with_pet = false;
|
// with_pet = false;
|
||||||
// }
|
// }
|
||||||
// if (Pet* pet = bot->GetPet())
|
// if (Pet* pet = bot->GetPet())
|
||||||
// {
|
// {
|
||||||
// if (with_pet) {
|
// if (with_pet)
|
||||||
|
// {
|
||||||
// pet->SetReactState(REACT_DEFENSIVE);
|
// pet->SetReactState(REACT_DEFENSIVE);
|
||||||
// pet->SetTarget(target->GetGUID());
|
// pet->SetTarget(target->GetGUID());
|
||||||
// pet->GetCharmInfo()->SetIsCommandAttack(true);
|
// pet->GetCharmInfo()->SetIsCommandAttack(true);
|
||||||
// pet->AI()->AttackStart(target);
|
// pet->AI()->AttackStart(target);
|
||||||
// } else {
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
// pet->SetReactState(REACT_PASSIVE);
|
// pet->SetReactState(REACT_PASSIVE);
|
||||||
// pet->GetCharmInfo()->SetIsCommandFollow(true);
|
// pet->GetCharmInfo()->SetIsCommandFollow(true);
|
||||||
// pet->GetCharmInfo()->IsReturning();
|
// pet->GetCharmInfo()->IsReturning();
|
||||||
@@ -182,4 +191,4 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
|
|
||||||
bool AttackDuelOpponentAction::isUseful() { return AI_VALUE(Unit*, "duel target"); }
|
bool AttackDuelOpponentAction::isUseful() { return AI_VALUE(Unit*, "duel target"); }
|
||||||
|
|
||||||
bool AttackDuelOpponentAction::Execute(Event event) { return Attack(AI_VALUE(Unit*, "duel target")); }
|
bool AttackDuelOpponentAction::Execute(Event /*event*/) { return Attack(AI_VALUE(Unit*, "duel target")); }
|
||||||
@@ -75,36 +75,79 @@ void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out)
|
|||||||
void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out)
|
void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out)
|
||||||
{
|
{
|
||||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||||
|
factory.InitSkills();
|
||||||
factory.InitClassSpells();
|
factory.InitClassSpells();
|
||||||
factory.InitAvailableSpells();
|
factory.InitAvailableSpells();
|
||||||
factory.InitSkills();
|
|
||||||
factory.InitPet();
|
factory.InitPet();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
||||||
{
|
{
|
||||||
// CreatureTemplate const* co = sCreatureStorage.LookupEntry<CreatureTemplate>(id);
|
|
||||||
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
||||||
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
||||||
{
|
{
|
||||||
//uint32 questId = i->first; //not used, line marked for removal.
|
|
||||||
Quest const* quest = i->second;
|
Quest const* quest = i->second;
|
||||||
|
|
||||||
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10)
|
// only process class-specific quests to learn class-related spells, cuz
|
||||||
|
// we don't want all these bunch of entries to be handled!
|
||||||
|
if (!quest->GetRequiredClasses())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->GetLevel() ||
|
// skip quests that are repeatable, too low level, or above bots' level
|
||||||
!bot->SatisfyQuestRace(quest, false))
|
if (quest->IsRepeatable() || quest->GetMinLevel() < 10 || quest->GetMinLevel() > bot->GetLevel())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (quest->GetRewSpellCast() > 0)
|
// skip if bot doesnt satisfy class, race, or skill requirements
|
||||||
|
if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) ||
|
||||||
|
!bot->SatisfyQuestSkill(quest, false))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// use the same logic and impl from Player::learnQuestRewardedSpells
|
||||||
|
|
||||||
|
int32 spellId = quest->GetRewSpellCast();
|
||||||
|
if (!spellId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (!spellInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// xinef: find effect with learn spell and check if we have this spell
|
||||||
|
bool found = false;
|
||||||
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
{
|
{
|
||||||
LearnSpell(quest->GetRewSpellCast(), out);
|
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell &&
|
||||||
|
!bot->HasSpell(spellInfo->Effects[i].TriggerSpell))
|
||||||
|
{
|
||||||
|
// pusywizard: don't re-add profession specialties!
|
||||||
|
if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell))
|
||||||
|
if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL)
|
||||||
|
break; // pussywizard: break and not cast the spell (found is false)
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (quest->GetRewSpell() > 0)
|
|
||||||
|
// xinef: we know the spell, continue
|
||||||
|
if (!found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bot->CastSpell(bot, spellId, true);
|
||||||
|
|
||||||
|
// Check if RewardDisplaySpell is set to output the proper spell learned
|
||||||
|
// after processing quests. Output the original RewardSpell otherwise.
|
||||||
|
uint32 rewSpellId = quest->GetRewSpell();
|
||||||
|
if (rewSpellId)
|
||||||
{
|
{
|
||||||
LearnSpell(quest->GetRewSpell(), out);
|
if (SpellInfo const* rewSpellInfo = sSpellMgr->GetSpellInfo(rewSpellId))
|
||||||
|
{
|
||||||
|
*out << FormatSpell(rewSpellInfo) << ", ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*out << FormatSpell(spellInfo) << ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,39 +164,6 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::LearnSpell(uint32 spellId, std::ostringstream* out)
|
|
||||||
{
|
|
||||||
SpellInfo const* proto = sSpellMgr->GetSpellInfo(spellId);
|
|
||||||
if (!proto)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool learned = false;
|
|
||||||
for (uint8 j = 0; j < 3; ++j)
|
|
||||||
{
|
|
||||||
if (proto->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
|
|
||||||
{
|
|
||||||
uint32 learnedSpell = proto->Effects[j].TriggerSpell;
|
|
||||||
|
|
||||||
if (!bot->HasSpell(learnedSpell))
|
|
||||||
{
|
|
||||||
bot->learnSpell(learnedSpell);
|
|
||||||
*out << FormatSpell(sSpellMgr->GetSpellInfo(learnedSpell)) << ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
learned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!learned)
|
|
||||||
{
|
|
||||||
if (!bot->HasSpell(spellId))
|
|
||||||
{
|
|
||||||
bot->learnSpell(spellId);
|
|
||||||
*out << FormatSpell(proto) << ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
@@ -176,4 +186,3 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
|||||||
factory.InitEquipment(true);
|
factory.InitEquipment(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ protected:
|
|||||||
void LearnSpells(std::ostringstream* out);
|
void LearnSpells(std::ostringstream* out);
|
||||||
void LearnTrainerSpells(std::ostringstream* out);
|
void LearnTrainerSpells(std::ostringstream* out);
|
||||||
void LearnQuestSpells(std::ostringstream* out);
|
void LearnQuestSpells(std::ostringstream* out);
|
||||||
void LearnSpell(uint32 spellId, std::ostringstream* out);
|
|
||||||
std::string const FormatSpell(SpellInfo const* sInfo);
|
std::string const FormatSpell(SpellInfo const* sInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ bool BankAction::Execute(Event event)
|
|||||||
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
|
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
|
||||||
{
|
{
|
||||||
Unit* npc = botAI->GetUnit(*i);
|
Unit* npc = botAI->GetUnit(*i);
|
||||||
if (!npc || !npc->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER))
|
if (!npc || !npc->HasNpcFlag(UNIT_NPC_FLAG_BANKER))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
return ExecuteBank(text, npc);
|
return ExecuteBank(text, npc);
|
||||||
@@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BattleGroundTactics.h"
|
#include "BattleGroundTactics.h"
|
||||||
#include "BattleGroundJoinAction.h"
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "ArenaTeam.h"
|
#include "ArenaTeam.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "BattleGroundJoinAction.h"
|
||||||
#include "Battleground.h"
|
#include "Battleground.h"
|
||||||
#include "BattlegroundAB.h"
|
#include "BattlegroundAB.h"
|
||||||
#include "BattlegroundAV.h"
|
#include "BattlegroundAV.h"
|
||||||
@@ -22,11 +24,12 @@
|
|||||||
#include "BattlegroundSA.h"
|
#include "BattlegroundSA.h"
|
||||||
#include "BattlegroundWS.h"
|
#include "BattlegroundWS.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
|
#include "GameObject.h"
|
||||||
#include "IVMapMgr.h"
|
#include "IVMapMgr.h"
|
||||||
|
#include "PathGenerator.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PositionValue.h"
|
#include "PositionValue.h"
|
||||||
#include "PvpTriggers.h"
|
#include "PvpTriggers.h"
|
||||||
#include "PathGenerator.h"
|
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
|
|
||||||
@@ -1754,7 +1757,7 @@ bool BGTactics::moveToStart(bool force)
|
|||||||
WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f),
|
WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f),
|
||||||
WS_WAITING_POS_ALLIANCE_2.GetPositionZ());
|
WS_WAITING_POS_ALLIANCE_2.GetPositionZ());
|
||||||
}
|
}
|
||||||
else // BB_WSG_WAIT_SPOT_SPAWN
|
else // BB_WSG_WAIT_SPOT_SPAWN
|
||||||
{
|
{
|
||||||
if (bot->GetTeamId() == TEAM_HORDE)
|
if (bot->GetTeamId() == TEAM_HORDE)
|
||||||
MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f),
|
MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f),
|
||||||
@@ -1894,13 +1897,13 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
bool isDefender = role < defendersProhab;
|
bool isDefender = role < defendersProhab;
|
||||||
bool isAdvanced = !isDefender && role > 8;
|
bool isAdvanced = !isDefender && role > 8;
|
||||||
|
|
||||||
const auto& attackObjectives =
|
auto const& attackObjectives =
|
||||||
(team == TEAM_HORDE) ? AV_AttackObjectives_Horde : AV_AttackObjectives_Alliance;
|
(team == TEAM_HORDE) ? AV_AttackObjectives_Horde : AV_AttackObjectives_Alliance;
|
||||||
const auto& defendObjectives =
|
auto const& defendObjectives =
|
||||||
(team == TEAM_HORDE) ? AV_DefendObjectives_Horde : AV_DefendObjectives_Alliance;
|
(team == TEAM_HORDE) ? AV_DefendObjectives_Horde : AV_DefendObjectives_Alliance;
|
||||||
|
|
||||||
uint32 destroyedNodes = 0;
|
uint32 destroyedNodes = 0;
|
||||||
for (const auto& [nodeId, _] : defendObjectives)
|
for (auto const& [nodeId, _] : defendObjectives)
|
||||||
if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED)
|
if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED)
|
||||||
destroyedNodes++;
|
destroyedNodes++;
|
||||||
|
|
||||||
@@ -2000,7 +2003,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
std::vector<GameObject*> contestedObjectives;
|
std::vector<GameObject*> contestedObjectives;
|
||||||
std::vector<GameObject*> availableObjectives;
|
std::vector<GameObject*> availableObjectives;
|
||||||
|
|
||||||
for (const auto& [nodeId, goId] : defendObjectives)
|
for (auto const& [nodeId, goId] : defendObjectives)
|
||||||
{
|
{
|
||||||
const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId);
|
const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId);
|
||||||
if (node.State == POINT_DESTROYED)
|
if (node.State == POINT_DESTROYED)
|
||||||
@@ -2026,7 +2029,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
if (!BgObjective)
|
if (!BgObjective)
|
||||||
{
|
{
|
||||||
uint32 towersDown = 0;
|
uint32 towersDown = 0;
|
||||||
for (const auto& [nodeId, _] : attackObjectives)
|
for (auto const& [nodeId, _] : attackObjectives)
|
||||||
if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED)
|
if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED)
|
||||||
towersDown++;
|
towersDown++;
|
||||||
|
|
||||||
@@ -2053,7 +2056,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
{
|
{
|
||||||
std::vector<GameObject*> candidates;
|
std::vector<GameObject*> candidates;
|
||||||
|
|
||||||
for (const auto& [nodeId, goId] : attackObjectives)
|
for (auto const& [nodeId, goId] : attackObjectives)
|
||||||
{
|
{
|
||||||
const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId);
|
const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId);
|
||||||
GameObject* go = bg->GetBGObject(goId);
|
GameObject* go = bg->GetBGObject(goId);
|
||||||
@@ -2105,13 +2108,13 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
Position objPos = BgObjective->GetPosition();
|
Position objPos = BgObjective->GetPosition();
|
||||||
|
|
||||||
Optional<uint8> linkedNodeId;
|
Optional<uint8> linkedNodeId;
|
||||||
for (const auto& [nodeId, goId] : attackObjectives)
|
for (auto const& [nodeId, goId] : attackObjectives)
|
||||||
if (bg->GetBGObject(goId) == BgObjective)
|
if (bg->GetBGObject(goId) == BgObjective)
|
||||||
linkedNodeId = nodeId;
|
linkedNodeId = nodeId;
|
||||||
|
|
||||||
if (!linkedNodeId)
|
if (!linkedNodeId)
|
||||||
{
|
{
|
||||||
for (const auto& [nodeId, goId] : defendObjectives)
|
for (auto const& [nodeId, goId] : defendObjectives)
|
||||||
if (bg->GetBGObject(goId) == BgObjective)
|
if (bg->GetBGObject(goId) == BgObjective)
|
||||||
linkedNodeId = nodeId;
|
linkedNodeId = nodeId;
|
||||||
}
|
}
|
||||||
@@ -2543,7 +2546,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
float bestDist = FLT_MAX;
|
float bestDist = FLT_MAX;
|
||||||
uint32 bestTrigger = 0;
|
uint32 bestTrigger = 0;
|
||||||
|
|
||||||
for (const auto& [nodeId, _, areaTrigger] : EY_AttackObjectives)
|
for (auto const& [nodeId, _, areaTrigger] : EY_AttackObjectives)
|
||||||
{
|
{
|
||||||
if (!IsOwned(nodeId))
|
if (!IsOwned(nodeId))
|
||||||
continue;
|
continue;
|
||||||
@@ -2610,7 +2613,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
// --- PRIORITY 2: Nearby unowned contested node ---
|
// --- PRIORITY 2: Nearby unowned contested node ---
|
||||||
if (!foundObjective)
|
if (!foundObjective)
|
||||||
{
|
{
|
||||||
for (const auto& [nodeId, _, __] : EY_AttackObjectives)
|
for (auto const& [nodeId, _, __] : EY_AttackObjectives)
|
||||||
{
|
{
|
||||||
if (IsOwned(nodeId))
|
if (IsOwned(nodeId))
|
||||||
continue;
|
continue;
|
||||||
@@ -2711,7 +2714,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
if (!foundObjective && strategy == EY_STRATEGY_FLAG_FOCUS)
|
if (!foundObjective && strategy == EY_STRATEGY_FLAG_FOCUS)
|
||||||
{
|
{
|
||||||
bool ownsAny = false;
|
bool ownsAny = false;
|
||||||
for (const auto& [nodeId, _, __] : EY_AttackObjectives)
|
for (auto const& [nodeId, _, __] : EY_AttackObjectives)
|
||||||
{
|
{
|
||||||
if (IsOwned(nodeId))
|
if (IsOwned(nodeId))
|
||||||
{
|
{
|
||||||
@@ -2739,7 +2742,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
float bestDist = FLT_MAX;
|
float bestDist = FLT_MAX;
|
||||||
Optional<uint32> bestNode;
|
Optional<uint32> bestNode;
|
||||||
|
|
||||||
for (const auto& [nodeId, _, __] : EY_AttackObjectives)
|
for (auto const& [nodeId, _, __] : EY_AttackObjectives)
|
||||||
{
|
{
|
||||||
if (IsOwned(nodeId))
|
if (IsOwned(nodeId))
|
||||||
continue;
|
continue;
|
||||||
@@ -2974,7 +2977,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives);
|
uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives);
|
||||||
for (uint32 i = 0; i < len; i++)
|
for (uint32 i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
const auto& objective =
|
auto const& objective =
|
||||||
IC_AttackObjectives[(i + role) %
|
IC_AttackObjectives[(i + role) %
|
||||||
len]; // use role to determine which objective checked first
|
len]; // use role to determine which objective checked first
|
||||||
if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H)
|
if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H)
|
||||||
@@ -3126,7 +3129,7 @@ bool BGTactics::selectObjective(bool reset)
|
|||||||
uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives);
|
uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives);
|
||||||
for (uint32 i = 0; i < len; i++)
|
for (uint32 i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
const auto& objective =
|
auto const& objective =
|
||||||
IC_AttackObjectives[(i + role) %
|
IC_AttackObjectives[(i + role) %
|
||||||
len]; // use role to determine which objective checked first
|
len]; // use role to determine which objective checked first
|
||||||
if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H)
|
if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H)
|
||||||
@@ -3365,12 +3368,12 @@ bool BGTactics::resetObjective()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Adjust role-change chance based on battleground type
|
// Adjust role-change chance based on battleground type
|
||||||
uint32 oddsToChangeRole = 1; // default low
|
uint32 oddsToChangeRole = 1; // default low
|
||||||
BattlegroundTypeId bgType = bg->GetBgTypeID();
|
BattlegroundTypeId bgType = bg->GetBgTypeID();
|
||||||
|
|
||||||
if (bgType == BATTLEGROUND_WS)
|
if (bgType == BATTLEGROUND_WS)
|
||||||
oddsToChangeRole = 2;
|
oddsToChangeRole = 2;
|
||||||
else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB)
|
else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB)
|
||||||
oddsToChangeRole = 1;
|
oddsToChangeRole = 1;
|
||||||
else if (bgType == BATTLEGROUND_AV)
|
else if (bgType == BATTLEGROUND_AV)
|
||||||
oddsToChangeRole = 0;
|
oddsToChangeRole = 0;
|
||||||
@@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
GuidVector closePlayers;
|
GuidVector closePlayers;
|
||||||
float flagRange = 0.0f;
|
float flagRange = 0.0f;
|
||||||
|
|
||||||
|
// Eye of the Storm helpers used later when handling capture positioning
|
||||||
|
BattlegroundEY* eyeBg = nullptr;
|
||||||
|
GameObject* eyCenterFlag = nullptr;
|
||||||
|
if (bgType == BATTLEGROUND_EY)
|
||||||
|
{
|
||||||
|
eyeBg = static_cast<BattlegroundEY*>(bg);
|
||||||
|
if (eyeBg)
|
||||||
|
eyCenterFlag = eyeBg->GetBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM);
|
||||||
|
}
|
||||||
|
|
||||||
// Set up appropriate search ranges and object lists based on BG type
|
// Set up appropriate search ranges and object lists based on BG type
|
||||||
switch (bgType)
|
switch (bgType)
|
||||||
{
|
{
|
||||||
@@ -3607,27 +3620,82 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (closeObjects.empty())
|
if (closeObjects.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
auto keepStationaryWhileCapturing = [&](CurrentSpellTypes spellType)
|
||||||
|
{
|
||||||
|
Spell* currentSpell = bot->GetCurrentSpell(spellType);
|
||||||
|
if (!currentSpell || !currentSpell->m_spellInfo || currentSpell->m_spellInfo->Id != SPELL_CAPTURE_BANNER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If the capture target is no longer available (another bot already captured it), stop channeling
|
||||||
|
if (GameObject* targetFlag = currentSpell->m_targets.GetGOTarget())
|
||||||
|
{
|
||||||
|
if (!targetFlag->isSpawned() || targetFlag->GetGoState() != GO_STATE_READY)
|
||||||
|
{
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
resetObjective();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
resetObjective();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->IsMounted())
|
||||||
|
{
|
||||||
|
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->IsInDisallowedMountForm())
|
||||||
|
{
|
||||||
|
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->isMoving())
|
||||||
|
{
|
||||||
|
bot->StopMoving();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we are already channeling the capture spell, keep the bot stationary and dismounted
|
||||||
|
if (keepStationaryWhileCapturing(CURRENT_CHANNELED_SPELL) || keepStationaryWhileCapturing(CURRENT_GENERIC_SPELL))
|
||||||
|
return true;
|
||||||
|
|
||||||
// First identify which flag/base we're trying to interact with
|
// First identify which flag/base we're trying to interact with
|
||||||
GameObject* targetFlag = nullptr;
|
GameObject* targetFlag = nullptr;
|
||||||
for (ObjectGuid const guid : closeObjects)
|
for (ObjectGuid const guid : closeObjects)
|
||||||
{
|
{
|
||||||
GameObject* go = botAI->GetGameObject(guid);
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
if (!go)
|
if (!go)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
|
||||||
|
|
||||||
// Check if this object is a valid capture target
|
// Check if this object is a valid capture target
|
||||||
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
std::vector<uint32>::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||||
if (f == vFlagIds.end())
|
if (f == vFlagIds.end() && !isEyCenterFlag)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the object is active and ready
|
// Verify the object is active and ready
|
||||||
if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY)
|
if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we're in range (using double range for enemy detection)
|
// Check if we're in range (using double range for enemy detection)
|
||||||
float const dist = bot->GetDistance(go);
|
float const dist = bot->GetDistance(go);
|
||||||
if (flagRange && dist > flagRange * 2.0f)
|
if (flagRange && dist > flagRange * 2.0f)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
targetFlag = go;
|
targetFlag = go;
|
||||||
break;
|
break;
|
||||||
@@ -3655,7 +3723,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if friendly players are already capturing
|
// Check if friendly players are already capturing
|
||||||
if (!closePlayers.empty())
|
if (!closePlayers.empty() && bgType != BATTLEGROUND_EY)
|
||||||
{
|
{
|
||||||
// Track number of friendly players capturing and the closest one
|
// Track number of friendly players capturing and the closest one
|
||||||
uint32 numCapturing = 0;
|
uint32 numCapturing = 0;
|
||||||
@@ -3664,14 +3732,17 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
{
|
{
|
||||||
if (Unit* pFriend = botAI->GetUnit(guid))
|
if (Unit* pFriend = botAI->GetUnit(guid))
|
||||||
{
|
{
|
||||||
// Check if they're casting the capture spell
|
// Check if they're casting or channeling the capture spell
|
||||||
if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL))
|
Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
|
if (!spell)
|
||||||
{
|
{
|
||||||
if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||||
{
|
}
|
||||||
numCapturing++;
|
|
||||||
capturingPlayer = pFriend;
|
if (spell && spell->m_spellInfo && spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
||||||
}
|
{
|
||||||
|
numCapturing++;
|
||||||
|
capturingPlayer = pFriend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3704,9 +3775,11 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (!go)
|
if (!go)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID();
|
||||||
|
|
||||||
// Validate this is a capture target
|
// Validate this is a capture target
|
||||||
std::vector<uint32>::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
std::vector<uint32>::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry());
|
||||||
if (f == vFlagIds.end())
|
if (f == vFlagIds.end() && !isEyCenterFlag)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check object is active
|
// Check object is active
|
||||||
@@ -3722,12 +3795,40 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Special handling for WSG and EY base flags
|
// Special handling for WSG and EY base flags
|
||||||
bool atBase = bgType == BATTLEGROUND_WS ? go->GetEntry() == vFlagsWS[bot->GetTeamId()]
|
bool isWsBaseFlag = bgType == BATTLEGROUND_WS && go->GetEntry() == vFlagsWS[bot->GetTeamId()];
|
||||||
: bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0]
|
bool isEyBaseFlag = bgType == BATTLEGROUND_EY && go->GetEntry() == vFlagsEY[0];
|
||||||
: false;
|
|
||||||
|
// Ensure bots are inside the Eye of the Storm capture circle before casting
|
||||||
|
if (bgType == BATTLEGROUND_EY)
|
||||||
|
{
|
||||||
|
GameObject* captureFlag = (isEyBaseFlag && eyCenterFlag) ? eyCenterFlag : go;
|
||||||
|
float const requiredRange = 2.5f;
|
||||||
|
if (!bot->IsWithinDistInMap(captureFlag, requiredRange))
|
||||||
|
{
|
||||||
|
// Stay mounted while relocating to avoid mount/dismount loops
|
||||||
|
return MoveTo(bot->GetMapId(), captureFlag->GetPositionX(), captureFlag->GetPositionY(),
|
||||||
|
captureFlag->GetPositionZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once inside the circle, dismount and stop before starting the channel
|
||||||
|
if (bot->IsMounted())
|
||||||
|
{
|
||||||
|
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->IsInDisallowedMountForm())
|
||||||
|
{
|
||||||
|
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->isMoving())
|
||||||
|
{
|
||||||
|
bot->StopMoving();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't capture own flag in WSG unless carrying enemy flag
|
// Don't capture own flag in WSG unless carrying enemy flag
|
||||||
if (atBase && bgType == BATTLEGROUND_WS &&
|
if (isWsBaseFlag && bgType == BATTLEGROUND_WS &&
|
||||||
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
|
!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -3743,7 +3844,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
{
|
{
|
||||||
float const moveDist = bot->GetObjectSize() + go->GetObjectSize() + 0.1f;
|
float const moveDist = bot->GetObjectSize() + go->GetObjectSize() + 0.1f;
|
||||||
return MoveTo(bot->GetMapId(), go->GetPositionX() + (urand(0, 1) ? -moveDist : moveDist),
|
return MoveTo(bot->GetMapId(), go->GetPositionX() + (urand(0, 1) ? -moveDist : moveDist),
|
||||||
go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ());
|
go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismount before capturing
|
// Dismount before capturing
|
||||||
@@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
if (dist < INTERACTION_DISTANCE)
|
if (dist < INTERACTION_DISTANCE)
|
||||||
{
|
{
|
||||||
// Handle flag capture at base
|
// Handle flag capture at base
|
||||||
if (atBase)
|
if (isWsBaseFlag)
|
||||||
{
|
{
|
||||||
if (bot->GetTeamId() == TEAM_HORDE)
|
if (bot->GetTeamId() == TEAM_HORDE)
|
||||||
{
|
{
|
||||||
@@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case BATTLEGROUND_EY:
|
case BATTLEGROUND_EY:
|
||||||
{ // Issue: Currently bots in EY take flag instantly without casttime
|
{ // Handle Netherstorm flag capture requiring a channel
|
||||||
if (dist < INTERACTION_DISTANCE)
|
if (dist < INTERACTION_DISTANCE)
|
||||||
{
|
{
|
||||||
// Dismount before interacting
|
// Dismount before interacting
|
||||||
if (bot->IsMounted())
|
if (bot->IsMounted())
|
||||||
|
{
|
||||||
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
bot->RemoveAurasByType(SPELL_AURA_MOUNTED);
|
||||||
|
}
|
||||||
|
|
||||||
if (bot->IsInDisallowedMountForm())
|
if (bot->IsInDisallowedMountForm())
|
||||||
|
{
|
||||||
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
|
bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle center flag differently (requires spell cast)
|
// Handle center flag differently (requires spell cast)
|
||||||
if (atBase)
|
if (isEyCenterFlag)
|
||||||
{
|
{
|
||||||
|
for (uint8 type = CURRENT_MELEE_SPELL; type <= CURRENT_CHANNELED_SPELL; ++type)
|
||||||
|
{
|
||||||
|
if (Spell* currentSpell = bot->GetCurrentSpell(static_cast<CurrentSpellTypes>(type)))
|
||||||
|
{
|
||||||
|
// m_spellInfo may be null in some states: protect access
|
||||||
|
if (currentSpell->m_spellInfo && currentSpell->m_spellInfo->Id == SPELL_CAPTURE_BANNER)
|
||||||
|
{
|
||||||
|
bot->StopMoving();
|
||||||
|
botAI->SetNextCheckDelay(500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER);
|
||||||
if (!spellInfo)
|
if (!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
||||||
spell->m_targets.SetGOTarget(go);
|
spell->m_targets.SetGOTarget(go);
|
||||||
|
|
||||||
|
bot->StopMoving();
|
||||||
spell->prepare(&spell->m_targets);
|
spell->prepare(&spell->m_targets);
|
||||||
|
|
||||||
botAI->WaitForSpellCast(spell);
|
botAI->WaitForSpellCast(spell);
|
||||||
//return true; Intended to make a bot cast SPELL_CAPTURE_BANNER and wait for spell finish, but doesn't work and causes infinite loop
|
resetObjective();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick up dropped flag
|
// Pick up dropped flag
|
||||||
@@ -3849,8 +3972,8 @@ bool BGTactics::atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<ui
|
|||||||
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
|
return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,42 +224,36 @@ bool BuyAction::Execute(Event event)
|
|||||||
|
|
||||||
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
|
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
|
||||||
{
|
{
|
||||||
uint32 oldCount = AI_VALUE2(uint32, "item count", proto->Name1);
|
if (!tItems || !proto)
|
||||||
|
|
||||||
if (!tItems)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 itemId = proto->ItemId;
|
uint32 itemId = proto->ItemId;
|
||||||
for (uint32 slot = 0; slot < tItems->GetItemCount(); slot++)
|
uint32 oldCount = bot->GetItemCount(itemId, false);
|
||||||
|
|
||||||
|
for (uint32 slot = 0; slot < tItems->GetItemCount(); ++slot)
|
||||||
{
|
{
|
||||||
if (tItems->GetItem(slot)->item == itemId)
|
if (tItems->GetItem(slot)->item != itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 botMoney = bot->GetMoney();
|
||||||
|
if (botAI->HasCheat(BotCheatMask::gold))
|
||||||
|
bot->SetMoney(10000000);
|
||||||
|
|
||||||
|
bot->BuyItemFromVendorSlot(vendorguid, slot, itemId, 1, NULL_BAG, NULL_SLOT);
|
||||||
|
|
||||||
|
if (botAI->HasCheat(BotCheatMask::gold))
|
||||||
|
bot->SetMoney(botMoney);
|
||||||
|
|
||||||
|
uint32 newCount = bot->GetItemCount(itemId, false);
|
||||||
|
if (newCount > oldCount)
|
||||||
{
|
{
|
||||||
uint32 botMoney = bot->GetMoney();
|
std::ostringstream out;
|
||||||
if (botAI->HasCheat(BotCheatMask::gold))
|
out << "Buying " << ChatHelper::FormatItem(proto);
|
||||||
{
|
botAI->TellMaster(out.str());
|
||||||
bot->SetMoney(10000000);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
bot->BuyItemFromVendorSlot(vendorguid, slot, itemId, 1, NULL_BAG, NULL_SLOT);
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::gold))
|
|
||||||
{
|
|
||||||
bot->SetMoney(botMoney);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldCount <
|
|
||||||
AI_VALUE2(
|
|
||||||
uint32, "item count",
|
|
||||||
proto->Name1)) // BuyItem Always returns false (unless unique) so we have to check the item counts.
|
|
||||||
{
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "Buying " << ChatHelper::FormatItem(proto);
|
|
||||||
botAI->TellMaster(out.str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "ChangeStrategyAction.h"
|
#include "ChangeStrategyAction.h"
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "PlayerbotDbStore.h"
|
#include "PlayerbotRepository.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool ChangeCombatStrategyAction::Execute(Event event)
|
bool ChangeCombatStrategyAction::Execute(Event event)
|
||||||
@@ -24,7 +24,7 @@ bool ChangeCombatStrategyAction::Execute(Event event)
|
|||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
case '~':
|
case '~':
|
||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotRepository->Save(botAI);
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
break;
|
break;
|
||||||
@@ -62,7 +62,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event)
|
|||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
case '~':
|
case '~':
|
||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotRepository->Save(botAI);
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
break;
|
break;
|
||||||
@@ -241,20 +241,6 @@ bool MaxDpsChatShortcutAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NaxxChatShortcutAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Player* master = GetMaster();
|
|
||||||
if (!master)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
botAI->Reset();
|
|
||||||
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
|
|
||||||
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
|
|
||||||
botAI->TellMasterNoFacing("Add Naxx Strategies!");
|
|
||||||
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BwlChatShortcutAction::Execute(Event event)
|
bool BwlChatShortcutAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
@@ -85,13 +85,6 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NaxxChatShortcutAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
|
|
||||||
virtual bool Execute(Event event);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BwlChatShortcutAction : public Action
|
class BwlChatShortcutAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -81,7 +81,7 @@ bool CheckMountStateAction::isUseful()
|
|||||||
// to mostly be an issue in tunnels of WSG and AV)
|
// to mostly be an issue in tunnels of WSG and AV)
|
||||||
float posZ = bot->GetPositionZ();
|
float posZ = bot->GetPositionZ();
|
||||||
float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ);
|
float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ);
|
||||||
if (!bot->IsMounted() && posZ < groundLevel)
|
if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Not useful when bot does not have mount strat and is not currently mounted
|
// Not useful when bot does not have mount strat and is not currently mounted
|
||||||
@@ -370,7 +370,7 @@ bool CheckMountStateAction::TryPreferredMount(Player* master) const
|
|||||||
|
|
||||||
bool CheckMountStateAction::TryRandomMountFiltered(const std::map<int32, std::vector<uint32>>& spells, int32 masterSpeed) const
|
bool CheckMountStateAction::TryRandomMountFiltered(const std::map<int32, std::vector<uint32>>& spells, int32 masterSpeed) const
|
||||||
{
|
{
|
||||||
for (const auto& pair : spells)
|
for (auto const& pair : spells)
|
||||||
{
|
{
|
||||||
int32 currentSpeed = pair.first;
|
int32 currentSpeed = pair.first;
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ bool CheckMountStateAction::TryRandomMountFiltered(const std::map<int32, std::ve
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Pick a random mount from the candidate group.
|
// Pick a random mount from the candidate group.
|
||||||
const auto& ids = pair.second;
|
auto const& ids = pair.second;
|
||||||
if (!ids.empty())
|
if (!ids.empty())
|
||||||
{
|
{
|
||||||
// Required here as otherwise bots won't mount in BG's due to them constant moving
|
// Required here as otherwise bots won't mount in BG's due to them constant moving
|
||||||
@@ -78,20 +78,17 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
|
|||||||
if (!trigger->IsActive())
|
if (!trigger->IsActive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NextAction** nextActions = triggerNode->getHandlers();
|
std::vector<NextAction> nextActions = triggerNode->getHandlers();
|
||||||
|
|
||||||
bool isRpg = false;
|
bool isRpg = false;
|
||||||
|
|
||||||
for (int32 i = 0; i < NextAction::size(nextActions); i++)
|
for (NextAction nextAction : nextActions)
|
||||||
{
|
{
|
||||||
NextAction* nextAction = nextActions[i];
|
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
|
||||||
|
|
||||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
|
|
||||||
|
|
||||||
if (dynamic_cast<RpgEnabled*>(action))
|
if (dynamic_cast<RpgEnabled*>(action))
|
||||||
isRpg = true;
|
isRpg = true;
|
||||||
}
|
}
|
||||||
NextAction::destroy(nextActions);
|
|
||||||
|
|
||||||
if (isRpg)
|
if (isRpg)
|
||||||
{
|
{
|
||||||
@@ -311,7 +308,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
|
|||||||
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
Player* gmaster = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
Player* realMaster = botAI->GetMaster();
|
Player* realMaster = botAI->GetMaster();
|
||||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
|
|
||||||
@@ -327,30 +324,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gmaster || bot == gmaster)
|
if (!groupLeader || bot == groupLeader)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2)
|
if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig->rpgDistance * 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||||
float distance = gmaster->GetDistance2d(pos.getX(), pos.getY());
|
float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY());
|
||||||
|
|
||||||
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
||||||
{
|
{
|
||||||
Player* player = gmaster;
|
Player* player = groupLeader;
|
||||||
if (gmaster && !gmaster->isMoving() ||
|
if (groupLeader && !groupLeader->isMoving() ||
|
||||||
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f)
|
if ((inDungeon || !groupLeader->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == groupLeader && distance > 5.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!gmaster->isMoving() && distance < 25.0f)
|
if (!groupLeader->isMoving() && distance < 25.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (distance < formation->GetMaxDistance())
|
if (distance < formation->GetMaxDistance())
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "LootObjectStack.h"
|
#include "LootObjectStack.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
#include "PossibleRpgTargetsValue.h"
|
#include "PossibleRpgTargetsValue.h"
|
||||||
#include "PvpTriggers.h"
|
#include "PvpTriggers.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
@@ -87,9 +88,7 @@ bool DropTargetAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
Spell const* spell = bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL); // Get the current spell being cast by the bot
|
Spell const* spell = bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL); // Get the current spell being cast by the bot
|
||||||
if (spell && spell->m_spellInfo->Id == 75) //Check spell is not nullptr before accessing m_spellInfo
|
if (spell && spell->m_spellInfo->Id == 75) //Check spell is not nullptr before accessing m_spellInfo
|
||||||
{
|
|
||||||
bot->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); // Interrupt Auto Shot
|
bot->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); // Interrupt Auto Shot
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
|
|
||||||
@@ -142,6 +141,23 @@ bool AttackRtiTargetAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
||||||
|
|
||||||
|
// Fallback: if the "rti target" value did not resolve a valid unit yet,
|
||||||
|
// try to resolve the raid icon directly from the group.
|
||||||
|
if (!rtiTarget)
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
std::string const rti = AI_VALUE(std::string, "rti");
|
||||||
|
int32 const index = RtiTargetValue::GetRtiIndex(rti);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
ObjectGuid const guid = group->GetTargetIcon(index);
|
||||||
|
if (!guid.IsEmpty())
|
||||||
|
rtiTarget = botAI->GetUnit(guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
|
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
|
||||||
{
|
{
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
|
||||||
@@ -153,9 +169,7 @@ bool AttackRtiTargetAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
botAI->TellError("I dont see my rti attack target");
|
botAI->TellError("I dont see my rti attack target");
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,6 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Continue current target. 90% chance
|
//Continue current target. 90% chance
|
||||||
if (!foundTarget && urand(1, 100) > 10)
|
if (!foundTarget && urand(1, 100) > 10)
|
||||||
{
|
{
|
||||||
@@ -181,7 +180,7 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge
|
|||||||
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
|
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
|
||||||
{
|
{
|
||||||
// Tell the master where we are going.
|
// Tell the master where we are going.
|
||||||
if (!bot->GetGroup() || (botAI->GetGroupMaster() == bot))
|
if (!bot->GetGroup() || (botAI->GetGroupLeader() == bot))
|
||||||
ReportTravelTarget(newTarget, oldTarget);
|
ReportTravelTarget(newTarget, oldTarget);
|
||||||
|
|
||||||
// If we are heading to a creature/npc clear it from the ignore list.
|
// If we are heading to a creature/npc clear it from the ignore list.
|
||||||
@@ -673,7 +672,7 @@ bool ChooseTravelTargetAction::SetExploreTarget(TravelTarget* target)
|
|||||||
//271 south shore
|
//271 south shore
|
||||||
//35 booty bay
|
//35 booty bay
|
||||||
//380 The Barrens The Crossroads
|
//380 The Barrens The Crossroads
|
||||||
if(((ExploreTravelDestination * )activeTarget)->getAreaId() == 380)
|
if (((ExploreTravelDestination * )activeTarget)->getAreaId() == 380)
|
||||||
{
|
{
|
||||||
activePoints.push_back(activeTarget->getPoints(true)[0]);
|
activePoints.push_back(activeTarget->getPoints(true)[0]);
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,6 @@ bool DropQuestAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CleanQuestLogAction::Execute(Event event)
|
bool CleanQuestLogAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
|
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
|
||||||
@@ -165,7 +164,6 @@ bool CleanQuestLogAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete)
|
void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete)
|
||||||
{
|
{
|
||||||
std::vector<uint8> slots;
|
std::vector<uint8> slots;
|
||||||
@@ -837,8 +837,8 @@ uint32 TalkAction::GetRandomEmote(Unit* unit, bool textEmote)
|
|||||||
types.push_back(TEXT_EMOTE_TALKEX);
|
types.push_back(TEXT_EMOTE_TALKEX);
|
||||||
types.push_back(TEXT_EMOTE_TALKQ);
|
types.push_back(TEXT_EMOTE_TALKQ);
|
||||||
|
|
||||||
if (unit && (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) ||
|
if (unit && (unit->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) ||
|
||||||
unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)))
|
unit->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER)))
|
||||||
{
|
{
|
||||||
types.push_back(TEXT_EMOTE_SALUTE);
|
types.push_back(TEXT_EMOTE_SALUTE);
|
||||||
}
|
}
|
||||||
@@ -864,8 +864,8 @@ uint32 TalkAction::GetRandomEmote(Unit* unit, bool textEmote)
|
|||||||
types.push_back(EMOTE_ONESHOT_EXCLAMATION);
|
types.push_back(EMOTE_ONESHOT_EXCLAMATION);
|
||||||
types.push_back(EMOTE_ONESHOT_QUESTION);
|
types.push_back(EMOTE_ONESHOT_QUESTION);
|
||||||
|
|
||||||
if (unit && (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) ||
|
if (unit && (unit->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) ||
|
||||||
unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)))
|
unit->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER)))
|
||||||
{
|
{
|
||||||
types.push_back(EMOTE_ONESHOT_SALUTE);
|
types.push_back(EMOTE_ONESHOT_SALUTE);
|
||||||
}
|
}
|
||||||
@@ -271,19 +271,38 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
{
|
{
|
||||||
if (equippedItems[1])
|
if (equippedItems[1])
|
||||||
{
|
{
|
||||||
// Both slots are full - pick the worst item to replace
|
// Both slots are full - pick the worst item to replace, but only if new item is better
|
||||||
StatsWeightCalculator calc(bot);
|
StatsWeightCalculator calc(bot);
|
||||||
calc.SetItemSetBonus(false);
|
calc.SetItemSetBonus(false);
|
||||||
calc.SetOverflowPenalty(false);
|
calc.SetOverflowPenalty(false);
|
||||||
|
|
||||||
float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId);
|
// Calculate new item score with random properties
|
||||||
float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId);
|
int32 newItemRandomProp = item->GetItemRandomPropertyId();
|
||||||
|
float newItemScore = calc.CalculateItem(itemId, newItemRandomProp);
|
||||||
|
|
||||||
// If the second slot is worse, place the new item there
|
// Calculate equipped items scores with random properties
|
||||||
if (firstItemScore > secondItemScore)
|
int32 firstRandomProp = equippedItems[0]->GetItemRandomPropertyId();
|
||||||
|
int32 secondRandomProp = equippedItems[1]->GetItemRandomPropertyId();
|
||||||
|
float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId, firstRandomProp);
|
||||||
|
float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId, secondRandomProp);
|
||||||
|
|
||||||
|
// Determine which slot (if any) should be replaced
|
||||||
|
bool betterThanFirst = newItemScore > firstItemScore;
|
||||||
|
bool betterThanSecond = newItemScore > secondItemScore;
|
||||||
|
|
||||||
|
// Early return if new item is not better than either equipped item
|
||||||
|
if (!betterThanFirst && !betterThanSecond)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (betterThanFirst && betterThanSecond)
|
||||||
{
|
{
|
||||||
dstSlot++;
|
// New item is better than both - replace the worse of the two equipped items
|
||||||
|
if (firstItemScore > secondItemScore)
|
||||||
|
dstSlot++; // Replace second slot (worse)
|
||||||
|
// else: keep dstSlot as-is (replace first slot)
|
||||||
}
|
}
|
||||||
|
else if (betterThanSecond)
|
||||||
|
dstSlot++; // Only better than second slot - replace it
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -309,7 +328,6 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
botAI->TellMaster(out);
|
botAI->TellMaster(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EquipUpgradesAction::Execute(Event event)
|
bool EquipUpgradesAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
@@ -326,6 +344,27 @@ bool EquipUpgradesAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.GetSource() == "item push result")
|
||||||
|
{
|
||||||
|
WorldPacket p(event.getPacket());
|
||||||
|
p.rpos(0);
|
||||||
|
ObjectGuid playerGuid;
|
||||||
|
uint32 received, created, sendChatMessage, itemSlot, itemId;
|
||||||
|
uint8 bagSlot;
|
||||||
|
|
||||||
|
p >> playerGuid;
|
||||||
|
p >> received;
|
||||||
|
p >> created;
|
||||||
|
p >> sendChatMessage;
|
||||||
|
p >> bagSlot;
|
||||||
|
p >> itemSlot;
|
||||||
|
p >> itemId;
|
||||||
|
|
||||||
|
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CollectItemsVisitor visitor;
|
CollectItemsVisitor visitor;
|
||||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||||
|
|
||||||
@@ -338,9 +377,12 @@ bool EquipUpgradesAction::Execute(Event event)
|
|||||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||||
uint32 itemId = item->GetTemplate()->ItemId;
|
uint32 itemId = item->GetTemplate()->ItemId;
|
||||||
std::string itemUsageParam;
|
std::string itemUsageParam;
|
||||||
if (randomProperty != 0) {
|
if (randomProperty != 0)
|
||||||
|
{
|
||||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
itemUsageParam = std::to_string(itemId);
|
itemUsageParam = std::to_string(itemId);
|
||||||
}
|
}
|
||||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||||
@@ -369,9 +411,12 @@ bool EquipUpgradeAction::Execute(Event event)
|
|||||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||||
uint32 itemId = item->GetTemplate()->ItemId;
|
uint32 itemId = item->GetTemplate()->ItemId;
|
||||||
std::string itemUsageParam;
|
std::string itemUsageParam;
|
||||||
if (randomProperty != 0) {
|
if (randomProperty != 0)
|
||||||
|
{
|
||||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
itemUsageParam = std::to_string(itemId);
|
itemUsageParam = std::to_string(itemId);
|
||||||
}
|
}
|
||||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||||
510
src/Bot/BaseAi/Actions/FishingAction.cpp
Normal file
510
src/Bot/BaseAi/Actions/FishingAction.cpp
Normal file
@@ -0,0 +1,510 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
71
src/Bot/BaseAi/Actions/FishingAction.h
Normal file
71
src/Bot/BaseAi/Actions/FishingAction.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -70,7 +70,7 @@ bool FollowAction::isUseful()
|
|||||||
if (!target.empty())
|
if (!target.empty())
|
||||||
fTarget = AI_VALUE(Unit*, target);
|
fTarget = AI_VALUE(Unit*, target);
|
||||||
else
|
else
|
||||||
fTarget = AI_VALUE(Unit*, "master target");
|
fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
|
|
||||||
if (fTarget)
|
if (fTarget)
|
||||||
{
|
{
|
||||||
@@ -97,6 +97,8 @@ bool FollowAction::isUseful()
|
|||||||
|
|
||||||
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
||||||
}
|
}
|
||||||
|
if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT))
|
||||||
|
return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster);
|
||||||
|
|
||||||
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
||||||
}
|
}
|
||||||
@@ -114,9 +116,9 @@ bool FollowAction::CanDeadFollow(Unit* target)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FleeToMasterAction::Execute(Event event)
|
bool FleeToGroupLeaderAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* fTarget = AI_VALUE(Unit*, "master target");
|
Unit* fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
bool canFollow = Follow(fTarget);
|
bool canFollow = Follow(fTarget);
|
||||||
if (!canFollow)
|
if (!canFollow)
|
||||||
{
|
{
|
||||||
@@ -146,22 +148,22 @@ bool FleeToMasterAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FleeToMasterAction::isUseful()
|
bool FleeToGroupLeaderAction::isUseful()
|
||||||
{
|
{
|
||||||
if (!botAI->GetGroupMaster())
|
if (!botAI->GetGroupLeader())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->GetGroupMaster() == bot)
|
if (botAI->GetGroupLeader() == bot)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (target && botAI->GetGroupMaster()->GetTarget() == target->GetGUID())
|
if (target && botAI->GetGroupLeader()->GetTarget() == target->GetGUID())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* fTarget = AI_VALUE(Unit*, "master target");
|
Unit* fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
|
|
||||||
if (!CanDeadFollow(fTarget))
|
if (!CanDeadFollow(fTarget))
|
||||||
return false;
|
return false;
|
||||||
@@ -20,10 +20,10 @@ public:
|
|||||||
bool CanDeadFollow(Unit* target);
|
bool CanDeadFollow(Unit* target);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FleeToMasterAction : public FollowAction
|
class FleeToGroupLeaderAction : public FollowAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FleeToMasterAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to master") {}
|
FleeToGroupLeaderAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to group leader") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
@@ -29,7 +29,6 @@ namespace ai::buff
|
|||||||
// Returns false if the spellId is invalid.
|
// Returns false if the spellId is invalid.
|
||||||
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
||||||
|
|
||||||
|
|
||||||
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
||||||
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
||||||
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
||||||
@@ -190,8 +190,8 @@ CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string con
|
|||||||
|
|
||||||
bool CastEnchantItemAction::isPossible()
|
bool CastEnchantItemAction::isPossible()
|
||||||
{
|
{
|
||||||
// if (!CastSpellAction::isPossible()) {
|
// if (!CastSpellAction::isPossible())
|
||||||
|
// {
|
||||||
// botAI->TellMasterNoFacing("Impossible: " + spell);
|
// botAI->TellMasterNoFacing("Impossible: " + spell);
|
||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
@@ -265,11 +265,6 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NextAction** CastSpellAction::getPrerequisites()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("attacker without aura", spell);
|
return context->GetValue<Unit*>("attacker without aura", spell);
|
||||||
@@ -364,7 +359,8 @@ bool UseTrinketAction::UseTrinket(Item* item)
|
|||||||
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
||||||
{
|
{
|
||||||
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
|
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
|
||||||
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) {
|
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA)
|
||||||
|
{
|
||||||
applyAura = true;
|
applyAura = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,11 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
|
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
|
||||||
|
|
||||||
NextAction** getPrerequisites() override;
|
std::vector<NextAction> getPrerequisites() override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string const getSpell() { return spell; }
|
std::string const getSpell() { return spell; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -193,10 +197,12 @@ public:
|
|||||||
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "party member to resurrect"; }
|
std::string const GetTargetName() override { return "party member to resurrect"; }
|
||||||
NextAction** getPrerequisites() override
|
std::vector<NextAction> getPrerequisites() override
|
||||||
{
|
{
|
||||||
return NextAction::merge(NextAction::array(0, new NextAction("reach party member to resurrect"), NULL),
|
return NextAction::merge(
|
||||||
Action::getPrerequisites());
|
{ NextAction("reach party member to resurrect") },
|
||||||
|
Action::getPrerequisites()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user