Compare commits

..

48 Commits

Author SHA1 Message Date
bash
1ee7d5fdfe fix(Core/Travel): Hoist portal/transport cheat above 2-point reject 2026-05-19 00:03:55 +02:00
bash
1e55842c36 fix(Core/Travel): Match cmangos buildPath stitching, drop 75y guard 2026-05-18 23:52:19 +02:00
bash
1e07ef3d02 fix(Core/Travel): Preserve walk paths from taxi-path overwrite 2026-05-18 23:27:39 +02:00
bash
34600888a2 chore(Core/Travel): Warn admins to shutdown after generatenode 2026-05-18 23:18:24 +02:00
bash
c7eb1f7238 fix(Core/Travel): Skip 5y dedup when loading nodes from DB 2026-05-18 22:56:50 +02:00
bash
9ca851f837 chore(DB/Travel): Temporarily disable Aldrassil ramp anchors 2026-05-18 01:30:37 +02:00
bash
8662316212 fix(Core/Travel): Drop 2-point check, keep last-segment teleport guard 2026-05-18 01:26:30 +02:00
bash
39a8bf4396 fix(Core/Travel): Reject paths with >75y final-segment teleport jumps 2026-05-18 01:22:02 +02:00
bash
bb747404ba fix(Core/Travel): Reject 2-point BuildShortcut paths between non-adjacent nodes 2026-05-18 01:14:14 +02:00
bash
aa7baa99b2 chore(Core/Travel): Bump 2-point shortcut threshold to 75y 2026-05-18 01:11:12 +02:00
bash
6ebf7e8ec1 fix(Core/Travel): Reject 2-point BuildShortcut teleports in chained probe 2026-05-18 01:09:46 +02:00
bash
0e28bf26ee Revert non-progress chained-probe detection (broke valid paths) 2026-05-18 00:59:59 +02:00
bash
720d3f91e1 fix(Core/Travel): Loosen chained-probe non-progress threshold 2026-05-18 00:50:32 +02:00
bash
26d260d3a9 fix(Core/Travel): Bail chained probe on non-progress oscillation 2026-05-18 00:32:40 +02:00
bash
ac19374ed8 fix(Core/Travel): Chunk all saveNodeStore phases (deletes, nodes, links) 2026-05-17 13:05:33 +02:00
bash
793d3f8fdc fix(Core/Travel): Chunk saveNodeStore path inserts to avoid mega-tx 2026-05-17 12:56:39 +02:00
bash
7a8a2cd10c feat(DB/Travel): Add Aldrassil ramp travelnode anchors 2026-05-17 12:09:08 +02:00
bash
0e0b605e17 chore(Core/Debug): Compact debug-move whisper format 2026-05-17 11:39:58 +02:00
bash
6cf10c5057 feat(Core/Travel): Sparse-segment clip in LaunchWalkSpline 2026-05-17 01:29:28 +02:00
bash
13dba79982 feat(Core/RPG): Prefix-trim and sparse-segment clip on path dispatch 2026-05-17 01:23:01 +02:00
bash
4f193cb0cb feat(Core/RPG): Port cmangos 8-angle LOS+navmesh-snap to MoveWorldObjectTo 2026-05-17 01:15:29 +02:00
bash
0ba9908007 chore(Core/RPG): Loosen Z-mismatch threshold from 5y to 10y 2026-05-17 01:04:13 +02:00
bash
c5435f15c1 fix(Core/RPG): Reject mmap paths whose endpoint Z misses dest 2026-05-17 01:00:57 +02:00
bash
ba1b429bc5 fix(Core/RPG): Reject mmap paths that LOS-fail any segment 2026-05-15 00:12:46 +02:00
bash
ab0c593898 feat(Core/RPG): Switch POI when current cluster is empty 2026-05-14 23:49:55 +02:00
bash
20a8ec1ef0 fix(Core/RPG): Stop next to quest objects instead of on top of them 2026-05-14 23:09:41 +02:00
bash
e42b284d84 chore: Drop bot movement console logs 2026-05-14 22:51:36 +02:00
bash
d91aeebd38 chore: Tighten comments in travel and movement code 2026-05-14 22:38:12 +02:00
bash
1be1ee37d1 chore(Core/Travel): Drop cmangos reference in RefineWalkPoints comment 2026-05-14 22:17:42 +02:00
bash
f516104f97 fix(Core/RPG): LOS check on MoveRandomNear samples to avoid tree tunneling 2026-05-14 22:07:14 +02:00
bash
6ace916242 Revert "fix(Core/Travel): LOS check before trusting raw cmangos waypoints" 2026-05-14 22:01:00 +02:00
bash
a7a3a5fb78 fix(Core/Travel): LOS gate on empty-probe single-waypoint fallback 2026-05-14 21:59:35 +02:00
bash
6a0dc3f2f6 fix(Core/Travel): LOS check before trusting raw cmangos waypoints 2026-05-14 21:56:54 +02:00
bash
76d776e572 chore(Core/Travel): Revert travelnode threshold to 50y 2026-05-14 21:40:10 +02:00
bash
9cc0e5cbb0 chore(Core/Travel): Bump travelnode threshold to 75y 2026-05-14 21:38:41 +02:00
bash
a949aa9288 fix(Core/Travel): Trust travelnode waypoints when AC mmap rejects segments 2026-05-14 21:21:40 +02:00
bash
2825e130a7 feat(Core/Travel): Hardcode 50y travelnode threshold 2026-05-14 21:19:28 +02:00
bash
3fcd5d6924 core filter isnt working yet 2026-05-14 19:48:38 +02:00
bash
565b853f53 refactor(Core/Travel): Drop redundant NAV_GROUND_STEEP excludes (core handles via IsBot) 2026-05-10 20:20:07 +02:00
bash
c9c63faff8 fix(Core/Travel): Exclude NAV_GROUND_STEEP at all bot PathGenerator sites 2026-05-10 18:23:54 +02:00
bash
e8da8e0e43 feat(Core/Travel): Align MoveFarTo and probe pipeline with cmangos 2026-05-10 18:14:42 +02:00
bash
44cb9a5783 feat(Core/Travel): Cap bots at 50° via NAV_GROUND_STEEP exclude 2026-05-10 17:31:19 +02:00
bash
a1ad139716 feat(Core/Debug): Trace movement entry points and visualize travel nodes 2026-05-10 17:31:01 +02:00
bash
9a21ccabfb feat(Core/RPG): MoveFarTo flow, quest-pursuit at POI, MoveRandomNear retries 2026-05-10 17:30:56 +02:00
bash
29ec04e577 feat(Core/Travel): Travel-node graph routing for long-distance pathing 2026-05-10 17:30:50 +02:00
bash
80dc6a856e feat(Core/Loot): Quest GO loot, bag-make-room, item-pursuit 2026-05-10 17:30:44 +02:00
bash
01d91092ec chore(Tools): Add mmap/vmap client-data extraction script 2026-05-10 17:30:38 +02:00
bash
b49253821b feat(DB/Travel): Import cmangos travel-node graph 2026-05-10 17:30:33 +02:00
178 changed files with 13245 additions and 39117 deletions

View File

@ -1,5 +1,4 @@
name: windows-build
on:
push:
branches: [ "master", "test-staging" ]
@ -7,7 +6,7 @@ on:
branches: [ "master", "test-staging" ]
concurrency:
group: "windows-build-${{ github.event.pull_request.number || github.ref }}"
group: "windows-build-${{ github.event.pull_request.number }}"
cancel-in-progress: true
jobs:
@ -16,108 +15,35 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}
env:
BOOST_ROOT: C:\local\boost_1_87_0
CMAKE_GENERATOR: Ninja
CTOOLS_BUILD: all
BOOST_ROOT: C:\local\boost_1_82_0
steps:
- name: Checkout AzerothCore
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: 'mod-playerbots/azerothcore-wotlk'
ref: ${{ (github.base_ref || github.ref_name) == 'test-staging' && 'test-staging' || 'Playerbot' }}
path: a
path: 'ac'
- name: Checkout Playerbot Module
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: 'mod-playerbots/mod-playerbots'
path: a/modules/mod-playerbots
- name: Move source tree to short path
shell: powershell
run: |
if (Test-Path C:\ac) {
Remove-Item C:\ac -Recurse -Force
}
New-Item -ItemType Directory -Path C:\ac | Out-Null
robocopy "${{ github.workspace }}\a" "C:\ac" /MIR
if ($LASTEXITCODE -le 7) {
exit 0
}
exit $LASTEXITCODE
- name: Install Ninja
shell: powershell
run: |
choco install ninja -y
#path: 'modules/mod-playerbots'
path: ac/modules/mod-playerbots
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.13
- name: Configure OS
shell: bash
working-directory: C:\ac
working-directory: ac
env:
CONTINUOUS_INTEGRATION: true
run: |
./acore.sh install-deps
- name: Create AzerothCore CI config
shell: bash
working-directory: C:\ac
run: |
cat > conf/config.sh <<'EOF'
CCOMPILERC="cl"
CCOMPILERCXX="cl"
CTYPE="Release"
CSCRIPTS="static"
CMODULES="static"
CTOOLS_BUILD="all"
CCUSTOMOPTIONS="-DCMAKE_RC_COMPILER=rc -DCMAKE_NINJA_FORCE_RESPONSE_FILE=ON -DCMAKE_NINJA_CMCLDEPS_RC=OFF -DCMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS=ON -DCMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS=ON -DCMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES=ON -DCMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES=ON -DCMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES=ON -DCMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES=ON"
EOF
cat conf/config.sh
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756
with:
arch: x64
- name: Build
shell: bash
working-directory: C:\ac
working-directory: ac
run: |
export CTOOLS_BUILD=all
export CMAKE_GENERATOR=Ninja
export CC=cl
export CXX=cl
export RC=rc
cmake --version
ninja --version
echo "CMAKE_GENERATOR=$CMAKE_GENERATOR"
echo "CC=$CC"
echo "CXX=$CXX"
which cl || true
which rc || true
cl || true
rc || true
rm -rf var/build/obj
./acore.sh compiler build

View File

@ -548,17 +548,6 @@ AiPlayerbot.AutoGearCommand = 1
# Default: 1 (enabled)
AiPlayerbot.AutoGearCommandAltBots = 1
# If 1 (enabled) chat command: autogear bis, gives bots BiS gear from the playerbots_bis_gear table
# whose item-level floor matches AutoGearScoreLimit (e.g. 290 = ICC, 245 = ToC,
# 125 = Kara, 78 = MC). See the AutoGearScoreLimit comment below for the full list.
# chat command: autogear bis x, (x must be positive integer) will give items based on value of x.
# If x is bigger than AutoGearScoreLimit, bis wont be given, if lower it will match x when giving items.
# Commands falls back to autogear when no tier matches or the table is empty for the bot's class/spec.
# Requires AutoGearQualityLimit = 4 and AutoGearCommand = 1.
# If AutoGearCommandAltBots = 1 it will be anbled for alt bots.
# Default: 0 (disabled)
AiPlayerbot.AutoGearBisCommand = 0
# Equipment quality limitation for autogear command (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary)
# Default: 3 (rare)
AiPlayerbot.AutoGearQualityLimit = 3
@ -583,7 +572,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
# "mana" (bots have infinite mana)
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use certain cheats implemented into raid strategies)
# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar))
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid"
@ -1443,15 +1432,15 @@ AiPlayerbot.PermanentlyInWorldTime = 31104000
#
AiPlayerbot.PremadeSpecName.1.0 = arms pve
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,43397,43421
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421
AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100002012211231241
AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032023335100102012213231251-305-2033
AiPlayerbot.PremadeSpecName.1.1 = fury pve
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43396,49084,43432
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432
AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351
AiPlayerbot.PremadeSpecLink.1.1.80 = 32002300233-305053000500310153120511351
AiPlayerbot.PremadeSpecName.1.2 = prot pve
AiPlayerbot.PremadeSpecGlyph.1.2 = 43429,43397,43425,43399,49084,45797
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
AiPlayerbot.PremadeSpecLink.1.2.80 = 3500030023-301-053351225000210521030113321
AiPlayerbot.PremadeSpecName.1.3 = arms pvp
@ -1480,13 +1469,13 @@ AiPlayerbot.PremadeSpecLink.1.5.80 = 0502300123-3-250031220223012521332113321
AiPlayerbot.PremadeSpecName.2.0 = holy pve
AiPlayerbot.PremadeSpecGlyph.2.0 = 41106,43367,45741,43368,43365,41109
AiPlayerbot.PremadeSpecLink.2.0.60 = 50350151020013053100515221
AiPlayerbot.PremadeSpecLink.2.0.80 = 50350152100013053100515221-50320104203
AiPlayerbot.PremadeSpecLink.2.0.80 = 50350152220013053100515221-503201312
AiPlayerbot.PremadeSpecName.2.1 = prot pve
AiPlayerbot.PremadeSpecGlyph.2.1 = 41100,43367,43869,43368,43369,45745
AiPlayerbot.PremadeSpecGlyph.2.1 = 41099,43367,43869,43368,43369,45745
AiPlayerbot.PremadeSpecLink.2.1.60 = -05005135203102311333112321
AiPlayerbot.PremadeSpecLink.2.1.80 = -05005135203102311333312321-502302012003
AiPlayerbot.PremadeSpecName.2.2 = ret pve
AiPlayerbot.PremadeSpecGlyph.2.2 = 41092,43367,41099,43368,43340,43869
AiPlayerbot.PremadeSpecGlyph.2.2 = 41092,43367,41099,43368,43369,43869
AiPlayerbot.PremadeSpecLink.2.2.60 = --05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.65 = -05-05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
@ -1517,7 +1506,7 @@ AiPlayerbot.PremadeSpecName.3.0 = bm pve
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.0.40 = 512002015051122301
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112233110531151
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112233111531351-0323031-5
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243130531351-005305101
AiPlayerbot.PremadeSpecName.3.1 = mm pve
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,45625,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.1.60 = -035305101030013233115031151
@ -1543,8 +1532,8 @@ AiPlayerbot.PremadeSpecLink.3.5.80 = -005305201-2300302510233330533135001031
# HUNTER PET
#
# Ferocity
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003130003010101
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100003130103010122
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122
# Tenacity
AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001
AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002
@ -1565,7 +1554,7 @@ AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221
AiPlayerbot.PremadeSpecName.4.0 = as pve
AiPlayerbot.PremadeSpecGlyph.4.0 = 45768,43379,45761,43380,43378,45766
AiPlayerbot.PremadeSpecLink.4.0.60 = 005303104352100520103331051
AiPlayerbot.PremadeSpecLink.4.0.80 = 005303005352100520103331051-005005003-502
AiPlayerbot.PremadeSpecLink.4.0.80 = 005303104352100520103331051-005005005003-2
AiPlayerbot.PremadeSpecName.4.1 = combat pve
AiPlayerbot.PremadeSpecGlyph.4.1 = 42962,43379,45762,43380,43378,42969
AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251
@ -1606,7 +1595,7 @@ AiPlayerbot.PremadeSpecGlyph.5.1 = 42408,43371,42400,43374,43342,42396
AiPlayerbot.PremadeSpecLink.5.1.60 = -035050031301152530000331331
AiPlayerbot.PremadeSpecLink.5.1.80 = 05032031-235050032302152530000331351
AiPlayerbot.PremadeSpecName.5.2 = shadow pve
AiPlayerbot.PremadeSpecGlyph.5.2 = 45753,43371,42407,43374,43370,42415
AiPlayerbot.PremadeSpecGlyph.5.2 = 42406,43371,42407,43374,43342,42415
AiPlayerbot.PremadeSpecLink.5.2.60 = --325003041203010323150301351
AiPlayerbot.PremadeSpecLink.5.2.80 = 0503203--325023051223010323152301351
AiPlayerbot.PremadeSpecName.5.3 = disc pvp
@ -1633,19 +1622,19 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
#
AiPlayerbot.PremadeSpecName.6.0 = blood pve
AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43538,43544,43672,43542
AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.0.60 = 035502150300331320102013111-005
AiPlayerbot.PremadeSpecLink.6.0.80 = 0055021533303310201020131-305020510002-00522
AiPlayerbot.PremadeSpecLink.6.0.80 = 0355021533003313201020131351-005-005032
AiPlayerbot.PremadeSpecName.6.1 = frost pve
AiPlayerbot.PremadeSpecGlyph.6.1 = 45805,43673,43547,43544,43672,43543
AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351
AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003
AiPlayerbot.PremadeSpecName.6.2 = unholy pve
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,43546,43535,43672,43549
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,43546,43544,43672,43549
AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151
AiPlayerbot.PremadeSpecLink.6.2.80 = 23050202--2302303350032152000150003133151
AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43538,43544,43672,43554
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
AiPlayerbot.PremadeSpecLink.6.3.80 = 005512153330030320102013-3050505002023001-002
AiPlayerbot.PremadeSpecName.6.4 = blood pvp
@ -1675,13 +1664,13 @@ AiPlayerbot.PremadeSpecLink.6.6.80 = -320050410002-23013233010021522301012031331
AiPlayerbot.PremadeSpecName.7.0 = ele pve
AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776
AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351
AiPlayerbot.PremadeSpecLink.7.0.80 = 4530001523213351302301351-00525003
AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031
AiPlayerbot.PremadeSpecName.7.1 = enh pve
AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,43725,45771
AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,45771
AiPlayerbot.PremadeSpecLink.7.1.60 = -30305003105021333031121131051
AiPlayerbot.PremadeSpecLink.7.1.80 = 053030152-30305003105021333031131131051
AiPlayerbot.PremadeSpecName.7.2 = resto pve
AiPlayerbot.PremadeSpecGlyph.7.2 = 41527,43385,41517,43386,43725,45775
AiPlayerbot.PremadeSpecGlyph.7.2 = 41527,43385,41517,43386,44923,45775
AiPlayerbot.PremadeSpecLink.7.2.60 = --50005301235310501102321251
AiPlayerbot.PremadeSpecLink.7.2.80 = -00502033-50005331335310501122331251
AiPlayerbot.PremadeSpecName.7.3 = ele pvp
@ -1715,7 +1704,7 @@ AiPlayerbot.PremadeSpecLink.8.0.80 = 230005231100330150323102505321-03-203303001
AiPlayerbot.PremadeSpecName.8.1 = fire pve
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503110003-0055032012303330053120300351
AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503110003-0055030011302331053120321351
AiPlayerbot.PremadeSpecName.8.2 = frost pve
AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751
AiPlayerbot.PremadeSpecLink.8.2.60 = --0533030313203100030152231151
@ -1788,20 +1777,20 @@ AiPlayerbot.PremadeSpecLink.9.5.80 = -2032003311302-05230015220331351005031051
AiPlayerbot.PremadeSpecName.11.0 = balance pve
AiPlayerbot.PremadeSpecGlyph.11.0 = 40916,43331,40921,43335,44922,40919
AiPlayerbot.PremadeSpecLink.11.0.60 = 5032003125031003213304301231
AiPlayerbot.PremadeSpecLink.11.0.80 = 5032003125331303213305301231--205003012
AiPlayerbot.PremadeSpecLink.11.0.60 = 5022203105331003213005301231
AiPlayerbot.PremadeSpecLink.11.0.80 = 5032203105331303213305301231--205003012
AiPlayerbot.PremadeSpecName.11.1 = bear pve
AiPlayerbot.PremadeSpecGlyph.11.1 = 40897,43331,46372,43335,43332,40899
AiPlayerbot.PremadeSpecLink.11.1.60 = -503232132322010303120300013501
AiPlayerbot.PremadeSpecLink.11.1.80 = -503232132322010353120303013511-20350001
AiPlayerbot.PremadeSpecLink.11.1.60 = -500232130322110353100301310501
AiPlayerbot.PremadeSpecLink.11.1.80 = -501232130322110353120303313511-20350001
AiPlayerbot.PremadeSpecName.11.2 = resto pve
AiPlayerbot.PremadeSpecGlyph.11.2 = 40906,43331,45602,43335,43674,45603
AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031502331050313031
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320031--230033312031502431053313051
AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,44922,45602
AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031500531050113051
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320031--230033312031501531053313051
AiPlayerbot.PremadeSpecName.11.3 = cat pve
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43674,43335,45604
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053120030310511-203503012
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
AiPlayerbot.PremadeSpecName.11.4 = balance pvp
AiPlayerbot.PremadeSpecGlyph.11.4 = 40921,43331,45622,43674,43335,45623
AiPlayerbot.PremadeSpecLink.11.4.60 = 5012203115331002213032311231
@ -2164,27 +2153,6 @@ AiPlayerbot.RandomClassSpecIndex.11.6 = 6
#
####################################################################################################
###################################
# #
# RAIDS #
# #
###################################
####################################################################################################
#
#
#
# Enable buffs in ICC to make Heroic easier and more casual. Default is 1.
# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for
# non tank bots, increased threat for tank bots.
# Buffs will be applied on LDW, PP, Sindragosa and Lich King.
AiPlayerbot.EnableICCBuffs = 1
#
#
#
####################################################################################################
###################################
# #
@ -2468,3 +2436,9 @@ AiPlayerbot.TargetPosRecalcDistance = 0.1
# Allow bots to be summoned near innkeepers
AiPlayerbot.SummonAtInnkeepersEnabled = 1
# Enable buffs in ICC to make Heroic easier and more casual.
# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots.
# Buffs will be applied on PP, Sindragosa and Lich King
AiPlayerbot.EnableICCBuffs = 1

View File

@ -1,183 +0,0 @@
-- #########################################################
-- Playerbots - Add /p autogear bis command texts
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
-- zhTW, esES, esMX, ruRU)
-- #########################################################
DELETE FROM ai_playerbot_texts WHERE name IN (
'bis_autogear_unavailable_error',
'bis_no_rows_fallback',
'bis_command_unavailable_error',
'bis_altbot_refused_error',
'bis_quality_floor_error',
'bis_pvp_refused_error',
'bis_invalid_arg_error',
'bis_arg_above_limit_error',
'bis_no_rows_autogear_msg',
'bis_closest_match_msg',
'bis_applying_msg',
'bis_applied_msg'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'bis_autogear_unavailable_error',
'bis_no_rows_fallback',
'bis_command_unavailable_error',
'bis_altbot_refused_error',
'bis_quality_floor_error',
'bis_pvp_refused_error',
'bis_invalid_arg_error',
'bis_arg_above_limit_error',
'bis_no_rows_autogear_msg',
'bis_closest_match_msg',
'bis_applying_msg',
'bis_applied_msg'
);
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
(1767, 'bis_autogear_unavailable_error',
'autogear command is not allowed, please check the configuration.', 0, 0,
'자동 장비 명령이 허용되지 않습니다. 설정을 확인하세요.',
'La commande autogear n''est pas autorisée, veuillez vérifier la configuration.',
'Der autogear-Befehl ist nicht erlaubt, bitte überprüfe die Konfiguration.',
'自动装备命令未启用,请检查配置。',
'自動裝備指令未啟用,請檢查設定。',
'El comando autogear no está permitido, por favor revisa la configuración.',
'El comando autogear no está permitido, por favor revisa la configuración.',
'Команда autogear не разрешена, проверьте конфигурацию.'),
(1768, 'bis_no_rows_fallback',
'No BiS for your tier/spec/level, check cfg, running autogear instead', 0, 0,
'해당 등급/전문화/레벨에 BiS 목록이 없습니다. 설정을 확인하세요. 대신 자동 장비를 실행합니다.',
'Pas de BiS pour votre tier/spé/niveau, vérifiez la config, exécution d''autogear à la place.',
'Kein BiS für deinen Tier/Spec/Level, prüfe die Config, führe stattdessen autogear aus.',
'您的等级/天赋/级别没有BiS数据请检查配置改为运行autogear。',
'你的等級/天賦/級別沒有BiS資料請檢查設定改為執行autogear。',
'No hay BiS para tu tier/spec/nivel, revisa la configuración, ejecutando autogear en su lugar.',
'No hay BiS para tu tier/spec/nivel, revisa la configuración, ejecutando autogear en su lugar.',
'Нет BiS для вашего тира/спека/уровня, проверьте конфиг, запускаю autogear.'),
(1769, 'bis_command_unavailable_error',
'bis command is not allowed, please check the configuration.', 0, 0,
'bis 명령이 허용되지 않습니다. 설정을 확인하세요.',
'La commande bis n''est pas autorisée, veuillez vérifier la configuration.',
'Der bis-Befehl ist nicht erlaubt, bitte überprüfe die Konfiguration.',
'bis命令未启用请检查配置。',
'bis指令未啟用請檢查設定。',
'El comando bis no está permitido, por favor revisa la configuración.',
'El comando bis no está permitido, por favor revisa la configuración.',
'Команда bis не разрешена, проверьте конфигурацию.'),
(1770, 'bis_altbot_refused_error',
'You cannot use bis on alt bots.', 0, 0,
'부캐 봇에는 bis를 사용할 수 없습니다.',
'Vous ne pouvez pas utiliser bis sur des bots alternatifs.',
'Du kannst bis nicht auf Zweitbots verwenden.',
'你不能在副号机器人上使用bis。',
'你不能在副號機器人上使用bis。',
'No puedes usar bis en bots alternativos.',
'No puedes usar bis en bots alternativos.',
'Вы не можете использовать bis на дополнительных ботах.'),
(1771, 'bis_quality_floor_error',
'AutoGearQualityLimit must be 4 for BiS.', 0, 0,
'BiS를 사용하려면 AutoGearQualityLimit이 4여야 합니다.',
'AutoGearQualityLimit doit être à 4 pour utiliser BiS.',
'AutoGearQualityLimit muss für BiS auf 4 stehen.',
'BiS要求AutoGearQualityLimit设置为4。',
'BiS要求AutoGearQualityLimit設定為4。',
'AutoGearQualityLimit debe ser 4 para BiS.',
'AutoGearQualityLimit debe ser 4 para BiS.',
'AutoGearQualityLimit должен быть 4 для BiS.'),
(1772, 'bis_pvp_refused_error',
'bis is PvE only, bot is configured as PvP.', 0, 0,
'bis는 PvE 전용이며, 이 봇은 PvP로 설정되어 있습니다.',
'bis est uniquement pour le JcE, ce bot est configuré en JcJ.',
'bis ist nur für PvE, dieser Bot ist als PvP konfiguriert.',
'bis仅适用于PvE此机器人配置为PvP。',
'bis僅適用於PvE此機器人設定為PvP。',
'bis es solo para PvE, el bot está configurado como PvP.',
'bis es solo para PvE, el bot está configurado como PvP.',
'bis только для PvE, бот настроен на PvP.'),
(1773, 'bis_invalid_arg_error',
'Invalid BiS ilvl argument ''%param''. Use a positive integer.', 0, 0,
'잘못된 BiS 아이템 레벨 인수 ''%param''. 양의 정수를 사용하세요.',
'Argument iLvl BiS invalide ''%param''. Utilisez un entier positif.',
'Ungültiges BiS-iLvl-Argument ''%param''. Verwende eine positive ganze Zahl.',
'无效的BiS物品等级参数“%param”。请使用正整数。',
'無效的BiS物品等級參數「%param」。請使用正整數。',
'Argumento iLvl BiS inválido ''%param''. Usa un entero positivo.',
'Argumento iLvl BiS inválido ''%param''. Usa un entero positivo.',
'Неверный аргумент iLvl BiS ''%param''. Используйте положительное целое число.'),
(1774, 'bis_arg_above_limit_error',
'BiS ilvl %requested exceeds AutoGearScoreLimit %limit, refusing', 0, 0,
'BiS 아이템 레벨 %requested이(가) AutoGearScoreLimit %limit을(를) 초과합니다. 거부합니다.',
'iLvl BiS %requested dépasse AutoGearScoreLimit %limit, refusé.',
'BiS-iLvl %requested überschreitet AutoGearScoreLimit %limit, abgelehnt.',
'BiS物品等级%requested超过AutoGearScoreLimit %limit已拒绝。',
'BiS物品等級%requested超過AutoGearScoreLimit %limit已拒絕。',
'iLvl BiS %requested supera AutoGearScoreLimit %limit, rechazado.',
'iLvl BiS %requested supera AutoGearScoreLimit %limit, rechazado.',
'BiS iLvl %requested превышает AutoGearScoreLimit %limit, отказано.'),
(1775, 'bis_no_rows_autogear_msg',
'No BiS at ilvl %ilvl, using Autogear %ilvl instead', 0, 0,
'아이템 레벨 %ilvl의 BiS가 없어 대신 Autogear %ilvl을(를) 사용합니다.',
'Pas de BiS à l''iLvl %ilvl, utilisation d''Autogear %ilvl à la place.',
'Kein BiS auf iLvl %ilvl, verwende stattdessen Autogear %ilvl.',
'物品等级%ilvl没有BiS改用Autogear %ilvl。',
'物品等級%ilvl沒有BiS改用Autogear %ilvl。',
'No hay BiS en iLvl %ilvl, usando Autogear %ilvl en su lugar.',
'No hay BiS en iLvl %ilvl, usando Autogear %ilvl en su lugar.',
'Нет BiS на iLvl %ilvl, использую Autogear %ilvl.'),
(1776, 'bis_closest_match_msg',
'No BiS at ilvl %requested, using closest match at ilvl %resolved', 0, 0,
'아이템 레벨 %requested의 BiS가 없어 가장 가까운 아이템 레벨 %resolved을(를) 사용합니다.',
'Pas de BiS à l''iLvl %requested, utilisation de la correspondance la plus proche à l''iLvl %resolved.',
'Kein BiS auf iLvl %requested, verwende nächstliegende Übereinstimmung auf iLvl %resolved.',
'物品等级%requested没有BiS使用最接近的物品等级%resolved。',
'物品等級%requested沒有BiS使用最接近的物品等級%resolved。',
'No hay BiS en iLvl %requested, usando coincidencia más cercana en iLvl %resolved.',
'No hay BiS en iLvl %requested, usando coincidencia más cercana en iLvl %resolved.',
'Нет BiS на iLvl %requested, использую ближайшее совпадение на iLvl %resolved.'),
(1777, 'bis_applying_msg', 'Applying BiS gear', 0, 0,
'BiS 장비를 적용합니다.',
'Application de l''équipement BiS.',
'Wende BiS-Ausrüstung an.',
'正在装备BiS装备。',
'正在裝備BiS裝備。',
'Aplicando equipo BiS.',
'Aplicando equipo BiS.',
'Применяю BiS-снаряжение.'),
(1778, 'bis_applied_msg', 'BiS applied', 0, 0,
'BiS 장비가 적용되었습니다.',
'Équipement BiS appliqué.',
'BiS-Ausrüstung angewendet.',
'BiS装备已应用。',
'BiS裝備已套用。',
'Equipo BiS aplicado.',
'Equipo BiS aplicado.',
'BiS-снаряжение применено.');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('bis_autogear_unavailable_error', 100),
('bis_no_rows_fallback', 100),
('bis_command_unavailable_error', 100),
('bis_altbot_refused_error', 100),
('bis_quality_floor_error', 100),
('bis_pvp_refused_error', 100),
('bis_invalid_arg_error', 100),
('bis_arg_above_limit_error', 100),
('bis_no_rows_autogear_msg', 100),
('bis_closest_match_msg', 100),
('bis_applying_msg', 100),
('bis_applied_msg', 100);

View File

@ -1,489 +0,0 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'quest_accept_debug',
'quest_already_have_error',
'quest_cant_take_error',
'arena_team_already_in_team',
'arena_team_thanks_for_invite',
'area_trigger_follow_too_far_error',
'area_trigger_wait_for_me',
'attack_no_target_error',
'attack_target_not_in_world_error',
'attack_in_flight_error',
'attack_pvp_prohibited_error',
'attack_target_friendly_error',
'attack_target_dead_error',
'attack_target_not_in_sight_error',
'attack_already_attacking_error',
'attack_invalid_target_error',
'bank_no_banker_nearby_error',
'move_from_group',
'running_away',
'clean_quest_log_started',
'quest_trivial_will_remove',
'quest_has_been_removed',
'quest_not_trivial_kept',
'quest_removed_debug',
'quest_removed_with_name',
'guild_accept_inviter_not_in_guild',
'guild_accept_already_in_guild',
'guild_accept_declined',
'outfit_usage_add',
'outfit_usage_remove',
'outfit_usage_equip',
'outfit_set_as',
'outfit_equipping',
'outfit_replace_current',
'outfit_resetting',
'outfit_updating_current',
'outfit_item_removed_from',
'outfit_item_added_to',
'release_spirit_not_dead_wait',
'release_spirit_already_spirit',
'release_spirit_releasing',
'release_spirit_meet_graveyard',
'send_mail_no_mailbox_nearby',
'send_mail_one_item_only',
'send_mail_cannot_send_money',
'send_mail_not_enough_money',
'send_mail_sending_to',
'send_mail_cannot_send_item',
'send_mail_item_not_for_sale',
'send_mail_sent_to',
'craft_reset',
'craft_usage',
'craft_cannot_craft',
'craft_summary',
'set_home_success',
'set_home_no_innkeeper_error',
'quest_shared',
'tame_invalid_id_error',
'tame_usage_error',
'tame_pet_changed',
'tame_pet_changed_initialized',
'tame_exotic_requires_beast_mastery',
'tame_no_pet_by_name',
'tame_no_pet_by_id',
'tame_no_pet_by_family',
'tame_no_pet_to_rename',
'tame_pet_name_length_error',
'tame_pet_name_alpha_error',
'tame_pet_name_forbidden_error',
'tame_pet_renamed',
'tame_pet_rename_refresh_hint',
'tame_only_hunters_level_10',
'tame_creature_template_not_found',
'tame_create_pet_failed',
'tame_pet_abandoned',
'tame_no_hunter_pet_to_abandon',
'taxi_ready_next_flight',
'taxi_cant_fly_with_you',
'taxi_no_flightmaster_nearby',
'trade_busy_now',
'trade_disabled',
'trade_thank_you_player',
'trade_selling_disabled',
'trade_buying_disabled',
'trade_item_not_for_sale',
'trade_item_not_needed',
'trade_no_items_error',
'trade_discount_buy_only',
'trade_success_pleasure',
'trade_success_fair_trade',
'trade_success_thanks',
'trade_success_off_with_you',
'trade_want_money_for_this',
'use_item_none_available',
'use_gameobject',
'socket_does_not_fit',
'use_item_on_target',
'use_item',
'socketing_item_with_gem',
'meeting_stone_in_combat',
'meeting_stone_welcome',
'meeting_stone_none_nearby',
'meeting_stone_none_near_you',
'meeting_stone_no_hearthstone_self',
'meeting_stone_no_hearthstone_you',
'meeting_stone_hearthstone_not_ready_self',
'meeting_stone_hearthstone_not_ready_you',
'meeting_stone_no_innkeepers_nearby',
'meeting_stone_no_innkeepers_near_you',
'meeting_stone_cannot_summon_vehicle',
'meeting_stone_cannot_summon_master_in_combat',
'meeting_stone_cannot_summon_master_dead',
'meeting_stone_cannot_summon_bot_dead',
'meeting_stone_revived',
'meeting_stone_not_enough_space',
'new_rpg_quest_accepted',
'new_rpg_quest_rewarded',
'new_rpg_quest_dropped',
'rpg_item_better_for_player',
'rpg_start_trade_with_player'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'quest_accept_debug',
'quest_already_have_error',
'quest_cant_take_error',
'arena_team_already_in_team',
'arena_team_thanks_for_invite',
'area_trigger_follow_too_far_error',
'area_trigger_wait_for_me',
'attack_no_target_error',
'attack_target_not_in_world_error',
'attack_in_flight_error',
'attack_pvp_prohibited_error',
'attack_target_friendly_error',
'attack_target_dead_error',
'attack_target_not_in_sight_error',
'attack_already_attacking_error',
'attack_invalid_target_error',
'bank_no_banker_nearby_error',
'move_from_group',
'running_away',
'clean_quest_log_started',
'quest_trivial_will_remove',
'quest_has_been_removed',
'quest_not_trivial_kept',
'quest_removed_debug',
'quest_removed_with_name',
'guild_accept_inviter_not_in_guild',
'guild_accept_already_in_guild',
'guild_accept_declined',
'outfit_usage_add',
'outfit_usage_remove',
'outfit_usage_equip',
'outfit_set_as',
'outfit_equipping',
'outfit_replace_current',
'outfit_resetting',
'outfit_updating_current',
'outfit_item_removed_from',
'outfit_item_added_to',
'release_spirit_not_dead_wait',
'release_spirit_already_spirit',
'release_spirit_releasing',
'release_spirit_meet_graveyard',
'send_mail_no_mailbox_nearby',
'send_mail_one_item_only',
'send_mail_cannot_send_money',
'send_mail_not_enough_money',
'send_mail_sending_to',
'send_mail_cannot_send_item',
'send_mail_item_not_for_sale',
'send_mail_sent_to',
'craft_reset',
'craft_usage',
'craft_cannot_craft',
'craft_summary',
'set_home_success',
'set_home_no_innkeeper_error',
'quest_shared',
'tame_invalid_id_error',
'tame_usage_error',
'tame_pet_changed',
'tame_pet_changed_initialized',
'tame_exotic_requires_beast_mastery',
'tame_no_pet_by_name',
'tame_no_pet_by_id',
'tame_no_pet_by_family',
'tame_no_pet_to_rename',
'tame_pet_name_length_error',
'tame_pet_name_alpha_error',
'tame_pet_name_forbidden_error',
'tame_pet_renamed',
'tame_pet_rename_refresh_hint',
'tame_only_hunters_level_10',
'tame_creature_template_not_found',
'tame_create_pet_failed',
'tame_pet_abandoned',
'tame_no_hunter_pet_to_abandon',
'taxi_ready_next_flight',
'taxi_cant_fly_with_you',
'taxi_no_flightmaster_nearby',
'trade_busy_now',
'trade_disabled',
'trade_thank_you_player',
'trade_selling_disabled',
'trade_buying_disabled',
'trade_item_not_for_sale',
'trade_item_not_needed',
'trade_no_items_error',
'trade_discount_buy_only',
'trade_success_pleasure',
'trade_success_fair_trade',
'trade_success_thanks',
'trade_success_off_with_you',
'trade_want_money_for_this',
'use_item_none_available',
'use_gameobject',
'socket_does_not_fit',
'use_item_on_target',
'use_item',
'socketing_item_with_gem',
'meeting_stone_in_combat',
'meeting_stone_welcome',
'meeting_stone_none_nearby',
'meeting_stone_none_near_you',
'meeting_stone_no_hearthstone_self',
'meeting_stone_no_hearthstone_you',
'meeting_stone_hearthstone_not_ready_self',
'meeting_stone_hearthstone_not_ready_you',
'meeting_stone_no_innkeepers_nearby',
'meeting_stone_no_innkeepers_near_you',
'meeting_stone_cannot_summon_vehicle',
'meeting_stone_cannot_summon_master_in_combat',
'meeting_stone_cannot_summon_master_dead',
'meeting_stone_cannot_summon_bot_dead',
'meeting_stone_revived',
'meeting_stone_not_enough_space',
'new_rpg_quest_accepted',
'new_rpg_quest_rewarded',
'new_rpg_quest_dropped',
'rpg_item_better_for_player',
'rpg_start_trade_with_player'
);
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
(1779, 'quest_accept_debug', 'Quest [%quest] accepted', 0, 0, '퀘스트 [%quest] 수락', 'Quête [%quest] acceptée', 'Quest [%quest] angenommen', '任务 [%quest] 已接受', '任務 [%quest] 已接受', 'Misión [%quest] aceptada', 'Misión [%quest] aceptada', 'Задание [%quest] принято'),
(1780, 'quest_already_have_error', 'I have this quest', 0, 0, '이미 이 퀘스트를 가지고 있습니다', 'J''ai déjà cette quête', 'Ich habe diese Quest bereits', '我已经有这个任务了', '我已經有這個任務了', 'Ya tengo esta misión', 'Ya tengo esta misión', 'У меня уже есть это задание'),
(1781, 'quest_cant_take_error', 'I can''t take this quest', 0, 0, '이 퀘스트를 받을 수 없습니다', 'Je ne peux pas prendre cette quête', 'Ich kann diese Quest nicht annehmen', '我无法接受这个任务', '我無法接受這個任務', 'No puedo aceptar esta misión', 'No puedo aceptar esta misión', 'Я не могу взять это задание'),
(1782, 'arena_team_already_in_team', 'Sorry, I am already in such team', 0, 0, '죄송하지만 이미 그런 팀에 속해 있습니다', 'Désolé, je suis déjà dans une telle équipe', 'Entschuldigung, ich bin bereits in so einem Team', '抱歉,我已经在这样的队伍里了', '抱歉,我已經在這樣的隊伍裡了', 'Lo siento, ya estoy en un equipo así', 'Lo siento, ya estoy en un equipo así', 'Извините, я уже состою в такой команде'),
(1783, 'arena_team_thanks_for_invite', 'Thanks for the invite!', 0, 0, '초대해 주셔서 감사합니다!', 'Merci pour l''invitation !', 'Danke für die Einladung!', '谢谢你的邀请!', '謝謝你的邀請!', 'Gracias por la invitación!', 'Gracias por la invitación!', 'Спасибо за приглашение!'),
(1784, 'area_trigger_follow_too_far_error', 'I won''t follow: too far away', 0, 0, '너무 멀어서 따라가지 않겠습니다', 'Je ne suivrai pas : c''est trop loin', 'Ich werde nicht folgen: zu weit entfernt', '我不会跟随:距离太远了', '我不會跟隨:距離太遠了', 'No te seguiré: estás demasiado lejos', 'No te seguiré: estás demasiado lejos', 'Я не пойду следом: слишком далеко'),
(1785, 'area_trigger_wait_for_me', 'Wait for me', 0, 0, '잠깐만 기다려 주세요', 'Attendez-moi', 'Warte auf mich', '等等我', '等等我', 'Espérame', 'Espérame', 'Подожди меня'),
(1786, 'attack_no_target_error', 'I have no target', 0, 0, '대상이 없습니다', 'Je n''ai pas de cible', 'Ich habe kein Ziel', '我没有目标', '我沒有目標', 'No tengo objetivo', 'No tengo objetivo', 'У меня нет цели'),
(1787, 'attack_target_not_in_world_error', '%target is no longer in the world.', 0, 0, '%target은(는) 더 이상 월드에 없습니다.', '%target n''est plus dans le monde.', '%target ist nicht mehr in der Welt.', '%target 已不在世界中。', '%target 已不在世界中。', '%target ya no está en el mundo.', '%target ya no está en el mundo.', '%target больше не находится в мире.'),
(1788, 'attack_in_flight_error', 'I cannot attack in flight', 0, 0, '비행 중에는 공격할 수 없습니다', 'Je ne peux pas attaquer en vol', 'Ich kann im Flug nicht angreifen', '飞行中无法攻击', '飛行中無法攻擊', 'No puedo atacar en vuelo', 'No puedo atacar en vuelo', 'Я не могу атаковать в полёте'),
(1789, 'attack_pvp_prohibited_error', 'I cannot attack other players in PvP prohibited areas.', 0, 0, 'PvP 금지 지역에서는 다른 플레이어를 공격할 수 없습니다.', 'Je ne peux pas attaquer d''autres joueurs dans les zones où le PvP est interdit.', 'Ich kann andere Spieler in PvP-verbotenen Gebieten nicht angreifen.', '我不能在禁止 PvP 的区域攻击其他玩家。', '我不能在禁止 PvP 的區域攻擊其他玩家。', 'No puedo atacar a otros jugadores en zonas donde el JcJ está prohibido.', 'No puedo atacar a otros jugadores en zonas donde el JcJ está prohibido.', 'Я не могу атаковать других игроков в зонах, где PvP запрещено.'),
(1790, 'attack_target_friendly_error', '%target is friendly to me.', 0, 0, '%target은(는) 우호적인 대상입니다.', '%target m''est amical.', '%target ist mir gegenüber freundlich.', '%target 对我是友方目标。', '%target 對我是友方目標。', '%target es amistoso conmigo.', '%target es amistoso conmigo.', '%target дружелюбен ко мне.'),
(1791, 'attack_target_dead_error', '%target is dead.', 0, 0, '%target은(는) 죽었습니다.', '%target est mort.', '%target ist tot.', '%target 已经死了。', '%target 已經死了。', '%target está muerto.', '%target está muerto.', '%target мёртв.'),
(1792, 'attack_target_not_in_sight_error', '%target is not in my sight.', 0, 0, '%target이(가) 시야에 없습니다.', '%target n''est pas dans mon champ de vision.', '%target ist nicht in Sichtweite.', '%target 不在我的视野中。', '%target 不在我的視野中。', '%target no está a mi vista.', '%target no está a mi vista.', '%target вне поля моего зрения.'),
(1793, 'attack_already_attacking_error', 'I am already attacking %target.', 0, 0, '이미 %target을(를) 공격하고 있습니다.', 'J''attaque déjà %target.', 'Ich greife %target bereits an.', '我已经在攻击 %target。', '我已經在攻擊 %target。', 'Ya estoy atacando a %target.', 'Ya estoy atacando a %target.', 'Я уже атакую %target.'),
(1794, 'attack_invalid_target_error', 'I cannot attack an invalid target.', 0, 0, '유효하지 않은 대상은 공격할 수 없습니다.', 'Je ne peux pas attaquer une cible invalide.', 'Ich kann kein ungültiges Ziel angreifen.', '我不能攻击无效目标。', '我不能攻擊無效目標。', 'No puedo atacar un objetivo no válido.', 'No puedo atacar un objetivo no válido.', 'Я не могу атаковать недопустимую цель.'),
(1795, 'bank_no_banker_nearby_error', 'Cannot find banker nearby', 0, 0, '근처에 은행원이 없습니다', 'Impossible de trouver un banquier à proximité', 'Kein Bankier in der Nähe gefunden', '附近找不到银行职员', '附近找不到銀行職員', 'No encuentro un banquero cerca', 'No encuentro un banquero cerca', 'Поблизости нет банкира'),
(1796, 'move_from_group', 'Moving away from group', 0, 0, '파티에서 멀어지는 중입니다', 'Je m''éloigne du groupe', 'Ich entferne mich von der Gruppe', '正在远离队伍', '正在遠離隊伍', 'Me estoy alejando del grupo', 'Me estoy alejando del grupo', 'Отхожу от группы'),
(1797, 'running_away', 'Running away', 0, 0, '도망치는 중입니다', 'Je m''enfuis', 'Ich laufe weg', '正在逃跑', '正在逃跑', 'Estoy huyendo', 'Estoy huyendo', 'Убегаю'),
(1798, 'clean_quest_log_started', 'Clean Quest Log command received, removing grey/trivial quests...', 0, 0, '퀘스트 로그 정리 명령을 받았습니다. 회색/사소한 퀘스트를 제거하는 중입니다...', 'Commande de nettoyage du journal des quêtes reçue, suppression des quêtes grises/triviales...', 'Befehl zum Bereinigen des Questlogs erhalten, graue/triviale Quests werden entfernt...', '已收到清理任务日志命令,正在移除灰色/琐碎任务...', '已收到清理任務日誌命令,正在移除灰色/瑣碎任務...', 'Comando para limpiar el registro de misiones recibido, eliminando misiones grises/triviales...', 'Comando para limpiar el registro de misiones recibido, eliminando misiones grises/triviales...', 'Получена команда очистки журнала заданий, удаляю серые/простые задания...'),
(1799, 'quest_trivial_will_remove', 'Quest [%title] will be removed because it is trivial (grey).', 0, 0, '퀘스트 [%title]은(는) 사소한(회색) 퀘스트이므로 제거됩니다.', 'La quête [%title] sera supprimée car elle est triviale (grise).', 'Quest [%title] wird entfernt, weil sie trivial (grau) ist.', '任务 [%title] 将被移除,因为它是琐碎的(灰色)任务。', '任務 [%title] 將被移除,因為它是瑣碎的(灰色)任務。', 'La misión [%title] se eliminará porque es trivial (gris).', 'La misión [%title] se eliminará porque es trivial (gris).', 'Задание [%title] будет удалено, так как оно простое (серое).'),
(1800, 'quest_has_been_removed', 'Quest [%title] has been removed.', 0, 0, '퀘스트 [%title]이(가) 제거되었습니다.', 'La quête [%title] a été supprimée.', 'Quest [%title] wurde entfernt.', '任务 [%title] 已被移除。', '任務 [%title] 已被移除。', 'La misión [%title] ha sido eliminada.', 'La misión [%title] ha sido eliminada.', 'Задание [%title] было удалено.'),
(1801, 'quest_not_trivial_kept', 'Quest [%title] is not trivial and will be kept.', 0, 0, '퀘스트 [%title]은(는) 사소하지 않으므로 유지됩니다.', 'La quête [%title] n''est pas triviale et sera conservée.', 'Quest [%title] ist nicht trivial und wird behalten.', '任务 [%title] 并不琐碎,将被保留。', '任務 [%title] 並不瑣碎,將被保留。', 'La misión [%title] no es trivial y se conservará.', 'La misión [%title] no es trivial y se conservará.', 'Задание [%title] не является простым и будет сохранено.'),
(1802, 'quest_removed_debug', 'Quest [%quest] removed', 0, 0, '퀘스트 [%quest] 제거됨', 'Quête [%quest] supprimée', 'Quest [%quest] entfernt', '任务 [%quest] 已移除', '任務 [%quest] 已移除', 'Misión [%quest] eliminada', 'Misión [%quest] eliminada', 'Задание [%quest] удалено'),
(1803, 'quest_removed_with_name', 'Quest removed %quest', 0, 0, '퀘스트 제거됨 %quest', 'Quête supprimée %quest', 'Quest entfernt %quest', '任务已移除 %quest', '任務已移除 %quest', 'Misión eliminada %quest', 'Misión eliminada %quest', 'Задание удалено %quest'),
(1804, 'guild_accept_inviter_not_in_guild', 'You are not in a guild!', 0, 0, '당신은 길드에 속해 있지 않습니다!', 'Vous n''êtes pas dans une guilde !', 'Du bist in keiner Gilde!', '你不在公会中!', '你不在公會中!', 'No estás en un gremio!', 'No estás en un gremio!', 'Вы не состоите в гильдии!'),
(1805, 'guild_accept_already_in_guild', 'Sorry, I am in a guild already', 0, 0, '죄송하지만 이미 길드에 속해 있습니다', 'Désolé, je suis déjà dans une guilde', 'Entschuldigung, ich bin bereits in einer Gilde', '抱歉,我已经在公会里了', '抱歉,我已經在公會裡了', 'Lo siento, ya estoy en un gremio', 'Lo siento, ya estoy en un gremio', 'Извините, я уже в гильдии'),
(1806, 'guild_accept_declined', 'Sorry, I don''t want to join your guild :(', 0, 0, '죄송하지만 당신의 길드에 가입하고 싶지 않습니다 :(', 'Désolé, je ne veux pas rejoindre votre guilde :(', 'Entschuldigung, ich möchte deiner Gilde nicht beitreten :(', '抱歉,我不想加入你的公会 :(', '抱歉,我不想加入你的公會 :(', 'Lo siento, no quiero unirme a tu gremio :(', 'Lo siento, no quiero unirme a tu gremio :(', 'Извините, я не хочу вступать в вашу гильдию :('),
(1807, 'outfit_usage_add', 'outfit <name> +[item] to add items', 0, 0, '아이템을 추가하려면 outfit <name> +[item]', 'outfit <name> +[item] pour ajouter des objets', 'outfit <name> +[item], um Gegenstände hinzuzufügen', '使用 outfit <name> +[item] 添加物品', '使用 outfit <name> +[item] 添加物品', 'outfit <name> +[item] para agregar objetos', 'outfit <name> +[item] para agregar objetos', 'outfit <name> +[item], чтобы добавить предметы'),
(1808, 'outfit_usage_remove', 'outfit <name> -[item] to remove items', 0, 0, '아이템을 제거하려면 outfit <name> -[item]', 'outfit <name> -[item] pour retirer des objets', 'outfit <name> -[item], um Gegenstände zu entfernen', '使用 outfit <name> -[item] 移除物品', '使用 outfit <name> -[item] 移除物品', 'outfit <name> -[item] para eliminar objetos', 'outfit <name> -[item] para eliminar objetos', 'outfit <name> -[item], чтобы убрать предметы'),
(1809, 'outfit_usage_equip', 'outfit <name> equip/replace to equip items', 0, 0, '아이템을 장착하려면 outfit <name> equip/replace', 'outfit <name> equip/replace pour équiper des objets', 'outfit <name> equip/replace, um Gegenstände anzulegen', '使用 outfit <name> equip/replace 装备物品', '使用 outfit <name> equip/replace 裝備物品', 'outfit <name> equip/replace para equipar objetos', 'outfit <name> equip/replace para equipar objetos', 'outfit <name> equip/replace, чтобы экипировать предметы'),
(1810, 'outfit_set_as', 'Setting outfit %name as %param', 0, 0, '복장 %name을(를) %param(으)로 설정하는 중입니다', 'Définition de la tenue %name comme %param', 'Setze Outfit %name als %param', '正在将装备方案 %name 设置为 %param', '正在將裝備方案 %name 設定為 %param', 'Estableciendo el atuendo %name como %param', 'Estableciendo el atuendo %name como %param', 'Устанавливаю комплект %name как %param'),
(1811, 'outfit_equipping', 'Equipping outfit %name', 0, 0, '복장 %name을(를) 장착하는 중입니다', 'Équipement de la tenue %name', 'Outfit %name wird angelegt', '正在装备方案 %name', '正在裝備方案 %name', 'Equipando el atuendo %name', 'Equipando el atuendo %name', 'Экипирую комплект %name'),
(1812, 'outfit_replace_current', 'Replacing current equip with outfit %name', 0, 0, '현재 장비를 복장 %name으로 교체하는 중입니다', 'Remplacement de l''équipement actuel par la tenue %name', 'Aktuelle Ausrüstung wird durch Outfit %name ersetzt', '正在用装备方案 %name 替换当前装备', '正在用裝備方案 %name 替換目前裝備', 'Reemplazando el equipo actual con el atuendo %name', 'Reemplazando el equipo actual con el atuendo %name', 'Заменяю текущую экипировку комплектом %name'),
(1813, 'outfit_resetting', 'Resetting outfit %name', 0, 0, '복장 %name을(를) 초기화하는 중입니다', 'Réinitialisation de la tenue %name', 'Outfit %name wird zurückgesetzt', '正在重置装备方案 %name', '正在重置裝備方案 %name', 'Restableciendo el atuendo %name', 'Restableciendo el atuendo %name', 'Сбрасываю комплект %name'),
(1814, 'outfit_updating_current', 'Updating with current items outfit %name', 0, 0, '현재 아이템으로 복장 %name을(를) 업데이트하는 중입니다', 'Mise à jour de la tenue %name avec les objets actuels', 'Outfit %name wird mit der aktuellen Ausrüstung aktualisiert', '正在用当前物品更新装备方案 %name', '正在用目前物品更新裝備方案 %name', 'Actualizando el atuendo %name con los objetos actuales', 'Actualizando el atuendo %name con los objetos actuales', 'Обновляю комплект %name текущими предметами'),
(1815, 'outfit_item_removed_from', '%item removed from %name', 0, 0, '%name에서 %item이(가) 제거되었습니다', '%item retiré de %name', '%item aus %name entfernt', '已从 %name 中移除 %item', '已從 %name 中移除 %item', '%item eliminado de %name', '%item eliminado de %name', '%item удалён из %name'),
(1816, 'outfit_item_added_to', '%item added to %name', 0, 0, '%item이(가) %name에 추가되었습니다', '%item ajouté à %name', '%item zu %name hinzugefügt', '已将 %item 添加到 %name', '已將 %item 添加到 %name', '%item agregado a %name', '%item agregado a %name', '%item добавлен в %name'),
(1817, 'release_spirit_not_dead_wait', 'I am not dead, will wait here', 0, 0, '저는 죽지 않았습니다. 여기서 기다리겠습니다', 'Je ne suis pas mort, j''attendrai ici', 'Ich bin nicht tot und werde hier warten', '我还没死,会在这里等', '我還沒死,會在這裡等', 'No estoy muerto, esperaré aquí', 'No estoy muerto, esperaré aquí', 'Я не мёртв, буду ждать здесь'),
(1818, 'release_spirit_already_spirit', 'I am already a spirit', 0, 0, '저는 이미 유령입니다', 'Je suis déjà un esprit', 'Ich bin bereits ein Geist', '我已经是灵魂状态了', '我已經是靈魂狀態了', 'Ya soy un espíritu', 'Ya soy un espíritu', 'Я уже дух'),
(1819, 'release_spirit_releasing', 'Releasing...', 0, 0, '해방 중...', 'Je libère mon esprit...', 'Geist wird freigesetzt...', '正在释放灵魂...', '正在釋放靈魂...', 'Liberando espíritu...', 'Liberando espíritu...', 'Освобождаю дух...'),
(1820, 'release_spirit_meet_graveyard', 'Meet me at the graveyard', 0, 0, '묘지에서 만나요', 'Retrouvez-moi au cimetière', 'Triff mich auf dem Friedhof', '墓地见', '墓地見', 'Encuéntrame en el cementerio', 'Encuéntrame en el cementerio', 'Встречаемся на кладбище'),
(1821, 'send_mail_no_mailbox_nearby', 'There is no mailbox nearby', 0, 0, '근처에 우체통이 없습니다', 'Il n''y a pas de boîte aux lettres à proximité', 'Kein Briefkasten in der Nähe', '附近没有邮箱', '附近沒有郵箱', 'No hay un buzón cerca', 'No hay un buzón cerca', 'Поблизости нет почтового ящика'),
(1822, 'send_mail_one_item_only', 'You can not request more than one item', 0, 0, '둘 이상의 아이템은 요청할 수 없습니다', 'Vous ne pouvez pas demander plus d''un objet', 'Du kannst nicht mehr als einen Gegenstand anfordern', '你不能请求多于一件物品', '你不能要求超過一件物品', 'No puedes solicitar más de un objeto', 'No puedes solicitar más de un objeto', 'Нельзя запрашивать больше одного предмета'),
(1823, 'send_mail_cannot_send_money', 'I cannot send money', 0, 0, '돈은 보낼 수 없습니다', 'Je ne peux pas envoyer d''argent', 'Ich kann kein Geld senden', '我不能寄送钱', '我不能寄送金錢', 'No puedo enviar dinero', 'No puedo enviar dinero', 'Я не могу отправить деньги'),
(1824, 'send_mail_not_enough_money', 'I don''t have enough money', 0, 0, '돈이 충분하지 않습니다', 'Je n''ai pas assez d''argent', 'Ich habe nicht genug Geld', '我没有足够的钱', '我沒有足夠的錢', 'No tengo suficiente dinero', 'No tengo suficiente dinero', 'У меня недостаточно денег'),
(1825, 'send_mail_sending_to', 'Sending mail to %receiver', 0, 0, '%receiver에게 우편을 보내는 중입니다', 'Envoi du courrier à %receiver', 'Sende Post an %receiver', '正在向 %receiver 发送邮件', '正在向 %receiver 發送郵件', 'Enviando correo a %receiver', 'Enviando correo a %receiver', 'Отправляю почту %receiver'),
(1826, 'send_mail_cannot_send_item', 'Cannot send %item', 0, 0, '%item을(를) 보낼 수 없습니다', 'Impossible d''envoyer %item', '%item kann nicht gesendet werden', '无法发送 %item', '無法發送 %item', 'No se puede enviar %item', 'No se puede enviar %item', 'Невозможно отправить %item'),
(1827, 'send_mail_item_not_for_sale', '%item: it is not for sale', 0, 0, '%item: 판매 대상이 아닙니다', '%item : ce n''est pas à vendre', '%item: Das steht nicht zum Verkauf', '%item这不是出售物品', '%item這不是出售物品', '%item: esto no está en venta', '%item: esto no está en venta', '%item: это не продаётся'),
(1828, 'send_mail_sent_to', 'Sent mail to %receiver', 0, 0, '%receiver에게 우편을 보냈습니다', 'Courrier envoyé à %receiver', 'Post an %receiver gesendet', '已向 %receiver 发送邮件', '已向 %receiver 發送郵件', 'Correo enviado a %receiver', 'Correo enviado a %receiver', 'Почта отправлена %receiver'),
(1829, 'craft_reset', 'I will not craft anything', 0, 0, '아무것도 제작하지 않겠습니다', 'Je ne fabriquerai rien', 'Ich werde nichts herstellen', '我不会制作任何东西', '我不會製作任何東西', 'No fabricaré nada', 'No fabricaré nada', 'Я ничего не буду создавать'),
(1830, 'craft_usage', 'Usage: ''craft [itemId]'' or ''craft reset''', 0, 0, '사용법: ''craft [itemId]'' 또는 ''craft reset''', 'Utilisation : ''craft [itemId]'' ou ''craft reset''', 'Verwendung: ''craft [itemId]'' oder ''craft reset''', '用法:''craft [itemId]''''craft reset''', '用法:''craft [itemId]''''craft reset''', 'Uso: ''craft [itemId]'' o ''craft reset''', 'Uso: ''craft [itemId]'' o ''craft reset''', 'Использование: ''craft [itemId]'' или ''craft reset'''),
(1831, 'craft_cannot_craft', 'I cannot craft this', 0, 0, '이것은 제작할 수 없습니다', 'Je ne peux pas fabriquer ceci', 'Ich kann das nicht herstellen', '我无法制作这个', '我無法製作這個', 'No puedo fabricar esto', 'No puedo fabricar esto', 'Я не могу это создать'),
(1832, 'craft_summary', 'I will craft %item using reagents: %reagents (craft fee: %money)', 0, 0, '재료 %reagents을(를) 사용해 %item을(를) 제작하겠습니다 (제작 수수료: %money)', 'Je fabriquerai %item en utilisant les composants : %reagents (frais de fabrication : %money)', 'Ich werde %item mit folgenden Reagenzien herstellen: %reagents (Herstellungsgebühr: %money)', '我将使用材料 %reagents 制作 %item制作费用%money', '我將使用材料 %reagents 製作 %item製作費用%money', 'Fabricaré %item usando los materiales: %reagents (tarifa de fabricación: %money)', 'Fabricaré %item usando los materiales: %reagents (tarifa de fabricación: %money)', 'Я изготовлю %item, используя реагенты: %reagents (плата за изготовление: %money)'),
(1833, 'set_home_success', 'This inn is my new home', 0, 0, '이 여관을 새로운 집으로 설정했습니다', 'Cette auberge est ma nouvelle maison', 'Dieses Gasthaus ist mein neues Zuhause', '这家旅店是我的新家', '這間旅店是我的新家', 'Esta posada es mi nuevo hogar', 'Esta posada es mi nuevo hogar', 'Этот трактир теперь мой новый дом'),
(1834, 'set_home_no_innkeeper_error', 'Can''t find any innkeeper around', 0, 0, '주변에서 여관주인을 찾을 수 없습니다', 'Impossible de trouver un aubergiste aux alentours', 'Kein Gastwirt in der Nähe gefunden', '附近找不到旅店老板', '附近找不到旅店老闆', 'No encuentro ningún posadero cerca', 'No encuentro ningún posadero cerca', 'Не удалось найти поблизости трактирщика'),
(1835, 'quest_shared', 'Quest shared', 0, 0, '퀘스트 공유됨', 'Quête partagée', 'Quest geteilt', '任务已共享', '任務已共享', 'Misión compartida', 'Misión compartida', 'Задание поделено'),
(1836, 'tame_invalid_id_error', 'Invalid tame id.', 0, 0, '잘못된 길들이기 ID입니다.', 'Identifiant de dressage invalide.', 'Ungültige Zähm-ID.', '无效的驯服 ID。', '無效的馴服 ID。', 'ID de domesticación no válido.', 'ID de domesticación no válido.', 'Недопустимый ID приручения.'),
(1837, 'tame_usage_error', 'Usage: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 0, 0, '사용법: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 'Utilisation : tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 'Verwendung: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', '用法tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', '用法tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 'Uso: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 'Uso: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon', 'Использование: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon'),
(1838, 'tame_pet_changed', 'Pet changed to %name, ID: %id.', 0, 0, '소환수가 %name, ID: %id(으)로 변경되었습니다.', 'Familier changé en %name, ID : %id.', 'Begleiter geändert zu %name, ID: %id.', '宠物已更改为 %nameID%id。', '寵物已更改為 %nameID%id。', 'Mascota cambiada a %name, ID: %id.', 'Mascota cambiada a %name, ID: %id.', 'Питомец изменён на %name, ID: %id.'),
(1839, 'tame_pet_changed_initialized', 'Pet changed and initialized!', 0, 0, '소환수가 변경되고 초기화되었습니다!', 'Familier changé et initialisé !', 'Begleiter geändert und initialisiert!', '宠物已更改并初始化!', '寵物已更改並初始化!', 'La mascota ha sido cambiada e inicializada!', 'La mascota ha sido cambiada e inicializada!', 'Питомец изменён и инициализирован!'),
(1840, 'tame_exotic_requires_beast_mastery', 'I cannot use exotic pets unless I have the Beast Mastery talent.', 0, 0, '야수 지배 특성이 없으면 특수 야수 소환수를 사용할 수 없습니다.', 'Je ne peux pas utiliser de familiers exotiques sans le talent Maîtrise des bêtes.', 'Ich kann ohne das Talent Tierherrschaft keine exotischen Begleiter benutzen.', '如果没有野兽掌握天赋,我无法使用异种宠物。', '如果沒有野獸控制天賦,我無法使用異種寵物。', 'No puedo usar mascotas exóticas a menos que tenga el talento Dominio de bestias.', 'No puedo usar mascotas exóticas a menos que tenga el talento Dominio de bestias.', 'Я не могу использовать экзотических питомцев без таланта Повелитель зверей.'),
(1841, 'tame_no_pet_by_name', 'No tameable pet found with name: %name', 0, 0, '이름이 %name인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec le nom : %name', 'Kein zähmbares Tier mit dem Namen %name gefunden', '未找到名为 %name 的可驯服宠物', '未找到名為 %name 的可馴服寵物', 'No se encontró ninguna mascota domesticable con el nombre: %name', 'No se encontró ninguna mascota domesticable con el nombre: %name', 'Не найден приручаемый питомец с именем: %name'),
(1842, 'tame_no_pet_by_id', 'No tameable pet found with id: %id', 0, 0, 'ID가 %id인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec l''id : %id', 'Kein zähmbares Tier mit der ID %id gefunden', '未找到 ID 为 %id 的可驯服宠物', '未找到 ID 為 %id 的可馴服寵物', 'No se encontró ninguna mascota domesticable con el id: %id', 'No se encontró ninguna mascota domesticable con el id: %id', 'Не найден приручаемый питомец с id: %id'),
(1843, 'tame_no_pet_by_family', 'No tameable pet found with family: %family', 0, 0, '계열이 %family인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec la famille : %family', 'Kein zähmbares Tier mit der Familie %family gefunden', '未找到家族为 %family 的可驯服宠物', '未找到家族為 %family 的可馴服寵物', 'No se encontró ninguna mascota domesticable con la familia: %family', 'No se encontró ninguna mascota domesticable con la familia: %family', 'Не найден приручаемый питомец семейства: %family'),
(1844, 'tame_no_pet_to_rename', 'You have no pet to rename.', 0, 0, '이름을 바꿀 소환수가 없습니다.', 'Vous n''avez pas de familier à renommer.', 'Du hast kein Tier zum Umbenennen.', '你没有可重命名的宠物。', '你沒有可重新命名的寵物。', 'No tienes ninguna mascota para renombrar.', 'No tienes ninguna mascota para renombrar.', 'У вас нет питомца для переименования.'),
(1845, 'tame_pet_name_length_error', 'Pet name must be between 1 and 12 alphabetic characters.', 0, 0, '소환수 이름은 1자에서 12자의 알파벳 문자여야 합니다.', 'Le nom du familier doit contenir entre 1 et 12 caractères alphabétiques.', 'Der Name des Begleiters muss zwischen 1 und 12 alphabetischen Zeichen lang sein.', '宠物名字必须为 1 到 12 个字母字符。', '寵物名字必須為 1 到 12 個字母字元。', 'El nombre de la mascota debe tener entre 1 y 12 caracteres alfabéticos.', 'El nombre de la mascota debe tener entre 1 y 12 caracteres alfabéticos.', 'Имя питомца должно содержать от 1 до 12 буквенных символов.'),
(1846, 'tame_pet_name_alpha_error', 'Pet name must only contain alphabetic characters (A-Z, a-z).', 0, 0, '소환수 이름에는 알파벳 문자(A-Z, a-z)만 포함될 수 있습니다.', 'Le nom du familier ne doit contenir que des caractères alphabétiques (A-Z, a-z).', 'Der Name des Begleiters darf nur alphabetische Zeichen (A-Z, a-z) enthalten.', '宠物名字只能包含字母字符A-Z, a-z', '寵物名字只能包含字母字元A-Z, a-z', 'El nombre de la mascota solo puede contener caracteres alfabéticos (A-Z, a-z).', 'El nombre de la mascota solo puede contener caracteres alfabéticos (A-Z, a-z).', 'Имя питомца должно содержать только буквенные символы (A-Z, a-z).'),
(1847, 'tame_pet_name_forbidden_error', 'That pet name is forbidden. Please choose another name.', 0, 0, '그 소환수 이름은 사용할 수 없습니다. 다른 이름을 선택해 주세요.', 'Ce nom de familier est interdit. Veuillez en choisir un autre.', 'Dieser Name für den Begleiter ist verboten. Bitte wähle einen anderen Namen.', '该宠物名称被禁止使用。请选择其他名字。', '該寵物名稱被禁止使用。請選擇其他名字。', 'Ese nombre de mascota está prohibido. Por favor, elige otro nombre.', 'Ese nombre de mascota está prohibido. Por favor, elige otro nombre.', 'Это имя для питомца запрещено. Пожалуйста, выберите другое имя.'),
(1848, 'tame_pet_renamed', 'Your pet has been renamed to %name!', 0, 0, '당신의 소환수 이름이 %name(으)로 변경되었습니다!', 'Votre familier a été renommé en %name !', 'Dein Begleiter wurde in %name umbenannt!', '你的宠物已重命名为 %name', '你的寵物已重新命名為 %name', 'Tu mascota ha sido renombrada a %name!', 'Tu mascota ha sido renombrada a %name!', 'Ваш питомец был переименован в %name!'),
(1849, 'tame_pet_rename_refresh_hint', 'If you do not see the new name, please dismiss and recall your pet.', 0, 0, '새 이름이 보이지 않으면 소환수를 해제했다가 다시 소환해 주세요.', 'Si vous ne voyez pas le nouveau nom, veuillez renvoyer puis rappeler votre familier.', 'Wenn du den neuen Namen nicht siehst, schicke deinen Begleiter weg und rufe ihn erneut.', '如果你看不到新名字,请先解散再召回你的宠物。', '如果你看不到新名字,請先解散再召回你的寵物。', 'Si no ves el nuevo nombre, por favor retira y vuelve a invocar a tu mascota.', 'Si no ves el nuevo nombre, por favor retira y vuelve a invocar a tu mascota.', 'Если вы не видите новое имя, отпустите и снова призовите питомца.'),
(1850, 'tame_only_hunters_level_10', 'Only level 10+ hunters can have pets.', 0, 0, '10레벨 이상의 사냥꾼만 소환수를 가질 수 있습니다.', 'Seuls les chasseurs de niveau 10+ peuvent avoir des familiers.', 'Nur Jäger ab Stufe 10 können Begleiter haben.', '只有 10 级以上的猎人才能拥有宠物。', '只有 10 級以上的獵人才能擁有寵物。', 'Solo los cazadores de nivel 10 o superior pueden tener mascotas.', 'Solo los cazadores de nivel 10 o superior pueden tener mascotas.', 'Только охотники 10 уровня и выше могут иметь питомцев.'),
(1851, 'tame_creature_template_not_found', 'Creature template not found.', 0, 0, '생물 템플릿을 찾을 수 없습니다.', 'Modèle de créature introuvable.', 'Kreaturvorlage nicht gefunden.', '未找到生物模板。', '未找到生物範本。', 'No se encontró la plantilla de criatura.', 'No se encontró la plantilla de criatura.', 'Шаблон существа не найден.'),
(1852, 'tame_create_pet_failed', 'Failed to create pet.', 0, 0, '소환수 생성에 실패했습니다.', 'Échec de la création du familier.', 'Erstellen des Begleiters fehlgeschlagen.', '创建宠物失败。', '建立寵物失敗。', 'No se pudo crear la mascota.', 'No se pudo crear la mascota.', 'Не удалось создать питомца.'),
(1853, 'tame_pet_abandoned', 'Your pet has been abandoned.', 0, 0, '당신의 소환수를 버렸습니다.', 'Votre familier a été abandonné.', 'Dein Begleiter wurde freigelassen.', '你的宠物已被放弃。', '你的寵物已被放棄。', 'Tu mascota ha sido abandonada.', 'Tu mascota ha sido abandonada.', 'Ваш питомец был брошен.'),
(1854, 'tame_no_hunter_pet_to_abandon', 'You have no hunter pet to abandon.', 0, 0, '버릴 사냥꾼 소환수가 없습니다.', 'Vous n''avez pas de familier de chasseur à abandonner.', 'Du hast kein Jägertier zum Freilassen.', '你没有可放弃的猎人宠物。', '你沒有可放棄的獵人寵物。', 'No tienes ninguna mascota de cazador que abandonar.', 'No tienes ninguna mascota de cazador que abandonar.', 'У вас нет питомца охотника, которого можно бросить.'),
(1855, 'taxi_ready_next_flight', 'I am ready for the next flight', 0, 0, '다음 비행을 탈 준비가 되었습니다', 'Je suis prêt pour le prochain vol', 'Ich bin bereit für den nächsten Flug', '我已准备好进行下一次飞行', '我已準備好進行下一次飛行', 'Estoy listo para el siguiente vuelo', 'Estoy listo para el siguiente vuelo', 'Я готов к следующему перелёту'),
(1856, 'taxi_cant_fly_with_you', 'I can''t fly with you', 0, 0, '당신과 함께 날 수 없습니다', 'Je ne peux pas voler avec vous', 'Ich kann nicht mit dir fliegen', '我不能和你一起飞', '我不能和你一起飛', 'No puedo volar contigo', 'No puedo volar contigo', 'Я не могу лететь с вами'),
(1857, 'taxi_no_flightmaster_nearby', 'Cannot find any flightmaster to talk', 0, 0, '대화할 비행 조련사를 찾을 수 없습니다', 'Impossible de trouver un maître de vol à qui parler', 'Keinen Flugmeister zum Ansprechen gefunden', '找不到可以交谈的飞行管理员', '找不到可以交談的飛行管理員', 'No encuentro ningún maestro de vuelo con quien hablar', 'No encuentro ningún maestro de vuelo con quien hablar', 'Не могу найти распорядителя полётов, с которым можно поговорить'),
(1858, 'trade_busy_now', 'I''m kind of busy now', 0, 0, '지금은 좀 바쁩니다', 'Je suis un peu occupé en ce moment', 'Ich bin gerade etwas beschäftigt', '我现在有点忙', '我現在有點忙', 'Ahora estoy un poco ocupado', 'Ahora estoy un poco ocupado', 'Сейчас я немного занят'),
(1859, 'trade_disabled', 'Trading is disabled', 0, 0, '거래가 비활성화되어 있습니다', 'L''échange est désactivé', 'Handel ist deaktiviert', '交易已禁用', '交易已停用', 'El comercio está deshabilitado', 'El comercio está deshabilitado', 'Торговля отключена'),
(1860, 'trade_thank_you_player', 'Thank you %player', 0, 0, '고마워요 %player', 'Merci %player', 'Danke %player', '谢谢你 %player', '謝謝你 %player', 'Gracias %player', 'Gracias %player', 'Спасибо, %player'),
(1861, 'trade_selling_disabled', 'Selling is disabled.', 0, 0, '판매가 비활성화되어 있습니다.', 'La vente est désactivée.', 'Verkaufen ist deaktiviert.', '出售已禁用。', '出售已停用。', 'La venta está deshabilitada.', 'La venta está deshabilitada.', 'Продажа отключена.'),
(1862, 'trade_buying_disabled', 'Buying is disabled.', 0, 0, '구매가 비활성화되어 있습니다.', 'L''achat est désactivé.', 'Kaufen ist deaktiviert.', '购买已禁用。', '購買已停用。', 'La compra está deshabilitada.', 'La compra está deshabilitada.', 'Покупка отключена.'),
(1863, 'trade_item_not_for_sale', '%item - This is not for sale', 0, 0, '%item - 이것은 판매용이 아닙니다', '%item - Ceci n''est pas à vendre', '%item - Das steht nicht zum Verkauf', '%item - 这不是出售物品', '%item - 這不是出售物品', '%item - Esto no está en venta', '%item - Esto no está en venta', '%item - это не продаётся'),
(1864, 'trade_item_not_needed', '%item - I don''t need this', 0, 0, '%item - 저는 이것이 필요 없습니다', '%item - Je n''en ai pas besoin', '%item - Das brauche ich nicht', '%item - 我不需要这个', '%item - 我不需要這個', '%item - No necesito esto', '%item - No necesito esto', '%item - мне это не нужно'),
(1865, 'trade_no_items_error', 'There are no items to trade', 0, 0, '거래할 아이템이 없습니다', 'Il n''y a aucun objet à échanger', 'Es gibt keine Gegenstände zum Handeln', '没有可交易的物品', '沒有可交易的物品', 'No hay objetos para comerciar', 'No hay objetos para comerciar', 'Нет предметов для обмена'),
(1866, 'trade_discount_buy_only', 'You can use discount to buy items only', 0, 0, '할인은 아이템 구매에만 사용할 수 있습니다', 'Vous ne pouvez utiliser la réduction que pour acheter des objets', 'Rabatte können nur zum Kauf von Gegenständen verwendet werden', '折扣只能用于购买物品', '折扣只能用於購買物品', 'Solo puedes usar el descuento para comprar objetos', 'Solo puedes usar el descuento para comprar objetos', 'Скидку можно использовать только для покупки предметов'),
(1867, 'trade_success_pleasure', 'A pleasure doing business with you', 0, 0, '당신과 거래해서 즐거웠습니다', 'Un plaisir de faire affaire avec vous', 'Es war mir ein Vergnügen, mit dir Geschäfte zu machen', '很高兴和你做生意', '很高興和你做生意', 'Un placer hacer negocios contigo', 'Un placer hacer negocios contigo', 'Приятно было иметь с вами дело'),
(1868, 'trade_success_fair_trade', 'Fair trade', 0, 0, '공정한 거래입니다', 'Échange équitable', 'Fairer Handel', '公平交易', '公平交易', 'Intercambio justo', 'Intercambio justo', 'Честная сделка'),
(1869, 'trade_success_thanks', 'Thanks', 0, 0, '감사합니다', 'Merci', 'Danke', '谢谢', '謝謝', 'Gracias', 'Gracias', 'Спасибо'),
(1870, 'trade_success_off_with_you', 'Off with you', 0, 0, '이제 가보세요', 'Allez-vous-en maintenant', 'Nun geh deiner Wege', '走吧你', '走吧你', 'Ahora vete', 'Ahora vete', 'Иди уже'),
(1871, 'trade_want_money_for_this', 'I want %money for this', 0, 0, '이것에 대해 %money을(를) 원합니다', 'Je veux %money pour ceci', 'Ich möchte %money dafür', '这个我想要 %money', '這個我想要 %money', 'Quiero %money por esto', 'Quiero %money por esto', 'Я хочу %money за это'),
(1872, 'use_item_none_available', 'No items (or game objects) available', 0, 0, '사용할 수 있는 아이템(또는 게임 오브젝트)이 없습니다', 'Aucun objet (ou objet interactif) disponible', 'Keine Gegenstände (oder Spielobjekte) verfügbar', '没有可用的物品(或游戏物体)', '沒有可用的物品(或遊戲物件)', 'No hay objetos (ni objetos del juego) disponibles', 'No hay objetos (ni objetos del juego) disponibles', 'Нет доступных предметов (или игровых объектов)'),
(1873, 'use_gameobject', 'Using %gameobject', 0, 0, '%gameobject 사용 중', 'Utilisation de %gameobject', 'Benutze %gameobject', '正在使用 %gameobject', '正在使用 %gameobject', 'Usando %gameobject', 'Usando %gameobject', 'Использую %gameobject'),
(1874, 'socket_does_not_fit', 'Socket does not fit', 0, 0, '소켓이 맞지 않습니다', 'La châsse ne correspond pas', 'Sockel passt nicht', '插槽不匹配', '插槽不匹配', 'La ranura no encaja', 'La ranura no encaja', 'Гнездо не подходит'),
(1875, 'use_item_on_target', 'Using %item on %target', 0, 0, '%target에게 %item을(를) 사용하는 중', 'Utilisation de %item sur %target', 'Benutze %item auf %target', '正在对 %target 使用 %item', '正在對 %target 使用 %item', 'Usando %item en %target', 'Usando %item en %target', 'Использую %item на %target'),
(1876, 'use_item', 'Using %item', 0, 0, '%item 사용 중', 'Utilisation de %item', 'Benutze %item', '正在使用 %item', '正在使用 %item', 'Usando %item', 'Usando %item', 'Использую %item'),
(1877, 'socketing_item_with_gem', 'Socketing %item with %gem', 0, 0, '%item에 %gem 보석을 장착하는 중', 'Sertissage de %item avec %gem', 'Sockele %item mit %gem', '正在将 %gem 镶嵌到 %item 上', '正在將 %gem 鑲嵌到 %item 上', 'Engarzando %item con %gem', 'Engarzando %item con %gem', 'Вставляю %gem в %item'),
(1878, 'meeting_stone_in_combat', 'I am in combat', 0, 0, '전투 중입니다', 'Je suis en combat', 'Ich bin im Kampf', '我在战斗中', '我在戰鬥中', 'Estoy en combate', 'Estoy en combate', 'Я в бою'),
(1879, 'meeting_stone_welcome', 'Welcome!', 0, 0, '환영합니다!', 'Bienvenue !', 'Willkommen!', '欢迎!', '歡迎!', 'Bienvenido!', 'Bienvenido!', 'Добро пожаловать!'),
(1880, 'meeting_stone_none_nearby', 'There is no meeting stone nearby', 0, 0, '근처에 집결의 돌이 없습니다', 'Il n''y a pas de pierre de rencontre à proximité', 'Es gibt keinen Beschwörungsstein in der Nähe', '附近没有集合石', '附近沒有集合石', 'No hay ninguna piedra de encuentro cerca', 'No hay ninguna piedra de encuentro cerca', 'Поблизости нет камня встреч'),
(1881, 'meeting_stone_none_near_you', 'There is no meeting stone near you', 0, 0, '당신 근처에 집결의 돌이 없습니다', 'Il n''y a pas de pierre de rencontre près de vous', 'Es gibt keinen Beschwörungsstein in deiner Nähe', '你附近没有集合石', '你附近沒有集合石', 'No hay ninguna piedra de encuentro cerca de ti', 'No hay ninguna piedra de encuentro cerca de ti', 'Рядом с вами нет камня встреч'),
(1882, 'meeting_stone_no_hearthstone_self', 'I have no hearthstone', 0, 0, '귀환석이 없습니다', 'Je n''ai pas de pierre de foyer', 'Ich habe keinen Ruhestein', '我没有炉石', '我沒有爐石', 'No tengo piedra de hogar', 'No tengo piedra de hogar', 'У меня нет камня возвращения'),
(1883, 'meeting_stone_no_hearthstone_you', 'You have no hearthstone', 0, 0, '당신에게 귀환석이 없습니다', 'Vous n''avez pas de pierre de foyer', 'Du hast keinen Ruhestein', '你没有炉石', '你沒有爐石', 'No tienes piedra de hogar', 'No tienes piedra de hogar', 'У вас нет камня возвращения'),
(1884, 'meeting_stone_hearthstone_not_ready_self', 'My hearthstone is not ready', 0, 0, '제 귀환석은 아직 준비되지 않았습니다', 'Ma pierre de foyer n''est pas prête', 'Mein Ruhestein ist nicht bereit', '我的炉石还没准备好', '我的爐石還沒準備好', 'Mi piedra de hogar no está lista', 'Mi piedra de hogar no está lista', 'Мой камень возвращения ещё не готов'),
(1885, 'meeting_stone_hearthstone_not_ready_you', 'Your hearthstone is not ready', 0, 0, '당신의 귀환석은 아직 준비되지 않았습니다', 'Votre pierre de foyer n''est pas prête', 'Dein Ruhestein ist nicht bereit', '你的炉石还没准备好', '你的爐石還沒準備好', 'Tu piedra de hogar no está lista', 'Tu piedra de hogar no está lista', 'Ваш камень возвращения ещё не готов'),
(1886, 'meeting_stone_no_innkeepers_nearby', 'There are no innkeepers nearby', 0, 0, '근처에 여관주인이 없습니다', 'Il n''y a pas d''aubergistes à proximité', 'Es gibt keine Gastwirte in der Nähe', '附近没有旅店老板', '附近沒有旅店老闆', 'No hay posaderos cerca', 'No hay posaderos cerca', 'Поблизости нет трактирщиков'),
(1887, 'meeting_stone_no_innkeepers_near_you', 'There are no innkeepers near you', 0, 0, '당신 근처에 여관주인이 없습니다', 'Il n''y a pas d''aubergistes près de vous', 'Es gibt keine Gastwirte in deiner Nähe', '你附近没有旅店老板', '你附近沒有旅店老闆', 'No hay posaderos cerca de ti', 'No hay posaderos cerca de ti', 'Рядом с вами нет трактирщиков'),
(1888, 'meeting_stone_cannot_summon_vehicle', 'You cannot summon me while I''m on a vehicle', 0, 0, '제가 탈것/차량에 타고 있는 동안에는 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer tant que je suis sur un véhicule', 'Du kannst mich nicht beschwören, solange ich auf einem Fahrzeug bin', '当我在载具上时,你不能召唤我', '當我在載具上時,你不能召喚我', 'No puedes invocarme mientras esté en un vehículo', 'No puedes invocarme mientras esté en un vehículo', 'Вы не можете призвать меня, пока я на транспорте'),
(1889, 'meeting_stone_cannot_summon_master_in_combat', 'You cannot summon me while you''re in combat', 0, 0, '당신이 전투 중일 때는 저를 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer pendant que vous êtes en combat', 'Du kannst mich nicht beschwören, solange du im Kampf bist', '当你在战斗中时,不能召唤我', '當你在戰鬥中時,不能召喚我', 'No puedes invocarme mientras estés en combate', 'No puedes invocarme mientras estés en combate', 'Вы не можете призвать меня, пока вы в бою'),
(1890, 'meeting_stone_cannot_summon_master_dead', 'You cannot summon me while you''re dead', 0, 0, '당신이 죽어 있는 동안에는 저를 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer pendant que vous êtes mort', 'Du kannst mich nicht beschwören, solange du tot bist', '当你死亡时,不能召唤我', '當你死亡時,不能召喚我', 'No puedes invocarme mientras estés muerto', 'No puedes invocarme mientras estés muerto', 'Вы не можете призвать меня, пока вы мертвы'),
(1891, 'meeting_stone_cannot_summon_bot_dead', 'You cannot summon me while I''m dead, you need to release my spirit first', 0, 0, '제가 죽어 있는 동안에는 저를 소환할 수 없습니다. 먼저 제 영혼을 해방해야 합니다', 'Vous ne pouvez pas m''invoquer tant que je suis mort, vous devez d''abord libérer mon esprit', 'Du kannst mich nicht beschwören, solange ich tot bin; du musst zuerst meinen Geist freisetzen', '当我死亡时,你不能召唤我,你需要先释放我的灵魂', '當我死亡時,你不能召喚我,你需要先釋放我的靈魂', 'No puedes invocarme mientras esté muerto, primero debes liberar mi espíritu', 'No puedes invocarme mientras esté muerto, primero debes liberar mi espíritu', 'Вы не можете призвать меня, пока я мёртв, сначала нужно освободить мой дух'),
(1892, 'meeting_stone_revived', 'I live, again!', 0, 0, '다시 살아났습니다!', 'Je vis à nouveau !', 'Ich lebe wieder!', '我又活了!', '我又活了!', 'Vivo de nuevo!', 'Vivo de nuevo!', 'Я снова жив!'),
(1893, 'meeting_stone_not_enough_space', 'Not enough place to summon', 0, 0, '소환할 공간이 부족합니다', 'Pas assez de place pour invoquer', 'Nicht genug Platz zum Beschwören', '没有足够的空间进行召唤', '沒有足夠的空間進行召喚', 'No hay suficiente espacio para invocar', 'No hay suficiente espacio para invocar', 'Недостаточно места для призыва'),
(1894, 'new_rpg_quest_accepted', 'Quest accepted %quest', 0, 0, '퀘스트 수락 %quest', 'Quête acceptée %quest', 'Quest angenommen %quest', '任务已接受 %quest', '任務已接受 %quest', 'Misión aceptada %quest', 'Misión aceptada %quest', 'Задание принято %quest'),
(1895, 'new_rpg_quest_rewarded', 'Quest rewarded %quest', 0, 0, '퀘스트 보상 받음 %quest', 'Quête récompensée %quest', 'Quest abgeschlossen %quest', '任务已奖励 %quest', '任務已獎勵 %quest', 'Misión completada %quest', 'Misión completada %quest', 'Награда за задание получена %quest'),
(1896, 'new_rpg_quest_dropped', 'Quest dropped %quest', 0, 0, '퀘스트 포기 %quest', 'Quête abandonnée %quest', 'Quest abgebrochen %quest', '任务已放弃 %quest', '任務已放棄 %quest', 'Misión abandonada %quest', 'Misión abandonada %quest', 'Задание отменено %quest'),
(1897, 'rpg_item_better_for_player', 'You can use this %item better than me, %player.', 0, 0, '%player님, 이 %item은(는) 저보다 당신에게 더 잘 맞습니다.', 'Vous pouvez mieux utiliser cet objet %item que moi, %player.', 'Du kannst diesen %item besser gebrauchen als ich, %player.', '%player这个 %item 比我更适合你使用。', '%player這個 %item 比我更適合你使用。', 'Tú puedes usar este %item mejor que yo, %player.', 'Tú puedes usar este %item mejor que yo, %player.', '%player, ты можешь использовать %item лучше, чем я.'),
(1898, 'rpg_start_trade_with_player', 'Start trade with %player', 0, 0, '%player와(과) 거래를 시작합니다', 'Début de l''échange avec %player', 'Beginne Handel mit %player', '开始与 %player 交易', '開始與 %player 交易', 'Iniciando intercambio con %player', 'Iniciando intercambio con %player', 'Начинаю обмен с %player');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('quest_accept_debug', 100),
('quest_already_have_error', 100),
('quest_cant_take_error', 100),
('arena_team_already_in_team', 100),
('arena_team_thanks_for_invite', 100),
('area_trigger_follow_too_far_error', 100),
('area_trigger_wait_for_me', 100),
('attack_no_target_error', 100),
('attack_target_not_in_world_error', 100),
('attack_in_flight_error', 100),
('attack_pvp_prohibited_error', 100),
('attack_target_friendly_error', 100),
('attack_target_dead_error', 100),
('attack_target_not_in_sight_error', 100),
('attack_already_attacking_error', 100),
('attack_invalid_target_error', 100),
('bank_no_banker_nearby_error', 100),
('move_from_group', 100),
('running_away', 100),
('clean_quest_log_started', 100),
('quest_trivial_will_remove', 100),
('quest_has_been_removed', 100),
('quest_not_trivial_kept', 100),
('quest_removed_debug', 100),
('quest_removed_with_name', 100),
('guild_accept_inviter_not_in_guild', 100),
('guild_accept_already_in_guild', 100),
('guild_accept_declined', 100),
('outfit_usage_add', 100),
('outfit_usage_remove', 100),
('outfit_usage_equip', 100),
('outfit_set_as', 100),
('outfit_equipping', 100),
('outfit_replace_current', 100),
('outfit_resetting', 100),
('outfit_updating_current', 100),
('outfit_item_removed_from', 100),
('outfit_item_added_to', 100),
('release_spirit_not_dead_wait', 100),
('release_spirit_already_spirit', 100),
('release_spirit_releasing', 100),
('release_spirit_meet_graveyard', 100),
('send_mail_no_mailbox_nearby', 100),
('send_mail_one_item_only', 100),
('send_mail_cannot_send_money', 100),
('send_mail_not_enough_money', 100),
('send_mail_sending_to', 100),
('send_mail_cannot_send_item', 100),
('send_mail_item_not_for_sale', 100),
('send_mail_sent_to', 100),
('craft_reset', 100),
('craft_usage', 100),
('craft_cannot_craft', 100),
('craft_summary', 100),
('set_home_success', 100),
('set_home_no_innkeeper_error', 100),
('quest_shared', 100),
('tame_invalid_id_error', 100),
('tame_usage_error', 100),
('tame_pet_changed', 100),
('tame_pet_changed_initialized', 100),
('tame_exotic_requires_beast_mastery', 100),
('tame_no_pet_by_name', 100),
('tame_no_pet_by_id', 100),
('tame_no_pet_by_family', 100),
('tame_no_pet_to_rename', 100),
('tame_pet_name_length_error', 100),
('tame_pet_name_alpha_error', 100),
('tame_pet_name_forbidden_error', 100),
('tame_pet_renamed', 100),
('tame_pet_rename_refresh_hint', 100),
('tame_only_hunters_level_10', 100),
('tame_creature_template_not_found', 100),
('tame_create_pet_failed', 100),
('tame_pet_abandoned', 100),
('tame_no_hunter_pet_to_abandon', 100),
('taxi_ready_next_flight', 100),
('taxi_cant_fly_with_you', 100),
('taxi_no_flightmaster_nearby', 100),
('trade_busy_now', 100),
('trade_disabled', 100),
('trade_thank_you_player', 100),
('trade_selling_disabled', 100),
('trade_buying_disabled', 100),
('trade_item_not_for_sale', 100),
('trade_item_not_needed', 100),
('trade_no_items_error', 100),
('trade_discount_buy_only', 100),
('trade_success_pleasure', 100),
('trade_success_fair_trade', 100),
('trade_success_thanks', 100),
('trade_success_off_with_you', 100),
('trade_want_money_for_this', 100),
('use_item_none_available', 100),
('use_gameobject', 100),
('socket_does_not_fit', 100),
('use_item_on_target', 100),
('use_item', 100),
('socketing_item_with_gem', 100),
('meeting_stone_in_combat', 100),
('meeting_stone_welcome', 100),
('meeting_stone_none_nearby', 100),
('meeting_stone_none_near_you', 100),
('meeting_stone_no_hearthstone_self', 100),
('meeting_stone_no_hearthstone_you', 100),
('meeting_stone_hearthstone_not_ready_self', 100),
('meeting_stone_hearthstone_not_ready_you', 100),
('meeting_stone_no_innkeepers_nearby', 100),
('meeting_stone_no_innkeepers_near_you', 100),
('meeting_stone_cannot_summon_vehicle', 100),
('meeting_stone_cannot_summon_master_in_combat', 100),
('meeting_stone_cannot_summon_master_dead', 100),
('meeting_stone_cannot_summon_bot_dead', 100),
('meeting_stone_revived', 100),
('meeting_stone_not_enough_space', 100),
('new_rpg_quest_accepted', 100),
('new_rpg_quest_rewarded', 100),
('new_rpg_quest_dropped', 100),
('rpg_item_better_for_player', 100),
('rpg_start_trade_with_player', 100);

View File

@ -9,7 +9,6 @@
#include "ObjectAccessor.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotSecurity.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "WorldPacket.h"
@ -56,7 +55,7 @@ bool AcceptInvitationAction::Execute(Event event)
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
botAI->Reset();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {}));
botAI->TellMaster("Hello");
if (sPlayerbotAIConfig.summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig.sightDistance)
{

View File

@ -6,7 +6,6 @@
#include "AcceptQuestAction.h"
#include "Event.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver)
@ -19,11 +18,7 @@ bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver)
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
LOG_INFO("playerbots", "{} => Quest [{}] accepted", bot->GetName(), quest->GetTitle());
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_accept_debug",
"Quest [%quest] accepted",
{{"%quest", text_quest}});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Quest [" + text_quest + "] accepted", LANG_UNIVERSAL);
}
return true;
@ -118,8 +113,7 @@ bool AcceptQuestShareAction::Execute(Event event)
if (bot->HasQuest(quest))
{
bot->SetDivider(ObjectGuid::Empty);
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_already_have_error", "I have this quest", {}));
botAI->TellError("I have this quest");
return false;
}
@ -127,8 +121,7 @@ bool AcceptQuestShareAction::Execute(Event event)
{
// can't take quest
bot->SetDivider(ObjectGuid::Empty);
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_cant_take_error", "I can't take this quest", {}));
botAI->TellError("I can't take this quest");
return false;
}
@ -156,8 +149,7 @@ bool AcceptQuestShareAction::Execute(Event event)
bot->CastSpell(bot, qInfo->GetSrcSpell(), true);
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_accept", "Quest accepted", {}));
botAI->TellMaster("Quest accepted");
return true;
}

View File

@ -7,7 +7,6 @@
#include "Event.h"
#include "LastMovementValue.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "Transport.h"
@ -37,8 +36,7 @@ bool ReachAreaTriggerAction::Execute(Event event)
if (bot->GetMapId() != at->map)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"area_trigger_follow_too_far_error", "I won't follow: too far away", {}));
botAI->TellError("I won't follow: too far away");
return true;
}
@ -53,8 +51,7 @@ bool ReachAreaTriggerAction::Execute(Event event)
float distance = bot->GetDistance(at->x, at->y, at->z);
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig.reactDelay;
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"area_trigger_wait_for_me", "Wait for me", {}));
botAI->TellError("Wait for me");
botAI->SetNextCheckDelay(delay);
context->GetValue<LastMovement&>("last area trigger")->Get().lastAreaTrigger = triggerId;
@ -79,6 +76,6 @@ bool AreaTriggerAction::Execute(Event /*event*/)
p.rpos(0);
bot->GetSession()->HandleAreaTriggerOpcode(p);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {}));
botAI->TellMaster("Hello");
return true;
}

View File

@ -6,7 +6,6 @@
#include "ArenaTeamActions.h"
#include "ArenaTeamMgr.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool ArenaTeamAcceptAction::Execute(Event event)
@ -32,9 +31,7 @@ bool ArenaTeamAcceptAction::Execute(Event event)
if (bot->GetArenaTeamId(at->GetSlot()))
{
// bot is already in an arena team
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"arena_team_already_in_team", "Sorry, I am already in such team", {});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Sorry, I am already in such team", LANG_UNIVERSAL);
accept = false;
}
@ -42,9 +39,7 @@ bool ArenaTeamAcceptAction::Execute(Event event)
{
WorldPacket data(CMSG_ARENA_TEAM_ACCEPT);
bot->GetSession()->HandleArenaTeamAcceptOpcode(data);
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"arena_team_thanks_for_invite", "Thanks for the invite!", {});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Thanks for the invite!", LANG_UNIVERSAL);
LOG_INFO("playerbots", "Bot {} <{}> accepts Arena Team invite", bot->GetGUID().ToString().c_str(),
bot->GetName().c_str());
return true;

View File

@ -10,7 +10,6 @@
#include "LastMovementValue.h"
#include "LootObjectStack.h"
#include "PlayerbotAI.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
@ -39,8 +38,7 @@ bool AttackMyTargetAction::Execute(Event /*event*/)
if (!guid)
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"pull_no_target_error", "You have no target", {}));
botAI->TellError("You have no target");
return false;
}
@ -58,8 +56,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (!target)
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_no_target_error", "I have no target", {}));
botAI->TellError("I have no target");
return false;
}
@ -67,10 +64,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (!target->IsInWorld())
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_target_not_in_world_error",
"%target is no longer in the world.",
{{"%target", target->GetName()}}));
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
return false;
}
@ -79,8 +73,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_in_flight_error", "I cannot attack in flight", {}));
botAI->TellError("I cannot attack in flight");
return false;
}
@ -92,10 +85,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
sPlayerbotAIConfig.IsPvpProhibited(target->GetZoneId(), target->GetAreaId())))
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_pvp_prohibited_error",
"I cannot attack other players in PvP prohibited areas.",
{}));
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
return false;
}
@ -103,10 +93,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (bot->IsFriendlyTo(target))
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_target_friendly_error",
"%target is friendly to me.",
{{"%target", target->GetName()}}));
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
return false;
}
@ -114,10 +101,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (target->isDead())
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_target_dead_error",
"%target is dead.",
{{"%target", target->GetName()}}));
botAI->TellError(std::string(target->GetName()) + " is dead.");
return false;
}
@ -125,10 +109,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (!bot->IsWithinLOSInMap(target))
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_target_not_in_sight_error",
"%target is not in my sight.",
{{"%target", target->GetName()}}));
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
return false;
}
@ -148,10 +129,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (sameTarget && inCombat && sameAttackMode)
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_already_attacking_error",
"I am already attacking %target.",
{{"%target", target->GetName()}}));
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
return false;
}
@ -159,8 +137,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
if (!bot->IsValidAttackTarget(target))
{
if (verbose)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attack_invalid_target_error", "I cannot attack an invalid target.", {}));
botAI->TellError("I cannot attack an invalid target.");
return false;
}

View File

@ -7,7 +7,6 @@
#include "Event.h"
#include "ItemCountValue.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool BankAction::Execute(Event event)
@ -24,8 +23,7 @@ bool BankAction::Execute(Event event)
return ExecuteBank(text, npc);
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bank_no_banker_nearby_error", "Cannot find banker nearby", {}));
botAI->TellError("Cannot find banker nearby");
return false;
}

View File

@ -1299,7 +1299,7 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session,
Player* player = session->GetPlayer();
if (!player)
return "Error - session player not found";
if (!player->CanBeGameMaster())
if (player->GetSession()->GetSecurity() < SEC_GAMEMASTER)
return "Command can only be used by a GM";
Battleground* bg = player->GetBattleground();
if (!bg)

View File

@ -49,7 +49,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event)
uint32 account = bot->GetSession()->GetAccountId();
if (sPlayerbotAIConfig.IsInRandomAccountList(account) && botAI->GetMaster() &&
!botAI->GetMaster()->CanBeGameMaster())
botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER)
{
if (text.find("loot") != std::string::npos || text.find("gather") != std::string::npos)
{

View File

@ -7,7 +7,6 @@
#include "Event.h"
#include "Formations.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "PositionValue.h"
@ -86,8 +85,7 @@ bool FollowChatShortcutAction::Execute(Event /*event*/)
if (moved)
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"following", "Following", {}));
botAI->TellMaster("Following");
return true;
}
}
@ -110,8 +108,7 @@ bool FollowChatShortcutAction::Execute(Event /*event*/)
}
*/
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"following", "Following", {}));
botAI->TellMaster("Following");
return true;
}
@ -128,8 +125,7 @@ bool StayChatShortcutAction::Execute(Event /*event*/)
SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"staying", "Staying", {}));
botAI->TellMaster("Staying");
return true;
}
@ -144,8 +140,7 @@ bool MoveFromGroupChatShortcutAction::Execute(Event /*event*/)
botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"move_from_group", "Moving away from group", {}));
botAI->TellMaster("Moving away from group");
return true;
}
@ -164,13 +159,11 @@ bool FleeChatShortcutAction::Execute(Event /*event*/)
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig.sightDistance)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"fleeing_far", "I will not flee with you - too far away", {}));
botAI->TellError("I will not flee with you - too far away");
return true;
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"fleeing", "Fleeing", {}));
botAI->TellMaster("Fleeing");
return true;
}
@ -187,8 +180,7 @@ bool GoawayChatShortcutAction::Execute(Event /*event*/)
ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"running_away", "Running away", {}));
botAI->TellMaster("Running away");
return true;
}
@ -204,8 +196,7 @@ bool GrindChatShortcutAction::Execute(Event /*event*/)
ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"grinding", "Grinding", {}));
botAI->TellMaster("Grinding");
return true;
}
@ -225,8 +216,7 @@ bool TankAttackChatShortcutAction::Execute(Event /*event*/)
ResetReturnPosition();
ResetStayPosition();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"attacking", "Attacking", {}));
botAI->TellMaster("Attacking");
return true;
}

View File

@ -7,7 +7,6 @@
#include "ChatHelper.h"
#include "Event.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool DropQuestAction::Execute(Event event)
@ -52,15 +51,10 @@ bool DropQuestAction::Execute(Event event)
const Quest* pQuest = sObjectMgr->GetQuestTemplate(entry);
const std::string text_quest = ChatHelper::FormatQuest(pQuest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), pQuest->GetTitle());
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_removed_debug",
"Quest [%quest] removed",
{{"%quest", text_quest}});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_remove", "Quest removed", {}));
botAI->TellMaster("Quest removed");
return true;
}
@ -75,10 +69,7 @@ bool CleanQuestLogAction::Execute(Event event)
// Only output this message if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"clean_quest_log_started",
"Clean Quest Log command received, removing grey/trivial quests...",
{}));
botAI->TellMaster("Clean Quest Log command received, removing grey/trivial quests...");
uint8 botLevel = bot->GetLevel(); // Get bot's level
@ -112,10 +103,7 @@ bool CleanQuestLogAction::Execute(Event event)
{
// Output only if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_trivial_will_remove",
"Quest [%title] will be removed because it is trivial (grey).",
{{"%title", quest->GetTitle()}}));
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey).");
// Remove quest
botAI->rpgStatistic.questDropped++;
@ -128,27 +116,17 @@ bool CleanQuestLogAction::Execute(Event event)
{
const std::string text_quest = ChatHelper::FormatQuest(quest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle());
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_removed_debug",
"Quest [%quest] removed",
{{"%quest", text_quest}});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_has_been_removed",
"Quest [%title] has been removed.",
{{"%title", quest->GetTitle()}}));
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed.");
}
else
{
// Only output if "debug rpg" strategy is enabled
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_not_trivial_kept",
"Quest [%title] is not trivial and will be kept.",
{{"%title", quest->GetTitle()}}));
botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept.");
}
}
@ -226,16 +204,9 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG
{
const std::string text_quest = ChatHelper::FormatQuest(quest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle());
std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_removed_debug",
"Quest [%quest] removed",
{{"%quest", text_quest}});
bot->Say(text, LANG_UNIVERSAL);
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_removed_with_name",
"Quest removed %quest",
{{"%quest", chat->FormatQuest(quest)}}));
botAI->TellMaster("Quest removed" + chat->FormatQuest(quest));
}
}

View File

@ -8,7 +8,6 @@
#include "Player.h"
#include "Pet.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotTextMgr.h"
#include "CreatureAI.h"
#include "Playerbots.h"
#include "CharmInfo.h"
@ -50,10 +49,7 @@ bool MeleeAction::isUseful()
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
return false;
// Do not start autoattack while prowled — let opener spells break stealth intentionally.
// Future rogue stealth implementation should use this instead:
// return !(botAI->HasAura("stealth", bot) || botAI->HasAura("prowl", bot));
return !botAI->HasAura("prowl", bot);
return true;
}
bool TogglePetSpellAutoCastAction::Execute(Event /*event*/)
@ -182,8 +178,7 @@ bool SetPetStanceAction::Execute(Event /*event*/)
// If there are no controlled pets or guardians, notify the player and exit
if (targets.empty())
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"pet_no_pet_error", "You have no pet or guardian pet.", {}));
botAI->TellError("You have no pet or guardian pet.");
return false;
}

View File

@ -124,6 +124,7 @@ bool GoAction::Execute(Event event)
if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT))
{
PathGenerator path(bot);
path.CalculatePath(x, y, z, false);
Movement::Vector3 end = path.GetEndPosition();

View File

@ -8,7 +8,6 @@
#include "Event.h"
#include "GuildPackets.h"
#include "PlayerbotSecurity.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool GuildAcceptAction::Execute(Event event)
@ -29,20 +28,17 @@ bool GuildAcceptAction::Execute(Event event)
uint32 guildId = inviter->GetGuildId();
if (!guildId)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"guild_accept_inviter_not_in_guild", "You are not in a guild!", {}));
botAI->TellError("You are not in a guild!");
accept = false;
}
else if (bot->GetGuildId())
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"guild_accept_already_in_guild", "Sorry, I am in a guild already", {}));
botAI->TellError("Sorry, I am in a guild already");
accept = false;
}
else if (!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, inviter, true))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"guild_accept_declined", "Sorry, I don't want to join your guild :(", {}));
botAI->TellError("Sorry, I don't want to join your guild :(");
accept = false;
}

View File

@ -308,8 +308,9 @@ bool LfgAction::Execute(Event event)
allowedRoles[BOT_ROLE_HEALER] = 1;
allowedRoles[BOT_ROLE_DPS] = 3;
BotRoles role = botAI->IsTank(requester, true) ? BOT_ROLE_TANK
: (botAI->IsHeal(requester, true) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
BotRoles role = botAI->IsTank(requester, false)
? BOT_ROLE_TANK
: (botAI->IsHeal(requester, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
Classes cls = (Classes)requester->getClass();
if (group)
@ -382,8 +383,8 @@ bool LfgAction::Execute(Event event)
if (!botAI->IsSafe(player))
return false;
role = botAI->IsTank(player, true) ? BOT_ROLE_TANK
: (botAI->IsHeal(player, true) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
role = botAI->IsTank(player, false) ? BOT_ROLE_TANK
: (botAI->IsHeal(player, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
cls = (Classes)player->getClass();
if (allowedRoles[role] > 0)
@ -402,7 +403,7 @@ bool LfgAction::Execute(Event event)
allowedClassNr[cls][role]--;
}
role = botAI->IsTank(bot, true) ? BOT_ROLE_TANK : (botAI->IsHeal(bot, true) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
role = botAI->IsTank(bot, false) ? BOT_ROLE_TANK : (botAI->IsHeal(bot, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS);
cls = (Classes)bot->getClass();
if (allowedRoles[role] == 0)

View File

@ -7,7 +7,6 @@
#include "Event.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool LeaveGroupAction::Execute(Event event)
@ -87,9 +86,7 @@ bool LeaveGroupAction::Leave()
Player* master = botAI -> GetMaster();
if (master)
botAI->TellMaster(
PlayerbotTextMgr::instance().GetBotTextOrDefault("goodbye", "Goodbye!", {}),
PLAYERBOT_SECURITY_TALK);
botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK);
botAI->LeaveOrDisbandGroup();
return true;

View File

@ -7,8 +7,6 @@
#include "Event.h"
#include "ItemVisitors.h"
#include "PlayerbotTextMgr.h"
#include "PlayerbotRepository.h"
#include "Playerbots.h"
#include "ItemPackets.h"
@ -19,12 +17,9 @@ bool OutfitAction::Execute(Event event)
if (param == "?")
{
List();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_usage_add", "outfit <name> +[item] to add items", {}));
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_usage_remove", "outfit <name> -[item] to remove items", {}));
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_usage_equip", "outfit <name> equip/replace to equip items", {}));
botAI->TellMaster("outfit <name> +[item] to add items");
botAI->TellMaster("outfit <name> -[item] to remove items");
botAI->TellMaster("outfit <name> equip/replace to equip items");
}
else
{
@ -33,13 +28,10 @@ bool OutfitAction::Execute(Event event)
if (!name.empty())
{
Save(name, items);
PlayerbotRepository::instance().Save(botAI);
std::ostringstream out;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_set_as",
"Setting outfit %name as %param",
{{"%name", name}, {"%param", param}}));
out << "Setting outfit " << name << " as " << param;
botAI->TellMaster(out);
return true;
}
@ -55,20 +47,18 @@ bool OutfitAction::Execute(Event event)
std::string const command = param.substr(space + 1);
if (command == "equip")
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_equipping",
"Equipping outfit %name",
{{"%name", name}}));
std::ostringstream out;
out << "Equipping outfit " << name;
botAI->TellMaster(out);
EquipItems(outfit);
return true;
}
else if (command == "replace")
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_replace_current",
"Replacing current equip with outfit %name",
{{"%name", name}}));
std::ostringstream out;
out << "Replacing current equip with outfit " << name;
botAI->TellMaster(out);
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
{
@ -91,24 +81,20 @@ bool OutfitAction::Execute(Event event)
}
else if (command == "reset")
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_resetting",
"Resetting outfit %name",
{{"%name", name}}));
std::ostringstream out;
out << "Resetting outfit " << name;
botAI->TellMaster(out);
Save(name, ItemIds());
PlayerbotRepository::instance().Save(botAI);
return true;
}
else if (command == "update")
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_updating_current",
"Updating with current items outfit %name",
{{"%name", name}}));
std::ostringstream out;
out << "Updating with current items outfit " << name;
botAI->TellMaster(out);
Update(name);
PlayerbotRepository::instance().Save(botAI);
return true;
}
@ -117,29 +103,27 @@ bool OutfitAction::Execute(Event event)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
std::ostringstream out;
out << chat->FormatItem(proto);
if (remove)
{
std::set<uint32>::iterator j = outfit.find(itemid);
if (j != outfit.end())
outfit.erase(j);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_item_removed_from",
"%item removed from %name",
{{"%item", chat->FormatItem(proto)}, {"%name", name}}));
out << " removed from ";
}
else
{
outfit.insert(itemid);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"outfit_item_added_to",
"%item added to %name",
{{"%item", chat->FormatItem(proto)}, {"%name", name}}));
out << " added to ";
}
out << name;
botAI->TellMaster(out.str());
}
Save(name, outfit);
PlayerbotRepository::instance().Save(botAI);
}
return true;

View File

@ -10,7 +10,6 @@
#include "NearestNpcsValue.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "Corpse.h"
@ -23,8 +22,7 @@ bool ReleaseSpiritAction::Execute(Event event)
{
if (!bot->InBattleground())
{
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"release_spirit_not_dead_wait", "I am not dead, will wait here", {}));
botAI->TellMasterNoFacing("I am not dead, will wait here");
// -follow in bg is overwriten each tick with +follow
// +stay in bg causes stuttering effect as bot is cycled between +stay and +follow each tick
botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT);
@ -35,15 +33,14 @@ bool ReleaseSpiritAction::Execute(Event event)
if (bot->GetCorpse() && bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
{
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"release_spirit_already_spirit", "I am already a spirit", {}));
botAI->TellMasterNoFacing("I am already a spirit");
return false;
}
const WorldPacket& packet = event.getPacket();
const std::string message = !packet.empty() && packet.GetOpcode() == CMSG_REPOP_REQUEST
? PlayerbotTextMgr::instance().GetBotTextOrDefault("release_spirit_releasing", "Releasing...", {})
: PlayerbotTextMgr::instance().GetBotTextOrDefault("release_spirit_meet_graveyard", "Meet me at the graveyard", {});
? "Releasing..."
: "Meet me at the graveyard";
botAI->TellMasterNoFacing(message);
IncrementDeathCount();

View File

@ -9,7 +9,6 @@
#include "FleeManager.h"
#include "GameGraveyard.h"
#include "MapMgr.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "ServerFacade.h"
@ -323,7 +322,7 @@ bool SpiritHealerAction::Execute(Event /*event*/)
bot->SpawnCorpseBones();
context->GetValue<Unit*>("current target")->Set(nullptr);
bot->SetTarget();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {}));
botAI->TellMaster("Hello");
if (dCount > 20)
context->GetValue<uint32>("death count")->Set(0);

View File

@ -12,7 +12,7 @@ bool SecurityCheckAction::isUseful()
{
return RandomPlayerbotMgr::instance().IsRandomBot(bot)
&& botAI->GetMaster()
&& !botAI->GetMaster()->CanBeGameMaster()
&& botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER
&& !GET_PLAYERBOT_AI(botAI->GetMaster());
}

View File

@ -9,7 +9,6 @@
#include "Event.h"
#include "ItemVisitors.h"
#include "Mail.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool SendMailAction::Execute(Event event)
@ -54,18 +53,14 @@ bool SendMailAction::Execute(Event event)
if (!mailboxFound && !randomBot)
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_no_mailbox_nearby", "There is no mailbox nearby", {}),
LANG_UNIVERSAL, tellTo);
bot->Whisper("There is no mailbox nearby", LANG_UNIVERSAL, tellTo);
return false;
}
ItemIds ids = chat->parseItems(text);
if (ids.size() > 1)
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_one_item_only", "You can not request more than one item", {}),
LANG_UNIVERSAL, tellTo);
bot->Whisper("You can not request more than one item", LANG_UNIVERSAL, tellTo);
return false;
}
@ -77,16 +72,13 @@ bool SendMailAction::Execute(Event event)
if (randomBot)
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_cannot_send_money", "I cannot send money", {}),
LANG_UNIVERSAL, tellTo);
bot->Whisper("I cannot send money", LANG_UNIVERSAL, tellTo);
return false;
}
if (bot->GetMoney() < money)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_not_enough_money", "I don't have enough money", {}));
botAI->TellError("I don't have enough money");
return false;
}
@ -108,10 +100,8 @@ bool SendMailAction::Execute(Event event)
CharacterDatabase.CommitTransaction(trans);
std::ostringstream out;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_sending_to",
"Sending mail to %receiver",
{{"%receiver", receiver->GetName()}}));
out << "Sending mail to " << receiver->GetName();
botAI->TellMaster(out.str());
return true;
}
@ -135,10 +125,7 @@ bool SendMailAction::Execute(Event event)
if (item->IsSoulBound() || item->IsConjuredConsumable())
{
std::ostringstream out;
out << PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_cannot_send_item",
"Cannot send %item",
{{"%item", ChatHelper::FormatItem(item->GetTemplate())}});
out << "Cannot send " << ChatHelper::FormatItem(item->GetTemplate());
bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo);
continue;
}
@ -153,10 +140,7 @@ bool SendMailAction::Execute(Event event)
if (!price)
{
std::ostringstream out;
out << PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_item_not_for_sale",
"%item: it is not for sale",
{{"%item", ChatHelper::FormatItem(item->GetTemplate())}});
out << ChatHelper::FormatItem(item->GetTemplate()) << ": it is not for sale";
bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo);
return false;
}
@ -176,10 +160,7 @@ bool SendMailAction::Execute(Event event)
CharacterDatabase.CommitTransaction(trans);
std::ostringstream out;
out << PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_sent_to",
"Sent mail to %receiver",
{{"%receiver", receiver->GetName()}});
out << "Sent mail to " << receiver->GetName();
bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo);
return true;
}

View File

@ -8,7 +8,6 @@
#include "ChatHelper.h"
#include "CraftValue.h"
#include "Event.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
std::map<uint32, SkillLineAbilityEntry const*> SetCraftAction::skillSpells;
@ -25,8 +24,7 @@ bool SetCraftAction::Execute(Event event)
if (link == "reset")
{
data.Reset();
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"craft_reset", "I will not craft anything", {}));
botAI->TellMaster("I will not craft anything");
return true;
}
@ -39,8 +37,7 @@ bool SetCraftAction::Execute(Event event)
ItemIds itemIds = chat->parseItems(link);
if (itemIds.empty())
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"craft_usage", "Usage: 'craft [itemId]' or 'craft reset'", {}));
botAI->TellMaster("Usage: 'craft [itemId]' or 'craft reset'");
return false;
}
@ -97,8 +94,7 @@ bool SetCraftAction::Execute(Event event)
if (data.required.empty())
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"craft_cannot_craft", "I cannot craft this", {}));
botAI->TellMaster("I cannot craft this");
return false;
}
@ -113,8 +109,7 @@ void SetCraftAction::TellCraft()
CraftData& data = AI_VALUE(CraftData&, "craft");
if (data.IsEmpty())
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"craft_reset", "I will not craft anything", {}));
botAI->TellMaster("I will not craft anything");
return;
}
@ -122,7 +117,8 @@ void SetCraftAction::TellCraft()
if (!proto)
return;
std::ostringstream reagentsOut;
std::ostringstream out;
out << "I will craft " << chat->FormatItem(proto) << " using reagents: ";
bool first = true;
for (std::map<uint32, uint32>::iterator i = data.required.begin(); i != data.required.end(); ++i)
@ -134,23 +130,20 @@ void SetCraftAction::TellCraft()
{
if (first)
first = false;
else
reagentsOut << ", ";
reagentsOut << chat->FormatItem(reagent, required);
else
out << ", ";
out << chat->FormatItem(reagent, required);
uint32 given = data.obtained[item];
if (given)
reagentsOut << "|cffffff00(x" << given << " given)|r ";
out << "|cffffff00(x" << given << " given)|r ";
}
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"craft_summary",
"I will craft %item using reagents: %reagents (craft fee: %money)",
{{"%item", chat->FormatItem(proto)},
{"%reagents", reagentsOut.str()},
{"%money", chat->formatMoney(GetCraftFee(data))}}));
out << " (craft fee: " << chat->formatMoney(GetCraftFee(data)) << ")";
botAI->TellMaster(out.str());
}
uint32 SetCraftAction::GetCraftFee(CraftData& data)

View File

@ -6,7 +6,6 @@
#include "SetHomeAction.h"
#include "Event.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool SetHomeAction::Execute(Event /*event*/)
@ -29,8 +28,7 @@ bool SetHomeAction::Execute(Event /*event*/)
{
Creature* creature = botAI->GetCreature(selection);
bot->GetSession()->SendBindPoint(creature);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"set_home_success", "This inn is my new home", {}));
botAI->TellMaster("This inn is my new home");
return true;
}
@ -42,12 +40,10 @@ bool SetHomeAction::Execute(Event /*event*/)
continue;
bot->GetSession()->SendBindPoint(unit);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"set_home_success", "This inn is my new home", {}));
botAI->TellMaster("This inn is my new home");
return true;
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"set_home_no_innkeeper_error", "Can't find any innkeeper around", {}));
botAI->TellError("Can't find any innkeeper around");
return false;
}

View File

@ -6,7 +6,6 @@
#include "ShareQuestAction.h"
#include "Event.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool ShareQuestAction::Execute(Event event)
@ -33,8 +32,7 @@ bool ShareQuestAction::Execute(Event event)
WorldPacket p;
p << entry;
bot->GetSession()->HandlePushQuestToParty(p);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_shared", "Quest shared", {}));
botAI->TellMaster("Quest shared");
return true;
}
}
@ -100,8 +98,7 @@ bool AutoShareQuestAction::Execute(Event /*event*/)
WorldPacket p;
p << logQuest;
bot->GetSession()->HandlePushQuestToParty(p);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"quest_shared", "Quest shared", {}));
botAI->TellMaster("Quest shared");
shared = true;
}

View File

@ -15,7 +15,6 @@
#include "Player.h"
#include "PlayerbotAI.h"
#include "PlayerbotFactory.h"
#include "PlayerbotTextMgr.h"
#include "SpellMgr.h"
#include "WorldSession.h"
@ -124,8 +123,7 @@ bool TameAction::Execute(Event event)
}
catch (...)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_invalid_id_error", "Invalid tame id.", {}));
botAI->TellError("Invalid tame id.");
}
}
else if (mode == "family" && !value.empty())
@ -139,10 +137,8 @@ bool TameAction::Execute(Event event)
else
{
// Unrecognized command or missing argument; show usage
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_usage_error",
"Usage: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon",
{}));
botAI->TellError(
"Usage: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon");
return false;
}
@ -161,15 +157,12 @@ bool TameAction::Execute(Event event)
if (!lastPetName.empty() && lastPetId != 0)
{
std::ostringstream oss;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_changed",
"Pet changed to %name, ID: %id.",
{{"%name", lastPetName}, {"%id", std::to_string(lastPetId)}}));
oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << ".";
botAI->TellMaster(oss.str());
}
else
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_changed_initialized", "Pet changed and initialized!", {}));
botAI->TellMaster("Pet changed and initialized!");
}
}
@ -204,10 +197,7 @@ bool TameAction::SetPetByName(const std::string& name)
// If the creature is exotic and the bot doesn't have Beast Mastery, show error and fail
if (IsExoticPet(&creature) && !HasBeastMastery(bot))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_exotic_requires_beast_mastery",
"I cannot use exotic pets unless I have the Beast Mastery talent.",
{}));
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
return false;
}
@ -224,8 +214,7 @@ bool TameAction::SetPetByName(const std::string& name)
}
// If no suitable pet found, show an error and return failure
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_by_name", "No tameable pet found with name: %name", {{"%name", name}}));
botAI->TellError("No tameable pet found with name: " + name);
return false;
}
@ -242,26 +231,21 @@ bool TameAction::SetPetById(uint32 id)
if (!creature->IsTameable(true))
{
// If not tameable at all, show an error and fail
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}}));
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
// If it's an exotic pet, make sure the bot has the Beast Mastery talent
if (IsExoticPet(creature) && !HasBeastMastery(bot))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_exotic_requires_beast_mastery",
"I cannot use exotic pets unless I have the Beast Mastery talent.",
{}));
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
return false;
}
// Check if the bot is actually allowed to tame this pet (honoring exotic pet rules)
if (!creature->IsTameable(bot->CanTameExoticPets()))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}}));
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
@ -273,8 +257,7 @@ bool TameAction::SetPetById(uint32 id)
}
// If no valid creature was found by id, show an error
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}}));
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
@ -332,13 +315,9 @@ bool TameAction::SetPetByFamily(const std::string& family)
if (candidates.empty())
{
if (foundExotic && !HasBeastMastery(bot))
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_exotic_requires_beast_mastery",
"I cannot use exotic pets unless I have the Beast Mastery talent.",
{}));
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
else
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_by_family", "No tameable pet found with family: %family", {{"%family", family}}));
botAI->TellError("No tameable pet found with family: " + family);
return false;
}
@ -363,18 +342,14 @@ bool TameAction::RenamePet(const std::string& newName)
// Check if the bot currently has a pet
if (!pet)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_pet_to_rename", "You have no pet to rename.", {}));
botAI->TellError("You have no pet to rename.");
return false;
}
// Validate the new name: must not be empty and max 12 characters
if (newName.empty() || newName.length() > 12)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_name_length_error",
"Pet name must be between 1 and 12 alphabetic characters.",
{}));
botAI->TellError("Pet name must be between 1 and 12 alphabetic characters.");
return false;
}
@ -383,10 +358,7 @@ bool TameAction::RenamePet(const std::string& newName)
{
if (!std::isalpha(static_cast<unsigned char>(c)))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_name_alpha_error",
"Pet name must only contain alphabetic characters (A-Z, a-z).",
{}));
botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z).");
return false;
}
}
@ -400,10 +372,7 @@ bool TameAction::RenamePet(const std::string& newName)
// Check if the new name is reserved or forbidden
if (sObjectMgr->IsReservedName(normalized))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_name_forbidden_error",
"That pet name is forbidden. Please choose another name.",
{}));
botAI->TellError("That pet name is forbidden. Please choose another name.");
return false;
}
@ -413,12 +382,8 @@ bool TameAction::RenamePet(const std::string& newName)
bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry());
// Notify the master about the rename and give a tip to update the client name display
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_renamed", "Your pet has been renamed to %name!", {{"%name", normalized}}));
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_rename_refresh_hint",
"If you do not see the new name, please dismiss and recall your pet.",
{}));
botAI->TellMaster("Your pet has been renamed to " + normalized + "!");
botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet.");
// Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true);
@ -436,8 +401,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry)
// Ensure the player is a hunter and at least level 10 (required for pets)
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_only_hunters_level_10", "Only level 10+ hunters can have pets.", {}));
botAI->TellError("Only level 10+ hunters can have pets.");
return false;
}
@ -445,8 +409,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry)
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry);
if (!creature)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_creature_template_not_found", "Creature template not found.", {}));
botAI->TellError("Creature template not found.");
return false;
}
@ -467,8 +430,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry)
Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0);
if (!pet)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_create_pet_failed", "Failed to create pet.", {}));
botAI->TellError("Failed to create pet.");
return false;
}
@ -523,15 +485,13 @@ bool TameAction::AbandonPet()
// Remove the pet from the bot and mark it as deleted in the database
bot->RemovePet(pet, PET_SAVE_AS_DELETED);
// Inform the bot's master/player that the pet was abandoned
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_pet_abandoned", "Your pet has been abandoned.", {}));
botAI->TellMaster("Your pet has been abandoned.");
return true;
}
else
{
// If there is no hunter pet, show an error message
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"tame_no_hunter_pet_to_abandon", "You have no hunter pet to abandon.", {}));
botAI->TellError("You have no hunter pet to abandon.");
return false;
}
}

View File

@ -7,7 +7,6 @@
#include "Event.h"
#include "LastMovementValue.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "PlayerbotAIConfig.h"
#include "Config.h"
@ -25,8 +24,7 @@ bool TaxiAction::Execute(Event event)
{
movement.taxiNodes.clear();
movement.Set(nullptr);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"taxi_ready_next_flight", "I am ready for the next flight", {}));
botAI->TellMaster("I am ready for the next flight");
return true;
}
@ -122,15 +120,13 @@ bool TaxiAction::Execute(Event event)
{
movement.taxiNodes.clear();
movement.Set(nullptr);
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"taxi_cant_fly_with_you", "I can't fly with you", {}));
botAI->TellError("I can't fly with you");
return false;
}
return true;
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"taxi_no_flightmaster_nearby", "Cannot find any flightmaster to talk", {}));
botAI->TellError("Cannot find any flightmaster to talk");
return false;
}

View File

@ -12,7 +12,6 @@
#include "ItemVisitors.h"
#include "PlayerbotMgr.h"
#include "PlayerbotSecurity.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "SetCraftAction.h"
@ -29,17 +28,13 @@ bool TradeStatusAction::Execute(Event event)
// Allow the master and group members to trade
if (trader != master && !traderBotAI && (!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID())))
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_busy_now", "I'm kind of busy now", {}),
LANG_UNIVERSAL, trader);
bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader);
return false;
}
if (sPlayerbotAIConfig.enableRandomBotTrading == 0 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot)))
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_disabled", "Trading is disabled", {}),
LANG_UNIVERSAL, trader);
bot->Whisper("Trading is disabled", LANG_UNIVERSAL, trader);
return false;
}
@ -185,15 +180,9 @@ bool TradeStatusAction::CheckTrade()
{
if (bot->GetGroup() && bot->GetGroup()->IsMember(bot->GetTrader()->GetGUID()) &&
botAI->HasRealPlayerMaster())
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_thank_you_player",
"Thank you %player",
{{"%player", chat->FormatWorldobject(bot->GetTrader())}}));
botAI->TellMasterNoFacing("Thank you " + chat->FormatWorldobject(bot->GetTrader()));
else
bot->Say(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_thank_you_player",
"Thank you %player",
{{"%player", chat->FormatWorldobject(bot->GetTrader())}}),
bot->Say("Thank you " + chat->FormatWorldobject(bot->GetTrader()),
(bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
}
return isGettingItem;
@ -221,16 +210,12 @@ bool TradeStatusAction::CheckTrade()
int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney;
if (botItemsMoney > 0 && sPlayerbotAIConfig.enableRandomBotTrading == 2 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot)))
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_selling_disabled", "Selling is disabled.", {}),
LANG_UNIVERSAL, trader);
bot->Whisper("Selling is disabled.", LANG_UNIVERSAL, trader);
return false;
}
if (playerItemsMoney && sPlayerbotAIConfig.enableRandomBotTrading == 3 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot)))
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_buying_disabled", "Buying is disabled.", {}),
LANG_UNIVERSAL, trader);
bot->Whisper("Buying is disabled.", LANG_UNIVERSAL, trader);
return false;
}
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
@ -239,10 +224,8 @@ bool TradeStatusAction::CheckTrade()
if (item && !item->GetTemplate()->SellPrice && !item->GetTemplate()->IsConjuredConsumable())
{
std::ostringstream out;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_item_not_for_sale",
"%item - This is not for sale",
{{"%item", chat->FormatItem(item->GetTemplate())}}));
out << chat->FormatItem(item->GetTemplate()) << " - This is not for sale";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
@ -256,10 +239,8 @@ bool TradeStatusAction::CheckTrade()
if ((botMoney && !item->GetTemplate()->BuyPrice) || usage == ITEM_USAGE_NONE)
{
std::ostringstream out;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_item_not_needed",
"%item - I don't need this",
{{"%item", chat->FormatItem(item->GetTemplate())}}));
out << chat->FormatItem(item->GetTemplate()) << " - I don't need this";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
@ -271,8 +252,7 @@ bool TradeStatusAction::CheckTrade()
if (!botItemsMoney && !playerItemsMoney)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_no_items_error", "There are no items to trade", {}));
botAI->TellError("There are no items to trade");
return false;
}
@ -286,8 +266,7 @@ bool TradeStatusAction::CheckTrade()
{
if (moneyDelta < 0)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_discount_buy_only", "You can use discount to buy items only", {}));
botAI->TellError("You can use discount to buy items only");
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
@ -303,20 +282,16 @@ bool TradeStatusAction::CheckTrade()
switch (urand(0, 4))
{
case 0:
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_success_pleasure", "A pleasure doing business with you", {}));
botAI->TellMaster("A pleasure doing business with you");
break;
case 1:
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_success_fair_trade", "Fair trade", {}));
botAI->TellMaster("Fair trade");
break;
case 2:
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_success_thanks", "Thanks", {}));
botAI->TellMaster("Thanks");
break;
case 3:
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_success_off_with_you", "Off with you", {}));
botAI->TellMaster("Off with you");
break;
}
@ -325,10 +300,8 @@ bool TradeStatusAction::CheckTrade()
}
std::ostringstream out;
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"trade_want_money_for_this",
"I want %money for this",
{{"%money", chat->formatMoney(-(delta + discount))}}));
out << "I want " << chat->formatMoney(-(delta + discount)) << " for this";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}

View File

@ -5,14 +5,10 @@
#include "TrainerAction.h"
#include "AiFactory.h"
#include "BisListMgr.h"
#include "BudgetValues.h"
#include "Event.h"
#include "PlayerbotFactory.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "ReputationMgr.h"
#include "Trainer.h"
bool TrainerAction::Execute(Event event)
@ -273,282 +269,6 @@ bool MaintenanceAction::Execute(Event /*event*/)
return true;
}
bool BisGearAction::RunAutogearFallback(uint16 effectiveIlvl)
{
if (!sPlayerbotAIConfig.autoGearCommand)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_autogear_unavailable_error",
"autogear command is not allowed, please check the configuration.", {}));
return false;
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_no_rows_fallback",
"No BiS for your tier/spec/level, check cfg, running autogear instead", {}));
// Wipe all equipped slots so autogear gears from scratch at the requested ilvl
// (avoids old high-tier items surviving the incremental 1.2x threshold).
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
continue;
if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
}
uint32 gs = effectiveIlvl == 0
? 0
: PlayerbotFactory::CalcMixedGearScore(effectiveIlvl, sPlayerbotAIConfig.autoGearQualityLimit);
PlayerbotFactory factory(bot, bot->GetLevel(), sPlayerbotAIConfig.autoGearQualityLimit, gs);
factory.InitEquipment(false, sPlayerbotAIConfig.twoRoundsGearInit);
factory.InitAmmo();
if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
factory.ApplyEnchantAndGemsNew();
bot->DurabilityRepairAll(false, 1.0f, false);
return true;
}
bool BisGearAction::Execute(Event event)
{
if (!sPlayerbotAIConfig.autoGearBisCommand)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_command_unavailable_error",
"bis command is not allowed, please check the configuration.", {}));
return false;
}
if (!sPlayerbotAIConfig.autoGearCommandAltBots &&
!sPlayerbotAIConfig.IsInRandomAccountList(bot->GetSession()->GetAccountId()))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_altbot_refused_error", "You cannot use bis on alt bots.", {}));
return false;
}
if (sPlayerbotAIConfig.autoGearQualityLimit < 4)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_quality_floor_error", "AutoGearQualityLimit must be 4 for BiS.", {}));
return false;
}
if (sRandomPlayerbotMgr.IsSpecPvp(bot->GetGUID().GetCounter(), bot->getClass()))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_pvp_refused_error", "bis is PvE only, bot is configured as PvP.", {}));
return false;
}
uint16 ilvl = static_cast<uint16>(sPlayerbotAIConfig.autoGearScoreLimit);
// Optional explicit ilvl override: `/p autogear bis 55`.
// Garbage or out-of-range args are hard-rejected: no autogear fallback, no gear change.
std::string const param = event.getParam();
if (!param.empty())
{
unsigned long parsed = 0;
size_t pos = 0;
bool valid = false;
try
{
parsed = std::stoul(param, &pos);
valid = (parsed > 0 && pos == param.size() && parsed <= 0xFFFFu);
}
catch (...)
{
valid = false;
}
if (!valid)
{
std::map<std::string, std::string> phs;
phs["%param"] = param;
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_invalid_arg_error",
"Invalid BiS ilvl argument '%param'. Use a positive integer.", phs));
return false;
}
if (parsed > static_cast<unsigned long>(sPlayerbotAIConfig.autoGearScoreLimit))
{
std::map<std::string, std::string> phs;
phs["%requested"] = std::to_string(parsed);
phs["%limit"] = std::to_string(sPlayerbotAIConfig.autoGearScoreLimit);
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_arg_above_limit_error",
"BiS ilvl %requested exceeds AutoGearScoreLimit %limit, refusing", phs));
return false;
}
ilvl = static_cast<uint16>(parsed);
}
uint8 cls = bot->getClass();
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
uint8 faction = bot->GetTeamId() == TEAM_ALLIANCE ? 1 : 2;
// Druid Bear (Feral Tank) shares tab 1 with Cat. Use sentinel tab 10 when tank strategy active.
constexpr uint8 BIS_TAB_DRUID_BEAR = 10;
constexpr uint16 BIS_ILVL_FALLBACK_WINDOW = 20;
uint16 resolvedIlvl = 0;
std::map<uint8, uint32> bisMap;
if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && PlayerbotAI::IsTank(bot))
bisMap = sBisListMgr->GetBisForNearest(ilvl, BIS_ILVL_FALLBACK_WINDOW, cls, BIS_TAB_DRUID_BEAR, faction,
&resolvedIlvl);
if (bisMap.empty())
bisMap = sBisListMgr->GetBisForNearest(ilvl, BIS_ILVL_FALLBACK_WINDOW, cls, tab, faction, &resolvedIlvl);
// No rows within fallback window -> full autogear fallback at the effective ilvl.
if (bisMap.empty())
{
std::map<std::string, std::string> phs;
phs["%ilvl"] = std::to_string(ilvl);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_no_rows_autogear_msg",
"No BiS at ilvl %ilvl, using Autogear %ilvl instead", phs));
return RunAutogearFallback(ilvl);
}
if (resolvedIlvl != ilvl)
{
std::map<std::string, std::string> phs;
phs["%requested"] = std::to_string(ilvl);
phs["%resolved"] = std::to_string(resolvedIlvl);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_closest_match_msg",
"No BiS at ilvl %requested, using closest match at ilvl %resolved", phs));
}
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_applying_msg", "Applying BiS gear", {}));
// 1. Wipe everything currently equipped so autogear starts from a clean slate.
// Old items linger in inventory otherwise and autogear leaves slots empty on bag conflicts.
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
continue;
if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
}
// Wipe equippable items from bags too. Autogear can shove old equipped items into bags
// (HandleAutoStoreBagItemOpcode), and a unique-equipped duplicate stuck in a bag blocks
// CanEquipNewItem on subsequent BiS runs. Spare consumables/reagents.
auto destroyIfEquippable = [&](uint8 bag, uint8 slot)
{
Item* item = bot->GetItemByPos(bag, slot);
if (!item)
return;
ItemTemplate const* tmpl = item->GetTemplate();
if (!tmpl)
return;
if (tmpl->Class == ITEM_CLASS_WEAPON || tmpl->Class == ITEM_CLASS_ARMOR)
bot->DestroyItem(bag, slot, true);
};
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
destroyIfEquippable(INVENTORY_SLOT_BAG_0, slot);
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag* container = bot->GetBagByPos(bag))
for (uint32 slot = 0; slot < container->GetBagSize(); ++slot)
destroyIfEquippable(bag, slot);
}
// 2. Run full autogear on the empty bot so every slot gets a best-available pick.
// Uncovered slots will keep the autogear pick; BiS overwrites the rest below.
if (sPlayerbotAIConfig.autoGearCommand)
{
uint32 fillGs = ilvl == 0
? 0
: PlayerbotFactory::CalcMixedGearScore(ilvl, sPlayerbotAIConfig.autoGearQualityLimit);
PlayerbotFactory fillFactory(bot, bot->GetLevel(), sPlayerbotAIConfig.autoGearQualityLimit, fillGs);
fillFactory.InitEquipment(false, sPlayerbotAIConfig.twoRoundsGearInit);
}
// 2b. Pre-destroy autogear picks that would conflict with any BiS item by entry.
// Autogear may have placed the exact item BiS wants into trinket2/finger2 (or vice versa);
// unique-equipped enforcement would then make BiS's equip silently drop one copy.
std::set<uint32> bisEntries;
for (auto const& kv : bisMap)
bisEntries.insert(kv.second);
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
if (bisEntries.count(item->GetEntry()))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
}
// 3. Apply BiS: only touch slots where the bot can actually equip the BiS item.
// If item requires reputation, grant the required rank first. If CanUseItem still
// fails (class/race/skill/level), keep autogear's pick for that slot.
for (auto const& kv : bisMap)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(kv.second);
if (!proto)
continue;
// Grant required reputation rank if the item gates on it.
if (proto->RequiredReputationFaction && proto->RequiredReputationRank > 0)
{
if (FactionEntry const* fac = sFactionStore.LookupEntry(proto->RequiredReputationFaction))
{
ReputationRank requiredRank = static_cast<ReputationRank>(proto->RequiredReputationRank);
if (bot->GetReputationRank(proto->RequiredReputationFaction) < requiredRank)
{
int32 standing = ReputationMgr::ReputationRankToStanding(
static_cast<ReputationRank>(requiredRank - 1)) + 1;
bot->GetReputationMgr().SetReputation(fac, standing);
}
}
}
if (bot->CanUseItem(proto) != EQUIP_ERR_OK)
continue;
uint8 slot = kv.first;
if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
uint16 dest = 0;
InventoryResult eqResult = bot->CanEquipNewItem(slot, dest, kv.second, false);
// Paired slots (finger 10<->11, trinket 12<->13): destroy paired slot and retry once
// when unique-equipped or autogear residue blocks the first attempt.
if (eqResult != EQUIP_ERR_OK)
{
uint8 pairedSlot = 0xFF;
if (slot == EQUIPMENT_SLOT_FINGER1) pairedSlot = EQUIPMENT_SLOT_FINGER2;
else if (slot == EQUIPMENT_SLOT_FINGER2) pairedSlot = EQUIPMENT_SLOT_FINGER1;
else if (slot == EQUIPMENT_SLOT_TRINKET1) pairedSlot = EQUIPMENT_SLOT_TRINKET2;
else if (slot == EQUIPMENT_SLOT_TRINKET2) pairedSlot = EQUIPMENT_SLOT_TRINKET1;
if (pairedSlot != 0xFF)
{
if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, pairedSlot))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, pairedSlot, true);
eqResult = bot->CanEquipNewItem(slot, dest, kv.second, false);
}
}
if (eqResult == EQUIP_ERR_OK)
{
bot->EquipNewItem(dest, kv.second, true);
bot->AutoUnequipOffhandIfNeed();
}
}
PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_EPIC, 0);
factory.InitAmmo();
if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
factory.ApplyEnchantAndGemsNew();
bot->DurabilityRepairAll(false, 1.0f, false);
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"bis_applied_msg", "BiS applied", {}));
return true;
}
bool RemoveGlyphAction::Execute(Event /*event*/)
{
for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex)

View File

@ -53,14 +53,4 @@ public:
bool Execute(Event event) override;
};
class BisGearAction : public Action
{
public:
BisGearAction(PlayerbotAI* botAI) : Action(botAI, "autogear bis") {}
bool Execute(Event event) override;
private:
bool RunAutogearFallback(uint16 effectiveIlvl);
};
#endif

View File

@ -9,7 +9,6 @@
#include "Event.h"
#include "ItemPackets.h"
#include "ItemUsageValue.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
bool UseItemAction::Execute(Event event)
@ -36,8 +35,7 @@ bool UseItemAction::Execute(Event event)
return UseItemOnGameObject(*items.begin(), *gos.begin());
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"use_item_none_available", "No items (or game objects) available", {}));
botAI->TellError("No items (or game objects) available");
return false;
}
@ -50,10 +48,8 @@ bool UseItemAction::UseGameObject(ObjectGuid guid)
go->Use(bot);
std::ostringstream out;
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"use_gameobject",
"Using %gameobject",
{{"%gameobject", chat->FormatGameobject(go)}}));
out << "Using " << chat->FormatGameobject(go);
botAI->TellMasterNoFacing(out.str());
return true;
}
@ -96,16 +92,16 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
bool targetSelected = false;
std::string itemText = chat->FormatItem(item->GetTemplate());
std::string targetText;
std::ostringstream out;
out << "Using " << chat->FormatItem(item->GetTemplate());
if (item->GetTemplate()->Stackable > 1)
{
uint32 count = item->GetCount();
if (count > 1)
itemText += " (" + std::to_string(count) + " available)";
out << " (" << count << " available) ";
else
itemText += " (the last one!)";
out << " (the last one!)";
}
if (goGuid)
@ -118,7 +114,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
packet << targetFlag;
packet << goGuid.WriteAsPacked();
targetText = chat->FormatGameobject(go);
out << " on " << chat->FormatGameobject(go);
targetSelected = true;
}
@ -128,8 +124,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
{
bool fit = SocketItem(itemTarget, item) || SocketItem(itemTarget, item, true);
if (!fit)
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"socket_does_not_fit", "Socket does not fit", {}));
botAI->TellMaster("Socket does not fit");
return fit;
}
@ -138,7 +133,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
targetFlag = TARGET_FLAG_ITEM;
packet << targetFlag;
packet << itemTarget->GetGUID().WriteAsPacked();
targetText = chat->FormatItem(itemTarget->GetTemplate());
out << " on " << chat->FormatItem(itemTarget->GetTemplate());
targetSelected = true;
}
}
@ -154,7 +149,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
{
targetFlag = TARGET_FLAG_UNIT;
packet << targetFlag << masterSelection.WriteAsPacked();
targetText = unit->GetName();
out << " on " << unit->GetName();
targetSelected = true;
}
}
@ -164,7 +159,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
{
targetFlag = TARGET_FLAG_UNIT;
packet << targetFlag << unitTarget->GetGUID().WriteAsPacked();
targetText = unitTarget->GetName();
out << " on " << unitTarget->GetName();
targetSelected = true;
}
@ -178,7 +173,9 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
packet << uint32(0);
bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(packet);
botAI->TellMasterNoFacing("Got quest " + chat->FormatQuest(qInfo));
std::ostringstream out;
out << "Got quest " << chat->FormatQuest(qInfo);
botAI->TellMasterNoFacing(out.str());
return true;
}
}
@ -220,7 +217,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
targetFlag = TARGET_FLAG_TRADE_ITEM;
packet << targetFlag << (uint8)1 << ObjectGuid((uint64)TRADE_SLOT_NONTRADED).WriteAsPacked();
targetSelected = true;
targetText = "traded item";
out << " on traded item";
}
else
{
@ -228,7 +225,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
packet << targetFlag;
packet << itemForSpell->GetGUID().WriteAsPacked();
targetSelected = true;
targetText = chat->FormatItem(itemForSpell->GetTemplate());
out << " on " << chat->FormatItem(itemForSpell->GetTemplate());
}
uint32 castTime = spellInfo->CalcCastTime();
botAI->SetNextCheckDelay(castTime + sPlayerbotAIConfig.reactDelay);
@ -249,17 +246,17 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
targetSelected = true;
if (unitTarget == bot || !unitTarget->IsInWorld() || unitTarget->IsDuringRemoveFromWorld())
targetText = "self";
out << " on self";
else if (unitTarget->IsHostileTo(bot))
targetText = "self";
out << " on self";
else
targetText = unitTarget->GetName();
out << " on " << unitTarget->GetName();
}
else
{
packet << bot->GetPackGUID();
targetSelected = true;
targetText = "self";
out << " on self";
}
}
@ -310,12 +307,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
return false;
// botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown);
std::string useText = targetSelected
? PlayerbotTextMgr::instance().GetBotTextOrDefault(
"use_item_on_target", "Using %item on %target", {{"%item", itemText}, {"%target", targetText}})
: PlayerbotTextMgr::instance().GetBotTextOrDefault(
"use_item", "Using %item", {{"%item", itemText}});
botAI->TellMasterNoFacing(useText);
botAI->TellMasterNoFacing(out.str());
bot->GetSession()->HandleUseItemOpcode(packet);
return true;
}
@ -380,10 +372,10 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
if (fits)
{
botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"socketing_item_with_gem",
"Socketing %item with %gem",
{{"%item", chat->FormatItem(item->GetTemplate())}, {"%gem", chat->FormatItem(gem->GetTemplate())}}));
std::ostringstream out;
out << "Socketing " << chat->FormatItem(item->GetTemplate());
out << " with " << chat->FormatItem(gem->GetTemplate());
botAI->TellMaster(out);
WorldPackets::Item::SocketGems nicePacket(std::move(packet));
nicePacket.Read();

View File

@ -11,7 +11,6 @@
#include "GridNotifiersImpl.h"
#include "NearestGameObjects.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotTextMgr.h"
#include "Playerbots.h"
#include "PositionValue.h"
@ -37,8 +36,7 @@ bool UseMeetingStoneAction::Execute(Event event)
if (bot->IsInCombat())
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_in_combat", "I am in combat", {}));
botAI->TellError("I am in combat");
return false;
}
@ -75,15 +73,13 @@ bool SummonAction::Execute(Event /*event*/)
if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true))
{
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"hello", "Hello!", {}));
botAI->TellMasterNoFacing("Hello!");
return true;
}
if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true))
{
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_welcome", "Welcome!", {}));
botAI->TellMasterNoFacing("Welcome!");
return true;
}
@ -103,10 +99,7 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserv
return Teleport(summoner, player, preserveAuras);
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
summoner == bot ? "meeting_stone_none_nearby" : "meeting_stone_none_near_you",
summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you",
{}));
botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you");
return false;
}
@ -126,19 +119,13 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser
{
if (!player->HasItemCount(6948, 1, false))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
player == bot ? "meeting_stone_no_hearthstone_self" : "meeting_stone_no_hearthstone_you",
player == bot ? "I have no hearthstone" : "You have no hearthstone",
{}));
botAI->TellError(player == bot ? "I have no hearthstone" : "You have no hearthstone");
return false;
}
if (player->HasSpellCooldown(8690))
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
player == bot ? "meeting_stone_hearthstone_not_ready_self" : "meeting_stone_hearthstone_not_ready_you",
player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready",
{}));
botAI->TellError(player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready");
return false;
}
@ -154,10 +141,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser
}
}
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
summoner == bot ? "meeting_stone_no_innkeepers_nearby" : "meeting_stone_no_innkeepers_near_you",
summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you",
{}));
botAI->TellError(summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you");
return false;
}
@ -169,8 +153,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras
if (player->GetVehicle())
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_cannot_summon_vehicle", "You cannot summon me while I'm on a vehicle", {}));
botAI->TellError("You cannot summon me while I'm on a vehicle");
return false;
}
@ -191,29 +174,20 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras
if (summoner->IsInCombat() && !sPlayerbotAIConfig.allowSummonInCombat)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_cannot_summon_master_in_combat",
"You cannot summon me while you're in combat",
{}));
botAI->TellError("You cannot summon me while you're in combat");
return false;
}
if (!summoner->IsAlive() && !sPlayerbotAIConfig.allowSummonWhenMasterIsDead)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_cannot_summon_master_dead",
"You cannot summon me while you're dead",
{}));
botAI->TellError("You cannot summon me while you're dead");
return false;
}
if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) &&
!sPlayerbotAIConfig.allowSummonWhenBotIsDead)
{
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_cannot_summon_bot_dead",
"You cannot summon me while I'm dead, you need to release my spirit first",
{}));
botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first");
return false;
}
@ -225,8 +199,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras
{
bot->ResurrectPlayer(1.0f, false);
bot->SpawnCorpseBones();
botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_revived", "I live, again!", {}));
botAI->TellMasterNoFacing("I live, again!");
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
}
@ -256,7 +229,6 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras
}
if (summoner != player)
botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"meeting_stone_not_enough_space", "Not enough place to summon", {}));
botAI->TellError("Not enough place to summon");
return false;
}

View File

@ -139,7 +139,6 @@ public:
creators["maintenance"] = &ChatActionContext::maintenance;
creators["remove glyph"] = &ChatActionContext::remove_glyph;
creators["autogear"] = &ChatActionContext::autogear;
creators["autogear bis"] = &ChatActionContext::autogear_bis;
creators["equip upgrade"] = &ChatActionContext::equip_upgrade;
creators["attack my target"] = &ChatActionContext::attack_my_target;
creators["pull my target"] = &ChatActionContext::pull_my_target;
@ -262,7 +261,6 @@ private:
static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); }
static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); }
static Action* autogear(PlayerbotAI* botAI) { return new AutoGearAction(botAI); }
static Action* autogear_bis(PlayerbotAI* botAI) { return new BisGearAction(botAI); }
static Action* equip_upgrade(PlayerbotAI* botAI) { return new EquipUpgradeAction(botAI); }
static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); }
static Action* nc(PlayerbotAI* botAI) { return new ChangeNonCombatStrategyAction(botAI); }

View File

@ -65,7 +65,6 @@ public:
creators["maintenance"] = &ChatTriggerContext::maintenance;
creators["remove glyph"] = &ChatTriggerContext::remove_glyph;
creators["autogear"] = &ChatTriggerContext::autogear;
creators["autogear bis"] = &ChatTriggerContext::autogear_bis;
creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade;
creators["attack"] = &ChatTriggerContext::attack;
creators["pull"] = &ChatTriggerContext::pull;
@ -221,7 +220,6 @@ private:
static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); }
static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); }
static Trigger* autogear(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear"); }
static Trigger* autogear_bis(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear bis"); }
static Trigger* equip_upgrade(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "equip upgrade"); }
static Trigger* co(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "co"); }
static Trigger* nc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "nc"); }

View File

@ -20,23 +20,20 @@ private:
}
};
// Commands where trigger name =/= action name.
void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
PassTroughStrategy::InitTriggers(triggers);
// Keep single action triggers on one line, and multi-action triggers on multiple lines.
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
triggers.push_back(new TriggerNode("pvp stats", { NextAction("tell pvp stats", relevance) }));
triggers.push_back(new TriggerNode("q",
{ NextAction("query quest", relevance),
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
NextAction("query item usage", relevance) }));
triggers.push_back(new TriggerNode("add all loot",
{ NextAction("add all loot", relevance),
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
NextAction("loot", relevance) }));
triggers.push_back(new TriggerNode("u", { NextAction("use", relevance) }));
triggers.push_back(new TriggerNode("c", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("items", { NextAction("item count", relevance) }));
triggers.push_back(
new TriggerNode("items", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("inv", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("e", { NextAction("equip", relevance) }));
triggers.push_back(new TriggerNode("ue", { NextAction("unequip", relevance) }));
@ -45,40 +42,81 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back(new TriggerNode("s", { NextAction("sell", relevance) }));
triggers.push_back(new TriggerNode("b", { NextAction("buy", relevance) }));
triggers.push_back(new TriggerNode("r", { NextAction("reward", relevance) }));
triggers.push_back(new TriggerNode("attack", { NextAction("attack my target", relevance) }));
triggers.push_back(new TriggerNode("accept", { NextAction("accept quest", relevance) }));
triggers.push_back(new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("tank attack", { NextAction("tank attack chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("talk",
{ NextAction("gossip hello", relevance),
triggers.push_back(
new TriggerNode("attack", { NextAction("attack my target", relevance) }));
triggers.push_back(
new TriggerNode("accept", { NextAction("accept quest", relevance) }));
triggers.push_back(
new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
triggers.push_back(new TriggerNode(
"tank attack", { NextAction("tank attack chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("talk", { NextAction("gossip hello", relevance),
NextAction("talk to quest giver", relevance) }));
triggers.push_back(new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
triggers.push_back(new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
triggers.push_back(new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
triggers.push_back(new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
triggers.push_back(new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
triggers.push_back(new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
triggers.push_back(new TriggerNode("target", { NextAction("tell target", relevance) }));
triggers.push_back(new TriggerNode("pull", { NextAction("pull my target", relevance) }));
triggers.push_back(new TriggerNode("pull back", { NextAction("pull my target", relevance) }));
triggers.push_back(new TriggerNode("pull rti", { NextAction("pull rti target", relevance) }));
triggers.push_back(new TriggerNode("ready", { NextAction("ready check", relevance) }));
triggers.push_back(new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
triggers.push_back(new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
triggers.push_back(new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
triggers.push_back(new TriggerNode("qi", { NextAction("query item usage", relevance) }));
triggers.push_back(
new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
triggers.push_back(
new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
triggers.push_back(
new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
triggers.push_back(
new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
triggers.push_back(
new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
triggers.push_back(
new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
triggers.push_back(
new TriggerNode("target", { NextAction("tell target", relevance) }));
triggers.push_back(
new TriggerNode("pull", { NextAction("pull my target", relevance) }));
triggers.push_back(
new TriggerNode("pull back", { NextAction("pull my target", relevance) }));
triggers.push_back(
new TriggerNode("pull rti", { NextAction("pull rti target", relevance) }));
triggers.push_back(
new TriggerNode("ready", { NextAction("ready check", relevance) }));
triggers.push_back(
new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance)}));
triggers.push_back(
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
triggers.push_back(
new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
triggers.push_back(
new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
triggers.push_back(
new TriggerNode("open items", { NextAction("open items", relevance) }));
triggers.push_back(
new TriggerNode("qi", { NextAction("query item usage", relevance) }));
triggers.push_back(
new TriggerNode("unlock items", { NextAction("unlock items", relevance) }));
triggers.push_back(
new TriggerNode("unlock traded item", { NextAction("unlock traded item", relevance) }));
triggers.push_back(
new TriggerNode("wipe", { NextAction("wipe", relevance) }));
triggers.push_back(new TriggerNode("tame", { NextAction("tame", relevance) }));
triggers.push_back(new TriggerNode("glyphs", { NextAction("glyphs", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("glyph equip", { NextAction("glyph equip", relevance) })); // Added for custom Glyphs
triggers.push_back(new TriggerNode("pet", { NextAction("pet", relevance) }));
triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) }));
triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) }));
triggers.push_back(new TriggerNode("focus heal", { NextAction("focus heal targets", relevance) }));
triggers.push_back(new TriggerNode("emblems", { NextAction("emblems", relevance) }));
}
// Commands where trigger name == action name.
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
{
actionNodeFactories.Add(new ChatCommandActionNodeFactoryInternal());
@ -111,7 +149,6 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("maintenance");
supported.push_back("remove glyph");
supported.push_back("autogear");
supported.push_back("autogear bis");
supported.push_back("equip upgrade");
supported.push_back("chat");
supported.push_back("home");
@ -162,15 +199,15 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("rtsc");
supported.push_back("drink");
supported.push_back("calc");
supported.push_back("roll");
supported.push_back("open items");
supported.push_back("qi");
supported.push_back("unlock items");
supported.push_back("unlock traded item");
supported.push_back("wipe");
supported.push_back("tame");
supported.push_back("glyphs");
supported.push_back("glyph equip");
supported.push_back("pet");
supported.push_back("pet attack");
supported.push_back("wait for attack time");
supported.push_back("focus heal");
}

View File

@ -34,11 +34,6 @@ bool MediumManaTrigger::IsActive()
AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.mediumMana;
}
bool LowEnergyTrigger::IsActive()
{
return AI_VALUE2(uint8, "energy", "self target") < threshold;
}
bool NoPetTrigger::IsActive()
{
return (bot->GetMinionGUID().IsEmpty()) && (!AI_VALUE(Unit*, "pet target")) && (!bot->GetGuardianPet()) &&

View File

@ -550,17 +550,6 @@ public:
bool IsActive() override;
};
class LowEnergyTrigger : public Trigger
{
public:
LowEnergyTrigger(PlayerbotAI* botAI, uint8 threshold = 30) : Trigger(botAI, "low energy"), threshold(threshold) {}
bool IsActive() override;
private:
uint8 threshold;
};
BEGIN_TRIGGER(PanicTrigger, Trigger) // cppcheck-suppress unknownMacro
std::string const getName() override { return "panic"; }
END_TRIGGER()

View File

@ -22,15 +22,6 @@ bool DeadTrigger::IsActive() { return AI_VALUE2(bool, "dead", GetTargetName());
bool AoeHealTrigger::IsActive() { return AI_VALUE2(uint8, "aoe heal", type) >= count; }
bool HealerLowManaTrigger::IsActive()
{
Unit* target = GetTarget();
if (!target)
return false;
return target->GetPowerPct(POWER_MANA) < sPlayerbotAIConfig.lowMana;
}
bool AoeInGroupTrigger::IsActive()
{
int32 member = botAI->GetNearGroupMemberCount();

View File

@ -143,15 +143,6 @@ public:
TargetCriticalHealthTrigger(PlayerbotAI* botAI) : TargetLowHealthTrigger(botAI, 20) {}
};
class HealerLowManaTrigger : public Trigger
{
public:
HealerLowManaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "healer low mana") {}
std::string const GetTargetName() override { return "healer low mana"; }
bool IsActive() override;
};
class PartyMemberDeadTrigger : public Trigger
{
public:

View File

@ -18,19 +18,3 @@ bool NoRtiTrigger::IsActive()
Unit* target = AI_VALUE(Unit*, "rti target");
return target == nullptr;
}
// Fires when the RTI CC target should be crowd controlled by this spell.
// Standard path: the target is already in the attackers list and "cc target" matches the RTI
// mark — delegates to HasCcTargetTrigger to confirm no one else is already CCing it.
bool RtiCcTrigger::IsActive()
{
Unit* rtiCcTarget = AI_VALUE(Unit*, "rti cc target");
if (!rtiCcTarget)
return false;
Unit* ccTarget = AI_VALUE2(Unit*, "cc target", getName());
if (ccTarget && ccTarget == rtiCcTarget)
return HasCcTargetTrigger::IsActive();
return botAI->CanCastSpell(getName(), rtiCcTarget);
}

View File

@ -6,7 +6,6 @@
#ifndef _PLAYERBOT_RTITRIGGERS_H
#define _PLAYERBOT_RTITRIGGERS_H
#include "GenericTriggers.h"
#include "Trigger.h"
class PlayerbotAI;
@ -19,12 +18,4 @@ public:
bool IsActive() override;
};
class RtiCcTrigger : public HasCcTargetTrigger
{
public:
RtiCcTrigger(PlayerbotAI* botAI, std::string const name) : HasCcTargetTrigger(botAI, name) {}
bool IsActive() override;
};
#endif

View File

@ -51,7 +51,6 @@ public:
creators["low mana"] = &TriggerContext::LowMana;
creators["medium mana"] = &TriggerContext::MediumMana;
creators["low energy"] = &TriggerContext::LowEnergy;
creators["high mana"] = &TriggerContext::HighMana;
creators["almost full mana"] = &TriggerContext::AlmostFullMana;
creators["enough mana"] = &TriggerContext::EnoughMana;
@ -60,7 +59,6 @@ public:
creators["party member low health"] = &TriggerContext::PartyMemberLowHealth;
creators["party member medium health"] = &TriggerContext::PartyMemberMediumHealth;
creators["party member almost full health"] = &TriggerContext::PartyMemberAlmostFullHealth;
creators["healer low mana"] = &TriggerContext::HealerLowMana;
creators["generic boost"] = &TriggerContext::generic_boost;
creators["loss of control"] = &TriggerContext::loss_of_control;
@ -314,7 +312,6 @@ private:
static Trigger* TargetCriticalHealth(PlayerbotAI* botAI) { return new TargetCriticalHealthTrigger(botAI); }
static Trigger* LowMana(PlayerbotAI* botAI) { return new LowManaTrigger(botAI); }
static Trigger* MediumMana(PlayerbotAI* botAI) { return new MediumManaTrigger(botAI); }
static Trigger* LowEnergy(PlayerbotAI* botAI) { return new LowEnergyTrigger(botAI); }
static Trigger* HighMana(PlayerbotAI* botAI) { return new HighManaTrigger(botAI); }
static Trigger* AlmostFullMana(PlayerbotAI* botAI) { return new AlmostFullManaTrigger(botAI); }
static Trigger* EnoughMana(PlayerbotAI* botAI) { return new EnoughManaTrigger(botAI); }
@ -386,7 +383,6 @@ private:
{
return new PartyMemberCriticalHealthTrigger(botAI);
}
static Trigger* HealerLowMana(PlayerbotAI* botAI) { return new HealerLowManaTrigger(botAI); }
static Trigger* protect_party_member(PlayerbotAI* botAI) { return new ProtectPartyMemberTrigger(botAI); }
static Trigger* no_pet(PlayerbotAI* botAI) { return new NoPetTrigger(botAI); }
static Trigger* has_pet(PlayerbotAI* botAI) { return new HasPetTrigger(botAI); }

View File

@ -29,7 +29,7 @@ public:
if (!botAI->GetBot()->IsWithinLOSInMap(unit))
return false;
return botAI->IsMovementImpaired(unit) && !botAI->HasAnyAuraOf(unit, "stealth", "prowl", nullptr);
return botAI->IsMovementImpaired(unit);
}
};

View File

@ -135,32 +135,6 @@ bool PartyMemberToHeal::Check(Unit* player)
bot->GetDistance2d(player) < sPlayerbotAIConfig.healDistance * 2 && bot->IsWithinLOSInMap(player);
}
Unit* HealerLowMana::Calculate()
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
MinValueCalculator calc(100);
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (!player || player == bot)
continue;
if (player->IsGameMaster() || !player->IsAlive())
continue;
if (!botAI->IsHeal(player))
continue;
float mana = player->GetPowerPct(POWER_MANA);
if (mana < calc.minValue)
calc.probe(mana, player);
}
return (Unit*)calc.param;
}
Unit* PartyMemberToProtect::Calculate()
{
return nullptr;

View File

@ -37,13 +37,4 @@ protected:
Unit* Calculate() override;
};
class HealerLowMana : public PartyMemberValue
{
public:
HealerLowMana(PlayerbotAI* botAI) : PartyMemberValue(botAI, "healer low mana") {}
protected:
Unit* Calculate() override;
};
#endif

View File

@ -132,7 +132,6 @@ public:
creators["attacker without aura"] = &ValueContext::attacker_without_aura;
creators["melee attacker without aura"] = &ValueContext::melee_attacker_without_aura;
creators["party member to heal"] = &ValueContext::party_member_to_heal;
creators["healer low mana"] = &ValueContext::healer_low_mana;
creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect;
creators["current target"] = &ValueContext::current_target;
creators["self target"] = &ValueContext::self_target;
@ -452,7 +451,6 @@ private:
return new MeleeAttackerWithoutAuraTargetValue(botAI);
}
static UntypedValue* party_member_to_heal(PlayerbotAI* botAI) { return new PartyMemberToHeal(botAI); }
static UntypedValue* healer_low_mana(PlayerbotAI* botAI) { return new HealerLowMana(botAI); }
static UntypedValue* party_member_to_resurrect(PlayerbotAI* botAI) { return new PartyMemberToResurrect(botAI); }
static UntypedValue* party_member_to_dispel(PlayerbotAI* botAI) { return new PartyMemberToDispel(botAI); }
static UntypedValue* party_member_to_protect(PlayerbotAI* botAI) { return new PartyMemberToProtect(botAI); }

View File

@ -41,7 +41,6 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
triggers.push_back(
new TriggerNode("horn of winter", { NextAction("horn of winter", 21.0f) }));
triggers.push_back(

View File

@ -11,9 +11,6 @@
#include "AoeValues.h"
#include "TargetValue.h"
constexpr uint32 SPELL_ECLIPSE_SOLAR = 48517;
constexpr uint32 SPELL_ECLIPSE_LUNAR = 48518;
namespace
{
bool PrepareThornsTarget(PlayerbotAI* botAI, Unit* target)
@ -67,89 +64,16 @@ bool CastThornsOnMainTankAction::Execute(Event event)
return PrepareThornsTarget(botAI, GetTarget()) && BuffOnMainTankAction::Execute(event);
}
bool CastWrathAction::isUseful()
{
time_t now = time(nullptr);
time_t solarTime = context->GetValue<time_t>("eclipse solar proc time")->Get();
time_t lunarTime = context->GetValue<time_t>("eclipse lunar proc time")->Get();
// --- Update Solar Eclipse tracking ---
// Wrath is selected during Solar Eclipse (eclipse trigger at 20.0f), so we reliably see it here.
if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && !solarTime)
context->GetValue<time_t>("eclipse solar proc time")->Set(now);
// Lunar procced — Solar fishing window is over, new cycle begins
if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && solarTime)
context->GetValue<time_t>("eclipse solar proc time")->Set(0);
// 30 s cooldown window expired
if (solarTime && (now - solarTime) >= 30)
context->GetValue<time_t>("eclipse solar proc time")->Set(0);
// --- Update Lunar Eclipse tracking (belt-and-suspenders in case Starfire isn't evaluated) ---
if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && !lunarTime)
context->GetValue<time_t>("eclipse lunar proc time")->Set(now);
// Solar procced — Lunar fishing window is over
if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && lunarTime)
context->GetValue<time_t>("eclipse lunar proc time")->Set(0);
if (lunarTime && (now - lunarTime) >= 30)
context->GetValue<time_t>("eclipse lunar proc time")->Set(0);
// Block Wrath while in Lunar Eclipse / post-Lunar fishing window
if (context->GetValue<time_t>("eclipse lunar proc time")->Get())
return false;
return CastSpellAction::isUseful();
}
bool CastStarfireAction::isUseful()
{
time_t now = time(nullptr);
time_t solarTime = context->GetValue<time_t>("eclipse solar proc time")->Get();
time_t lunarTime = context->GetValue<time_t>("eclipse lunar proc time")->Get();
// --- Update Lunar Eclipse tracking ---
// Starfire is selected during Lunar Eclipse (eclipse trigger at 20.0f), so we reliably see it here.
if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && !lunarTime)
context->GetValue<time_t>("eclipse lunar proc time")->Set(now);
// Solar procced — Lunar fishing window is over, new cycle begins
if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && lunarTime)
context->GetValue<time_t>("eclipse lunar proc time")->Set(0);
// 30 s cooldown window expired
if (lunarTime && (now - lunarTime) >= 30)
context->GetValue<time_t>("eclipse lunar proc time")->Set(0);
// --- Update Solar Eclipse tracking (belt-and-suspenders in case Wrath isn't evaluated) ---
if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && !solarTime)
context->GetValue<time_t>("eclipse solar proc time")->Set(now);
// Lunar procced — Solar fishing window is over
if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && solarTime)
context->GetValue<time_t>("eclipse solar proc time")->Set(0);
if (solarTime && (now - solarTime) >= 30)
context->GetValue<time_t>("eclipse solar proc time")->Set(0);
// Block Starfire while in Solar Eclipse / post-Solar fishing window
if (context->GetValue<time_t>("eclipse solar proc time")->Get())
return false;
return CastSpellAction::isUseful();
}
Value<Unit*>* CastEntanglingRootsCcAction::GetTargetValue()
{
return context->GetValue<Unit*>("rti cc target");
return context->GetValue<Unit*>("cc target", "entangling roots");
}
Value<Unit*>* CastHibernateCcAction::GetTargetValue() { return context->GetValue<Unit*>("rti cc target"); }
bool CastEntanglingRootsCcAction::Execute(Event /*event*/) { return botAI->CastSpell("entangling roots", GetTarget()); }
Value<Unit*>* CastCycloneCcAction::GetTargetValue() { return context->GetValue<Unit*>("rti cc target"); }
bool CastTyphoonAction::isUseful()
{
bool facingTarget = AI_VALUE2(bool, "facing", "current target");
bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan(
AI_VALUE2(float, "distance", GetTargetName()), 15.f);
return facingTarget && targetClose;
}
Value<Unit*>* CastHibernateCcAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", "hibernate"); }
bool CastHibernateCcAction::Execute(Event /*event*/) { return botAI->CastSpell("hibernate", GetTarget()); }
bool CastStarfallAction::isUseful()
{
if (!CastSpellAction::isUseful())
@ -165,26 +89,12 @@ bool CastStarfallAction::isUseful()
return false;
}
// Suppress if any unengaged hostile unit is within 40 yards — Starfall's 36-yard radius would pull them.
Unit* currentTarget = AI_VALUE(Unit*, "current target");
GuidVector const& nearbyNpcs = AI_VALUE(GuidVector, "possible targets");
for (ObjectGuid const& guid : nearbyNpcs)
// Avoid single-target usage on initial pull
uint8 aoeCount = *context->GetValue<uint8>("aoe count");
if (aoeCount < 2)
{
Unit* unit = botAI->GetUnit(guid);
// Standard null/world-state guard before touching the unit.
if (!unit || !unit->IsAlive() || !unit->IsInWorld())
continue;
// Already our target — its in-combat flag covers it.
if (unit == currentTarget)
continue;
// Safety net for any hostile-faction trigger creature that carries NON_ATTACKABLE flags.
if (!bot->IsValidAttackTarget(unit))
continue;
// Outside Starfall's actual radius; no pull risk.
if (ServerFacade::instance().GetDistance2d(bot, unit) > 40.0f)
continue;
// Unengaged mob within range — casting would pull it.
if (!unit->IsInCombat())
Unit* target = context->GetValue<Unit*>("current target")->Get();
if (!target || (!botAI->HasAura("moonfire", target) && !botAI->HasAura("insect swarm", target)))
return false;
}
@ -209,24 +119,6 @@ bool CastRebirthAction::isUseful()
AI_VALUE2(float, "distance", GetTargetName()) <= sPlayerbotAIConfig.spellDistance;
}
bool CastInnervateOnHealerAction::isPossible()
{
Unit* target = GetTarget();
if (!target || !target->IsInWorld())
return false;
if (botAI->HasAura("innervate", target))
return false;
uint32 spellId = AI_VALUE2(uint32, "spell id", "innervate");
return spellId && !bot->HasSpellCooldown(spellId);
}
std::vector<NextAction> CastInnervateOnHealerAction::getPrerequisites()
{
return { NextAction("caster form") };
}
Unit* CastRejuvenationOnNotFullAction::GetTarget()
{
Group* group = bot->GetGroup();
@ -257,63 +149,3 @@ bool CastRejuvenationOnNotFullAction::isUseful()
{
return GetTarget();
}
// --- Blanket HoT actions ---
Unit* CastBlanketHotAction::GetBlanketTarget(std::string const& auraName)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
auto eligible = [&](Player* member) -> bool
{
return member && member->IsAlive() &&
!member->IsGameMaster() &&
bot->GetDistance2d(member) <= sPlayerbotAIConfig.spellDistance &&
!botAI->HasAura(auraName, member, false, true);
};
Player* firstMelee = nullptr;
Player* firstRanged = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!eligible(member))
continue;
if (PlayerbotAI::IsTank(member))
return member;
else if (!firstMelee && PlayerbotAI::IsMelee(member) && !PlayerbotAI::IsTank(member))
firstMelee = member;
else if (!firstRanged && PlayerbotAI::IsRanged(member))
firstRanged = member;
if (firstMelee && firstRanged)
break;
}
if (firstMelee) return firstMelee;
return firstRanged;
}
Unit* CastRejuvenationBlanketAction::GetTarget()
{
return GetBlanketTarget("rejuvenation");
}
bool CastRejuvenationBlanketAction::isUseful()
{
return GetTarget() != nullptr;
}
Unit* CastWildGrowthBlanketAction::GetTarget()
{
return GetBlanketTarget("wild growth");
}
bool CastWildGrowthBlanketAction::isUseful()
{
return GetTarget() != nullptr;
}

View File

@ -8,7 +8,6 @@
#include "GenericSpellActions.h"
#include "SharedDefines.h"
#include "Value.h"
class PlayerbotAI;
class Unit;
@ -65,7 +64,7 @@ class CastHealingTouchOnPartyAction : public HealPartyMemberAction
{
public:
CastHealingTouchOnPartyAction(PlayerbotAI* botAI)
: HealPartyMemberAction(botAI, "healing touch", 50.0f, HealingManaEfficiency::MEDIUM)
: HealPartyMemberAction(botAI, "healing touch", 50.0f, HealingManaEfficiency::LOW)
{
}
};
@ -143,11 +142,16 @@ public:
bool isUseful() override;
};
class CastOmenOfClarityAction : public CastBuffSpellAction
{
public:
CastOmenOfClarityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "omen of clarity") {}
};
class CastWrathAction : public CastSpellAction
{
public:
CastWrathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "wrath") {}
bool isUseful() override;
};
class CastStarfallAction : public CastSpellAction
@ -165,14 +169,6 @@ public:
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastTyphoonAction : public CastSpellAction
{
public:
CastTyphoonAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "typhoon") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
bool isUseful() override;
};
class CastMoonfireAction : public CastDebuffSpellAction
{
public:
@ -189,7 +185,6 @@ class CastStarfireAction : public CastSpellAction
{
public:
CastStarfireAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "starfire") {}
bool isUseful() override;
};
class CastEntanglingRootsAction : public CastSpellAction
@ -198,11 +193,12 @@ public:
CastEntanglingRootsAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "entangling roots") {}
};
class CastEntanglingRootsCcAction : public CastCrowdControlSpellAction
class CastEntanglingRootsCcAction : public CastSpellAction
{
public:
CastEntanglingRootsCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "entangling roots") {}
CastEntanglingRootsCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "entangling roots on cc") {}
Value<Unit*>* GetTargetValue() override;
bool Execute(Event event) override;
};
class CastHibernateAction : public CastSpellAction
@ -211,18 +207,12 @@ public:
CastHibernateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hibernate") {}
};
class CastHibernateCcAction : public CastCrowdControlSpellAction
class CastHibernateCcAction : public CastSpellAction
{
public:
CastHibernateCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "hibernate") {}
Value<Unit*>* GetTargetValue() override;
};
class CastCycloneCcAction : public CastCrowdControlSpellAction
{
public:
CastCycloneCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "cyclone") {}
CastHibernateCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hibernate on cc") {}
Value<Unit*>* GetTargetValue() override;
bool Execute(Event event) override;
};
class CastNaturesGraspAction : public CastBuffSpellAction
@ -274,16 +264,6 @@ public:
std::string const GetTargetName() override { return "self target"; }
};
class CastInnervateOnHealerAction : public CastSpellAction
{
public:
CastInnervateOnHealerAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "innervate") {}
std::string const GetTargetName() override { return "healer low mana"; }
bool isPossible() override;
std::vector<NextAction> getPrerequisites() override;
};
class CastTranquilityAction : public CastAoeHealSpellAction
{
public:
@ -332,15 +312,13 @@ public:
class CastInsectSwarmOnAttackerAction : public CastDebuffSpellOnAttackerAction
{
public:
CastInsectSwarmOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "insect swarm", true, 0.0f) {}
bool isUseful() override { return CastAuraSpellAction::isUseful(); }
CastInsectSwarmOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "insect swarm") {}
};
class CastMoonfireOnAttackerAction : public CastDebuffSpellOnAttackerAction
{
public:
CastMoonfireOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "moonfire", true, 0.0f) {}
bool isUseful() override { return CastAuraSpellAction::isUseful(); }
CastMoonfireOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "moonfire") {}
};
class CastEnrageAction : public CastBuffSpellAction
@ -366,48 +344,4 @@ public:
CastForceOfNatureAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "force of nature") {}
};
// Base for blanket HoT actions. Provides GetBlanketTarget() as a member so
// subclasses can use AI_VALUE and the standard context machinery.
class CastBlanketHotAction : public CastSpellAction
{
public:
CastBlanketHotAction(PlayerbotAI* ai, std::string const& spell) : CastSpellAction(ai, spell)
{
range = botAI->GetRange("heal");
}
protected:
Unit* GetBlanketTarget(std::string const& auraName);
};
class CastRejuvenationBlanketAction : public CastBlanketHotAction
{
public:
CastRejuvenationBlanketAction(PlayerbotAI* ai) : CastBlanketHotAction(ai, "rejuvenation") {}
bool isUseful() override;
Unit* GetTarget() override;
std::string const getName() override { return "rejuvenation blanket"; }
};
class CastWildGrowthBlanketAction : public CastBlanketHotAction
{
public:
CastWildGrowthBlanketAction(PlayerbotAI* ai) : CastBlanketHotAction(ai, "wild growth") {}
bool isUseful() override;
Unit* GetTarget() override;
std::string const getName() override { return "wild growth blanket"; }
};
class EclipseSolarProcTimeValue : public ManualSetValue<time_t>
{
public:
EclipseSolarProcTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0) {}
};
class EclipseLunarProcTimeValue : public ManualSetValue<time_t>
{
public:
EclipseLunarProcTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0) {}
};
#endif

View File

@ -9,23 +9,12 @@
#include "GenericSpellActions.h"
#include "ReachTargetActions.h"
constexpr uint32 SPELL_POUNCE_RANK_1 = 9005;
constexpr uint32 SPELL_RAVAGE_RANK_1 = 6785;
class PlayerbotAI;
class CastFeralChargeCatAction : public CastReachTargetSpellAction
{
public:
CastFeralChargeCatAction(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, "feral charge - cat", 1.5f) {}
bool isUseful() override
{
if (botAI->HasAura("prowl", bot))
return false;
return CastReachTargetSpellAction::isUseful();
}
};
class CastCowerAction : public CastBuffSpellAction
@ -59,47 +48,28 @@ public:
CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake", true, 6.0f) {}
};
class CastRakeOnMeleeAttackersAction : public CastDebuffSpellOnMeleeAttackerAction
{
public:
CastRakeOnMeleeAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnMeleeAttackerAction(botAI, "rake", true, 6.0f) {}
};
class CastClawAction : public CastMeleeSpellAction
{
public:
CastClawAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "claw") {}
bool isUseful() override
{
// Block Claw once Pounce is learned; Claw remains available as the stealth opener before then.
if (botAI->HasAura("prowl", bot) && bot->HasSpell(SPELL_POUNCE_RANK_1))
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastMangleCatAction : public CastMeleeSpellAction
{
public:
CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "mangle (cat)") {}
bool isUseful() override
{
if (botAI->HasAura("prowl", bot))
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastSwipeCatAction : public CastMeleeSpellAction
{
public:
CastSwipeCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "swipe (cat)") {}
bool isUseful() override
{
if (botAI->HasAura("prowl", bot))
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastFerociousBiteAction : public CastMeleeSpellAction
@ -108,21 +78,6 @@ public:
CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {}
};
class CastMaimAction : public CastMeleeSpellAction
{
public:
CastMaimAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "maim") {}
bool isUseful() override
{
Unit* target = GetTarget();
if (!target || !target->ToPlayer())
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastRipAction : public CastMeleeDebuffSpellAction
{
public:
@ -133,14 +88,6 @@ class CastShredAction : public CastMeleeSpellAction
{
public:
CastShredAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "shred") {}
bool isUseful() override
{
if (botAI->HasAura("prowl", bot) && bot->HasSpell(SPELL_RAVAGE_RANK_1))
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastProwlAction : public CastBuffSpellAction
@ -159,28 +106,12 @@ class CastRavageAction : public CastMeleeSpellAction
{
public:
CastRavageAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ravage") {}
bool isUseful() override
{
if (!botAI->HasAura("prowl", bot))
return false;
return CastMeleeSpellAction::isUseful();
}
};
class CastPounceAction : public CastMeleeSpellAction
{
public:
CastPounceAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "pounce") {}
bool isUseful() override
{
if (!botAI->HasAura("prowl", bot))
return false;
return CastMeleeSpellAction::isUseful();
}
};
#endif

View File

@ -5,9 +5,9 @@
#include "DruidAiObjectContext.h"
#include "BalanceDruidStrategy.h"
#include "BearDruidStrategy.h"
#include "CatDruidStrategy.h"
#include "BearTankDruidStrategy.h"
#include "CasterDruidStrategy.h"
#include "CatDpsDruidStrategy.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "DruidCatActions.h"
@ -15,7 +15,9 @@
#include "DruidTriggers.h"
#include "GenericDruidNonCombatStrategy.h"
#include "GenericDruidStrategy.h"
#include "RestoDruidStrategy.h"
#include "HealDruidStrategy.h"
#include "MeleeDruidStrategy.h"
#include "OffhealDruidCatStrategy.h"
#include "Playerbots.h"
#include "DruidPullStrategy.h"
@ -26,31 +28,30 @@ public:
{
creators["nc"] = &DruidStrategyFactoryInternal::nc;
creators["pull"] = &DruidStrategyFactoryInternal::pull;
creators["aoe"] = &DruidStrategyFactoryInternal::aoe;
creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe;
creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe;
creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff;
creators["dps debuff"] = &DruidStrategyFactoryInternal::caster_debuff;
creators["cure"] = &DruidStrategyFactoryInternal::cure;
creators["melee"] = &DruidStrategyFactoryInternal::melee;
creators["buff"] = &DruidStrategyFactoryInternal::buff;
creators["boost"] = &DruidStrategyFactoryInternal::boost;
creators["cc"] = &DruidStrategyFactoryInternal::cc;
creators["healer dps"] = &DruidStrategyFactoryInternal::healer_dps;
creators["offheal"] = &DruidStrategyFactoryInternal::offheal;
creators["blanketing"] = &DruidStrategyFactoryInternal::blanketing;
creators["tranquility"] = &DruidStrategyFactoryInternal::tranquility;
creators["feral charge"] = &DruidStrategyFactoryInternal::feral_charge;
}
private:
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); }
static Strategy* pull(PlayerbotAI* botAI) { return new DruidPullStrategy(botAI); }
static Strategy* aoe(PlayerbotAI* botAI) { return new DruidAoeStrategy(botAI); }
static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); }
static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); }
static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); }
static Strategy* cure(PlayerbotAI* botAI) { return new DruidCureStrategy(botAI); }
static Strategy* melee(PlayerbotAI* botAI) { return new MeleeDruidStrategy(botAI); }
static Strategy* buff(PlayerbotAI* botAI) { return new GenericDruidBuffStrategy(botAI); }
static Strategy* boost(PlayerbotAI* botAI) { return new DruidBoostStrategy(botAI); }
static Strategy* cc(PlayerbotAI* botAI) { return new DruidCcStrategy(botAI); }
static Strategy* healer_dps(PlayerbotAI* botAI) { return new DruidHealerDpsStrategy(botAI); }
static Strategy* offheal(PlayerbotAI* botAI) { return new CatOffhealStrategy(botAI); }
static Strategy* blanketing(PlayerbotAI* botAI) { return new DruidBlanketStrategy(botAI); }
static Strategy* tranquility(PlayerbotAI* botAI) { return new DruidTranquilityStrategy(botAI); }
static Strategy* feral_charge(PlayerbotAI* botAI) { return new FeralChargeDruidStrategy(botAI); }
};
class DruidDruidStrategyFactoryInternal : public NamedObjectContext<Strategy>
@ -61,16 +62,18 @@ public:
creators["bear"] = &DruidDruidStrategyFactoryInternal::bear;
creators["tank"] = &DruidDruidStrategyFactoryInternal::bear;
creators["cat"] = &DruidDruidStrategyFactoryInternal::cat;
creators["balance"] = &DruidDruidStrategyFactoryInternal::balance;
creators["caster"] = &DruidDruidStrategyFactoryInternal::caster;
creators["dps"] = &DruidDruidStrategyFactoryInternal::cat;
creators["resto"] = &DruidDruidStrategyFactoryInternal::heal;
creators["heal"] = &DruidDruidStrategyFactoryInternal::heal;
creators["offheal"] = &DruidDruidStrategyFactoryInternal::offheal;
}
private:
static Strategy* bear(PlayerbotAI* botAI) { return new BearDruidStrategy(botAI); }
static Strategy* cat(PlayerbotAI* botAI) { return new CatDruidStrategy(botAI); }
static Strategy* balance(PlayerbotAI* botAI) { return new BalanceDruidStrategy(botAI); }
static Strategy* heal(PlayerbotAI* botAI) { return new RestoDruidStrategy(botAI); }
static Strategy* bear(PlayerbotAI* botAI) { return new BearTankDruidStrategy(botAI); }
static Strategy* cat(PlayerbotAI* botAI) { return new CatDpsDruidStrategy(botAI); }
static Strategy* caster(PlayerbotAI* botAI) { return new CasterDruidStrategy(botAI); }
static Strategy* heal(PlayerbotAI* botAI) { return new HealDruidStrategy(botAI); }
static Strategy* offheal(PlayerbotAI* botAI) { return new OffhealDruidCatStrategy(botAI); }
};
class DruidTriggerFactoryInternal : public NamedObjectContext<Trigger>
@ -78,6 +81,7 @@ class DruidTriggerFactoryInternal : public NamedObjectContext<Trigger>
public:
DruidTriggerFactoryInternal()
{
creators["omen of clarity"] = &DruidTriggerFactoryInternal::omen_of_clarity;
creators["clearcasting"] = &DruidTriggerFactoryInternal::clearcasting;
creators["thorns"] = &DruidTriggerFactoryInternal::thorns;
creators["thorns on party"] = &DruidTriggerFactoryInternal::thorns_on_party;
@ -86,12 +90,10 @@ public:
creators["faerie fire (feral)"] = &DruidTriggerFactoryInternal::faerie_fire_feral;
creators["faerie fire"] = &DruidTriggerFactoryInternal::faerie_fire;
creators["insect swarm"] = &DruidTriggerFactoryInternal::insect_swarm;
creators["insect swarm on attacker"] = &DruidTriggerFactoryInternal::insect_swarm_on_attacker;
creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire;
creators["moonfire on attacker"] = &DruidTriggerFactoryInternal::moonfire_on_attacker;
creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp;
creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury;
creators["berserk"] = &DruidTriggerFactoryInternal::berserk;
creators["berserk active"] = &DruidTriggerFactoryInternal::berserk_active;
creators["savage roar"] = &DruidTriggerFactoryInternal::savage_roar;
creators["rake"] = &DruidTriggerFactoryInternal::rake;
creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild;
@ -108,34 +110,17 @@ public:
creators["eclipse (lunar)"] = &DruidTriggerFactoryInternal::eclipse_lunar;
creators["bash on enemy healer"] = &DruidTriggerFactoryInternal::bash_on_enemy_healer;
creators["nature's swiftness"] = &DruidTriggerFactoryInternal::natures_swiftness;
creators["nature's swiftness active"] = &DruidTriggerFactoryInternal::natures_swiftness_active;
creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse;
creators["mangle (bear)"] = &DruidTriggerFactoryInternal::mangle_bear_trigger;
creators["lacerate"] = &DruidTriggerFactoryInternal::lacerate_trigger;
creators["demoralizing roar"] = &DruidTriggerFactoryInternal::demoralize_roar;
creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown;
creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown;
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
creators["ferocious bite execute"] = &DruidTriggerFactoryInternal::ferocious_bite_execute;
creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check;
creators["no healer dps strategy"] = &DruidTriggerFactoryInternal::no_healer_dps_strategy;
creators["starfall"] = &DruidTriggerFactoryInternal::starfall;
creators["force of nature"] = &DruidTriggerFactoryInternal::force_of_nature;
creators["cyclone"] = &DruidTriggerFactoryInternal::cyclone;
creators["predator's swiftness"] = &DruidTriggerFactoryInternal::predators_swiftness;
creators["predator's swiftness and cyclone"] = &DruidTriggerFactoryInternal::predators_swiftness_and_cyclone;
creators["predator's swiftness and hibernate"] = &DruidTriggerFactoryInternal::predators_swiftness_and_hibernate;
creators["predator's swiftness and entangling roots"] = &DruidTriggerFactoryInternal::predators_swiftness_and_entangling_roots;
creators["predator's swiftness and combat party member dead"] = &DruidTriggerFactoryInternal::predators_swiftness_and_combat_party_member_dead;
creators["clearcasting and medium aoe"] = &DruidTriggerFactoryInternal::clearcasting_and_medium_aoe;
creators["prowl"] = &DruidTriggerFactoryInternal::prowl_trigger;
creators["rejuvenation blanket"] = &DruidTriggerFactoryInternal::rejuvenation_blanket;
creators["wild growth blanket"] = &DruidTriggerFactoryInternal::wild_growth_blanket;
creators["aquatic form"] = &DruidTriggerFactoryInternal::aquatic_form;
}
private:
static Trigger* natures_swiftness(PlayerbotAI* botAI) { return new NaturesSwiftnessTrigger(botAI); }
static Trigger* natures_swiftness_active(PlayerbotAI* botAI) { return new NaturesSwiftnessActiveTrigger(botAI); }
static Trigger* clearcasting(PlayerbotAI* botAI) { return new ClearcastingTrigger(botAI); }
static Trigger* eclipse_solar(PlayerbotAI* botAI) { return new EclipseSolarTrigger(botAI); }
static Trigger* eclipse_lunar(PlayerbotAI* botAI) { return new EclipseLunarTrigger(botAI); }
@ -145,13 +130,11 @@ private:
static Trigger* bash(PlayerbotAI* botAI) { return new BashInterruptSpellTrigger(botAI); }
static Trigger* faerie_fire_feral(PlayerbotAI* botAI) { return new FaerieFireFeralTrigger(botAI); }
static Trigger* insect_swarm(PlayerbotAI* botAI) { return new InsectSwarmTrigger(botAI); }
static Trigger* insect_swarm_on_attacker(PlayerbotAI* botAI) { return new InsectSwarmOnAttackerTrigger(botAI); }
static Trigger* moonfire(PlayerbotAI* botAI) { return new MoonfireTrigger(botAI); }
static Trigger* moonfire_on_attacker(PlayerbotAI* botAI) { return new MoonfireOnAttackerTrigger(botAI); }
static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); }
static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); }
static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); }
static Trigger* berserk(PlayerbotAI* botAI) { return new BerserkTrigger(botAI); }
static Trigger* berserk_active(PlayerbotAI* botAI) { return new BerserkActiveTrigger(botAI); }
static Trigger* savage_roar(PlayerbotAI* botAI) { return new SavageRoarTrigger(botAI); }
static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); }
static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); }
@ -165,28 +148,14 @@ private:
static Trigger* cat_form(PlayerbotAI* botAI) { return new CatFormTrigger(botAI); }
static Trigger* tree_form(PlayerbotAI* botAI) { return new TreeFormTrigger(botAI); }
static Trigger* bash_on_enemy_healer(PlayerbotAI* botAI) { return new BashInterruptEnemyHealerSpellTrigger(botAI); }
static Trigger* omen_of_clarity(PlayerbotAI* botAI) { return new OmenOfClarityTrigger(botAI); }
static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); }
static Trigger* mangle_bear_trigger(PlayerbotAI* botAI) { return new MangleBearTrigger(botAI); }
static Trigger* lacerate_trigger(PlayerbotAI* botAI) { return new LacerateTrigger(botAI); }
static Trigger* demoralize_roar(PlayerbotAI* botAI) { return new DemoralizeRoarTrigger(botAI); }
static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); }
static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); }
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); }
static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); }
static Trigger* ferocious_bite_execute(PlayerbotAI* ai) { return new FerociousBiteExecuteTrigger(ai); }
static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); }
static Trigger* no_healer_dps_strategy(PlayerbotAI* ai) { return new NoHealerDpsStrategyTrigger(ai); }
static Trigger* starfall(PlayerbotAI* ai) { return new StarfallTrigger(ai); }
static Trigger* force_of_nature(PlayerbotAI* ai) { return new ForceOfNatureTrigger(ai); }
static Trigger* cyclone(PlayerbotAI* ai) { return new CycloneTrigger(ai); }
static Trigger* predators_swiftness(PlayerbotAI* ai) { return new PredatorsSwiftnessTrigger(ai); }
static Trigger* predators_swiftness_and_cyclone(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "cyclone"); }
static Trigger* predators_swiftness_and_hibernate(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "hibernate"); }
static Trigger* predators_swiftness_and_entangling_roots(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "entangling roots"); }
static Trigger* predators_swiftness_and_combat_party_member_dead(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "combat party member dead"); }
static Trigger* clearcasting_and_medium_aoe(PlayerbotAI* ai) { return new TwoTriggers(ai, "clearcasting", "medium aoe"); }
static Trigger* prowl_trigger(PlayerbotAI* ai) { return new ProwlTrigger(ai); }
static Trigger* rejuvenation_blanket(PlayerbotAI* ai) { return new BuffOnPartyTrigger(ai, "rejuvenation"); }
static Trigger* wild_growth_blanket(PlayerbotAI* ai) { return new BuffOnPartyTrigger(ai, "wild growth"); }
static Trigger* aquatic_form(PlayerbotAI* ai) { return new AquaticFormTrigger(ai); }
};
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
@ -224,8 +193,8 @@ public:
creators["hibernate"] = &DruidAiObjectContextInternal::hibernate;
creators["entangling roots"] = &DruidAiObjectContextInternal::entangling_roots;
creators["entangling roots on cc"] = &DruidAiObjectContextInternal::entangling_roots_on_cc;
creators["hibernate"] = &DruidAiObjectContextInternal::hibernate;
creators["hibernate on cc"] = &DruidAiObjectContextInternal::hibernate_on_cc;
creators["cyclone on cc"] = &DruidAiObjectContextInternal::cyclone_on_cc;
creators["wrath"] = &DruidAiObjectContextInternal::wrath;
creators["starfall"] = &DruidAiObjectContextInternal::starfall;
creators["insect swarm"] = &DruidAiObjectContextInternal::insect_swarm;
@ -236,9 +205,9 @@ public:
creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat;
creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat;
creators["rake"] = &DruidAiObjectContextInternal::rake;
creators["rake on attacker"] = &DruidAiObjectContextInternal::rake_on_attacker;
creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite;
creators["rip"] = &DruidAiObjectContextInternal::rip;
creators["maim"] = &DruidAiObjectContextInternal::maim;
creators["cower"] = &DruidAiObjectContextInternal::cower;
creators["survival instincts"] = &DruidAiObjectContextInternal::survival_instincts;
creators["frenzied regeneration"] = &DruidAiObjectContextInternal::frenzied_regeneration;
@ -268,9 +237,9 @@ public:
creators["lacerate"] = &DruidAiObjectContextInternal::lacerate;
creators["hurricane"] = &DruidAiObjectContextInternal::hurricane;
creators["innervate"] = &DruidAiObjectContextInternal::innervate;
creators["innervate on healer"] = &DruidAiObjectContextInternal::innervate_on_healer;
creators["tranquility"] = &DruidAiObjectContextInternal::tranquility;
creators["bash on enemy healer"] = &DruidAiObjectContextInternal::bash_on_enemy_healer;
creators["omen of clarity"] = &DruidAiObjectContextInternal::omen_of_clarity;
creators["nature's swiftness"] = &DruidAiObjectContextInternal::natures_swiftness;
creators["prowl"] = &DruidAiObjectContextInternal::prowl;
creators["dash"] = &DruidAiObjectContextInternal::dash;
@ -285,13 +254,11 @@ public:
creators["moonfire on attacker"] = &DruidAiObjectContextInternal::moonfire_on_attacker;
creators["enrage"] = &DruidAiObjectContextInternal::enrage;
creators["force of nature"] = &DruidAiObjectContextInternal::force_of_nature;
creators["typhoon"] = &DruidAiObjectContextInternal::typhoon;
creators["rejuvenation blanket"] = &DruidAiObjectContextInternal::rejuvenation_blanket;
creators["wild growth blanket"] = &DruidAiObjectContextInternal::wild_growth_blanket;
}
private:
static Action* natures_swiftness(PlayerbotAI* botAI) { return new CastNaturesSwiftnessAction(botAI); }
static Action* omen_of_clarity(PlayerbotAI* botAI) { return new CastOmenOfClarityAction(botAI); }
static Action* tranquility(PlayerbotAI* botAI) { return new CastTranquilityAction(botAI); }
static Action* feral_charge_bear(PlayerbotAI* botAI) { return new CastFeralChargeBearAction(botAI); }
static Action* feral_charge_cat(PlayerbotAI* botAI) { return new CastFeralChargeCatAction(botAI); }
@ -324,7 +291,6 @@ private:
static Action* entangling_roots(PlayerbotAI* botAI) { return new CastEntanglingRootsAction(botAI); }
static Action* hibernate_on_cc(PlayerbotAI* botAI) { return new CastHibernateCcAction(botAI); }
static Action* entangling_roots_on_cc(PlayerbotAI* botAI) { return new CastEntanglingRootsCcAction(botAI); }
static Action* cyclone_on_cc(PlayerbotAI* botAI) { return new CastCycloneCcAction(botAI); }
static Action* wrath(PlayerbotAI* botAI) { return new CastWrathAction(botAI); }
static Action* starfall(PlayerbotAI* botAI) { return new CastStarfallAction(botAI); }
static Action* insect_swarm(PlayerbotAI* botAI) { return new CastInsectSwarmAction(botAI); }
@ -335,9 +301,9 @@ private:
static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); }
static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); }
static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); }
static Action* rake_on_attacker(PlayerbotAI* botAI) { return new CastRakeOnMeleeAttackersAction(botAI); }
static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); }
static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); }
static Action* maim(PlayerbotAI* botAI) { return new CastMaimAction(botAI); }
static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); }
static Action* survival_instincts(PlayerbotAI* botAI) { return new CastSurvivalInstinctsAction(botAI); }
static Action* frenzied_regeneration(PlayerbotAI* botAI) { return new CastFrenziedRegenerationAction(botAI); }
@ -367,7 +333,6 @@ private:
static Action* lacerate(PlayerbotAI* botAI) { return new CastLacerateAction(botAI); }
static Action* hurricane(PlayerbotAI* botAI) { return new CastHurricaneAction(botAI); }
static Action* innervate(PlayerbotAI* botAI) { return new CastInnervateAction(botAI); }
static Action* innervate_on_healer(PlayerbotAI* botAI) { return new CastInnervateOnHealerAction(botAI); }
static Action* bash_on_enemy_healer(PlayerbotAI* botAI) { return new CastBashOnEnemyHealerAction(botAI); }
static Action* ravage(PlayerbotAI* botAI) { return new CastRavageAction(botAI); }
static Action* pounce(PlayerbotAI* botAI) { return new CastPounceAction(botAI); }
@ -382,9 +347,6 @@ private:
static Action* moonfire_on_attacker(PlayerbotAI* ai) { return new CastMoonfireOnAttackerAction(ai); }
static Action* enrage(PlayerbotAI* ai) { return new CastEnrageAction(ai); }
static Action* force_of_nature(PlayerbotAI* ai) { return new CastForceOfNatureAction(ai); }
static Action* typhoon(PlayerbotAI* ai) { return new CastTyphoonAction(ai); }
static Action* rejuvenation_blanket(PlayerbotAI* ai) { return new CastRejuvenationBlanketAction(ai); }
static Action* wild_growth_blanket(PlayerbotAI* ai) { return new CastWildGrowthBlanketAction(ai); }
};
SharedNamedObjectContextList<Strategy> DruidAiObjectContext::sharedStrategyContexts;
@ -424,22 +386,7 @@ void DruidAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextLi
triggerContexts.Add(new DruidTriggerFactoryInternal());
}
class DruidValueContextInternal : public NamedObjectContext<UntypedValue>
{
public:
DruidValueContextInternal()
{
creators["eclipse solar proc time"] = &DruidValueContextInternal::eclipse_solar_proc_time;
creators["eclipse lunar proc time"] = &DruidValueContextInternal::eclipse_lunar_proc_time;
}
private:
static UntypedValue* eclipse_solar_proc_time(PlayerbotAI* botAI) { return new EclipseSolarProcTimeValue(botAI); }
static UntypedValue* eclipse_lunar_proc_time(PlayerbotAI* botAI) { return new EclipseLunarProcTimeValue(botAI); }
};
void DruidAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
AiObjectContext::BuildSharedValueContexts(valueContexts);
valueContexts.Add(new DruidValueContextInternal());
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BALANCEDRUIDSTRATEGY_H
#define _PLAYERBOT_BALANCEDRUIDSTRATEGY_H
#include "GenericDruidStrategy.h"
class PlayerbotAI;
class BalanceDruidStrategy : public GenericDruidStrategy
{
public:
BalanceDruidStrategy(PlayerbotAI* botAI);
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "balance"; }
std::vector<NextAction> getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; }
};
#endif

View File

@ -3,17 +3,19 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "BearDruidStrategy.h"
#include "BearTankDruidStrategy.h"
#include "Playerbots.h"
class BearDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
BearDruidStrategyActionNodeFactory()
BearTankDruidStrategyActionNodeFactory()
{
creators["melee"] = &melee;
creators["feral charge - bear"] = &feral_charge_bear;
creators["swipe (bear)"] = &swipe_bear;
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["bear form"] = &bear_form;
creators["dire bear form"] = &dire_bear_form;
creators["mangle (bear)"] = &mangle_bear;
@ -26,6 +28,16 @@ public:
}
private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* feral_charge_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
@ -46,6 +58,16 @@ private:
);
}
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ { NextAction("feral charge - bear") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
@ -137,81 +159,99 @@ private:
}
};
BearDruidStrategy::BearDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
BearTankDruidStrategy::BearTankDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
{
actionNodeFactories.Add(new BearDruidStrategyActionNodeFactory());
actionNodeFactories.Add(new BearTankDruidStrategyActionNodeFactory());
}
std::vector<NextAction> BearDruidStrategy::getDefaultActions()
std::vector<NextAction> BearTankDruidStrategy::getDefaultActions()
{
return {
NextAction("maul", 5.2f),
NextAction("enrage", 5.1f),
NextAction("melee", 5.0f)
NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f),
NextAction("lacerate", ACTION_DEFAULT + 0.3f),
NextAction("maul", ACTION_DEFAULT + 0.2f),
NextAction("enrage", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void BearDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void BearTankDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"bear form",
{ NextAction("dire bear form", 28.0f) }
"enemy out of melee",
{
NextAction("feral charge - bear", ACTION_NORMAL + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"medium health",
{ NextAction("frenzied regeneration", 27.0f) }
"bear form",
{
NextAction("dire bear form", ACTION_HIGH + 8)
}
)
);
triggers.push_back(new TriggerNode(
"mangle (bear)", { NextAction("mangle (bear)", 17.5f) }
));
triggers.push_back(new TriggerNode(
"faerie fire (feral)", { NextAction("faerie fire (feral)", 17.0f) }
));
triggers.push_back(new TriggerNode(
"lacerate", { NextAction("lacerate", 16.0f) }
));
triggers.push_back(new TriggerNode(
"demoralizing roar", { NextAction("demoralizing roar", 15.5f) }
));
triggers.push_back(new TriggerNode("high aoe", { NextAction("challenging roar", 26.5f) }));
triggers.push_back(new TriggerNode("lose aggro",
triggers.push_back(
new TriggerNode(
"low health",
{
NextAction("growl", 26.0f),
NextAction("faerie fire (feral)", 25.5f)
NextAction("frenzied regeneration", ACTION_HIGH + 7)
}
));
triggers.push_back(new TriggerNode("berserk active", { NextAction("mangle (bear)", 25.0f) }));
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)",
{
NextAction("faerie fire (feral)", ACTION_HIGH + 7)
}
)
);
triggers.push_back(new TriggerNode("high aoe", {NextAction("challenging roar", ACTION_HIGH + 8)}));
triggers.push_back(
new TriggerNode(
"lose aggro",
{
NextAction("growl", ACTION_HIGH + 8)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("demoralizing roar", 24.5f),
NextAction("swipe (bear)", 24.0f)
NextAction("demoralizing roar", ACTION_HIGH + 6),
NextAction("swipe (bear)", ACTION_HIGH + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{ NextAction("swipe (bear)", 24.0f) }
{
NextAction("swipe (bear)", ACTION_HIGH + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"bash",
{ NextAction("bash", 42.0f) }
{
NextAction("bash", ACTION_INTERRUPT + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"bash on enemy healer",
{ NextAction("bash on enemy healer", 41.0f) }
{
NextAction("bash on enemy healer", ACTION_INTERRUPT + 1)
}
)
);
}

View File

@ -3,17 +3,17 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BEARDRUIDSTRATEGY_H
#define _PLAYERBOT_BEARDRUIDSTRATEGY_H
#ifndef _PLAYERBOT_BEARTANKDRUIDSTRATEGY_H
#define _PLAYERBOT_BEARTANKDRUIDSTRATEGY_H
#include "FeralDruidStrategy.h"
class PlayerbotAI;
class BearDruidStrategy : public FeralDruidStrategy
class BearTankDruidStrategy : public FeralDruidStrategy
{
public:
BearDruidStrategy(PlayerbotAI* botAI);
BearTankDruidStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "bear"; }

View File

@ -3,15 +3,15 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "BalanceDruidStrategy.h"
#include "CasterDruidStrategy.h"
#include "AiObjectContext.h"
#include "FeralDruidStrategy.h"
class BalanceDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
BalanceDruidStrategyActionNodeFactory()
CasterDruidStrategyActionNodeFactory()
{
creators["faerie fire"] = &faerie_fire;
creators["hibernate"] = &hibernate;
@ -23,10 +23,6 @@ public:
creators["moonfire"] = &moonfire;
creators["starfire"] = &starfire;
creators["moonkin form"] = &moonkin_form;
creators["typhoon"] = &typhoon;
creators["hurricane"] = &hurricane;
creators["force of nature"] = &force_of_nature;
creators["cyclone on cc"] = &cyclone_on_cc;
}
private:
@ -129,76 +125,130 @@ private:
/*C*/ {}
);
}
static ActionNode* typhoon([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"typhoon",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* hurricane([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"hurricane",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* force_of_nature([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"force of nature",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* cyclone_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cyclone on cc",
/*P*/ { NextAction("moonkin form") },
/*A*/ {},
/*C*/ {}
);
}
};
BalanceDruidStrategy::BalanceDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
{
actionNodeFactories.Add(new BalanceDruidStrategyActionNodeFactory());
actionNodeFactories.Add(new CasterDruidStrategyActionNodeFactory());
actionNodeFactories.Add(new ShapeshiftDruidStrategyActionNodeFactory());
}
std::vector<NextAction> BalanceDruidStrategy::getDefaultActions()
std::vector<NextAction> CasterDruidStrategy::getDefaultActions()
{
return {
NextAction("starfire", 5.4f),
NextAction("wrath", 5.3f),
NextAction("starfall", ACTION_HIGH + 1.0f),
NextAction("force of nature", ACTION_DEFAULT + 1.0f),
NextAction("wrath", ACTION_DEFAULT + 0.1f),
};
}
void BalanceDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void CasterDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
// Debuffs and DoTs
triggers.push_back(new TriggerNode("faerie fire", { NextAction("faerie fire", 29.5f) }));
triggers.push_back(new TriggerNode("insect swarm", { NextAction("insect swarm", 18.0f) }));
triggers.push_back(new TriggerNode("moonfire", { NextAction("moonfire", 17.5f) }));
// Eclipse procs
triggers.push_back(new TriggerNode("eclipse (solar)", { NextAction("wrath", 20.0f) }));
triggers.push_back(new TriggerNode("eclipse (lunar)", { NextAction("starfire", 20.0f) }));
// Utility/Defensive
triggers.push_back(new TriggerNode("medium mana", { NextAction("innervate", 29.0f) }));
triggers.push_back(new TriggerNode("enemy too close for spell", { NextAction("flee", 39.0f) }));
triggers.push_back(
new TriggerNode(
"eclipse (lunar) cooldown",
{
NextAction("starfire", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (solar) cooldown",
{
NextAction("wrath", ACTION_DEFAULT + 0.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"insect swarm",
{
NextAction("insect swarm", ACTION_NORMAL + 5)
}
)
);
triggers.push_back(
new TriggerNode(
"moonfire",
{
NextAction("moonfire", ACTION_NORMAL + 4)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (solar)",
{
NextAction("wrath", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"eclipse (lunar)",
{
NextAction("starfire", ACTION_NORMAL + 6)
}
)
);
triggers.push_back(
new TriggerNode(
"medium mana",
{
NextAction("innervate", ACTION_HIGH + 9)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy too close for spell",
{
NextAction("flee", ACTION_MOVE + 9)
}
)
);
}
void CasterDruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"hurricane channel check",
{
NextAction("cancel channel", ACTION_HIGH + 2)
}
)
);
triggers.push_back(
new TriggerNode(
"medium aoe",
{
NextAction("hurricane", ACTION_HIGH + 1)
}
)
);
triggers.push_back(
new TriggerNode(
"light aoe",
{
NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
NextAction("moonfire on attacker", ACTION_NORMAL + 3)
}
)
);
}
void CasterDruidDebuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"faerie fire",
{
NextAction("faerie fire", ACTION_HIGH)
}
)
);
}

View File

@ -0,0 +1,45 @@
/*
* 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_CASTERDRUIDSTRATEGY_H
#define _PLAYERBOT_CASTERDRUIDSTRATEGY_H
#include "GenericDruidStrategy.h"
class PlayerbotAI;
class CasterDruidStrategy : public GenericDruidStrategy
{
public:
CasterDruidStrategy(PlayerbotAI* botAI);
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "caster"; }
std::vector<NextAction> getDefaultActions() override;
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; }
};
class CasterDruidAoeStrategy : public CombatStrategy
{
public:
CasterDruidAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "caster aoe"; }
};
class CasterDruidDebuffStrategy : public CombatStrategy
{
public:
CasterDruidDebuffStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "caster debuff"; }
};
#endif

View File

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

View File

@ -3,17 +3,17 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CATDRUIDSTRATEGY_H
#define _PLAYERBOT_CATDRUIDSTRATEGY_H
#ifndef _PLAYERBOT_CATDPSDRUIDSTRATEGY_H
#define _PLAYERBOT_CATDPSDRUIDSTRATEGY_H
#include "FeralDruidStrategy.h"
class PlayerbotAI;
class CatDruidStrategy : public FeralDruidStrategy
class CatDpsDruidStrategy : public FeralDruidStrategy
{
public:
CatDruidStrategy(PlayerbotAI* botAI);
CatDpsDruidStrategy(PlayerbotAI* botAI);
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
@ -22,16 +22,14 @@ public:
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; }
};
// Optional additive strategy. Layers emergency heals on top of the "cat" strategy.
// Enable : co +offheal
// Disable: co -offheal
class CatOffhealStrategy : public CombatStrategy
class CatAoeDruidStrategy : public CombatStrategy
{
public:
CatOffhealStrategy(PlayerbotAI* botAI);
CatAoeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "offheal"; }
std::string const getName() override { return "cat aoe"; }
};
#endif

View File

@ -1,446 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "CatDruidStrategy.h"
#include "AiObjectContext.h"
class CatDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
CatDruidStrategyActionNodeFactory()
{
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["melee"] = &melee;
creators["feral charge - cat"] = &feral_charge_cat;
creators["cat form"] = &cat_form;
creators["claw"] = &claw;
creators["mangle (cat)"] = &mangle_cat;
creators["rake"] = &rake;
creators["ferocious bite"] = &ferocious_bite;
creators["rip"] = &rip;
creators["pounce"] = &pounce;
creators["ravage"] = &ravage;
creators["prowl"] = &prowl;
}
private:
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"feral charge - cat",
/*P*/ {},
/*A*/ { NextAction("reach melee") },
/*C*/ {}
);
}
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cat form",
/*P*/ { NextAction("caster form") },
/*A*/ { NextAction("bear form") },
/*C*/ {}
);
}
static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"claw",
/*P*/ {},
/*A*/ { NextAction("melee") },
/*C*/ {}
);
}
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ravage",
/*P*/ {},
/*A*/ { NextAction("pounce") },
/*C*/ {}
);
}
static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"pounce",
/*P*/ {},
/*A*/ { NextAction("shred") },
/*C*/ {}
);
}
static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"prowl",
/*P*/ { NextAction("cat form") },
/*A*/ {},
/*C*/ {}
);
}
};
CatDruidStrategy::CatDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
{
actionNodeFactories.Add(new CatDruidStrategyActionNodeFactory());
}
std::vector<NextAction> CatDruidStrategy::getDefaultActions()
{
return {
NextAction("melee", ACTION_DEFAULT)
};
}
void CatDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
FeralDruidStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode(
"healer low mana", {
NextAction("innervate on healer", 35.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"prowl", {
NextAction("prowl", 29.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"enemy out of melee", {
NextAction("dash", 28.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"cat form", {
NextAction("cat form", 28.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"low energy", {
NextAction("tiger's fury", 27.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"savage roar", {
NextAction("savage roar", 26.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points 5 available", {
NextAction("rip", 23.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points 5 available", {
NextAction("maim", 23.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"ferocious bite execute", {
NextAction("ferocious bite", 24.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"clearcasting", {
NextAction("shred", 24.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"ferocious bite time", {
NextAction("ferocious bite", 22.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"mangle (cat)", {
NextAction("mangle (cat)", 22.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"rake", {
NextAction("rake", 21.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"medium threat", {
NextAction("cower", 21.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available", {
NextAction("ravage", 5.6f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full", {
NextAction("ravage", 5.6f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available", {
NextAction("pounce", 5.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full", {
NextAction("pounce", 5.5f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available", {
NextAction("shred", 5.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full", {
NextAction("shred", 5.4f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available", {
NextAction("mangle (cat)", 5.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy", {
NextAction("mangle (cat)", 5.3f)
}
)
);
triggers.push_back(
new TriggerNode(
"almost full energy available", {
NextAction("claw", 5.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"combo points not full and high energy", {
NextAction("claw", 5.2f)
}
)
);
triggers.push_back(
new TriggerNode(
"faerie fire (feral)", {
NextAction("faerie fire (feral)", 5.0f)
}
)
);
}
// ============================================================
// CatOffhealStrategy
// Additive overlay — only the healing triggers. Designed to be
// stacked on top of "cat" so the bot stays in cat form for DPS
// but shifts out to heal when the party needs it.
// ============================================================
class CatOffhealStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
CatOffhealStrategyActionNodeFactory()
{
creators["healing touch on party"] = &healing_touch_on_party;
creators["regrowth on party"] = &regrowth_on_party;
creators["rejuvenation on party"] = &rejuvenation_on_party;
}
private:
// P: shift to caster form before casting C: shift back to cat form afterwards
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"healing touch on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"regrowth on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rejuvenation on party",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ { NextAction("cat form") }
);
}
};
CatOffhealStrategy::CatOffhealStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
{
actionNodeFactories.Add(new CatOffhealStrategyActionNodeFactory());
}
void CatOffhealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"party member critical health",
{
NextAction("regrowth on party", 36.0f),
NextAction("healing touch on party", 35.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"party member low health",
{
NextAction("healing touch on party", 25.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"party member medium health",
{
NextAction("rejuvenation on party", 18.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"party member to heal out of spell range",
{
NextAction("reach party member to heal", 93.0f)
}
)
);
triggers.push_back(
new TriggerNode(
"low mana",
{
NextAction("innervate", 24.0f)
}
)
);
}

View File

@ -14,10 +14,12 @@ public:
{
creators["survival instincts"] = &survival_instincts;
creators["thorns"] = &thorns;
creators["omen of clarity"] = &omen_of_clarity;
creators["cure poison"] = &cure_poison;
creators["cure poison on party"] = &cure_poison_on_party;
creators["abolish poison"] = &abolish_poison;
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["prowl"] = &prowl;
}
private:
@ -37,6 +39,14 @@ private:
/*C*/ {});
}
static ActionNode* omen_of_clarity([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("omen of clarity",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison",
@ -69,6 +79,13 @@ private:
/*C*/ {});
}
static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("prowl",
/*P*/ { NextAction("cat form") },
/*A*/ {},
/*C*/ {});
}
};
FeralDruidStrategy::FeralDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
@ -82,23 +99,15 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
GenericDruidStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"enemy out of melee", { NextAction("reach melee", 21.0f) }));
"enemy out of melee", { NextAction("reach melee", ACTION_HIGH + 1) }));
triggers.push_back(new TriggerNode(
"low health", { NextAction("survival instincts", 91.0f) }));
"critical health", { NextAction("survival instincts", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode(
"omen of clarity", { NextAction("omen of clarity", ACTION_HIGH + 9) }));
triggers.push_back(new TriggerNode("player has flag",
{ NextAction("dash", 92.0f) }));
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
triggers.push_back(new TriggerNode("enemy flagcarrier near",
{ NextAction("dash", 92.0f) }));
}
void FeralChargeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
Player* bot = botAI->GetBot();
if (bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE))
triggers.push_back(new TriggerNode(
"enemy out of melee", { NextAction("feral charge - cat", 29.0f) }));
else
triggers.push_back(new TriggerNode(
"enemy out of melee", { NextAction("feral charge - bear", 18.0f) }));
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
triggers.push_back(
new TriggerNode("berserk", { NextAction("berserk", ACTION_HIGH + 6) }));
}

View File

@ -3,14 +3,11 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FERALDRUIDSTRATEGY_H
#define _PLAYERBOT_FERALDRUIDSTRATEGY_H
#ifndef _PLAYERBOT_FERALRUIDSTRATEGY_H
#define _PLAYERBOT_FERALRUIDSTRATEGY_H
#include "GenericDruidStrategy.h"
constexpr uint32 SPELL_CAT_FORM = 768;
constexpr uint32 AURA_THICK_HIDE = 16931;
class PlayerbotAI;
class ShapeshiftDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
@ -86,18 +83,4 @@ public:
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; }
};
// Optional strategy — enabled by default for cat and bear.
// Registers the "enemy out of melee" → Feral Charge trigger, spec-gated at
// init time so cats get Feral Charge (Cat) and bears get Feral Charge (Bear).
// Disable with: co -feral charge
// Re-enable with: co +feral charge
class FeralChargeDruidStrategy : public CombatStrategy
{
public:
FeralChargeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "feral charge"; }
};
#endif

View File

@ -17,13 +17,12 @@ public:
creators["thorns on party"] = &thorns_on_party;
creators["mark of the wild"] = &mark_of_the_wild;
creators["mark of the wild on party"] = &mark_of_the_wild_on_party;
// creators["innervate"] = &innervate;
creators["regrowth_on_party"] = &regrowth_on_party;
creators["rejuvenation on party"] = &rejuvenation_on_party;
creators["remove curse on party"] = &remove_curse_on_party;
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["revive"] = &revive;
creators["prowl"] = &prowl;
creators["aquatic form"] = &aquatic_form;
}
private:
@ -93,23 +92,6 @@ private:
/*A*/ {},
/*C*/ {});
}
static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("prowl",
/*P*/ { NextAction("cat form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* aquatic_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aquatic form",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
};
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
@ -183,16 +165,11 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
NextAction("remove curse on party", ACTION_DISPEL + 7),
}));
triggers.push_back(new TriggerNode("aquatic form", { NextAction("aquatic form", 10.0f) }));
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
if (specTab == DRUID_TAB_BALANCE || specTab == DRUID_TAB_RESTORATION)
if (specTab == 0 || specTab == 2) // Balance or Restoration
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
if (specTab == DRUID_TAB_FERAL)
{
if (specTab == 1) // Feral
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
triggers.push_back(new TriggerNode("prowl", { NextAction("prowl", ACTION_INTERRUPT) }));
}
}

View File

@ -5,8 +5,6 @@
#include "GenericDruidStrategy.h"
#include "AiFactory.h"
#include "FeralDruidStrategy.h"
#include "Playerbots.h"
class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
@ -22,8 +20,6 @@ public:
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["rebirth"] = &rebirth;
creators["entangling roots on cc"] = &entangling_roots_on_cc;
creators["cyclone on cc"] = &cyclone_on_cc;
creators["hibernate on cc"] = &hibernate_on_cc;
creators["innervate"] = &innervate;
}
@ -92,22 +88,6 @@ private:
/*C*/ {});
}
static ActionNode* cyclone_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cyclone on cc",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* hibernate_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("hibernate on cc",
/*P*/ { NextAction("caster form") },
/*A*/ {},
/*C*/ {});
}
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("innervate",
@ -127,95 +107,41 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
CombatStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("almost full health", { NextAction("barkskin", 40.0f) }));
new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) }));
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == DRUID_TAB_FERAL)
{
if (!bot->HasAura(16931) /*thick hide — bear spec*/)
{
triggers.push_back(new TriggerNode("predator's swiftness and combat party member dead",
{ NextAction("rebirth", 29.0f) }));
triggers.push_back(new TriggerNode("combat party member dead",
{ NextAction("rebirth", 28.5f) }));
}
}
else
{
triggers.push_back(new TriggerNode("combat party member dead",
{ NextAction("rebirth", 29.0f) }));
}
{ NextAction("rebirth", ACTION_HIGH + 9) }));
triggers.push_back(new TriggerNode("being attacked",
{ NextAction("nature's grasp", 39.0f) }));
{ NextAction("nature's grasp", ACTION_HIGH + 1) }));
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
}
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("party member cure poison",
{ NextAction("abolish poison on party", 51.0f) }));
{ NextAction("abolish poison on party", ACTION_DISPEL + 1) }));
triggers.push_back(
new TriggerNode("party member remove curse",
{ NextAction("remove curse on party", 57.0f) }));
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
}
void DruidBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == DRUID_TAB_BALANCE)
{
triggers.push_back(new TriggerNode("force of nature", { NextAction("force of nature", 29.0f) }));
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
}
if (tab == DRUID_TAB_FERAL)
{
triggers.push_back(new TriggerNode("berserk", { NextAction("berserk", 27.5f) }));
}
triggers.push_back(new TriggerNode(
"nature's swiftness", { NextAction("nature's swiftness", ACTION_HIGH + 9) }));
}
void DruidCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == DRUID_TAB_BALANCE || tab == DRUID_TAB_RESTORATION)
{
triggers.push_back(new TriggerNode(
"cyclone", { NextAction("cyclone on cc", 42.0f) }));
"entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) }));
triggers.push_back(new TriggerNode(
"hibernate", { NextAction("hibernate on cc", 41.0f) }));
"entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) }));
triggers.push_back(new TriggerNode(
"entangling roots", { NextAction("entangling roots on cc", 40.0f) }));
}
if (tab == DRUID_TAB_FERAL)
{
if (bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE))
{
triggers.push_back(new TriggerNode(
"predator's swiftness and cyclone", { NextAction("cyclone on cc", 42.0f) }));
triggers.push_back(new TriggerNode(
"predator's swiftness and hibernate", { NextAction("hibernate on cc", 41.0f) }));
triggers.push_back(new TriggerNode(
"predator's swiftness and entangling roots", { NextAction("entangling roots on cc", 40.0f) }));
}
else
{
triggers.push_back(new TriggerNode(
"cyclone", { NextAction("cyclone on cc", 42.0f) }));
triggers.push_back(new TriggerNode(
"hibernate", { NextAction("hibernate on cc", 41.0f) }));
triggers.push_back(new TriggerNode(
"entangling roots", { NextAction("entangling roots on cc", 40.0f) }));
}
}
"hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) }));
}
void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@ -223,39 +149,10 @@ void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("healer should attack",
{
NextAction("cancel tree form", 5.4f),
NextAction("moonfire", 5.3f),
NextAction("wrath", 5.2f),
NextAction("starfire", 5.1f),
}));
}
void DruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == DRUID_TAB_BALANCE)
{
triggers.push_back(new TriggerNode("hurricane channel check", { NextAction("cancel channel", 22.0f) }));
triggers.push_back(new TriggerNode("starfall", { NextAction("starfall", 28.5f) }));
triggers.push_back(new TriggerNode("medium aoe", { NextAction("hurricane", 23.0f) }));
triggers.push_back(new TriggerNode("enemy within melee", { NextAction("typhoon", 40.0f) }));
triggers.push_back(new TriggerNode("insect swarm on attacker", { NextAction("insect swarm on attacker", 5.2f) }));
triggers.push_back(new TriggerNode("moonfire on attacker", { NextAction("moonfire on attacker", 5.1f) }));
}
if (tab == DRUID_TAB_RESTORATION)
{
triggers.push_back(new TriggerNode("hurricane channel check", { NextAction("cancel channel", 22.0f) }));
triggers.push_back(new TriggerNode("medium aoe", { NextAction("hurricane", 23.0f) }));
triggers.push_back(new TriggerNode("insect swarm on attacker", { NextAction("insect swarm on attacker", 5.2f) }));
triggers.push_back(new TriggerNode("moonfire on attacker", { NextAction("moonfire on attacker", 5.1f) }));
}
if (tab == DRUID_TAB_FERAL && bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE))
{
triggers.push_back(new TriggerNode("clearcasting and medium aoe", { NextAction("swipe (cat)", 25.5f) }));
triggers.push_back(new TriggerNode("medium aoe", { NextAction("swipe (cat)", 25.0f) }));
}
NextAction("cancel tree form", ACTION_DEFAULT + 0.4f),
NextAction("moonfire", ACTION_DEFAULT + 0.3f),
NextAction("wrath", ACTION_DEFAULT + 0.2f),
NextAction("starfire", ACTION_DEFAULT + 0.1f),
}));
}

View File

@ -55,13 +55,4 @@ public:
std::string const getName() override { return "healer dps"; }
};
class DruidAoeStrategy : public Strategy
{
public:
DruidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "aoe"; }
};
#endif

View File

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

View File

@ -0,0 +1,24 @@
/*
* 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_HEALDRUIDSTRATEGY_H
#define _PLAYERBOT_HEALDRUIDSTRATEGY_H
#include "GenericDruidStrategy.h"
#include "Strategy.h"
class PlayerbotAI;
class HealDruidStrategy : public GenericDruidStrategy
{
public:
HealDruidStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "heal"; }
uint32 GetType() const override { return STRATEGY_TYPE_RANGED | STRATEGY_TYPE_HEAL; }
};
#endif

View File

@ -0,0 +1,32 @@
/*
* 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 "MeleeDruidStrategy.h"
#include "Playerbots.h"
MeleeDruidStrategy::MeleeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
std::vector<NextAction> MeleeDruidStrategy::getDefaultActions()
{
return {
NextAction("faerie fire", ACTION_DEFAULT + 0.1f),
NextAction("melee", ACTION_DEFAULT)
};
}
void MeleeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"omen of clarity",
{
NextAction("omen of clarity", ACTION_HIGH + 9)
}
)
);
CombatStrategy::InitTriggers(triggers);
}

View File

@ -0,0 +1,21 @@
/*
* 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_MELEEDRUIDSTRATEGY_H
#define _PLAYERBOT_MELEEDRUIDSTRATEGY_H
#include "CombatStrategy.h"
class MeleeDruidStrategy : public CombatStrategy
{
public:
MeleeDruidStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "melee"; }
std::vector<NextAction> getDefaultActions() override;
};
#endif

View File

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

View File

@ -0,0 +1,27 @@
/*
* 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_OFFHEALDRUIDCATSTRATEGY_H
#define _PLAYERBOT_OFFHEALDRUIDCATSTRATEGY_H
#include "FeralDruidStrategy.h"
class PlayerbotAI;
class OffhealDruidCatStrategy : public FeralDruidStrategy
{
public:
OffhealDruidCatStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "offheal"; }
std::vector<NextAction> getDefaultActions() override;
uint32 GetType() const override
{
return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE;
}
};
#endif

View File

@ -1,120 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RestoDruidStrategy.h"
#include "Playerbots.h"
class RestoDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
RestoDruidStrategyActionNodeFactory() {
creators["nourish on party"] = &nourish_on_party;
}
private:
static ActionNode* nourish_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("nourish on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
};
RestoDruidStrategy::RestoDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
{
actionNodeFactories.Add(new RestoDruidStrategyActionNodeFactory());
}
void RestoDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDruidStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("no healer dps strategy",
{ NextAction("tree form", 5.0f) }));
triggers.push_back(new TriggerNode(
"party member to heal out of spell range",
{ NextAction("reach party member to heal", 39.0f) }));
triggers.push_back(
new TriggerNode("party member critical health",
{
NextAction("tree form", 34.1f),
NextAction("swiftmend on party", 34.0f),
NextAction("wild growth on party", 33.0f),
NextAction("nourish on party", 32.0f),
NextAction("regrowth on party", 31.0f),
NextAction("healing touch on party", 30.0f),
}));
triggers.push_back(
new TriggerNode("party member critical health",
{ NextAction("nature's swiftness", 58.0f) }));
triggers.push_back(new TriggerNode(
"nature's swiftness active",
{ NextAction("healing touch on party", 55.0f) }));
triggers.push_back(new TriggerNode("clearcasting",
{ NextAction("lifebloom on main tank", 13.0f) }));
// LOW
triggers.push_back(
new TriggerNode("party member low health",
{
NextAction("tree form", 21.5f),
NextAction("swiftmend on party", 21.4f),
NextAction("wild growth on party", 21.3f),
NextAction("nourish on party", 21.2f),
NextAction("regrowth on party", 21.1f),
NextAction("healing touch on party", 21.0f),
}));
// MEDIUM
triggers.push_back(
new TriggerNode("party member medium health",
{
NextAction("tree form", 20.5f),
NextAction("swiftmend on party", 20.4f),
NextAction("wild growth on party", 20.3f),
NextAction("nourish on party", 20.2f),
NextAction("regrowth on party", 20.1f),
NextAction("healing touch on party", 20.0f),
}));
// ALMOST FULL
triggers.push_back(
new TriggerNode("party member almost full health",
{
NextAction("wild growth on party", 10.3f),
NextAction("rejuvenation on party", 10.2f),
NextAction("regrowth on party", 10.1f),
}));
triggers.push_back(
new TriggerNode("medium mana", { NextAction("innervate", 25.0f) }));
triggers.push_back(new TriggerNode("enemy too close for spell",
{ NextAction("flee", 39.0f) }));
}
void DruidTranquilityStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("medium group heal setting",
{ NextAction("tree form", 30.6f), NextAction("tranquility", 30.5f) }));
}
void DruidBlanketStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"wild growth blanket",
{ NextAction("tree form", 8.1f), NextAction("wild growth blanket", 8.0f) }));
triggers.push_back(new TriggerNode(
"rejuvenation blanket",
{ NextAction("tree form", 6.1f), NextAction("rejuvenation blanket", 6.0f) }));
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RESTODRUIDSTRATEGY_H
#define _PLAYERBOT_RESTODRUIDSTRATEGY_H
#include "GenericDruidStrategy.h"
#include "Strategy.h"
class PlayerbotAI;
class RestoDruidStrategy : public GenericDruidStrategy
{
public:
RestoDruidStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "resto"; }
uint32 GetType() const override { return STRATEGY_TYPE_RANGED | STRATEGY_TYPE_HEAL; }
};
class DruidBlanketStrategy : public Strategy
{
public:
DruidBlanketStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "blanketing"; }
};
class DruidTranquilityStrategy : public Strategy
{
public:
DruidTranquilityStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "tranquility"; }
};
#endif

View File

@ -4,10 +4,8 @@
*/
#include "DruidTriggers.h"
#include "DynamicObject.h"
#include "Player.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool MarkOfTheWildOnPartyTrigger::IsActive()
{
@ -37,45 +35,6 @@ bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); }
bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); }
bool AquaticFormTrigger::IsActive()
{
return !bot->IsInCombat() && !botAI->HasAura("aquatic form", bot) &&
bot->GetLiquidData().Status == LIQUID_MAP_UNDER_WATER;
}
bool ProwlTrigger::IsActive()
{
if (botAI->HasAura("prowl", bot) || bot->IsInCombat())
return false;
uint32 prowlId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "prowl")->Get();
if (!prowlId || !bot->HasSpell(prowlId) || bot->HasSpellCooldown(prowlId))
return false;
float distance = 30.f;
Unit* target = AI_VALUE(Unit*, "enemy player target");
if (target && !target->IsInWorld())
return false;
if (!target)
target = AI_VALUE(Unit*, "grind target");
if (!target)
target = AI_VALUE(Unit*, "dps target");
if (!target)
return false;
if (target && target->GetVictim())
distance -= 10;
if (target->isMoving() && target->GetVictim())
distance -= 10;
if (bot->InBattleground())
distance += 15;
if (bot->InArena())
distance += 15;
return target && ServerFacade::instance().GetDistance2d(bot, target) < distance;
}
const std::set<uint32> HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = {
16914, // Hurricane Rank 1
17401, // Hurricane Rank 2
@ -86,38 +45,19 @@ const std::set<uint32> HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = {
bool HurricaneChannelCheckTrigger::IsActive()
{
Player* bot = botAI->GetBot();
// Check if the bot is channeling a spell
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
if (!HURRICANE_SPELL_IDS.count(spell->m_spellInfo->Id))
return false;
// Find this bot's own Hurricane DynamicObject
DynamicObject* dynObj = nullptr;
for (uint32 spellId : HURRICANE_SPELL_IDS)
// Only trigger if the spell being channeled is Hurricane
if (HURRICANE_SPELL_IDS.count(spell->m_spellInfo->Id))
{
dynObj = bot->GetDynObject(spellId);
if (dynObj)
break;
}
if (!dynObj)
return false;
// Count attackers actually inside the Hurricane AoE
float radius = dynObj->GetRadius();
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
uint32 count = 0;
for (ObjectGuid const& guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
if (unit->GetDistance(dynObj->GetPosition()) <= radius)
count++;
}
return count < minEnemies;
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
return attackerCount < minEnemies;
}
}
// Not channeling Hurricane
return false;
}

View File

@ -8,7 +8,6 @@
#include "CureTriggers.h"
#include "GenericTriggers.h"
#include "RtiTriggers.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
@ -16,8 +15,6 @@
#include "Trigger.h"
#include <set>
constexpr uint32 AURA_OMEN_OF_CLARITY = 16864;
class PlayerbotAI;
class MarkOfTheWildOnPartyTrigger : public BuffOnPartyTrigger
@ -58,30 +55,22 @@ public:
bool IsActive() override;
};
class OmenOfClarityTrigger : public BuffTrigger
{
public:
OmenOfClarityTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "omen of clarity") {}
};
class ClearcastingTrigger : public HasAuraTrigger
{
public:
ClearcastingTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "clearcasting") {}
};
class PredatorsSwiftnessTrigger : public HasAuraTrigger
{
public:
PredatorsSwiftnessTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "predator's swiftness") {}
};
class NaturesSwiftnessActiveTrigger : public HasAuraTrigger
{
public:
NaturesSwiftnessActiveTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "nature's swiftness") {}
bool IsActive() override { return botAI->HasAura("nature's swiftness", bot); }
};
class RakeTrigger : public DebuffTrigger
{
public:
RakeTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "rake", 1, true) {}
bool IsActive() override { return !botAI->HasAura("prowl", bot) && DebuffTrigger::IsActive(); }
};
class InsectSwarmTrigger : public DebuffTrigger
@ -90,26 +79,12 @@ public:
InsectSwarmTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "insect swarm", 1, true) {}
};
class InsectSwarmOnAttackerTrigger : public DebuffOnAttackerTrigger
{
public:
InsectSwarmOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "insect swarm", true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class MoonfireTrigger : public DebuffTrigger
{
public:
MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {}
};
class MoonfireOnAttackerTrigger : public DebuffOnAttackerTrigger
{
public:
MoonfireOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "moonfire", true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class FaerieFireTrigger : public DebuffTrigger
{
public:
@ -120,35 +95,6 @@ class FaerieFireFeralTrigger : public DebuffTrigger
{
public:
FaerieFireFeralTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "faerie fire (feral)") {}
bool IsActive() override
{
if (!bot->IsInCombat())
return false;
// Bear: every cast generates immediate threat/damage for free — spam it
if (botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr))
{
Unit* target = GetTarget();
return target && target->IsAlive() && target->IsInWorld();
}
if (!botAI->HasAura("cat form", bot))
return false;
if (botAI->HasAura("prowl", bot))
return false;
// Cat with Omen of Clarity: spam to fish for Clearcasting procs
if (bot->HasAura(AURA_OMEN_OF_CLARITY))
{
Unit* target = GetTarget();
return target && target->IsAlive() && target->IsInWorld();
}
// Cat without Omen of Clarity: apply as a normal debuff, don't reapply
return DebuffTrigger::IsActive();
}
};
class BashInterruptSpellTrigger : public InterruptSpellTrigger
@ -157,18 +103,18 @@ public:
BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {}
};
class TigersFuryTrigger : public BuffTrigger
{
public:
TigersFuryTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "tiger's fury") {}
};
class BerserkTrigger : public BoostTrigger
{
public:
BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {}
};
class BerserkActiveTrigger : public HasAuraTrigger
{
public:
BerserkActiveTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "berserk") {}
};
class SavageRoarTrigger : public BuffTrigger
{
public:
@ -181,10 +127,10 @@ public:
NaturesGraspTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "nature's grasp") {}
};
class EntanglingRootsTrigger : public RtiCcTrigger
class EntanglingRootsTrigger : public HasCcTargetTrigger
{
public:
EntanglingRootsTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "entangling roots") {}
EntanglingRootsTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "entangling roots") {}
};
class EntanglingRootsKiteTrigger : public DebuffTrigger
@ -195,16 +141,10 @@ public:
bool IsActive() override;
};
class HibernateTrigger : public RtiCcTrigger
class HibernateTrigger : public HasCcTargetTrigger
{
public:
HibernateTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "hibernate") {}
};
class CycloneTrigger : public RtiCcTrigger
{
public:
CycloneTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "cyclone") {}
HibernateTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "hibernate") {}
};
class CurePoisonTrigger : public NeedCureTrigger
@ -245,14 +185,6 @@ public:
bool IsActive() override;
};
class AquaticFormTrigger : public Trigger
{
public:
AquaticFormTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aquatic form") {}
bool IsActive() override;
};
class EclipseSolarTrigger : public HasAuraTrigger
{
public:
@ -286,69 +218,18 @@ public:
}
};
class StarfallTrigger : public SpellNoCooldownTrigger
class EclipseSolarCooldownTrigger : public SpellCooldownTrigger
{
public:
StarfallTrigger(PlayerbotAI* botAI) : SpellNoCooldownTrigger(botAI, "starfall") {}
EclipseSolarCooldownTrigger(PlayerbotAI* ai) : SpellCooldownTrigger(ai, "eclipse (solar)") {}
bool IsActive() override { return bot->HasSpellCooldown(48517); }
};
class ForceOfNatureTrigger : public BoostTrigger
class EclipseLunarCooldownTrigger : public SpellCooldownTrigger
{
public:
ForceOfNatureTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "force of nature") {}
};
class MangleBearTrigger : public DebuffTrigger
{
public:
MangleBearTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "mangle (bear)") {}
bool IsActive() override
{
if (!bot->IsInCombat() || !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr))
return false;
Unit* target = GetTarget();
return target && target->IsAlive() && target->IsInWorld();
}
};
class LacerateTrigger : public DebuffTrigger
{
public:
LacerateTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "lacerate") {}
bool IsActive() override
{
if (!bot->IsInCombat() || !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr))
return false;
Unit* target = GetTarget();
if (!target || !target->IsAlive() || !target->IsInWorld())
return false;
Aura* lacerate = botAI->GetAura("lacerate", target, false, false);
if (!lacerate)
return true;
if (lacerate->GetStackAmount() < 5)
return true;
return lacerate->GetDuration() <= 6000;
}
};
class DemoralizeRoarTrigger : public DebuffTrigger
{
public:
DemoralizeRoarTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "demoralizing roar") {}
bool IsActive() override
{
return DebuffTrigger::IsActive()
&& !botAI->HasAura("curse of weakness", GetTarget(), false, false)
&& !botAI->HasAura("demoralizing shout", GetTarget(), false, false)
&& !botAI->HasAura("vindication", GetTarget(), false, false);
}
EclipseLunarCooldownTrigger(PlayerbotAI* ai) : SpellCooldownTrigger(ai, "eclipse (lunar)") {}
bool IsActive() override { return bot->HasSpellCooldown(48518); }
};
class MangleCatTrigger : public DebuffTrigger
@ -357,8 +238,6 @@ public:
MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {}
bool IsActive() override
{
if (botAI->HasAura("prowl", bot))
return false;
return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true)
&& !botAI->HasAura("trauma", GetTarget(), false, false, -1, true);
}
@ -392,36 +271,10 @@ public:
}
};
class FerociousBiteExecuteTrigger : public Trigger
{
public:
FerociousBiteExecuteTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite execute") {}
bool IsActive() override
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target || !target->IsAlive())
return false;
if (!AI_VALUE2(uint32, "spell id", "ferocious bite"))
return false;
if (AI_VALUE2(uint8, "combo", "current target") < 1)
return false;
if (target->GetHealthPct() >= 25.0f)
return false;
if (target->GetHealth() >= 20000)
return false;
return true;
}
};
class HurricaneChannelCheckTrigger : public Trigger
{
public:
HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 3)
HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
: Trigger(botAI, "hurricane channel check"), minEnemies(minEnemies)
{
}
@ -444,12 +297,4 @@ public:
}
};
class ProwlTrigger : public Trigger
{
public:
ProwlTrigger(PlayerbotAI* botAI) : Trigger(botAI, "prowl") {}
bool IsActive() override;
};
#endif

View File

@ -11,21 +11,7 @@
#include "ServerFacade.h"
#include "SharedDefines.h"
std::vector<NextAction> CastMoltenArmorAction::getAlternatives()
{
if (!AI_VALUE2(uint32, "spell id", "molten armor"))
return NextAction::merge({ NextAction("mage armor") }, CastBuffSpellAction::getAlternatives());
return CastBuffSpellAction::getAlternatives();
}
std::vector<NextAction> CastMageArmorAction::getAlternatives()
{
if (!AI_VALUE2(uint32, "spell id", "mage armor"))
return NextAction::merge({ NextAction("ice armor") }, CastBuffSpellAction::getAlternatives());
return CastBuffSpellAction::getAlternatives();
}
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
bool UseManaSapphireAction::isUseful()
{
@ -66,23 +52,22 @@ bool UseManaAgateAction::isUseful()
bool CastFrostNovaAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target || !target->IsInWorld() || target->isFrozen() ||
(target->ToCreature() &&
target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1))))
{
if (!target || !target->IsInWorld())
return false;
}
return ServerFacade::instance().IsDistanceLessOrEqualThan(
AI_VALUE2(float, "distance", GetTargetName()), 10.f);
if (target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1)))
return false;
if (target->isFrozen())
return false;
return ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f);
}
bool CastConeOfColdAction::isUseful()
{
bool facingTarget = AI_VALUE2(bool, "facing", "current target");
bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan(
AI_VALUE2(float, "distance", GetTargetName()), 10.f);
bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f);
return facingTarget && targetClose;
}
@ -91,7 +76,6 @@ bool CastDragonsBreathAction::isUseful()
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
bool facingTarget = AI_VALUE2(bool, "facing", "current target");
bool targetClose = bot->IsWithinCombatRange(target, 10.0f);
return facingTarget && targetClose;
@ -102,7 +86,6 @@ bool CastBlastWaveAction::isUseful()
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
bool targetClose = bot->IsWithinCombatRange(target, 10.0f);
return targetClose;
}
@ -119,11 +102,14 @@ Unit* CastFocusMagicOnPartyAction::GetTarget()
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || member == bot || !member->IsAlive() || member->GetMap() != bot->GetMap() ||
bot->GetDistance(member) > sPlayerbotAIConfig.spellDistance || member->HasAura(54646)) // Focus Magic
{
if (!member || member == bot || !member->IsAlive())
continue;
if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig.spellDistance)
continue;
if (member->HasAura(54646))
continue;
}
if (member->getClass() == CLASS_MAGE)
return member;
@ -152,7 +138,7 @@ bool CastBlinkBackAction::Execute(Event event)
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
// can cast spell check passed in isUseful()
bot->SetOrientation(bot->GetAngle(target) + M_PI);
return CastSpellAction::Execute(event);
}

View File

@ -18,14 +18,12 @@ class CastMoltenArmorAction : public CastBuffSpellAction
{
public:
CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {}
std::vector<NextAction> getAlternatives() override;
};
class CastMageArmorAction : public CastBuffSpellAction
{
public:
CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {}
std::vector<NextAction> getAlternatives() override;
};
class CastIceArmorAction : public CastBuffSpellAction
@ -62,8 +60,7 @@ public:
class CastSummonWaterElementalAction : public CastBuffSpellAction
{
public:
CastSummonWaterElementalAction(PlayerbotAI* botAI)
: CastBuffSpellAction(botAI, "summon water elemental") {}
CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {}
};
// Boost Actions
@ -218,10 +215,11 @@ public:
// CC, Interrupt, and Dispel Actions
class CastPolymorphAction : public CastCrowdControlSpellAction
class CastPolymorphAction : public CastBuffSpellAction
{
public:
CastPolymorphAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "polymorph") {}
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
Value<Unit*>* GetTargetValue() override;
};
class CastSpellstealAction : public CastSpellAction
@ -239,8 +237,7 @@ public:
class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
{
public:
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI)
: CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
};
class CastFrostNovaAction : public CastSpellAction
@ -279,7 +276,9 @@ class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction
{
public:
CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI)
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE) {}
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE)
{
}
};
// Damage and Debuff Actions
@ -333,6 +332,7 @@ public:
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
@ -343,6 +343,7 @@ public:
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};

View File

@ -89,7 +89,6 @@ public:
creators["arcane intellect"] = &MageTriggerFactoryInternal::arcane_intellect;
creators["arcane intellect on party"] = &MageTriggerFactoryInternal::arcane_intellect_on_party;
creators["mage armor"] = &MageTriggerFactoryInternal::mage_armor;
creators["molten armor"] = &MageTriggerFactoryInternal::molten_armor;
creators["remove curse"] = &MageTriggerFactoryInternal::remove_curse;
creators["remove curse on party"] = &MageTriggerFactoryInternal::remove_curse_on_party;
creators["counterspell"] = &MageTriggerFactoryInternal::counterspell;
@ -144,7 +143,6 @@ private:
static Trigger* arcane_intellect(PlayerbotAI* botAI) { return new ArcaneIntellectTrigger(botAI); }
static Trigger* arcane_intellect_on_party(PlayerbotAI* botAI) { return new ArcaneIntellectOnPartyTrigger(botAI); }
static Trigger* mage_armor(PlayerbotAI* botAI) { return new MageArmorTrigger(botAI); }
static Trigger* molten_armor(PlayerbotAI* botAI) { return new MoltenArmorTrigger(botAI); }
static Trigger* remove_curse(PlayerbotAI* botAI) { return new RemoveCurseTrigger(botAI); }
static Trigger* remove_curse_on_party(PlayerbotAI* botAI) { return new PartyMemberRemoveCurseTrigger(botAI); }
static Trigger* counterspell(PlayerbotAI* botAI) { return new CounterspellInterruptSpellTrigger(botAI); }

View File

@ -13,18 +13,22 @@ public:
ArcaneMageStrategyActionNodeFactory()
{
creators["arcane blast"] = &arcane_blast;
creators["arcane barrage"] = &arcane_barrage;
creators["arcane missiles"] = &arcane_missiles;
creators["fire blast"] = &fire_blast;
creators["frostbolt"] = &frostbolt;
creators["arcane power"] = &arcane_power;
creators["icy veins"] = &icy_veins;
}
private:
// Arcane Barrage is the alternate for Arcane Blast (cast while moving, or
// when Arcane Blast is unavailable - e.g. not yet learned at low levels).
static ActionNode* arcane_blast([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("arcane blast",
/*P*/ {},
/*A*/ { NextAction("arcane barrage") },
/*C*/ {});
}
static ActionNode* arcane_blast(PlayerbotAI*) { return new ActionNode("arcane blast", {}, {}, {}); }
static ActionNode* arcane_barrage(PlayerbotAI*) { return new ActionNode("arcane barrage", {}, {}, {}); }
static ActionNode* arcane_missiles(PlayerbotAI*) { return new ActionNode("arcane missiles", {}, {}, {}); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); }
static ActionNode* arcane_power(PlayerbotAI*) { return new ActionNode("arcane power", {}, {}, {}); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); }
};
// ===== Single Target Strategy =====

View File

@ -7,9 +7,35 @@
#include "Playerbots.h"
#include "Strategy.h"
// ===== Action Node Factory =====
class FireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
FireMageStrategyActionNodeFactory()
{
creators["fireball"] = &fireball;
creators["frostbolt"] = &frostbolt;
creators["fire blast"] = &fire_blast;
creators["pyroblast"] = &pyroblast;
creators["scorch"] = &scorch;
creators["living bomb"] = &living_bomb;
creators["combustion"] = &combustion;
}
private:
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", {}, {}, {}); }
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", {}, {}, {}); }
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", {}, {}, {}); }
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", {}, {}, {}); }
};
// ===== Single Target Strategy =====
FireMageStrategy::FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
// No custom ActionNodeFactory needed
actionNodeFactories.Add(new FireMageStrategyActionNodeFactory());
}
// ===== Default Actions =====

View File

@ -28,5 +28,4 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "firestarter"; }
};
#endif

View File

@ -6,9 +6,35 @@
#include "FrostFireMageStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class FrostFireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
FrostFireMageStrategyActionNodeFactory()
{
creators["frostfire bolt"] = &frostfire_bolt;
creators["fire blast"] = &fire_blast;
creators["pyroblast"] = &pyroblast;
creators["combustion"] = &combustion;
creators["icy veins"] = &icy_veins;
creators["scorch"] = &scorch;
creators["living bomb"] = &living_bomb;
}
private:
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, {}, {}); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", {}, {}, {}); }
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", {}, {}, {}); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); }
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", {}, {}, {}); }
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", {}, {}, {}); }
};
// ===== Single Target Strategy =====
FrostFireMageStrategy::FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
// No custom ActionNodeFactory needed
actionNodeFactories.Add(new FrostFireMageStrategyActionNodeFactory());
}
// ===== Default Actions =====

View File

@ -4,11 +4,44 @@
*/
#include "FrostMageStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
FrostMageStrategyActionNodeFactory()
{
creators["cold snap"] = &cold_snap;
creators["ice barrier"] = &ice_barrier;
creators["summon water elemental"] = &summon_water_elemental;
creators["deep freeze"] = &deep_freeze;
creators["icy veins"] = &icy_veins;
creators["frostbolt"] = &frostbolt;
creators["ice lance"] = &ice_lance;
creators["fire blast"] = &fire_blast;
creators["fireball"] = &fireball;
creators["frostfire bolt"] = &frostfire_bolt;
}
private:
static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", {}, {}, {}); }
static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", {}, {}, {}); }
static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", {}, {}, {}); }
static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", {}, {}, {}); }
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); }
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); }
static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", {}, {}, {}); }
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); }
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, { NextAction("fireball") }, {}); }
};
// ===== Single Target Strategy =====
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
{
// No custom ActionNodeFactory needed
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
}
// ===== Default Actions =====

View File

@ -12,10 +12,28 @@ class GenericMageNonCombatStrategyActionNodeFactory : public NamedObjectFactory<
public:
GenericMageNonCombatStrategyActionNodeFactory()
{
creators["molten armor"] = &molten_armor;
creators["mage armor"] = &mage_armor;
creators["ice armor"] = &ice_armor;
}
private:
static ActionNode* molten_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("molten armor",
/*P*/ {},
/*A*/ { NextAction("mage armor") },
/*C*/ {});
}
static ActionNode* mage_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("mage armor",
/*P*/ {},
/*A*/ { NextAction("ice armor") },
/*C*/ {});
}
static ActionNode* ice_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("ice armor",
@ -47,7 +65,7 @@ void MageBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void MageBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("molten armor", { NextAction("molten armor", 19.0f) }));
triggers.push_back(new TriggerNode("mage armor", { NextAction("molten armor", 19.0f) }));
}
void MageBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@ -15,8 +15,16 @@ public:
{
creators["frostbolt"] = &frostbolt;
creators["frostfire bolt"] = &frostfire_bolt;
creators["ice lance"] = &ice_lance;
creators["fire blast"] = &fire_blast;
creators["scorch"] = &scorch;
creators["frost nova"] = &frost_nova;
creators["cone of cold"] = &cone_of_cold;
creators["icy veins"] = &icy_veins;
creators["combustion"] = &combustion;
creators["evocation"] = &evocation;
creators["dragon's breath"] = &dragons_breath;
creators["blast wave"] = &blast_wave;
creators["remove curse"] = &remove_curse;
creators["remove curse on party"] = &remove_curse_on_party;
creators["fireball"] = &fireball;
@ -39,6 +47,22 @@ private:
/*C*/ {});
}
static ActionNode* ice_lance([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("ice lance",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* fire_blast([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("fire blast",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* scorch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("scorch",
@ -47,6 +71,38 @@ private:
/*C*/ {});
}
static ActionNode* frost_nova([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("frost nova",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cone_of_cold([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cone of cold",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* icy_veins([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icy veins",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* combustion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("combustion",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* evocation([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("evocation",
@ -55,6 +111,22 @@ private:
/*C*/ {});
}
static ActionNode* dragons_breath([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("dragon's breath",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blast_wave([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("blast wave",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("remove curse",
@ -134,13 +206,13 @@ void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == MAGE_TAB_ARCANE)
if (tab == 0) // Arcane
{
triggers.push_back(new TriggerNode("arcane power", { NextAction("arcane power", 29.0f) }));
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 28.5f) }));
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 28.0f) }));
}
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1)
{
if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/)
{ // Frostfire
@ -154,7 +226,7 @@ void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 17.5f) }));
}
}
else if (tab == MAGE_TAB_FROST) // Frost
else if (tab == 2) // Frost
{
triggers.push_back(new TriggerNode("cold snap", { NextAction("cold snap", 28.0f) }));
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 27.5f) }));
@ -182,14 +254,15 @@ void MageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
Player* bot = botAI->GetBot();
int tab = AiFactory::GetPlayerSpecTab(bot);
if (tab == MAGE_TAB_ARCANE)
if (tab == 0) // Arcane
{
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) }));
triggers.push_back(new TriggerNode("medium aoe", {
NextAction("flamestrike", 23.0f),
NextAction("blizzard", 22.0f) }));
triggers.push_back(new TriggerNode("light aoe", { NextAction("arcane explosion", 21.0f) }));
}
else if (tab == MAGE_TAB_FIRE)
else if (tab == 1) // Fire and Frostfire
{
triggers.push_back(
new TriggerNode("medium aoe", {
@ -202,7 +275,7 @@ void MageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("firestarter", { NextAction("flamestrike", 40.0f) }));
triggers.push_back(new TriggerNode("living bomb on attackers", { NextAction("living bomb on attackers", 21.0f) }));
}
else if (tab == MAGE_TAB_FROST)
else if (tab == 2) // Frost
{
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) }));
triggers.push_back(new TriggerNode("medium aoe", {

View File

@ -4,6 +4,7 @@
*/
#include "MageTriggers.h"
#include "MageActions.h"
#include "Playerbots.h"
#include "Player.h"
#include "Spell.h"
@ -22,7 +23,7 @@ bool NoManaGemTrigger::IsActive()
5513, // Mana Jade
5514 // Mana Agate
};
Player* bot = botAI->GetBot();
for (uint32 gemId : gemIds)
{
if (bot->GetItemCount(gemId, false) > 0) // false = only in bags
@ -44,35 +45,17 @@ bool ArcaneIntellectTrigger::IsActive()
bool MageArmorTrigger::IsActive()
{
Unit* target = GetTarget();
if (botAI->HasAura("mage armor", target))
return false;
if (AI_VALUE2(uint32, "spell id", "mage armor"))
return true;
return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
!botAI->HasAura("molten armor", target);
}
bool MoltenArmorTrigger::IsActive()
{
Unit* target = GetTarget();
if (botAI->HasAura("molten armor", target))
return false;
if (AI_VALUE2(uint32, "spell id", "molten armor"))
return true;
return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) &&
!botAI->HasAura("mage armor", target);
!botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target);
}
bool FrostNovaOnTargetTrigger::IsActive()
{
Unit* target = GetTarget();
if (!target || !target->IsAlive() || !target->IsInWorld())
{
return false;
}
return botAI->HasAura(spell, target);
}
@ -80,14 +63,15 @@ bool FrostbiteOnTargetTrigger::IsActive()
{
Unit* target = GetTarget();
if (!target || !target->IsAlive() || !target->IsInWorld())
{
return false;
}
return botAI->HasAura(spell, target);
}
bool NoFocusMagicTrigger::IsActive()
{
if (!bot->HasSpell(54646)) // Focus Magic
if (!bot->HasSpell(54646))
return false;
Group* group = bot->GetGroup();
@ -108,19 +92,24 @@ bool NoFocusMagicTrigger::IsActive()
bool DeepFreezeCooldownTrigger::IsActive()
{
Player* bot = botAI->GetBot();
static const uint32 DEEP_FREEZE_SPELL_ID = 44572;
// If the bot does NOT have Deep Freeze, treat as "on cooldown"
if (!bot->HasSpell(44572)) // Deep Freeze
if (!bot->HasSpell(DEEP_FREEZE_SPELL_ID))
return true;
// Otherwise, use the default cooldown logic
return SpellCooldownTrigger::IsActive();
}
const std::unordered_set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {
2120, 2121, 8422, 8423, 10215, 10216, 27086, 42925, 42926
};
const std::set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215,
10216, 27086, 42925, 42926};
bool FlamestrikeNearbyTrigger::IsActive()
{
Player* bot = botAI->GetBot();
for (uint32 spellId : FLAMESTRIKE_SPELL_IDS)
{
Aura* aura = bot->GetAura(spellId, bot->GetGUID());
@ -144,6 +133,7 @@ bool ImprovedScorchTrigger::IsActive()
if (!target || !target->IsAlive() || !target->IsInWorld())
return false;
// List of all spell IDs for Improved Scorch, Winter's Chill, and Shadow Mastery
static const uint32 ImprovedScorchExclusiveDebuffs[] = {// Shadow Mastery
17794, 17797, 17798, 17799, 17800,
// Winter's Chill
@ -157,10 +147,11 @@ bool ImprovedScorchTrigger::IsActive()
return false;
}
// Use default DebuffTrigger logic for the rest (only trigger if debuff is missing or expiring)
return DebuffTrigger::IsActive();
}
const std::unordered_set<uint32> BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = {
const std::set<uint32> BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = {
10, // Blizzard Rank 1
6141, // Blizzard Rank 2
8427, // Blizzard Rank 3
@ -174,12 +165,19 @@ const std::unordered_set<uint32> BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS
bool BlizzardChannelCheckTrigger::IsActive()
{
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
spell && BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id))
Player* bot = botAI->GetBot();
// Check if the bot is channeling a spell
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
// Only trigger if the spell being channeled is Blizzard
if (BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id))
{
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
return attackerCount < minEnemies;
}
}
// Not channeling Blizzard
return false;
}

View File

@ -11,15 +11,19 @@
#include "SharedDefines.h"
#include "Trigger.h"
#include "Playerbots.h"
#include "PlayerbotAI.h"
#include <set>
#include <unordered_set>
class PlayerbotAI;
// Buff and Out of Combat Triggers
class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger
{
public:
ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI)
: BuffOnPartyTrigger(botAI, "arcane intellect", 2 * 2000) {}
ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "arcane intellect", 2 * 2000) {}
bool IsActive() override;
};
@ -37,13 +41,6 @@ public:
bool IsActive() override;
};
class MoltenArmorTrigger : public BuffTrigger
{
public:
MoltenArmorTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "molten armor", 5 * 2000) {}
bool IsActive() override;
};
class NoFocusMagicTrigger : public Trigger
{
public:
@ -61,6 +58,7 @@ class NoManaGemTrigger : public Trigger
{
public:
NoManaGemTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no mana gem") {}
bool IsActive() override;
};
@ -111,8 +109,10 @@ public:
class ArcaneBlast4StacksAndMissileBarrageTrigger : public TwoTriggers
{
public:
ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* botAI)
: TwoTriggers(botAI, "arcane blast stack", "missile barrage") {}
ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* ai)
: TwoTriggers(ai, "arcane blast stack", "missile barrage")
{
}
};
class CombustionTrigger : public BoostTrigger
@ -138,7 +138,7 @@ public:
class ColdSnapTrigger : public TwoTriggers
{
public:
ColdSnapTrigger(PlayerbotAI* botAI) : TwoTriggers(botAI, "icy veins on cd", "deep freeze on cd") {}
ColdSnapTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "icy veins on cd", "deep freeze on cd") {}
};
class MirrorImageTrigger : public BoostTrigger
@ -181,8 +181,9 @@ public:
class PartyMemberRemoveCurseTrigger : public PartyMemberNeedCureTrigger
{
public:
PartyMemberRemoveCurseTrigger(PlayerbotAI* botAI)
: PartyMemberNeedCureTrigger(botAI, "remove curse", DISPEL_CURSE) {}
PartyMemberRemoveCurseTrigger(PlayerbotAI* botAI) : PartyMemberNeedCureTrigger(botAI, "remove curse", DISPEL_CURSE)
{
}
};
class SpellstealTrigger : public TargetAuraDispelTrigger
@ -215,7 +216,7 @@ public:
class LivingBombOnAttackersTrigger : public DebuffOnAttackerTrigger
{
public:
LivingBombOnAttackersTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "living bomb", true) {}
LivingBombOnAttackersTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "living bomb", true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
@ -281,13 +282,13 @@ public:
protected:
float radius;
static const std::unordered_set<uint32> FLAMESTRIKE_SPELL_IDS;
static const std::set<uint32> FLAMESTRIKE_SPELL_IDS;
};
class FlamestrikeBlizzardTrigger : public TwoTriggers
{
public:
FlamestrikeBlizzardTrigger(PlayerbotAI* botAI) : TwoTriggers(botAI, "flamestrike nearby", "medium aoe") {}
FlamestrikeBlizzardTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "flamestrike nearby", "medium aoe") {}
};
class BlizzardChannelCheckTrigger : public Trigger
@ -300,7 +301,7 @@ public:
protected:
uint32 minEnemies;
static const std::unordered_set<uint32> BLIZZARD_SPELL_IDS;
static const std::set<uint32> BLIZZARD_SPELL_IDS;
};
class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger
@ -312,8 +313,7 @@ public:
class BlastWaveOffCdTriggerAndMediumAoeTrigger : public TwoTriggers
{
public:
BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* botAI)
: TwoTriggers(botAI, "blast wave off cd", "medium aoe") {}
BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "blast wave off cd", "medium aoe") {}
};
class NoFirestarterStrategyTrigger : public Trigger

View File

@ -26,8 +26,8 @@ void GenericPaladinNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
triggers.push_back(new TriggerNode("not sensing undead", { NextAction("sense undead", ACTION_IDLE + 1.0f) }));
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
if (specTab == PALADIN_TAB_HOLY)
if (specTab == PALADIN_TAB_HOLY || specTab == PALADIN_TAB_PROTECTION)
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", ACTION_IDLE + 1.0f) }));
if (specTab == PALADIN_TAB_PROTECTION || specTab == PALADIN_TAB_RETRIBUTION)
if (specTab == PALADIN_TAB_RETRIBUTION)
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", ACTION_IDLE + 1.0f) }));
}

View File

@ -86,7 +86,6 @@ void HealPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode(
"party member almost full health",
{
NextAction("power word: shield on party", ACTION_LIGHT_HEAL + 3),
NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 2),
NextAction("renew on party", ACTION_LIGHT_HEAL + 1)
}

View File

@ -27,9 +27,6 @@ bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "s
// Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig.lowHealth; }
Value<Unit*>* CastBanishOnCcAction::GetTargetValue() { return context->GetValue<Unit*>("rti cc target"); }
Value<Unit*>* CastFearOnCcAction::GetTargetValue() { return context->GetValue<Unit*>("rti cc target"); }
// Checks if the target marked with the moon icon can be banished
bool CastBanishOnCcAction::isPossible()
{

View File

@ -170,7 +170,6 @@ class CastBanishOnCcAction : public CastCrowdControlSpellAction
{
public:
CastBanishOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "banish") {}
Value<Unit*>* GetTargetValue() override;
bool isPossible() override;
};
@ -178,7 +177,6 @@ class CastFearOnCcAction : public CastCrowdControlSpellAction
{
public:
CastFearOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "fear") {}
Value<Unit*>* GetTargetValue() override;
bool isPossible() override;
};

View File

@ -50,6 +50,22 @@ bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetB
bool OutOfSoulstoneTrigger::IsActive() { return GetSoulstoneCount(botAI->GetBot()) == 0; }
// Checks if the target marked with the moon icon can be banished
bool BanishTrigger::IsActive()
{
Unit* ccTarget = context->GetValue<Unit*>("cc target", "banish")->Get();
Unit* moonTarget = context->GetValue<Unit*>("rti cc target")->Get();
return ccTarget && moonTarget && ccTarget == moonTarget && HasCcTargetTrigger::IsActive();
}
// Checks if the target marked with the moon icon can be feared
bool FearTrigger::IsActive()
{
Unit* ccTarget = context->GetValue<Unit*>("cc target", "fear")->Get();
Unit* moonTarget = context->GetValue<Unit*>("rti cc target")->Get();
return ccTarget && moonTarget && ccTarget == moonTarget && HasCcTargetTrigger::IsActive();
}
bool DemonArmorTrigger::IsActive()
{
Unit* target = GetTarget();

View File

@ -8,7 +8,6 @@
#include "GenericTriggers.h"
#include "PlayerbotAI.h"
#include "RtiTriggers.h"
#include "Playerbots.h"
#include "CureTriggers.h"
#include "Trigger.h"
@ -138,16 +137,18 @@ public:
// CC and Pet Triggers
class BanishTrigger : public RtiCcTrigger
class BanishTrigger : public HasCcTargetTrigger
{
public:
BanishTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "banish") {}
BanishTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "banish") {}
bool IsActive() override;
};
class FearTrigger : public RtiCcTrigger
class FearTrigger : public HasCcTargetTrigger
{
public:
FearTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "fear") {}
FearTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "fear") {}
bool IsActive() override;
};
class SpellLockInterruptSpellTrigger : public InterruptSpellTrigger

File diff suppressed because it is too large Load Diff

View File

@ -1,565 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDBLACKTEMPLEACTIONS_H
#define _PLAYERBOT_RAIDBLACKTEMPLEACTIONS_H
#include "Action.h"
#include "AttackAction.h"
#include "MovementActions.h"
#include "RaidBlackTempleHelpers.h"
namespace BlackTempleHelpers
{
struct EyeBlastDangerArea;
}
// General
class BlackTempleEraseTimersAndTrackersAction : public Action
{
public:
BlackTempleEraseTimersAndTrackersAction(
PlayerbotAI* botAI) : Action(botAI, "black temple erase timers and trackers") {}
bool Execute(Event event) override;
};
// High Warlord Naj'entus
class HighWarlordNajentusMisdirectBossToMainTankAction : public AttackAction
{
public:
HighWarlordNajentusMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "high warlord naj'entus misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class HighWarlordNajentusTanksPositionBossAction : public AttackAction
{
public:
HighWarlordNajentusTanksPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "high warlord naj'entus tanks position boss") {}
bool Execute(Event event) override;
};
class HighWarlordNajentusDisperseRangedAction : public MovementAction
{
public:
HighWarlordNajentusDisperseRangedAction(
PlayerbotAI* botAI) : MovementAction(botAI, "naj'entus disperse ranged") {}
bool Execute(Event event) override;
};
class HighWarlordNajentusRemoveImpalingSpineAction : public MovementAction
{
public:
HighWarlordNajentusRemoveImpalingSpineAction(
PlayerbotAI* botAI) : MovementAction(botAI, "high warlord naj'entus remove impaling spine") {}
bool Execute(Event event) override;
};
class HighWarlordNajentusThrowImpalingSpineAction : public MovementAction
{
public:
HighWarlordNajentusThrowImpalingSpineAction(
PlayerbotAI* botAI) : MovementAction(botAI, "high warlord naj'entus throw impaling spine") {}
bool Execute(Event event) override;
};
// Supremus
class SupremusMisdirectBossToMainTankAction : public AttackAction
{
public:
SupremusMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "supremus misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class SupremusDisperseRangedAction : public MovementAction
{
public:
SupremusDisperseRangedAction(
PlayerbotAI* botAI) : MovementAction(botAI, "supremus disperse ranged") {}
bool Execute(Event event) override;
};
class SupremusKiteBossAction : public MovementAction
{
public:
SupremusKiteBossAction(
PlayerbotAI* botAI) : MovementAction(botAI, "supremus kite boss") {}
bool Execute(Event event) override;
};
class SupremusMoveAwayFromVolcanosAction : public MovementAction
{
public:
SupremusMoveAwayFromVolcanosAction(
PlayerbotAI* botAI) : MovementAction(botAI, "supremus move away from volcanos") {}
bool Execute(Event event) override;
private:
Position FindSafestNearbyPosition(
const std::vector<Unit*>& volcanos, float maxRadius, float hazardRadius);
bool IsPathSafeFromVolcanos(const Position& start,
const Position& end, const std::vector<Unit*>& volcanos, float hazardRadius);
std::vector<Unit*> GetAllSupremusVolcanos();
};
class SupremusManagePhaseTimerAction : public Action
{
public:
SupremusManagePhaseTimerAction(
PlayerbotAI* botAI) : Action(botAI, "supremus manage phase timer") {}
bool Execute(Event event) override;
};
// Shade of Akama
class ShadeOfAkamaMeleeDpsPrioritizeChannelersAction : public AttackAction
{
public:
ShadeOfAkamaMeleeDpsPrioritizeChannelersAction(
PlayerbotAI* botAI) : AttackAction(botAI, "shade of akama melee dps prioritize channelers") {}
bool Execute(Event event) override;
};
// Teron Gorefiend
class TeronGorefiendMisdirectBossToMainTankAction : public AttackAction
{
public:
TeronGorefiendMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "teron gorefiend misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class TeronGorefiendTanksPositionBossAction : public AttackAction
{
public:
TeronGorefiendTanksPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "teron gorefiend tanks position boss") {}
bool Execute(Event event) override;
};
class TeronGorefiendPositionRangedOnBalconyAction : public MovementAction
{
public:
TeronGorefiendPositionRangedOnBalconyAction(
PlayerbotAI* botAI) : MovementAction(botAI, "teron gorefiend position ranged on balcony") {}
bool Execute(Event event) override;
};
class TeronGorefiendAvoidShadowOfDeathAction : public Action
{
public:
TeronGorefiendAvoidShadowOfDeathAction(
PlayerbotAI* botAI) : Action(botAI, "teron gorefiend avoid shadow of death") {}
bool Execute(Event event) override;
};
class TeronGorefiendMoveToCornerToDieAction : public MovementAction
{
public:
TeronGorefiendMoveToCornerToDieAction(
PlayerbotAI* botAI) : MovementAction(botAI, "teron gorefiend move to corner to die") {}
bool Execute(Event event) override;
};
class TeronGorefiendControlAndDestroyShadowyConstructsAction : public MovementAction
{
public:
TeronGorefiendControlAndDestroyShadowyConstructsAction(
PlayerbotAI* botAI) : MovementAction(botAI, "teron gorefiend control and destroy shadowy constructs") {}
bool Execute(Event event) override;
};
// Gurtogg Bloodboil
class GurtoggBloodboilMisdirectBossToMainTankAction : public AttackAction
{
public:
GurtoggBloodboilMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "gurtogg bloodboil misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class GurtoggBloodboilTanksPositionBossAction : public AttackAction
{
public:
GurtoggBloodboilTanksPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "gurtogg bloodboil tanks position boss") {}
bool Execute(Event event) override;
};
class GurtoggBloodboilRotateRangedGroupsAction : public MovementAction
{
public:
GurtoggBloodboilRotateRangedGroupsAction(
PlayerbotAI* botAI) : MovementAction(botAI, "gurtogg bloodboil rotate ranged groups") {}
bool Execute(Event event) override;
};
class GurtoggBloodboilRangedMoveAwayFromEnragedPlayerAction : public MovementAction
{
public:
GurtoggBloodboilRangedMoveAwayFromEnragedPlayerAction(
PlayerbotAI* botAI) : MovementAction(botAI, "gurtogg bloodboil ranged move away from enraged player") {}
bool Execute(Event event) override;
};
class GurtoggBloodboilManagePhaseTimerAction : public Action
{
public:
GurtoggBloodboilManagePhaseTimerAction(
PlayerbotAI* botAI) : Action(botAI, "gurtogg bloodboil manage phase timer") {}
bool Execute(Event event) override;
};
// Reliquary of Souls
class ReliquaryOfSoulsMisdirectBossToMainTankAction : public AttackAction
{
public:
ReliquaryOfSoulsMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "reliquary of souls misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class ReliquaryOfSoulsAdjustDistanceFromSufferingAction : public MovementAction
{
public:
ReliquaryOfSoulsAdjustDistanceFromSufferingAction(
PlayerbotAI* botAI) : MovementAction(botAI, "reliquary of souls adjust distance from suffering") {}
bool Execute(Event event) override;
private:
bool TanksMoveToMinimumRange(Unit* suffering);
bool MeleeDpsStayAtMaximumRange(Unit* suffering);
bool RangedMoveAwayFromBoss(Unit* suffering);
};
class ReliquaryOfSoulsHealersDpsSufferingAction : public Action
{
public:
ReliquaryOfSoulsHealersDpsSufferingAction(
PlayerbotAI* botAI) : Action(botAI, "reliquary of souls healers dps suffering") {}
bool Execute(Event event) override;
};
class ReliquaryOfSoulsSpellstealRuneShieldAction : public Action
{
public:
ReliquaryOfSoulsSpellstealRuneShieldAction(
PlayerbotAI* botAI) : Action(botAI, "reliquary of souls spellsteal rune shield") {}
bool Execute(Event event) override;
};
class ReliquaryOfSoulsSpellReflectDeadenAction : public Action
{
public:
ReliquaryOfSoulsSpellReflectDeadenAction(
PlayerbotAI* botAI) : Action(botAI, "reliquary of souls spell reflect deaden") {}
bool Execute(Event event) override;
};
// Mother Shahraz
class MotherShahrazMisdirectBossToMainTankAction : public AttackAction
{
public:
MotherShahrazMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "mother shahraz misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class MotherShahrazTanksPositionBossUnderPillarAction : public AttackAction
{
public:
MotherShahrazTanksPositionBossUnderPillarAction(
PlayerbotAI* botAI) : AttackAction(botAI, "mother shahraz tanks position boss under pillar") {}
bool Execute(Event event) override;
};
class MotherShahrazMeleeDpsWaitAtSafePositionAction : public MovementAction
{
public:
MotherShahrazMeleeDpsWaitAtSafePositionAction(
PlayerbotAI* botAI) : MovementAction(botAI, "mother shahraz melee dps wait at safe position") {}
bool Execute(Event event) override;
};
class MotherShahrazPositionRangedUnderPillarAction : public MovementAction
{
public:
MotherShahrazPositionRangedUnderPillarAction(
PlayerbotAI* botAI) : MovementAction(botAI, "mother shahraz position ranged under pillar") {}
bool Execute(Event event) override;
};
class MotherShahrazRunAwayToBreakFatalAttractionAction : public MovementAction
{
public:
MotherShahrazRunAwayToBreakFatalAttractionAction(
PlayerbotAI* botAI) : MovementAction(botAI, "mother shahraz run away to break fatal attraction") {}
bool Execute(Event event) override;
private:
std::vector<Player*> GetAttractedPlayers();
};
// Illidari Council
class IllidariCouncilMisdirectBossesToTanksAction : public AttackAction
{
public:
IllidariCouncilMisdirectBossesToTanksAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council misdirect bosses to tanks") {}
bool Execute(Event event) override;
};
class IllidariCouncilMainTankPositionGathiosAction : public AttackAction
{
public:
IllidariCouncilMainTankPositionGathiosAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council main tank position gathios") {}
bool Execute(Event event) override;
};
class IllidariCouncilMainTankReflectJudgementOfCommandAction : public Action
{
public:
IllidariCouncilMainTankReflectJudgementOfCommandAction(
PlayerbotAI* botAI) : Action(botAI, "illidari council main tank reflect judgement of command") {}
bool Execute(Event event) override;
};
class IllidariCouncilFirstAssistTankFocusMalandeAction : public AttackAction
{
public:
IllidariCouncilFirstAssistTankFocusMalandeAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council first assist tank focus malande") {}
bool Execute(Event event) override;
};
class IllidariCouncilSecondAssistTankPositionDarkshadowAction : public AttackAction
{
public:
IllidariCouncilSecondAssistTankPositionDarkshadowAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council second assist tank position darkshadow") {}
bool Execute(Event event) override;
};
class IllidariCouncilMageTankPositionZerevorAction : public AttackAction
{
public:
IllidariCouncilMageTankPositionZerevorAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council mage tank position zerevor") {}
bool Execute(Event event) override;
};
class IllidariCouncilPositionMageTankHealerAction : public AttackAction
{
public:
IllidariCouncilPositionMageTankHealerAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council position mage tank healer") {}
bool Execute(Event event) override;
};
class IllidariCouncilAssignDpsTargetsAction : public AttackAction
{
public:
IllidariCouncilAssignDpsTargetsAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council assign dps targets") {}
bool Execute(Event event) override;
};
class IllidariCouncilDisperseRangedAction : public MovementAction
{
public:
IllidariCouncilDisperseRangedAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidari council disperse ranged") {}
bool Execute(Event event) override;
};
class IllidariCouncilCommandPetsToAttackGathiosAction : public AttackAction
{
public:
IllidariCouncilCommandPetsToAttackGathiosAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidari council command pets to attack gathios") {}
bool Execute(Event event) override;
};
class IllidariCouncilManageDpsTimerAction : public Action
{
public:
IllidariCouncilManageDpsTimerAction(
PlayerbotAI* botAI) : Action(botAI, "illidari council manage dps timer") {}
bool Execute(Event event) override;
};
// Illidan Stormrage <The Betrayer>
class IllidanStormrageMisdirectToTankAction : public AttackAction
{
public:
IllidanStormrageMisdirectToTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidan stormrage misdirect to tank") {}
bool Execute(Event event) override;
private:
bool TryMisdirectToFlameTanks(Group* group);
bool TryMisdirectToWarlockTank(Unit* illidan);
};
class IllidanStormrageMainTankRepositionBossAction : public AttackAction
{
public:
IllidanStormrageMainTankRepositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidan stormrage main tank reposition boss") {}
bool Execute(Event event) override;
private:
bool MoveToShadowTrap(GameObject* trap);
Position FindSafestNearbyPosition(
const std::vector<Unit*>& flameCrashes, float maxRadius, float hazardRadius);
bool IsPathSafeFromFlameCrashes(const Position& start,
const Position& end, const std::vector<Unit*>& flameCrashes, float hazardRadius);
};
class IllidanStormrageIsolateBotWithParasiteAction : public MovementAction
{
public:
IllidanStormrageIsolateBotWithParasiteAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage isolate bot with parasite") {}
bool Execute(Event event) override;
private:
bool InfectedBotMoveFromGroup(Unit* illidan, const Position& targetPos);
bool FreezeTrapShadowfiend(Player* bot, Unit* illidan, const Position& targetPos);
};
class IllidanStormrageSetEarthbindTotemAction : public Action
{
public:
IllidanStormrageSetEarthbindTotemAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage set earthbind totem") {}
bool Execute(Event event) override;
};
class IllidanStormrageAssistTanksHandleFlamesOfAzzinothAction : public AttackAction
{
public:
IllidanStormrageAssistTanksHandleFlamesOfAzzinothAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidan stormrage assist tanks handle flames of azzinoth") {}
bool Execute(Event event) override;
private:
bool RepositionToAvoidEyeBlast(Unit* illidan, const BlackTempleHelpers::EyeBlastDangerArea& dangerArea);
bool RepositionToAvoidBlaze(Unit* eastFlame, Unit* westFlame);
};
class IllidanStormrageControlPetAggressionAction : public Action
{
public:
IllidanStormrageControlPetAggressionAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage control pet aggression") {}
bool Execute(Event event) override;
};
class IllidanStormragePositionAboveGrateAction : public MovementAction
{
public:
IllidanStormragePositionAboveGrateAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage position above grate") {}
bool Execute(Event event) override;
};
class IllidanStormrageRemoveDarkBarrageAction : public Action
{
public:
IllidanStormrageRemoveDarkBarrageAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage remove dark barrage") {}
bool Execute(Event event) override;
};
class IllidanStormrageMoveAwayFromLandingPointAction : public MovementAction
{
public:
IllidanStormrageMoveAwayFromLandingPointAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage move away from landing point") {}
bool Execute(Event event) override;
};
class IllidanStormrageDisperseRangedAction : public MovementAction
{
public:
IllidanStormrageDisperseRangedAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage disperse ranged") {}
bool Execute(Event event) override;
private:
bool FanOutBehindInHumanPhase(Unit* illidan, Group* group);
bool SpreadInCircleInDemonPhase(Unit* illidan, Group* group);
};
class IllidanStormrageMeleeGoSomewhereToNotDieAction : public MovementAction
{
public:
IllidanStormrageMeleeGoSomewhereToNotDieAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage melee go somewhere to not die") {}
bool Execute(Event event) override;
};
class IllidanStormrageWarlockTankHandleDemonBossAction : public AttackAction
{
public:
IllidanStormrageWarlockTankHandleDemonBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidan stormrage warlock tank handle demon boss") {}
bool Execute(Event event) override;
};
class IllidanStormrageDpsPrioritizeAddsAction : public AttackAction
{
public:
IllidanStormrageDpsPrioritizeAddsAction(
PlayerbotAI* botAI) : AttackAction(botAI, "illidan stormrage dps prioritize adds") {}
bool Execute(Event event) override;
};
class IllidanStormrageUseShadowTrapAction : public MovementAction
{
public:
IllidanStormrageUseShadowTrapAction(
PlayerbotAI* botAI) : MovementAction(botAI, "illidan stormrage use shadow trap") {}
bool Execute(Event event) override;
};
class IllidanStormrageManageDpsTimerAndRtiAction : public Action
{
public:
IllidanStormrageManageDpsTimerAndRtiAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage manage dps timer and rti") {}
bool Execute(Event event) override;
};
class IllidanStormrageDestroyHazardsAction : public Action
{
public:
IllidanStormrageDestroyHazardsAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage destroy hazards") {}
bool Execute(Event event) override;
};
class IllidanStormrageHandleAddsCheatAction : public Action
{
public:
IllidanStormrageHandleAddsCheatAction(
PlayerbotAI* botAI) : Action(botAI, "illidan stormrage handle adds cheat") {}
bool Execute(Event event) override;
};
#endif

View File

@ -1,785 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RaidBlackTempleMultipliers.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "DruidShapeshiftActions.h"
#include "FollowActions.h"
#include "HunterActions.h"
#include "MageActions.h"
#include "PaladinActions.h"
#include "PriestActions.h"
#include "RaidBlackTempleActions.h"
#include "RaidBlackTempleHelpers.h"
#include "ReachTargetActions.h"
#include "RogueActions.h"
#include "ShamanActions.h"
#include "WarlockActions.h"
#include "WarriorActions.h"
#include "WipeAction.h"
using namespace BlackTempleHelpers;
static bool IsDpsCooldownAction(Action* action)
{
return dynamic_cast<CastHeroismAction*>(action) ||
dynamic_cast<CastBloodlustAction*>(action) ||
dynamic_cast<CastMetamorphosisAction*>(action) ||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
dynamic_cast<CastBladeFlurryAction*>(action) ||
dynamic_cast<CastIcyVeinsAction*>(action) ||
dynamic_cast<CastColdSnapAction*>(action) ||
dynamic_cast<CastArcanePowerAction*>(action) ||
dynamic_cast<CastPresenceOfMindAction*>(action) ||
dynamic_cast<CastCombustionAction*>(action) ||
dynamic_cast<CastRapidFireAction*>(action) ||
dynamic_cast<CastReadinessAction*>(action) ||
dynamic_cast<CastAvengingWrathAction*>(action) ||
dynamic_cast<CastElementalMasteryAction*>(action) ||
dynamic_cast<CastFeralSpiritAction*>(action) ||
dynamic_cast<CastFireElementalTotemAction*>(action) ||
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
dynamic_cast<CastForceOfNatureAction*>(action) ||
dynamic_cast<CastArmyOfTheDeadAction*>(action) ||
dynamic_cast<CastSummonGargoyleAction*>(action) ||
dynamic_cast<CastBerserkingAction*>(action) ||
dynamic_cast<CastBloodFuryAction*>(action);
}
// High Warlord Naj'entus
float HighWarlordNajentusDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* najentus = AI_VALUE2(Unit*, "find target", "high warlord naj'entus");
if (!najentus || najentus->GetHealthPct() < 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float HighWarlordNajentusDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "high warlord naj'entus"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
// Supremus
float SupremusDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* supremus = AI_VALUE2(Unit*, "find target", "supremus");
if (!supremus || supremus->GetHealthPct() < 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float SupremusFocusOnAvoidanceInPhase2Multiplier::GetValue(Action* action)
{
Unit* supremus = AI_VALUE2(Unit*, "find target", "supremus");
if (!supremus || supremus->GetVictim() != bot ||
!supremus->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_SNARE_SELF)))
{
return 1.0f;
}
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<SupremusKiteBossAction*>(action) &&
!dynamic_cast<SupremusMoveAwayFromVolcanosAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float SupremusHitboxIsBuggedMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_ROGUE ||
!AI_VALUE2(Unit*, "find target", "supremus"))
{
return 1.0f;
}
if (dynamic_cast<CastKillingSpreeAction*>(action))
return 0.0f;
return 1.0f;
}
// Teron Gorefiend
float TeronGorefiendDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* gorefiend = AI_VALUE2(Unit*, "find target", "teron gorefiend");
if (!gorefiend || gorefiend->GetHealthPct() < 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float TeronGorefiendControlMovementMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "teron gorefiend"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action))
{
return 0.0f;
}
if (botAI->IsRanged(bot) && dynamic_cast<ReachTargetAction*>(action))
return 0.0f;
return 1.0f;
}
float TeronGorefiendMarkedBotOnlyMoveToDieMultiplier::GetValue(Action* action)
{
Aura* aura = bot->GetAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SHADOW_OF_DEATH));
if (!aura || aura->GetDuration() >= 15000)
return 1.0f;
if (dynamic_cast<WipeAction*>(action))
return 1.0f;
else if (!dynamic_cast<TeronGorefiendMoveToCornerToDieAction*>(action))
return 0.0f;
return 1.0f;
}
float TeronGorefiendSpiritsAttackOnlyShadowyConstructsMultiplier::GetValue(Action* action)
{
if (!bot->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_SPIRITUAL_VENGEANCE)) ||
dynamic_cast<WipeAction*>(action))
{
return 1.0f;
}
if (!dynamic_cast<TeronGorefiendControlAndDestroyShadowyConstructsAction*>(action))
return 0.0f;
return 1.0f;
}
float TeronGorefiendDisableAttackingConstructsMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "teron gorefiend"))
return 1.0f;
if (bot->GetVictim() != nullptr &&
dynamic_cast<TankAssistAction*>(action))
{
return 0.0f;
}
if (!botAI->IsRangedDps(bot))
return 1.0f;
auto castSpellAction = dynamic_cast<CastSpellAction*>(action);
if (castSpellAction &&
castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
{
return 0.0f;
}
return 1.0f;
}
// Gurtogg Bloodboil
float GurtoggBloodboilDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* gurtogg = AI_VALUE2(Unit*, "find target", "gurtogg bloodboil");
if (!gurtogg || gurtogg->GetHealthPct() < 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float GurtoggBloodboilControlMovementMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "gurtogg bloodboil"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action))
{
return 0.0f;
}
if (bot->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_PLAYER_FEL_RAGE)) &&
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AttackAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
// Reliquary of Souls
float ReliquaryOfSoulsDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* suffering = AI_VALUE2(Unit*, "find target", "essence of suffering");
if (!suffering || suffering->GetHealthPct() < 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float ReliquaryOfSoulsDontWasteHealingMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "essence of suffering"))
return 1.0f;
if (dynamic_cast<CastPowerWordShieldOnAlmostFullHealthBelowAction*>(action) ||
dynamic_cast<CastPowerWordShieldOnNotFullAction*>(action) ||
dynamic_cast<CastPowerWordShieldAction*>(action) ||
dynamic_cast<CastPowerWordShieldOnPartyAction*>(action))
{
return 1.0f;
}
if (dynamic_cast<CastTreeFormAction*>(action) ||
dynamic_cast<CastHealingSpellAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
// Mother Shahraz
float MotherShahrazDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* shahraz = AI_VALUE2(Unit*, "find target", "mother shahraz");
if (!shahraz || shahraz->GetHealthPct() < 90.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float MotherShahrazControlMovementMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "mother shahraz"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float MotherShahrazBotsWithFatalAttractionOnlyRunAwayMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "mother shahraz") ||
!bot->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_FATAL_ATTRACTION)))
{
return 1.0f;
}
if (dynamic_cast<WipeAction*>(action))
return 1.0f;
if (!dynamic_cast<MotherShahrazRunAwayToBreakFatalAttractionAction*>(action))
return 0.0f;
return 1.0f;
}
// Illidari Council
float IllidariCouncilDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* gathios = AI_VALUE2(Unit*, "find target", "gathios the shatterer");
if (!gathios || gathios->GetHealthPct() < 90.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float IllidariCouncilDisableTankActionsMultiplier::GetValue(Action* action)
{
if (!botAI->IsTank(bot) ||
!AI_VALUE2(Unit*, "find target", "gathios the shatterer"))
{
return 1.0f;
}
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
return 0.0f;
if (dynamic_cast<CastTauntAction*>(action) ||
dynamic_cast<CastChallengingShoutAction*>(action) ||
dynamic_cast<CastShockwaveAction*>(action) ||
dynamic_cast<CastCleaveAction*>(action) ||
dynamic_cast<CastGrowlAction*>(action) ||
dynamic_cast<CastSwipeAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
dynamic_cast<CastRighteousDefenseAction*>(action) ||
dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastDeathAndDecayAction*>(action) ||
dynamic_cast<CastBloodBoilAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float IllidariCouncilControlMovementMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "high nethermancer zerevor"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action) &&
!dynamic_cast<TankFaceAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action))
{
return 0.0f;
}
if (botAI->IsAssistHealOfIndex(bot, 0, true) &&
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<IllidariCouncilPositionMageTankHealerAction*>(action)))
{
return 0.0f;
}
if (!botAI->IsAssistTankOfIndex(bot, 0, false) &&
dynamic_cast<TankFaceAction*>(action))
{
return 0.0f;
}
if ((botAI->IsMainTank(bot) ||
botAI->IsAssistTankOfIndex(bot, 0, false) ||
botAI->IsAssistTankOfIndex(bot, 1, false) ||
GetZerevorMageTank(bot) == bot) &&
dynamic_cast<AvoidAoeAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float IllidariCouncilControlMisdirectionMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_HUNTER ||
!AI_VALUE2(Unit*, "find target", "high nethermancer zerevor"))
{
return 1.0f;
}
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
return 0.0f;
return 1.0f;
}
float IllidariCouncilDisableIceBlockMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_MAGE ||
!AI_VALUE2(Unit*, "find target", "high nethermancer zerevor"))
{
return 1.0f;
}
if (GetZerevorMageTank(bot) != bot)
return 1.0f;
if (dynamic_cast<CastIceBlockAction*>(action))
return 0.0f;
return 1.0f;
}
float IllidariCouncilDisableArcaneShotOnZerevorMultiplier::GetValue(Action* action)
{
Unit* zerevor = AI_VALUE2(Unit*, "find target", "high nethermancer zerevor");
if (!zerevor)
return 1.0f;
Unit* target = AI_VALUE(Unit*, "current target");
if (!target || target->GetGUID() != zerevor->GetGUID())
return 1.0f;
if (dynamic_cast<CastArcaneShotAction*>(action))
return 0.0f;
return 1.0f;
}
float IllidariCouncilWaitForDpsMultiplier::GetValue(Action* action)
{
if (botAI->IsMainTank(bot) ||
botAI->IsAssistTankOfIndex(bot, 0, false) ||
botAI->IsAssistTankOfIndex(bot, 1, false) ||
GetZerevorMageTank(bot) == bot)
{
return 1.0f;
}
Unit* gathios = AI_VALUE2(Unit*, "find target", "gathios the shatterer");
if (!gathios)
return 1.0f;
if (dynamic_cast<IllidariCouncilMisdirectBossesToTanksAction*>(action))
return 1.0f;
const time_t now = std::time(nullptr);
constexpr uint8 dpsWaitSeconds = 5;
auto it = councilDpsWaitTimer.find(gathios->GetMap()->GetInstanceId());
if (it == councilDpsWaitTimer.end() || (now - it->second) >= dpsWaitSeconds)
return 1.0f;
if (dynamic_cast<AttackAction*>(action) ||
(dynamic_cast<CastSpellAction*>(action) &&
!dynamic_cast<CastHealingSpellAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
// Illidan Stormrage <The Betrayer>
float IllidanStormrageDelayDpsCooldownsMultiplier::GetValue(Action* action)
{
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan)
return 1.0f;
if (illidan->GetHealthPct() > 62.0f &&
(dynamic_cast<CastHeroismAction*>(action) ||
dynamic_cast<CastBloodlustAction*>(action)))
{
return 0.0f;
}
if (illidan->GetHealthPct() <= 62.0f || illidan->GetHealthPct() > 95.0f)
return 1.0f;
if (IsDpsCooldownAction(action) ||
(botAI->IsDps(bot) && dynamic_cast<UseTrinketAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float IllidanStormrageControlTankActionsMultiplier::GetValue(Action* action)
{
if (!botAI->IsTank(bot))
return 1.0f;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return 1.0f;
if (dynamic_cast<TankFaceAction*>(action))
return 0.0f;
if (GetIllidanPhase(illidan) != 2)
return 1.0f;
if (botAI->IsMainTank(bot))
{
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<IllidanStormragePositionAboveGrateAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<CastMeleeSpellAction*>(action) ||
dynamic_cast<CastReachTargetSpellAction*>(action))
{
return 0.0f;
}
}
else if (botAI->IsAssistTankOfIndex(bot, 0, false) ||
botAI->IsAssistTankOfIndex(bot, 1, false))
{
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<IllidanStormrageAssistTanksHandleFlamesOfAzzinothAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<CastHealingSpellAction*>(action))
return 0.0f;
}
return 1.0f;
}
float IllidanStormrageDisableDefaultTargetingMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr)
return 1.0f;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return 1.0f;
if (dynamic_cast<TankAssistAction*>(action))
return 0.0f;
int phase = GetIllidanPhase(illidan);
if (phase == 4 && dynamic_cast<DpsAssistAction*>(action))
return 0.0f;
if (botAI->IsRangedDps(bot))
{
if (phase != 2)
context->GetValue<bool>("neglect threat")->Set(true);
if (dynamic_cast<DpsAssistAction*>(action))
return 0.0f;
}
constexpr float searchRadius = 40.0f;
Unit* shadowDemon = bot->FindNearestCreature(
static_cast<uint32>(BlackTempleNpcs::NPC_SHADOW_DEMON), searchRadius);
Unit* shadowfiend = bot->FindNearestCreature(
static_cast<uint32>(BlackTempleNpcs::NPC_PARASITIC_SHADOWFIEND), searchRadius);
if (((shadowDemon && bot->GetTarget() == shadowDemon->GetGUID()) ||
(shadowfiend && bot->GetTarget() == shadowfiend->GetGUID())) &&
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float IllidanStormrageControlNonTankMovementMultiplier::GetValue(Action* action)
{
if (botAI->IsTank(bot))
return 1.0f;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
{
return 0.0f;
}
if (dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<FollowAction*>(action))
{
return 0.0f;
}
int phase = GetIllidanPhase(illidan);
if (phase == 2 &&
(dynamic_cast<SetBehindTargetAction*>(action) ||
dynamic_cast<CastKillingSpreeAction*>(action) ||
dynamic_cast<ReachTargetAction*>(action) ||
dynamic_cast<CastReachTargetSpellAction*>(action) ||
dynamic_cast<AvoidAoeAction*>(action)))
{
return 0.0f;
}
if (phase == 4 && botAI->IsHeal(bot) &&
dynamic_cast<ReachTargetAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float IllidanStormrageUseEarthbindTotemMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_SHAMAN)
return 1.0f;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || GetIllidanPhase(illidan) == 2)
return 1.0f;
if (dynamic_cast<CastStrengthOfEarthTotemAction*>(action) ||
dynamic_cast<CastStoneskinTotemAction*>(action) ||
dynamic_cast<CastStoneclawTotemAction*>(action) ||
dynamic_cast<CastTremorTotemAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float IllidanStormrageWaitForDpsMultiplier::GetValue(Action* action)
{
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan)
return 1.0f;
if (dynamic_cast<IllidanStormrageMisdirectToTankAction*>(action))
return 1.0f;
const time_t now = std::time(nullptr);
const uint32 instanceId = illidan->GetMap()->GetInstanceId();
int phase = GetIllidanPhase(illidan);
if ((phase == 1 || phase == 3 || phase == 5) &&
!botAI->IsMainTank(bot))
{
constexpr uint8 humanoidPhaseDpsWaitSeconds = 3;
auto it = illidanBossDpsWaitTimer.find(instanceId);
if ((it == illidanBossDpsWaitTimer.end() ||
(now - it->second) < humanoidPhaseDpsWaitSeconds) &&
(dynamic_cast<AttackAction*>(action) ||
(dynamic_cast<CastSpellAction*>(action) &&
!dynamic_cast<CastHealingSpellAction*>(action))))
{
return 0.0f;
}
}
if (phase == 4 && GetIllidanWarlockTank(bot) != bot)
{
constexpr uint8 demonPhaseDpsWaitSeconds = 8;
auto it = illidanBossDpsWaitTimer.find(instanceId);
if ((it == illidanBossDpsWaitTimer.end() ||
(now - it->second) < demonPhaseDpsWaitSeconds) &&
(dynamic_cast<AttackAction*>(action) ||
(dynamic_cast<CastSpellAction*>(action) &&
!dynamic_cast<CastHealingSpellAction*>(action))))
{
return 0.0f;
}
}
if (AI_VALUE2(Unit*, "find target", "flame of azzinoth") &&
!botAI->IsAssistTankOfIndex(bot, 0, true) &&
!botAI->IsAssistTankOfIndex(bot, 1, true))
{
constexpr uint8 flamePhaseDpsWaitSeconds = 6;
auto it = illidanFlameDpsWaitTimer.find(instanceId);
if ((it == illidanFlameDpsWaitTimer.end() ||
(now - it->second) < flamePhaseDpsWaitSeconds) &&
(dynamic_cast<AttackAction*>(action) ||
(dynamic_cast<CastSpellAction*>(action) &&
!dynamic_cast<CastHealingSpellAction*>(action))))
{
return 0.0f;
}
}
return 1.0f;
}

View File

@ -1,267 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDBLACKTEMPLEMULTIPLIERS_H
#define _PLAYERBOT_RAIDBLACKTEMPLEMULTIPLIERS_H
#include "Multiplier.h"
// High Warlord Naj'entus
class HighWarlordNajentusDelayDpsCooldownsMultiplier : public Multiplier
{
public:
HighWarlordNajentusDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "high warlord naj'entus delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class HighWarlordNajentusDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
HighWarlordNajentusDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "high warlord naj'entus disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
// Supremus
class SupremusDelayDpsCooldownsMultiplier : public Multiplier
{
public:
SupremusDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "supremus delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class SupremusFocusOnAvoidanceInPhase2Multiplier : public Multiplier
{
public:
SupremusFocusOnAvoidanceInPhase2Multiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "supremus focus on avoidance in phase 2 multiplier") {}
virtual float GetValue(Action* action);
};
class SupremusHitboxIsBuggedMultiplier : public Multiplier
{
public:
SupremusHitboxIsBuggedMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "supremus hitbox is bugged multiplier") {}
virtual float GetValue(Action* action);
};
// Teron Gorefiend
class TeronGorefiendDelayDpsCooldownsMultiplier : public Multiplier
{
public:
TeronGorefiendDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "teron gorefiend delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class TeronGorefiendControlMovementMultiplier : public Multiplier
{
public:
TeronGorefiendControlMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "teron gorefiend control movement multiplier") {}
virtual float GetValue(Action* action);
};
class TeronGorefiendMarkedBotOnlyMoveToDieMultiplier : public Multiplier
{
public:
TeronGorefiendMarkedBotOnlyMoveToDieMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "teron gorefiend marked bot only move to die multiplier") {}
virtual float GetValue(Action* action);
};
class TeronGorefiendSpiritsAttackOnlyShadowyConstructsMultiplier : public Multiplier
{
public:
TeronGorefiendSpiritsAttackOnlyShadowyConstructsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "teron gorefiend spirits attack only shadowy constructs multiplier") {}
virtual float GetValue(Action* action);
};
class TeronGorefiendDisableAttackingConstructsMultiplier : public Multiplier
{
public:
TeronGorefiendDisableAttackingConstructsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "teron gorefiend disable attacking constructs multiplier") {}
virtual float GetValue(Action* action);
};
// Gurtogg Bloodboil
class GurtoggBloodboilDelayDpsCooldownsMultiplier : public Multiplier
{
public:
GurtoggBloodboilDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "gurtogg bloodboil delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class GurtoggBloodboilControlMovementMultiplier : public Multiplier
{
public:
GurtoggBloodboilControlMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "gurtogg bloodboil control movement multiplier") {}
virtual float GetValue(Action* action);
};
// Reliquary of Souls
class ReliquaryOfSoulsDelayDpsCooldownsMultiplier : public Multiplier
{
public:
ReliquaryOfSoulsDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "reliquary of souls delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class ReliquaryOfSoulsDontWasteHealingMultiplier : public Multiplier
{
public:
ReliquaryOfSoulsDontWasteHealingMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "reliquary of souls don't waste healing multiplier") {}
virtual float GetValue(Action* action);
};
// Mother Shahraz
class MotherShahrazDelayDpsCooldownsMultiplier : public Multiplier
{
public:
MotherShahrazDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "mother shahraz delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class MotherShahrazControlMovementMultiplier : public Multiplier
{
public:
MotherShahrazControlMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "mother shahraz control movement multiplier") {}
virtual float GetValue(Action* action);
};
class MotherShahrazBotsWithFatalAttractionOnlyRunAwayMultiplier : public Multiplier
{
public:
MotherShahrazBotsWithFatalAttractionOnlyRunAwayMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "mother shahraz bots with fatal attraction only run away multiplier") {}
virtual float GetValue(Action* action);
};
// Illidari Council
class IllidariCouncilDelayDpsCooldownsMultiplier : public Multiplier
{
public:
IllidariCouncilDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilDisableTankActionsMultiplier : public Multiplier
{
public:
IllidariCouncilDisableTankActionsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council disable tank actions multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilControlMovementMultiplier : public Multiplier
{
public:
IllidariCouncilControlMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council control movement multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilControlMisdirectionMultiplier : public Multiplier
{
public:
IllidariCouncilControlMisdirectionMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council control misdirection multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilDisableArcaneShotOnZerevorMultiplier : public Multiplier
{
public:
IllidariCouncilDisableArcaneShotOnZerevorMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council disable arcane shot on zerevor multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilDisableIceBlockMultiplier : public Multiplier
{
public:
IllidariCouncilDisableIceBlockMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council disable ice block multiplier") {}
virtual float GetValue(Action* action);
};
class IllidariCouncilWaitForDpsMultiplier : public Multiplier
{
public:
IllidariCouncilWaitForDpsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidari council wait for dps multiplier") {}
virtual float GetValue(Action* action);
};
// Illidan Stormrage <The Betrayer>
class IllidanStormrageDelayDpsCooldownsMultiplier : public Multiplier
{
public:
IllidanStormrageDelayDpsCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage delay dps cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class IllidanStormrageControlTankActionsMultiplier : public Multiplier
{
public:
IllidanStormrageControlTankActionsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage control tank actions multiplier") {}
virtual float GetValue(Action* action);
};
class IllidanStormrageDisableDefaultTargetingMultiplier : public Multiplier
{
public:
IllidanStormrageDisableDefaultTargetingMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage disable default targeting multiplier") {}
virtual float GetValue(Action* action);
};
class IllidanStormrageControlNonTankMovementMultiplier : public Multiplier
{
public:
IllidanStormrageControlNonTankMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage control non-tank movement multiplier") {}
virtual float GetValue(Action* action);
};
class IllidanStormrageUseEarthbindTotemMultiplier : public Multiplier
{
public:
IllidanStormrageUseEarthbindTotemMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage use earthbind totem multiplier") {}
virtual float GetValue(Action* action);
};
class IllidanStormrageWaitForDpsMultiplier : public Multiplier
{
public:
IllidanStormrageWaitForDpsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "illidan stormrage wait for dps multiplier") {}
virtual float GetValue(Action* action);
};
#endif

View File

@ -1,406 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDBLACKTEMPLEACTIONCONTEXT_H
#define _PLAYERBOT_RAIDBLACKTEMPLEACTIONCONTEXT_H
#include "NamedObjectContext.h"
#include "RaidBlackTempleActions.h"
class RaidBlackTempleActionContext : public NamedObjectContext<Action>
{
public:
RaidBlackTempleActionContext()
{
// General
creators["black temple erase timers and trackers"] =
&RaidBlackTempleActionContext::black_temple_erase_timers_and_trackers;
// High Warlord Naj'entus
creators["high warlord naj'entus misdirect boss to main tank"] =
&RaidBlackTempleActionContext::high_warlord_najentus_misdirect_boss_to_main_tank;
creators["high warlord naj'entus tanks position boss"] =
&RaidBlackTempleActionContext::high_warlord_najentus_tanks_position_boss;
creators["high warlord naj'entus disperse ranged"] =
&RaidBlackTempleActionContext::high_warlord_najentus_disperse_ranged;
creators["high warlord naj'entus remove impaling spine"] =
&RaidBlackTempleActionContext::high_warlord_najentus_remove_impaling_spine;
creators["high warlord naj'entus throw impaling spine"] =
&RaidBlackTempleActionContext::high_warlord_najentus_throw_impaling_spine;
// Supremus
creators["supremus misdirect boss to main tank"] =
&RaidBlackTempleActionContext::supremus_misdirect_boss_to_main_tank;
creators["supremus disperse ranged"] =
&RaidBlackTempleActionContext::supremus_disperse_ranged;
creators["supremus kite boss"] =
&RaidBlackTempleActionContext::supremus_kite_boss;
creators["supremus move away from volcanos"] =
&RaidBlackTempleActionContext::supremus_move_away_from_volcanos;
creators["supremus manage phase timer"] =
&RaidBlackTempleActionContext::supremus_manage_phase_timer;
// Shade of Akama
creators["shade of akama melee dps prioritize channelers"] =
&RaidBlackTempleActionContext::shade_of_akama_melee_dps_prioritize_channelers;
// Teron Gorefiend
creators["teron gorefiend misdirect boss to main tank"] =
&RaidBlackTempleActionContext::teron_gorefiend_misdirect_boss_to_main_tank;
creators["teron gorefiend tanks position boss"] =
&RaidBlackTempleActionContext::teron_gorefiend_tanks_position_boss;
creators["teron gorefiend position ranged on balcony"] =
&RaidBlackTempleActionContext::teron_gorefiend_position_ranged_on_balcony;
creators["teron gorefiend avoid shadow of death"] =
&RaidBlackTempleActionContext::teron_gorefiend_avoid_shadow_of_death;
creators["teron gorefiend move to corner to die"] =
&RaidBlackTempleActionContext::teron_gorefiend_move_to_corner_to_die;
creators["teron gorefiend control and destroy shadowy constructs"] =
&RaidBlackTempleActionContext::teron_gorefiend_control_and_destroy_shadowy_constructs;
// Gurtogg Bloodboil
creators["gurtogg bloodboil misdirect boss to main tank"] =
&RaidBlackTempleActionContext::gurtogg_bloodboil_misdirect_boss_to_main_tank;
creators["gurtogg bloodboil tanks position boss"] =
&RaidBlackTempleActionContext::gurtogg_bloodboil_tanks_position_boss;
creators["gurtogg bloodboil rotate ranged groups"] =
&RaidBlackTempleActionContext::gurtogg_bloodboil_rotate_ranged_groups;
creators["gurtogg bloodboil ranged move away from enraged player"] =
&RaidBlackTempleActionContext::gurtogg_bloodboil_ranged_move_away_from_enraged_player;
creators["gurtogg bloodboil manage phase timer"] =
&RaidBlackTempleActionContext::gurtogg_bloodboil_manage_phase_timer;
// Reliquary of Souls
creators["reliquary of souls misdirect boss to main tank"] =
&RaidBlackTempleActionContext::reliquary_of_souls_misdirect_boss_to_main_tank;
creators["reliquary of souls adjust distance from suffering"] =
&RaidBlackTempleActionContext::reliquary_of_souls_adjust_distance_from_suffering;
creators["reliquary of souls healers dps suffering"] =
&RaidBlackTempleActionContext::reliquary_of_souls_healers_dps_suffering;
creators["reliquary of souls spellsteal rune shield"] =
&RaidBlackTempleActionContext::reliquary_of_souls_spellsteal_rune_shield;
creators["reliquary of souls spell reflect deaden"] =
&RaidBlackTempleActionContext::reliquary_of_souls_spell_reflect_deaden;
// Mother Shahraz
creators["mother shahraz misdirect boss to main tank"] =
&RaidBlackTempleActionContext::mother_shahraz_misdirect_boss_to_main_tank;
creators["mother shahraz tanks position boss under pillar"] =
&RaidBlackTempleActionContext::mother_shahraz_tanks_position_boss_under_pillar;
creators["mother shahraz melee dps wait at safe position"] =
&RaidBlackTempleActionContext::mother_shahraz_melee_dps_wait_at_safe_position;
creators["mother shahraz position ranged under pillar"] =
&RaidBlackTempleActionContext::mother_shahraz_position_ranged_under_pillar;
creators["mother shahraz run away to break fatal attraction"] =
&RaidBlackTempleActionContext::mother_shahraz_run_away_to_break_fatal_attraction;
// Illidari Council
creators["illidari council misdirect bosses to tanks"] =
&RaidBlackTempleActionContext::illidari_council_misdirect_bosses_to_tanks;
creators["illidari council main tank position gathios"] =
&RaidBlackTempleActionContext::illidari_council_main_tank_position_gathios;
creators["illidari council main tank reflect judgement of command"] =
&RaidBlackTempleActionContext::illidari_council_main_tank_reflect_judgement_of_command;
creators["illidari council first assist tank focus malande"] =
&RaidBlackTempleActionContext::illidari_council_first_assist_tank_focus_malande;
creators["illidari council second assist tank position darkshadow"] =
&RaidBlackTempleActionContext::illidari_council_second_assist_tank_position_darkshadow;
creators["illidari council mage tank position zerevor"] =
&RaidBlackTempleActionContext::illidari_council_mage_tank_position_zerevor;
creators["illidari council position mage tank healer"] =
&RaidBlackTempleActionContext::illidari_council_position_mage_tank_healer;
creators["illidari council disperse ranged"] =
&RaidBlackTempleActionContext::illidari_council_disperse_ranged;
creators["illidari council command pets to attack gathios"] =
&RaidBlackTempleActionContext::illidari_council_command_pets_to_attack_gathios;
creators["illidari council assign dps targets"] =
&RaidBlackTempleActionContext::illidari_council_assign_dps_targets;
creators["illidari council manage dps timer"] =
&RaidBlackTempleActionContext::illidari_council_manage_dps_timer;
// Illidan Stormrage <The Betrayer>
creators["illidan stormrage misdirect to tank"] =
&RaidBlackTempleActionContext::illidan_stormrage_misdirect_to_tank;
creators["illidan stormrage main tank reposition boss"] =
&RaidBlackTempleActionContext::illidan_stormrage_main_tank_reposition_boss;
creators["illidan stormrage isolate bot with parasite"] =
&RaidBlackTempleActionContext::illidan_stormrage_isolate_bot_with_parasite;
creators["illidan stormrage set earthbind totem"] =
&RaidBlackTempleActionContext::illidan_stormrage_set_earthbind_totem;
creators["illidan stormrage assist tanks handle flames of azzinoth"] =
&RaidBlackTempleActionContext::illidan_stormrage_assist_tanks_handle_flames_of_azzinoth;
creators["illidan stormrage control pet aggression"] =
&RaidBlackTempleActionContext::illidan_stormrage_control_pet_aggression;
creators["illidan stormrage position above grate"] =
&RaidBlackTempleActionContext::illidan_stormrage_position_above_grate;
creators["illidan stormrage remove dark barrage"] =
&RaidBlackTempleActionContext::illidan_stormrage_remove_dark_barrage;
creators["illidan stormrage move away from landing point"] =
&RaidBlackTempleActionContext::illidan_stormrage_move_away_from_landing_point;
creators["illidan stormrage disperse ranged"] =
&RaidBlackTempleActionContext::illidan_stormrage_disperse_ranged;
creators["illidan stormrage melee go somewhere to not die"] =
&RaidBlackTempleActionContext::illidan_stormrage_melee_go_somewhere_to_not_die;
creators["illidan stormrage warlock tank handle demon boss"] =
&RaidBlackTempleActionContext::illidan_stormrage_warlock_tank_handle_demon_boss;
creators["illidan stormrage dps prioritize adds"] =
&RaidBlackTempleActionContext::illidan_stormrage_dps_prioritize_adds;
creators["illidan stormrage use shadow trap"] =
&RaidBlackTempleActionContext::illidan_stormrage_use_shadow_trap;
creators["illidan stormrage manage dps timer and rti"] =
&RaidBlackTempleActionContext::illidan_stormrage_manage_dps_timer_and_rti;
creators["illidan stormrage destroy hazards"] =
&RaidBlackTempleActionContext::illidan_stormrage_destroy_hazards;
creators["illidan stormrage handle adds cheat"] =
&RaidBlackTempleActionContext::illidan_stormrage_handle_adds_cheat;
}
private:
// General
static Action* black_temple_erase_timers_and_trackers(
PlayerbotAI* botAI) { return new BlackTempleEraseTimersAndTrackersAction(botAI); }
// High Warlord Naj'entus
static Action* high_warlord_najentus_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new HighWarlordNajentusMisdirectBossToMainTankAction(botAI); }
static Action* high_warlord_najentus_tanks_position_boss(
PlayerbotAI* botAI) { return new HighWarlordNajentusTanksPositionBossAction(botAI); }
static Action* high_warlord_najentus_disperse_ranged(
PlayerbotAI* botAI) { return new HighWarlordNajentusDisperseRangedAction(botAI); }
static Action* high_warlord_najentus_remove_impaling_spine(
PlayerbotAI* botAI) { return new HighWarlordNajentusRemoveImpalingSpineAction(botAI); }
static Action* high_warlord_najentus_throw_impaling_spine(
PlayerbotAI* botAI) { return new HighWarlordNajentusThrowImpalingSpineAction(botAI); }
// Supremus
static Action* supremus_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new SupremusMisdirectBossToMainTankAction(botAI); }
static Action* supremus_disperse_ranged(
PlayerbotAI* botAI) { return new SupremusDisperseRangedAction(botAI); }
static Action* supremus_kite_boss(
PlayerbotAI* botAI) { return new SupremusKiteBossAction(botAI); }
static Action* supremus_move_away_from_volcanos(
PlayerbotAI* botAI) { return new SupremusMoveAwayFromVolcanosAction(botAI); }
static Action* supremus_manage_phase_timer(
PlayerbotAI* botAI) { return new SupremusManagePhaseTimerAction(botAI); }
// Shade of Akama
static Action* shade_of_akama_melee_dps_prioritize_channelers(
PlayerbotAI* botAI) { return new ShadeOfAkamaMeleeDpsPrioritizeChannelersAction(botAI); }
// Teron Gorefiend
static Action* teron_gorefiend_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new TeronGorefiendMisdirectBossToMainTankAction(botAI); }
static Action* teron_gorefiend_tanks_position_boss(
PlayerbotAI* botAI) { return new TeronGorefiendTanksPositionBossAction(botAI); }
static Action* teron_gorefiend_position_ranged_on_balcony(
PlayerbotAI* botAI) { return new TeronGorefiendPositionRangedOnBalconyAction(botAI); }
static Action* teron_gorefiend_avoid_shadow_of_death(
PlayerbotAI* botAI) { return new TeronGorefiendAvoidShadowOfDeathAction(botAI); }
static Action* teron_gorefiend_move_to_corner_to_die(
PlayerbotAI* botAI) { return new TeronGorefiendMoveToCornerToDieAction(botAI); }
static Action* teron_gorefiend_control_and_destroy_shadowy_constructs(
PlayerbotAI* botAI) { return new TeronGorefiendControlAndDestroyShadowyConstructsAction(botAI); }
// Gurtogg Bloodboil
static Action* gurtogg_bloodboil_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new GurtoggBloodboilMisdirectBossToMainTankAction(botAI); }
static Action* gurtogg_bloodboil_tanks_position_boss(
PlayerbotAI* botAI) { return new GurtoggBloodboilTanksPositionBossAction(botAI); }
static Action* gurtogg_bloodboil_rotate_ranged_groups(
PlayerbotAI* botAI) { return new GurtoggBloodboilRotateRangedGroupsAction(botAI); }
static Action* gurtogg_bloodboil_ranged_move_away_from_enraged_player(
PlayerbotAI* botAI) { return new GurtoggBloodboilRangedMoveAwayFromEnragedPlayerAction(botAI); }
static Action* gurtogg_bloodboil_manage_phase_timer(
PlayerbotAI* botAI) { return new GurtoggBloodboilManagePhaseTimerAction(botAI); }
// Reliquary of Souls
static Action* reliquary_of_souls_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsMisdirectBossToMainTankAction(botAI); }
static Action* reliquary_of_souls_adjust_distance_from_suffering(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsAdjustDistanceFromSufferingAction(botAI); }
static Action* reliquary_of_souls_healers_dps_suffering(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsHealersDpsSufferingAction(botAI); }
static Action* reliquary_of_souls_spellsteal_rune_shield(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsSpellstealRuneShieldAction(botAI); }
static Action* reliquary_of_souls_spell_reflect_deaden(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsSpellReflectDeadenAction(botAI); }
// Mother Shahraz
static Action* mother_shahraz_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new MotherShahrazMisdirectBossToMainTankAction(botAI); }
static Action* mother_shahraz_tanks_position_boss_under_pillar(
PlayerbotAI* botAI) { return new MotherShahrazTanksPositionBossUnderPillarAction(botAI); }
static Action* mother_shahraz_melee_dps_wait_at_safe_position(
PlayerbotAI* botAI) { return new MotherShahrazMeleeDpsWaitAtSafePositionAction(botAI); }
static Action* mother_shahraz_position_ranged_under_pillar(
PlayerbotAI* botAI) { return new MotherShahrazPositionRangedUnderPillarAction(botAI); }
static Action* mother_shahraz_run_away_to_break_fatal_attraction(
PlayerbotAI* botAI) { return new MotherShahrazRunAwayToBreakFatalAttractionAction(botAI); }
// Illidari Council
static Action* illidari_council_misdirect_bosses_to_tanks(
PlayerbotAI* botAI) { return new IllidariCouncilMisdirectBossesToTanksAction(botAI); }
static Action* illidari_council_main_tank_position_gathios(
PlayerbotAI* botAI) { return new IllidariCouncilMainTankPositionGathiosAction(botAI); }
static Action* illidari_council_main_tank_reflect_judgement_of_command(
PlayerbotAI* botAI) { return new IllidariCouncilMainTankReflectJudgementOfCommandAction(botAI); }
static Action* illidari_council_first_assist_tank_focus_malande(
PlayerbotAI* botAI) { return new IllidariCouncilFirstAssistTankFocusMalandeAction(botAI); }
static Action* illidari_council_second_assist_tank_position_darkshadow(
PlayerbotAI* botAI) { return new IllidariCouncilSecondAssistTankPositionDarkshadowAction(botAI); }
static Action* illidari_council_mage_tank_position_zerevor(
PlayerbotAI* botAI) { return new IllidariCouncilMageTankPositionZerevorAction(botAI); }
static Action* illidari_council_position_mage_tank_healer(
PlayerbotAI* botAI) { return new IllidariCouncilPositionMageTankHealerAction(botAI); }
static Action* illidari_council_disperse_ranged(
PlayerbotAI* botAI) { return new IllidariCouncilDisperseRangedAction(botAI); }
static Action* illidari_council_command_pets_to_attack_gathios(
PlayerbotAI* botAI) { return new IllidariCouncilCommandPetsToAttackGathiosAction(botAI); }
static Action* illidari_council_assign_dps_targets(
PlayerbotAI* botAI) { return new IllidariCouncilAssignDpsTargetsAction(botAI); }
static Action* illidari_council_manage_dps_timer(
PlayerbotAI* botAI) { return new IllidariCouncilManageDpsTimerAction(botAI); }
// Illidan Stormrage <The Betrayer>
static Action* illidan_stormrage_misdirect_to_tank(
PlayerbotAI* botAI) { return new IllidanStormrageMisdirectToTankAction(botAI); }
static Action* illidan_stormrage_main_tank_reposition_boss(
PlayerbotAI* botAI) { return new IllidanStormrageMainTankRepositionBossAction(botAI); }
static Action* illidan_stormrage_isolate_bot_with_parasite(
PlayerbotAI* botAI) { return new IllidanStormrageIsolateBotWithParasiteAction(botAI); }
static Action* illidan_stormrage_set_earthbind_totem(
PlayerbotAI* botAI) { return new IllidanStormrageSetEarthbindTotemAction(botAI); }
static Action* illidan_stormrage_assist_tanks_handle_flames_of_azzinoth(
PlayerbotAI* botAI) { return new IllidanStormrageAssistTanksHandleFlamesOfAzzinothAction(botAI); }
static Action* illidan_stormrage_control_pet_aggression(
PlayerbotAI* botAI) { return new IllidanStormrageControlPetAggressionAction(botAI); }
static Action* illidan_stormrage_position_above_grate(
PlayerbotAI* botAI) { return new IllidanStormragePositionAboveGrateAction(botAI); }
static Action* illidan_stormrage_remove_dark_barrage(
PlayerbotAI* botAI) { return new IllidanStormrageRemoveDarkBarrageAction(botAI); }
static Action* illidan_stormrage_move_away_from_landing_point(
PlayerbotAI* botAI) { return new IllidanStormrageMoveAwayFromLandingPointAction(botAI); }
static Action* illidan_stormrage_disperse_ranged(
PlayerbotAI* botAI) { return new IllidanStormrageDisperseRangedAction(botAI); }
static Action* illidan_stormrage_melee_go_somewhere_to_not_die(
PlayerbotAI* botAI) { return new IllidanStormrageMeleeGoSomewhereToNotDieAction(botAI); }
static Action* illidan_stormrage_warlock_tank_handle_demon_boss(
PlayerbotAI* botAI) { return new IllidanStormrageWarlockTankHandleDemonBossAction(botAI); }
static Action* illidan_stormrage_dps_prioritize_adds(
PlayerbotAI* botAI) { return new IllidanStormrageDpsPrioritizeAddsAction(botAI); }
static Action* illidan_stormrage_use_shadow_trap(
PlayerbotAI* botAI) { return new IllidanStormrageUseShadowTrapAction(botAI); }
static Action* illidan_stormrage_manage_dps_timer_and_rti(
PlayerbotAI* botAI) { return new IllidanStormrageManageDpsTimerAndRtiAction(botAI); }
static Action* illidan_stormrage_destroy_hazards(
PlayerbotAI* botAI) { return new IllidanStormrageDestroyHazardsAction(botAI); }
static Action* illidan_stormrage_handle_adds_cheat(
PlayerbotAI* botAI) { return new IllidanStormrageHandleAddsCheatAction(botAI); }
};
#endif

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