Compare commits

...

12 Commits

Author SHA1 Message Date
Keleborn
a02e4ca5c1
Merge pull request #2399 from mod-playerbots/test-staging
Test staging
2026-05-22 09:00:25 -07:00
Alex Dcnh
d0ba99f381
Updates the Windows CI workflow to build AzerothCore and mod-playerbots reliably with Ninja instead of the Visual Studio/MSBuild generator (#2383)
Added a Windows CI fix to build with Ninja and avoid Windows
command-line length failures.

<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
This PR updates the Windows CI workflow to build AzerothCore +
mod-playerbots reliably with Ninja instead of the Visual Studio/MSBuild
generator.

The previous Windows build could fail during the final `worldserver`
build steps because MSBuild/RC generated command lines that exceeded
Windows command-line limits. Enabling long-path support was not enough,
because the failure was caused by tool command length rather than only
filesystem path length.

This workflow now:
- installs and uses Ninja as the CMake generator;
- creates an AzerothCore `conf/config.sh` for CI;
- forces the compiler to MSVC `cl`;
- enables CMake/Ninja response files to reduce command-line length;
- moves the source tree to a short path (`C:\ac`) before configuring and
building.

This keeps the Windows build on MSVC while avoiding the MSBuild/RC
command-line length issue.

**Pros:**
- Fixes Windows CI command-line length failures;
- avoids unreliable `rc.exe` / `MSB6003` errors from the MSBuild
generator;
- keeps the build on MSVC;
- makes the Windows CI build more predictable;
- does not affect runtime code or bot behavior.

**Cons:**
- Visual Studio project files (`.vcxproj`) are no longer generated in
CI;
- the workflow now copies the source tree to a short path before
building.

## Feature Evaluation
- Minimum logic: CI-only workflow changes to use Ninja, force MSVC,
enable response files, and build from a short path.
- Processing cost: None at runtime. This only affects GitHub Actions
build tooling.

## How to Test the Changes
- Run the Windows CI workflow on GitHub Actions.
- Confirm that CMake reports `-- Building for: Ninja`.
- Confirm that CMake detects MSVC as the C and C++ compiler.
- Confirm that the Windows build completes successfully.
- Confirm that no MSBuild `MSB6003`, `rc.exe`, or Ninja `CreateProcess`
command-line length errors occur.

## Impact Assessment
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - [x] No, not at all

- Does this change modify default bot behavior?
    - [x] No

- Does this change add new decision branches or increase maintenance
complexity?
    - [x] No

## AI Assistance
Was AI assistance used while working on this change?
- [x] No
Maybe if should so i'd made less commits...

## Final Checklist
- [x] Stability is not compromised.
- [x] Performance impact is understood, tested, and acceptable.
- [x] Added logic complexity is justified and explained.
- [x] Any new bot dialogue lines are translated.
- [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
This change is CI-only and does not affect gameplay, bot logic, database
logic, or runtime performance.

The Windows workflow was failing because generated build commands became
too long for Windows process creation. Switching the CI build to Ninja,
forcing MSVC, enabling response files, and building from `C:\ac` fixes
the issue while keeping the compiler/toolchain consistent with the
Windows target.

---------

Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: Revision <tkn963@gmail.com>
Co-authored-by: kadeshar <kadeshar@gmail.com>
2026-05-15 23:36:59 -07:00
dillyns
9c9c386af7
Add ELEMENTAL_SHARPENING_STONE to prioritized weight stone IDs (#2395)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
Adds elemental sharpening stone to the list of weightstones for weapon
buff application.
Elemental sharpening stones can be used on any melee weapon, not just
sharp ones.


## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.



## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->
Give a bot that has a blunt weapon equipped (staff or mace) an elemental
sharpening stone (ID: 18262)
They should now use the stone on a blunt weapon.


## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:28:04 -07:00
Crow
9d787ca0b4
Make Vashj Strategy Compatible with Acore Bug & Use AI Instance for Targeting (#2391)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
Lady Vashj has been screwed up in Acore for a month or two. In Phase 2,
only one generator activates (instead of four). As a result, disabling
one generator brings Vashj into Phase 3. The strategy uses Vashj's HP +
unit state to determine phase--with the Acore bug, Vashj enters Phase 3
at <65% HP (instead of the correct <50% HP, as deactivating a generator
damages her by 5%) so under the current strategy, bots won't assume
Phase 3 strategies at the right moment. I tried looking into the issue
with Vashj in Acore and cannot figure it out, and I've had an issue open
with Acore and don't know if/when it will be fixed. In the meantime, I'm
modifying the strategy to use the shield barrier aura check on Vashj
instead so it will work regardless of Vashj's HP at the time the
generators are disabled. If/when Vashj is fixed, the strategy will still
work.

I also changed bot-side targeting checks throughout the strategies I've
written from GetVictim() and GetTarget() to using the AI's target state
via "current target." This approach I believe is the best way to
actually check for the target because a GetVictim() check can lock up
Elemental Shamans and Balance Druids due to them not being able to start
an attack to pass that check when outside of spell range, and a
GetTarget() check can occasionally not align with the target that the
bot is actually attacking.

- In effect, this should reduce cases where the bot AI thinks it is on
the right raid target but encounter logic disagrees because the live
victim or client selection is lagging or different.
- Exception: multipliers for blocking TankAssistAction still use a
GetVictim() check. This is because "current target" processes before
issuing an attack, and the targeting check for TankAssistAction is not
to suppress it if the bot hasn't actually issued an attack.

There is a lot more than could be refactored with past strategies to
make them better, but that's for another day. This PR is intended to be
limited and simple to review.

## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.
Logic is described above. There should be no relevant impact on
processing cost.


## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->

Attempt Lady Vashj with the current bugged version. After disabling the
lone generator, bots should enter Phase 3 strategies and the encounter
should be completable.

Otherwise, generally run raids, and confirm there are no issues with
bots switching targets or starting attacking.


## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:27:42 -07:00
kadeshar
0e0d9fbde2
Hand of Freedom fix for Stealth (#2388)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->

Paladin no longer using Hand of Freedom on Rogue with Stealth
Related with: #2385 

## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->

1. Invite paladin and rogue to group
2. Start fight
3. Rogue should use Stealth on fight beginning, Paladin should cast Hand
of Freedom
4. Apply some snare effect to Rogue bot (for example .aura 1715)
5. Paladin bot should use hand of freedom

## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:27:30 -07:00
Crow
d4f86764bb
Fix raid group condition in SayToRaid (#2386)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
PlayerbotAI::SayToRaid is not used currently but I may use it for
Sunwell for bot speech during Kalecgos due to tank handoff mechanics.
The function currently erroneously returns false for a bot in a raid.
There's no impact on bot behavior or performance from this PR.


## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.



## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->



## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:27:15 -07:00
Crow
05e8f4d82c
Implement Black Temple Strategies (#2381)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
This PR implements strategies for all bosses in the Black Temple. As
always, I’ve written these with the intent that all bosses be
completable with appropriate gear and 50/50 IP nerfs and boss HP
returned to TBC levels. Illidan is difficult at those parameters, but he
is certainly doable. You just won’t be able to roll a bunch of meme
specs or shitty compositions. Probably.


## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.

My goal with raid strategies is the same—I’m not just trying to make
encounters doable but am trying to allow people to experience them in a
way that feels like they are part of a coordinated raid of real players.
To me, being able to achieve that is the minimum, and that requires
getting bots to respond to all major mechanics, even if they can
technically just be powered through.


## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->
Run the Black Temple raid and see if my descriptions of strategies in
the next post check out.


## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [ ] No, not at all
    - - [x] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)
 
The performance impact exists only when the “blacktemple” strategy is
on. I’ve tested with pmon and done my best to properly gate checks to
limit the performance impact even during the instance and boss
encounters.


- Does this change modify default bot behavior?
    - - [ ] No
    - - [x] Yes (**explain why**)

The triggers and multipliers will be evaluated as long as the
"blacktemple" strategy is active, and it will be applied automatically
in the instance (and removed automatically when changing maps outside
the instance).


- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [ ] No
- - [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->
GPT-5.4, mainly for calculations and things that are more intermediate
concepts that I’m not proficient with like lambdas.


<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:26:55 -07:00
kadeshar
61dbae19dc
Fix for lfg command (#2379)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->

Fix for `lfg` to include healers in invitations and requester role.



## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->

Use command `/1 lfg 40` on server which contains correct amount of bots
possible to be invited. Such group should be create.

## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [ ] No
- - [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->

To identify reason.

<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:26:34 -07:00
kadeshar
05aebfea46
Outfit database persist (#2378)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->

Added storing outfit in database.
Related with: #2239 

## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->

1. Invite altbot
2. Use command `outfit testcollection +[itemlink]` to add outfit
collection and item
3. Check that is added `outfit ?`
4. Logout bot for example via multibot
5. Login bot
6. Check that is still added `outfit ?`



## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [ ] No
- - [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->

To identify places to change.

<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->

Result after bot relogin:
<img width="701" height="206" alt="obraz"
src="https://github.com/user-attachments/assets/ea6c875a-65dd-4a01-9d35-381c8b374abd"
/>
2026-05-15 23:26:22 -07:00
kadeshar
f1a2736a31
Polymorph spam fix (#2373)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->

Fix for polymorph spam in combat

## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->

1. Invite mage bot which have polymorph (check `spells polymorph`)
2. Enter combat and mage should not spam polymorph
3. Enter another combat best with multiple mobs and one of this mobs
mark moon
4. Mage should cast polymorph on marked mob

## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:26:03 -07:00
NoxMax
dbea1203b4
Refactor: Clean up triggers and reorganize supported commands (#2327)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
Removed duplicate and dead trigger-action mappings in
ChatCommandHandlerStrategy. Noticed while reviewing the recent edits to
test-staging. Also when trigger name == action name, that belongs to
supported in supported, so `wipe` and `roll` were moved there.
Beyond that, `InitTriggers` commands were changed to be in a single
line. Really it's easier to read that way, except multi-action lines,
where I gave each action a line of its own.


## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.
Logic has been further minimized to eliminate redundancies.


## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->
Test the affected commands. They should work same as before.
Removed duplicates from `InitTriggers` that are already in `supported`:
1. open items
2. unlock items
3. unlock traded item
4. tame
5. glyphs
6. glyph equip
7. pet
8. pet attack
9. emblems

Dead entries that were in `supported` but already worked through
`InitTriggers`:
1. qi
2. focus heal

Triggers that were moved from `InitTriggers` to `supported`:
1. wipe
2. roll



## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->
2026-05-15 23:19:58 -07:00
Fiery
eb3c101959
update to level 80 pve specs (#2366)
<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

## Pull Request Description
<!-- Describe what this change does and why it is needed -->

Some of the specs needed improvement.


## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.



## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->



## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [x] No, not at all
    - - [ ] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)



- Does this change modify default bot behavior?
    - - [x] No
    - - [ ] Yes (**explain why**)



- Does this change add new decision branches or increase maintenance
complexity?
    - - [x] No
    - - [ ] Yes (**explain below**)



## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
Was AI assistance used while working on this change?
- - [x] No
- - [ ] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->



<!--
TRANSLATIONS:
Anything new that the bots say in chat must be in a translatable format.
This is done using GetBotTextOrDefault,
which you can search for in the codebase to find examples. Your code
needs to have English as the default fallback,
while the full translations need to be in an SQL update file. The
languages in the file are the nine language
options supported by AzerothCore: English, Korean, French, German,
Chinese, Taiwanese, Spanish, Spanish Mexico, and
Russian. See
data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as
an example of a translation SQL
update, whose content are called within the codebase at
src/strategy/actions/FishingAction.cpp
-->

## Final Checklist

- - [x] Stability is not compromised.
- - [ ] Performance impact is understood, tested, and acceptable.
- - [x] Added logic complexity is justified and explained.
- - [x] Any new bot dialogue lines are translated.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->

---------

Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: Revision <tkn963@gmail.com>
Co-authored-by: kadeshar <kadeshar@gmail.com>
2026-05-15 23:19:18 -07:00
38 changed files with 8119 additions and 272 deletions

View File

@ -1,4 +1,5 @@
name: windows-build
on:
push:
branches: [ "master", "test-staging" ]
@ -6,7 +7,7 @@ on:
branches: [ "master", "test-staging" ]
concurrency:
group: "windows-build-${{ github.event.pull_request.number }}"
group: "windows-build-${{ github.event.pull_request.number || github.ref }}"
cancel-in-progress: true
jobs:
@ -15,35 +16,108 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os }}
env:
BOOST_ROOT: C:\local\boost_1_82_0
BOOST_ROOT: C:\local\boost_1_87_0
CMAKE_GENERATOR: Ninja
CTOOLS_BUILD: all
steps:
- name: Checkout AzerothCore
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: 'mod-playerbots/azerothcore-wotlk'
ref: ${{ (github.base_ref || github.ref_name) == 'test-staging' && 'test-staging' || 'Playerbot' }}
path: 'ac'
path: a
- name: Checkout Playerbot Module
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: 'mod-playerbots/mod-playerbots'
#path: 'modules/mod-playerbots'
path: ac/modules/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
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.13
- name: Configure OS
shell: bash
working-directory: ac
working-directory: C:\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: ac
working-directory: C:\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

@ -576,7 +576,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
# "mana" (bots have infinite mana)
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar))
# "raid" (bots use certain cheats implemented into raid strategies)
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid"
@ -1430,15 +1430,15 @@ AiPlayerbot.PermanentlyInWorldTime = 31104000
#
AiPlayerbot.PremadeSpecName.1.0 = arms pve
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,43397,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,43399,49084,43432
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43396,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 = 43424,43395,43425,43399,49084,45793
AiPlayerbot.PremadeSpecGlyph.1.2 = 43429,43397,43425,43399,49084,45797
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
AiPlayerbot.PremadeSpecLink.1.2.80 = 3500030023-301-053351225000210521030113321
AiPlayerbot.PremadeSpecName.1.3 = arms pvp
@ -1467,13 +1467,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 = 50350152220013053100515221-503201312
AiPlayerbot.PremadeSpecLink.2.0.80 = 50350152100013053100515221-50320104203
AiPlayerbot.PremadeSpecName.2.1 = prot pve
AiPlayerbot.PremadeSpecGlyph.2.1 = 41099,43367,43869,43368,43369,45745
AiPlayerbot.PremadeSpecGlyph.2.1 = 41100,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,43369,43869
AiPlayerbot.PremadeSpecGlyph.2.2 = 41092,43367,41099,43368,43340,43869
AiPlayerbot.PremadeSpecLink.2.2.60 = --05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.65 = -05-05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
@ -1504,7 +1504,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 = 51200201505112243130531351-005305101
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112233111531351-0323031-5
AiPlayerbot.PremadeSpecName.3.1 = mm pve
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,45625,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.1.60 = -035305101030013233115031151
@ -1530,8 +1530,8 @@ AiPlayerbot.PremadeSpecLink.3.5.80 = -005305201-2300302510233330533135001031
# HUNTER PET
#
# Ferocity
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003130003010101
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100003130103010122
# Tenacity
AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001
AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002
@ -1552,7 +1552,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 = 005303104352100520103331051-005005005003-2
AiPlayerbot.PremadeSpecLink.4.0.80 = 005303005352100520103331051-005005003-502
AiPlayerbot.PremadeSpecName.4.1 = combat pve
AiPlayerbot.PremadeSpecGlyph.4.1 = 42962,43379,45762,43380,43378,42969
AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251
@ -1593,7 +1593,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 = 42406,43371,42407,43374,43342,42415
AiPlayerbot.PremadeSpecGlyph.5.2 = 45753,43371,42407,43374,43370,42415
AiPlayerbot.PremadeSpecLink.5.2.60 = --325003041203010323150301351
AiPlayerbot.PremadeSpecLink.5.2.80 = 0503203--325023051223010323152301351
AiPlayerbot.PremadeSpecName.5.3 = disc pvp
@ -1620,19 +1620,19 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
#
AiPlayerbot.PremadeSpecName.6.0 = blood pve
AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43538,43544,43672,43542
AiPlayerbot.PremadeSpecLink.6.0.60 = 035502150300331320102013111-005
AiPlayerbot.PremadeSpecLink.6.0.80 = 0355021533003313201020131351-005-005032
AiPlayerbot.PremadeSpecLink.6.0.80 = 0055021533303310201020131-305020510002-00522
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,43544,43672,43549
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,43546,43535,43672,43549
AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151
AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151
AiPlayerbot.PremadeSpecLink.6.2.80 = 23050202--2302303350032152000150003133151
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43538,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
AiPlayerbot.PremadeSpecLink.6.3.80 = 005512153330030320102013-3050505002023001-002
AiPlayerbot.PremadeSpecName.6.4 = blood pvp
@ -1662,13 +1662,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 = 3530001523213351322301351-005050031
AiPlayerbot.PremadeSpecLink.7.0.80 = 4530001523213351302301351-00525003
AiPlayerbot.PremadeSpecName.7.1 = enh pve
AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,45771
AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,43725,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,44923,45775
AiPlayerbot.PremadeSpecGlyph.7.2 = 41527,43385,41517,43386,43725,45775
AiPlayerbot.PremadeSpecLink.7.2.60 = --50005301235310501102321251
AiPlayerbot.PremadeSpecLink.7.2.80 = -00502033-50005331335310501122331251
AiPlayerbot.PremadeSpecName.7.3 = ele pvp
@ -1702,7 +1702,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-0055030011302331053120321351
AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503110003-0055032012303330053120300351
AiPlayerbot.PremadeSpecName.8.2 = frost pve
AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751
AiPlayerbot.PremadeSpecLink.8.2.60 = --0533030313203100030152231151
@ -1780,15 +1780,15 @@ 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 = -500232130322110353100301310501
AiPlayerbot.PremadeSpecLink.11.1.80 = -501232130322110353120303313511-20350001
AiPlayerbot.PremadeSpecLink.11.1.80 = -503232132322010353120303013511-20350001
AiPlayerbot.PremadeSpecName.11.2 = resto pve
AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,44922,45602
AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,43674,45602
AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031500531050113051
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320031--230033312031501531053313051
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320131003--230023312131500531050313051
AiPlayerbot.PremadeSpecName.11.3 = cat pve
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,43674,45604
AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053120030310511-203503012
AiPlayerbot.PremadeSpecName.11.4 = balance pvp
AiPlayerbot.PremadeSpecGlyph.11.4 = 40921,43331,45622,43674,43335,45623
AiPlayerbot.PremadeSpecLink.11.4.60 = 5012203115331002213032311231

View File

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

View File

@ -7,6 +7,7 @@
#include "Event.h"
#include "ItemVisitors.h"
#include "PlayerbotRepository.h"
#include "Playerbots.h"
#include "ItemPackets.h"
@ -28,6 +29,7 @@ bool OutfitAction::Execute(Event event)
if (!name.empty())
{
Save(name, items);
PlayerbotRepository::instance().Save(botAI);
std::ostringstream out;
out << "Setting outfit " << name << " as " << param;
@ -86,6 +88,7 @@ bool OutfitAction::Execute(Event event)
botAI->TellMaster(out);
Save(name, ItemIds());
PlayerbotRepository::instance().Save(botAI);
return true;
}
else if (command == "update")
@ -95,6 +98,7 @@ bool OutfitAction::Execute(Event event)
botAI->TellMaster(out);
Update(name);
PlayerbotRepository::instance().Save(botAI);
return true;
}
@ -124,6 +128,7 @@ bool OutfitAction::Execute(Event event)
}
Save(name, outfit);
PlayerbotRepository::instance().Save(botAI);
}
return true;

View File

@ -20,20 +20,23 @@ 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),
NextAction("query item usage", relevance) }));
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
NextAction("loot", relevance) }));
triggers.push_back(new TriggerNode("q",
{ NextAction("query quest", relevance),
NextAction("query item usage", relevance) }));
triggers.push_back(new TriggerNode("add all loot",
{ NextAction("add all loot", relevance),
NextAction("loot", relevance) }));
triggers.push_back(new TriggerNode("u", { NextAction("use", relevance) }));
triggers.push_back(new TriggerNode("c", { NextAction("item count", relevance) }));
triggers.push_back(
new TriggerNode("items", { NextAction("item count", relevance) }));
triggers.push_back(new TriggerNode("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) }));
@ -42,81 +45,40 @@ 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),
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("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("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("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());
@ -199,15 +161,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

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

View File

@ -11,8 +11,6 @@
#include "ServerFacade.h"
#include "SharedDefines.h"
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
bool UseManaSapphireAction::isUseful()
{
Player* bot = botAI->GetBot();

View File

@ -215,11 +215,10 @@ public:
// CC, Interrupt, and Dispel Actions
class CastPolymorphAction : public CastBuffSpellAction
class CastPolymorphAction : public CastCrowdControlSpellAction
{
public:
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
Value<Unit*>* GetTargetValue() override;
CastPolymorphAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "polymorph") {}
};
class CastSpellstealAction : public CastSpellAction

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,565 @@
/*
* 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

@ -0,0 +1,785 @@
/*
* 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

@ -0,0 +1,267 @@
/*
* 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

@ -0,0 +1,406 @@
/*
* 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

View File

@ -0,0 +1,406 @@
/*
* 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_RAIDBLACKTEMPLETRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDBLACKTEMPLETRIGGERCONTEXT_H
#include "NamedObjectContext.h"
#include "RaidBlackTempleTriggers.h"
class RaidBlackTempleTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidBlackTempleTriggerContext()
{
// General
creators["black temple bot is not in combat"] =
&RaidBlackTempleTriggerContext::black_temple_bot_is_not_in_combat;
// High Warlord Naj'entus
creators["high warlord naj'entus pulling boss"] =
&RaidBlackTempleTriggerContext::high_warlord_najentus_pulling_boss;
creators["high warlord naj'entus boss engaged by tanks"] =
&RaidBlackTempleTriggerContext::high_warlord_najentus_boss_engaged_by_tanks;
creators["high warlord naj'entus casts needle spines"] =
&RaidBlackTempleTriggerContext::high_warlord_najentus_casts_needle_spines;
creators["high warlord naj'entus player is impaled"] =
&RaidBlackTempleTriggerContext::high_warlord_najentus_player_is_impaled;
creators["high warlord naj'entus boss has tidal shield"] =
&RaidBlackTempleTriggerContext::high_warlord_najentus_boss_has_tidal_shield;
// Supremus
creators["supremus pulling boss or changing phase"] =
&RaidBlackTempleTriggerContext::supremus_pulling_boss_or_changing_phase;
creators["supremus boss engaged by ranged"] =
&RaidBlackTempleTriggerContext::supremus_boss_engaged_by_ranged;
creators["supremus boss is fixated on bot"] =
&RaidBlackTempleTriggerContext::supremus_boss_is_fixated_on_bot;
creators["supremus volcano is nearby"] =
&RaidBlackTempleTriggerContext::supremus_volcano_is_nearby;
creators["supremus need to manage phase timer"] =
&RaidBlackTempleTriggerContext::supremus_need_to_manage_phase_timer;
// Shade of Akama
creators["shade of akama killing channelers starts phase 2"] =
&RaidBlackTempleTriggerContext::shade_of_akama_killing_channelers_starts_phase_2;
// Teron Gorefiend
creators["teron gorefiend pulling boss"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_pulling_boss;
creators["teron gorefiend boss engaged by tanks"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_boss_engaged_by_tanks;
creators["teron gorefiend boss engaged by ranged"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_boss_engaged_by_ranged;
creators["teron gorefiend boss is casting shadow of death"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_boss_is_casting_shadow_of_death;
creators["teron gorefiend bot has shadow of death"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_bot_has_shadow_of_death;
creators["teron gorefiend bot transformed into vengeful spirit"] =
&RaidBlackTempleTriggerContext::teron_gorefiend_bot_transformed_into_vengeful_spirit;
// Gurtogg Bloodboil
creators["gurtogg bloodboil pulling boss"] =
&RaidBlackTempleTriggerContext::gurtogg_bloodboil_pulling_boss;
creators["gurtogg bloodboil boss engaged by tanks"] =
&RaidBlackTempleTriggerContext::gurtogg_bloodboil_boss_engaged_by_tanks;
creators["gurtogg bloodboil boss casts bloodboil"] =
&RaidBlackTempleTriggerContext::gurtogg_bloodboil_boss_casts_bloodboil;
creators["gurtogg bloodboil bot has fel rage"] =
&RaidBlackTempleTriggerContext::gurtogg_bloodboil_bot_has_fel_rage;
creators["gurtogg bloodboil need to manage phase timer"] =
&RaidBlackTempleTriggerContext::gurtogg_bloodboil_need_to_manage_phase_timer;
// Reliquary of Souls
creators["reliquary of souls aggro resets upon phase change"] =
&RaidBlackTempleTriggerContext::reliquary_of_souls_aggro_resets_upon_phase_change;
creators["reliquary of souls essence of suffering fixates on closest target"] =
&RaidBlackTempleTriggerContext::reliquary_of_souls_essence_of_suffering_fixates_on_closest_target;
creators["reliquary of souls essence of suffering disables healing"] =
&RaidBlackTempleTriggerContext::reliquary_of_souls_essence_of_suffering_disables_healing;
creators["reliquary of souls essence of desire has rune shield"] =
&RaidBlackTempleTriggerContext::reliquary_of_souls_essence_of_desire_has_rune_shield;
creators["reliquary of souls essence of desire casting deaden"] =
&RaidBlackTempleTriggerContext::reliquary_of_souls_essence_of_desire_casting_deaden;
// Mother Shahraz
creators["mother shahraz pulling boss"] =
&RaidBlackTempleTriggerContext::mother_shahraz_pulling_boss;
creators["mother shahraz boss engaged by tanks"] =
&RaidBlackTempleTriggerContext::mother_shahraz_boss_engaged_by_tanks;
creators["mother shahraz tanks are positioning boss"] =
&RaidBlackTempleTriggerContext::mother_shahraz_tanks_are_positioning_boss;
creators["mother shahraz sinister beam knocks back players"] =
&RaidBlackTempleTriggerContext::mother_shahraz_sinister_beam_knocks_back_players;
creators["mother shahraz bots are linked by fatal attraction"] =
&RaidBlackTempleTriggerContext::mother_shahraz_bots_are_linked_by_fatal_attraction;
// Illidari Council
creators["illidari council pulling bosses"] =
&RaidBlackTempleTriggerContext::illidari_council_pulling_bosses;
creators["illidari council gathios engaged by main tank"] =
&RaidBlackTempleTriggerContext::illidari_council_gathios_engaged_by_main_tank;
creators["illidari council gathios casting judgement of command"] =
&RaidBlackTempleTriggerContext::illidari_council_gathios_casting_judgement_of_command;
creators["illidari council malande engaged by first assist tank"] =
&RaidBlackTempleTriggerContext::illidari_council_malande_engaged_by_first_assist_tank;
creators["illidari council darkshadow engaged by second assist tank"] =
&RaidBlackTempleTriggerContext::illidari_council_darkshadow_engaged_by_second_assist_tank;
creators["illidari council zerevor engaged by mage tank"] =
&RaidBlackTempleTriggerContext::illidari_council_zerevor_engaged_by_mage_tank;
creators["illidari council mage tank needs dedicated healer"] =
&RaidBlackTempleTriggerContext::illidari_council_mage_tank_needs_dedicated_healer;
creators["illidari council zerevor casts dangerous aoes"] =
&RaidBlackTempleTriggerContext::illidari_council_zerevor_casts_dangerous_aoes;
creators["illidari council pets screw up the pull"] =
&RaidBlackTempleTriggerContext::illidari_council_pets_screw_up_the_pull;
creators["illidari council determining dps assignments"] =
&RaidBlackTempleTriggerContext::illidari_council_determining_dps_assignments;
creators["illidari council need to manage dps timer"] =
&RaidBlackTempleTriggerContext::illidari_council_need_to_manage_dps_timer;
// Illidan Stormrage <The Betrayer>
creators["illidan stormrage tank needs aggro"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_tank_needs_aggro;
creators["illidan stormrage boss casts flame crash in front of main tank"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_casts_flame_crash_in_front_of_main_tank;
creators["illidan stormrage bot has parasitic shadowfiend"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_bot_has_parasitic_shadowfiend;
creators["illidan stormrage parasitic shadowfiends run wild"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_parasitic_shadowfiends_run_wild;
creators["illidan stormrage boss summoned flames of azzinoth"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_summoned_flames_of_azzinoth;
creators["illidan stormrage pets die to fire"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_pets_die_to_fire;
creators["illidan stormrage grate is safe from flames"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_grate_is_safe_from_flames;
creators["illidan stormrage bot struck by dark barrage"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_bot_struck_by_dark_barrage;
creators["illidan stormrage boss is preparing to land"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_is_preparing_to_land;
creators["illidan stormrage boss deals splash damage"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_deals_splash_damage;
creators["illidan stormrage this expansion hates melee"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_this_expansion_hates_melee;
creators["illidan stormrage boss transforms into demon"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_transforms_into_demon;
creators["illidan stormrage boss spawns adds"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_boss_spawns_adds;
creators["illidan stormrage maiev placed shadow trap"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_maiev_placed_shadow_trap;
creators["illidan stormrage need to manage dps timer and rti"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_need_to_manage_dps_timer_and_rti;
creators["illidan stormrage need to clear hazards between phases"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_need_to_clear_hazards_between_phases;
creators["illidan stormrage cheat"] =
&RaidBlackTempleTriggerContext::illidan_stormrage_cheat;
}
private:
// General
static Trigger* black_temple_bot_is_not_in_combat(
PlayerbotAI* botAI) { return new BlackTempleBotIsNotInCombatTrigger(botAI); }
// High Warlord Naj'entus
static Trigger* high_warlord_najentus_pulling_boss(
PlayerbotAI* botAI) { return new HighWarlordNajentusPullingBossTrigger(botAI); }
static Trigger* high_warlord_najentus_boss_engaged_by_tanks(
PlayerbotAI* botAI) { return new HighWarlordNajentusBossEngagedByTanksTrigger(botAI); }
static Trigger* high_warlord_najentus_casts_needle_spines(
PlayerbotAI* botAI) { return new HighWarlordNajentusCastsNeedleSpinesTrigger(botAI); }
static Trigger* high_warlord_najentus_player_is_impaled(
PlayerbotAI* botAI) { return new HighWarlordNajentusPlayerIsImpaledTrigger(botAI); }
static Trigger* high_warlord_najentus_boss_has_tidal_shield(
PlayerbotAI* botAI) { return new HighWarlordNajentusBossHasTidalShieldTrigger(botAI); }
// Supremus
static Trigger* supremus_pulling_boss_or_changing_phase(
PlayerbotAI* botAI) { return new SupremusPullingBossOrChangingPhaseTrigger(botAI); }
static Trigger* supremus_boss_engaged_by_ranged(
PlayerbotAI* botAI) { return new SupremusBossEngagedByRangedTrigger(botAI); }
static Trigger* supremus_boss_is_fixated_on_bot(
PlayerbotAI* botAI) { return new SupremusBossIsFixatedOnBotTrigger(botAI); }
static Trigger* supremus_volcano_is_nearby(
PlayerbotAI* botAI) { return new SupremusVolcanoIsNearbyTrigger(botAI); }
static Trigger* supremus_need_to_manage_phase_timer(
PlayerbotAI* botAI) { return new SupremusNeedToManagePhaseTimerTrigger(botAI); }
// Shade of Akama
static Trigger* shade_of_akama_killing_channelers_starts_phase_2(
PlayerbotAI* botAI) { return new ShadeOfAkamaKillingChannelersStartsPhase2Trigger(botAI); }
// Teron Gorefiend
static Trigger* teron_gorefiend_pulling_boss(
PlayerbotAI* botAI) { return new TeronGorefiendPullingBossTrigger(botAI); }
static Trigger* teron_gorefiend_boss_engaged_by_tanks(
PlayerbotAI* botAI) { return new TeronGorefiendBossEngagedByTanksTrigger(botAI); }
static Trigger* teron_gorefiend_boss_engaged_by_ranged(
PlayerbotAI* botAI) { return new TeronGorefiendBossEngagedByRangedTrigger(botAI); }
static Trigger* teron_gorefiend_boss_is_casting_shadow_of_death(
PlayerbotAI* botAI) { return new TeronGorefiendBossIsCastingShadowOfDeathTrigger(botAI); }
static Trigger* teron_gorefiend_bot_has_shadow_of_death(
PlayerbotAI* botAI) { return new TeronGorefiendBotHasShadowOfDeathTrigger(botAI); }
static Trigger* teron_gorefiend_bot_transformed_into_vengeful_spirit(
PlayerbotAI* botAI) { return new TeronGorefiendBotTransformedIntoVengefulSpiritTrigger(botAI); }
// Gurtogg Bloodboil
static Trigger* gurtogg_bloodboil_pulling_boss(
PlayerbotAI* botAI) { return new GurtoggBloodboilPullingBossTrigger(botAI); }
static Trigger* gurtogg_bloodboil_boss_engaged_by_tanks(
PlayerbotAI* botAI) { return new GurtoggBloodboilBossEngagedByTanksTrigger(botAI); }
static Trigger* gurtogg_bloodboil_boss_casts_bloodboil(
PlayerbotAI* botAI) { return new GurtoggBloodboilBossCastsBloodboilTrigger(botAI); }
static Trigger* gurtogg_bloodboil_bot_has_fel_rage(
PlayerbotAI* botAI) { return new GurtoggBloodboilBotHasFelRageTrigger(botAI); }
static Trigger* gurtogg_bloodboil_need_to_manage_phase_timer(
PlayerbotAI* botAI) { return new GurtoggBloodboilNeedToManagePhaseTimerTrigger(botAI); }
// Reliquary of Souls
static Trigger* reliquary_of_souls_aggro_resets_upon_phase_change(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsAggroResetsUponPhaseChangeTrigger(botAI); }
static Trigger* reliquary_of_souls_essence_of_suffering_fixates_on_closest_target(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsEssenceOfSufferingFixatesOnClosestTargetTrigger(botAI); }
static Trigger* reliquary_of_souls_essence_of_suffering_disables_healing(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsEssenceOfSufferingDisablesHealingTrigger(botAI); }
static Trigger* reliquary_of_souls_essence_of_desire_has_rune_shield(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsEssenceOfDesireHasRuneShieldTrigger(botAI); }
static Trigger* reliquary_of_souls_essence_of_desire_casting_deaden(
PlayerbotAI* botAI) { return new ReliquaryOfSoulsEssenceOfDesireCastingDeadenTrigger(botAI); }
// Mother Shahraz
static Trigger* mother_shahraz_pulling_boss(
PlayerbotAI* botAI) { return new MotherShahrazPullingBossTrigger(botAI); }
static Trigger* mother_shahraz_boss_engaged_by_tanks(
PlayerbotAI* botAI) { return new MotherShahrazBossEngagedByTanksTrigger(botAI); }
static Trigger* mother_shahraz_tanks_are_positioning_boss(
PlayerbotAI* botAI) { return new MotherShahrazTanksArePositioningBossTrigger(botAI); }
static Trigger* mother_shahraz_sinister_beam_knocks_back_players(
PlayerbotAI* botAI) { return new MotherShahrazSinisterBeamKnocksBackPlayersTrigger(botAI); }
static Trigger* mother_shahraz_bots_are_linked_by_fatal_attraction(
PlayerbotAI* botAI) { return new MotherShahrazBotsAreLinkedByFatalAttractionTrigger(botAI); }
// Illidari Council
static Trigger* illidari_council_pulling_bosses(
PlayerbotAI* botAI) { return new IllidariCouncilPullingBossesTrigger(botAI); }
static Trigger* illidari_council_gathios_engaged_by_main_tank(
PlayerbotAI* botAI) { return new IllidariCouncilGathiosEngagedByMainTankTrigger(botAI); }
static Trigger* illidari_council_gathios_casting_judgement_of_command(
PlayerbotAI* botAI) { return new IllidariCouncilGathiosCastingJudgementOfCommandTrigger(botAI); }
static Trigger* illidari_council_malande_engaged_by_first_assist_tank(
PlayerbotAI* botAI) { return new IllidariCouncilMalandeEngagedByFirstAssistTankTrigger(botAI); }
static Trigger* illidari_council_darkshadow_engaged_by_second_assist_tank(
PlayerbotAI* botAI) { return new IllidariCouncilDarkshadowEngagedBySecondAssistTankTrigger(botAI); }
static Trigger* illidari_council_zerevor_engaged_by_mage_tank(
PlayerbotAI* botAI) { return new IllidariCouncilZerevorEngagedByMageTankTrigger(botAI); }
static Trigger* illidari_council_mage_tank_needs_dedicated_healer(
PlayerbotAI* botAI) { return new IllidariCouncilMageTankNeedsDedicatedHealerTrigger(botAI); }
static Trigger* illidari_council_zerevor_casts_dangerous_aoes(
PlayerbotAI* botAI) { return new IllidariCouncilZerevorCastsDangerousAoesTrigger(botAI); }
static Trigger* illidari_council_pets_screw_up_the_pull(
PlayerbotAI* botAI) { return new IllidariCouncilPetsScrewUpThePullTrigger(botAI); }
static Trigger* illidari_council_determining_dps_assignments(
PlayerbotAI* botAI) { return new IllidariCouncilDeterminingDpsAssignmentsTrigger(botAI); }
static Trigger* illidari_council_need_to_manage_dps_timer(
PlayerbotAI* botAI) { return new IllidariCouncilNeedToManageDpsTimerTrigger(botAI); }
// Illidan Stormrage <The Betrayer>
static Trigger* illidan_stormrage_tank_needs_aggro(
PlayerbotAI* botAI) { return new IllidanStormrageTankNeedsAggroTrigger(botAI); }
static Trigger* illidan_stormrage_boss_casts_flame_crash_in_front_of_main_tank(
PlayerbotAI* botAI) { return new IllidanStormrageBossCastsFlameCrashInFrontOfMainTankTrigger(botAI); }
static Trigger* illidan_stormrage_bot_has_parasitic_shadowfiend(
PlayerbotAI* botAI) { return new IllidanStormrageBotHasParasiticShadowfiendTrigger(botAI); }
static Trigger* illidan_stormrage_parasitic_shadowfiends_run_wild(
PlayerbotAI* botAI) { return new IllidanStormrageParasiticShadowfiendsRunWildTrigger(botAI); }
static Trigger* illidan_stormrage_boss_summoned_flames_of_azzinoth(
PlayerbotAI* botAI) { return new IllidanStormrageBossSummonedFlamesOfAzzinothTrigger(botAI); }
static Trigger* illidan_stormrage_pets_die_to_fire(
PlayerbotAI* botAI) { return new IllidanStormragePetsDieToFireTrigger(botAI); }
static Trigger* illidan_stormrage_grate_is_safe_from_flames(
PlayerbotAI* botAI) { return new IllidanStormrageGrateIsSafeFromFlamesTrigger(botAI); }
static Trigger* illidan_stormrage_bot_struck_by_dark_barrage(
PlayerbotAI* botAI) { return new IllidanStormrageBotStruckByDarkBarrageTrigger(botAI); }
static Trigger* illidan_stormrage_boss_is_preparing_to_land(
PlayerbotAI* botAI) { return new IllidanStormrageBossIsPreparingToLandTrigger(botAI); }
static Trigger* illidan_stormrage_boss_deals_splash_damage(
PlayerbotAI* botAI) { return new IllidanStormrageBossDealsSplashDamageTrigger(botAI); }
static Trigger* illidan_stormrage_this_expansion_hates_melee(
PlayerbotAI* botAI) { return new IllidanStormrageThisExpansionHatesMeleeTrigger(botAI); }
static Trigger* illidan_stormrage_boss_transforms_into_demon(
PlayerbotAI* botAI) { return new IllidanStormrageBossTransformsIntoDemonTrigger(botAI); }
static Trigger* illidan_stormrage_boss_spawns_adds(
PlayerbotAI* botAI) { return new IllidanStormrageBossSpawnsAddsTrigger(botAI); }
static Trigger* illidan_stormrage_maiev_placed_shadow_trap(
PlayerbotAI* botAI) { return new IllidanStormrageMaievPlacedShadowTrapTrigger(botAI); }
static Trigger* illidan_stormrage_need_to_manage_dps_timer_and_rti(
PlayerbotAI* botAI) { return new IllidanStormrageNeedToManageDpsTimerAndRtiTrigger(botAI); }
static Trigger* illidan_stormrage_need_to_clear_hazards_between_phases(
PlayerbotAI* botAI) { return new IllidanStormrageNeedToClearHazardsBetweenPhasesTrigger(botAI); }
static Trigger* illidan_stormrage_cheat(
PlayerbotAI* botAI) { return new IllidanStormrageCheatTrigger(botAI); }
};
#endif

View File

@ -0,0 +1,253 @@
/*
* 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 "RaidBlackTempleStrategy.h"
#include "RaidBlackTempleMultipliers.h"
void RaidBlackTempleStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// General
triggers.push_back(new TriggerNode("black temple bot is not in combat", {
NextAction("black temple erase timers and trackers", ACTION_EMERGENCY + 11) }));
// High Warlord Naj'entus
triggers.push_back(new TriggerNode("high warlord naj'entus pulling boss", {
NextAction("high warlord naj'entus misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("high warlord naj'entus boss engaged by tanks", {
NextAction("high warlord naj'entus tanks position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("high warlord naj'entus casts needle spines", {
NextAction("high warlord naj'entus disperse ranged", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("high warlord naj'entus player is impaled", {
NextAction("high warlord naj'entus remove impaling spine", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("high warlord naj'entus boss has tidal shield", {
NextAction("high warlord naj'entus throw impaling spine", ACTION_RAID + 2) }));
// Supremus
triggers.push_back(new TriggerNode("supremus pulling boss or changing phase", {
NextAction("supremus misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("supremus boss engaged by ranged", {
NextAction("supremus disperse ranged", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("supremus boss is fixated on bot", {
NextAction("supremus kite boss", ACTION_EMERGENCY + 7) }));
triggers.push_back(new TriggerNode("supremus volcano is nearby", {
NextAction("supremus move away from volcanos", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("supremus need to manage phase timer", {
NextAction("supremus manage phase timer", ACTION_EMERGENCY + 10) }));
// Shade of Akama
triggers.push_back(new TriggerNode("shade of akama killing channelers starts phase 2", {
NextAction("shade of akama melee dps prioritize channelers", ACTION_RAID + 1) }));
// Teron Gorefiend
triggers.push_back(new TriggerNode("teron gorefiend pulling boss", {
NextAction("teron gorefiend misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("teron gorefiend boss engaged by tanks", {
NextAction("teron gorefiend tanks position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("teron gorefiend boss engaged by ranged", {
NextAction("teron gorefiend position ranged on balcony", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("teron gorefiend boss is casting shadow of death", {
NextAction("teron gorefiend avoid shadow of death", ACTION_EMERGENCY + 10) }));
triggers.push_back(new TriggerNode("teron gorefiend bot has shadow of death", {
NextAction("teron gorefiend move to corner to die", ACTION_EMERGENCY + 10) }));
triggers.push_back(new TriggerNode("teron gorefiend bot transformed into vengeful spirit", {
NextAction("teron gorefiend control and destroy shadowy constructs", ACTION_EMERGENCY + 10) }));
// Gurtogg Bloodboil
triggers.push_back(new TriggerNode("gurtogg bloodboil pulling boss", {
NextAction("gurtogg bloodboil misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("gurtogg bloodboil boss engaged by tanks", {
NextAction("gurtogg bloodboil tanks position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("gurtogg bloodboil boss casts bloodboil", {
NextAction("gurtogg bloodboil rotate ranged groups", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("gurtogg bloodboil bot has fel rage", {
NextAction("gurtogg bloodboil ranged move away from enraged player", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("gurtogg bloodboil need to manage phase timer", {
NextAction("gurtogg bloodboil manage phase timer", ACTION_EMERGENCY + 10) }));
// Reliquary of Souls
triggers.push_back(new TriggerNode("reliquary of souls aggro resets upon phase change", {
NextAction("reliquary of souls misdirect boss to main tank", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("reliquary of souls essence of suffering fixates on closest target", {
NextAction("reliquary of souls adjust distance from suffering", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("reliquary of souls essence of suffering disables healing", {
NextAction("reliquary of souls healers dps suffering", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("reliquary of souls essence of desire has rune shield", {
NextAction("reliquary of souls spellsteal rune shield", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("reliquary of souls essence of desire casting deaden", {
NextAction("reliquary of souls spell reflect deaden", ACTION_EMERGENCY + 6) }));
// Mother Shahraz
triggers.push_back(new TriggerNode("mother shahraz pulling boss", {
NextAction("mother shahraz misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("mother shahraz boss engaged by tanks", {
NextAction("mother shahraz tanks position boss under pillar", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("mother shahraz tanks are positioning boss", {
NextAction("mother shahraz melee dps wait at safe position", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("mother shahraz sinister beam knocks back players", {
NextAction("mother shahraz position ranged under pillar", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("mother shahraz bots are linked by fatal attraction", {
NextAction("mother shahraz run away to break fatal attraction", ACTION_EMERGENCY + 10) }));
// Illidari Council
triggers.push_back(new TriggerNode("illidari council pulling bosses", {
NextAction("illidari council misdirect bosses to tanks", ACTION_RAID + 4) }));
triggers.push_back(new TriggerNode("illidari council gathios engaged by main tank", {
NextAction("illidari council main tank position gathios", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidari council gathios casting judgement of command", {
NextAction("illidari council main tank reflect judgement of command", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("illidari council malande engaged by first assist tank", {
NextAction("illidari council first assist tank focus malande", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidari council darkshadow engaged by second assist tank", {
NextAction("illidari council second assist tank position darkshadow", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidari council zerevor engaged by mage tank", {
NextAction("illidari council mage tank position zerevor", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("illidari council mage tank needs dedicated healer", {
NextAction("illidari council position mage tank healer", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidari council zerevor casts dangerous aoes", {
NextAction("illidari council disperse ranged", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("illidari council pets screw up the pull", {
NextAction("illidari council command pets to attack gathios", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("illidari council determining dps assignments", {
NextAction("illidari council assign dps targets", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidari council need to manage dps timer", {
NextAction("illidari council manage dps timer", ACTION_EMERGENCY + 10) }));
// Illidan Stormrage <The Betrayer>
triggers.push_back(new TriggerNode("illidan stormrage tank needs aggro", {
NextAction("illidan stormrage misdirect to tank", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("illidan stormrage boss casts flame crash in front of main tank", {
NextAction("illidan stormrage main tank reposition boss", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("illidan stormrage bot has parasitic shadowfiend", {
NextAction("illidan stormrage isolate bot with parasite", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("illidan stormrage parasitic shadowfiends run wild", {
NextAction("illidan stormrage set earthbind totem", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("illidan stormrage boss summoned flames of azzinoth", {
NextAction("illidan stormrage assist tanks handle flames of azzinoth", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("illidan stormrage pets die to fire", {
NextAction("illidan stormrage control pet aggression", ACTION_RAID + 4) }));
triggers.push_back(new TriggerNode("illidan stormrage grate is safe from flames", {
NextAction("illidan stormrage position above grate", ACTION_EMERGENCY + 2) }));
triggers.push_back(new TriggerNode("illidan stormrage bot struck by dark barrage", {
NextAction("illidan stormrage remove dark barrage", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("illidan stormrage boss is preparing to land", {
NextAction("illidan stormrage move away from landing point", ACTION_EMERGENCY + 3) }));
triggers.push_back(new TriggerNode("illidan stormrage boss deals splash damage", {
NextAction("illidan stormrage disperse ranged", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("illidan stormrage this expansion hates melee", {
NextAction("illidan stormrage melee go somewhere to not die", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("illidan stormrage boss transforms into demon", {
NextAction("illidan stormrage warlock tank handle demon boss", ACTION_EMERGENCY + 9) }));
triggers.push_back(new TriggerNode("illidan stormrage boss spawns adds", {
NextAction("illidan stormrage dps prioritize adds", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("illidan stormrage maiev placed shadow trap", {
NextAction("illidan stormrage use shadow trap", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("illidan stormrage need to manage dps timer and rti", {
NextAction("illidan stormrage manage dps timer and rti", ACTION_EMERGENCY + 11) }));
triggers.push_back(new TriggerNode("illidan stormrage need to clear hazards between phases", {
NextAction("illidan stormrage destroy hazards", ACTION_EMERGENCY + 10) }));
triggers.push_back(new TriggerNode("illidan stormrage cheat", {
NextAction("illidan stormrage handle adds cheat", ACTION_EMERGENCY + 10) }));
}
void RaidBlackTempleStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
// High Warlord Naj'entus
multipliers.push_back(new HighWarlordNajentusDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new HighWarlordNajentusDisableCombatFormationMoveMultiplier(botAI));
// Supremus
multipliers.push_back(new SupremusDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new SupremusFocusOnAvoidanceInPhase2Multiplier(botAI));
multipliers.push_back(new SupremusHitboxIsBuggedMultiplier(botAI));
// Teron Gorefiend
multipliers.push_back(new TeronGorefiendDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new TeronGorefiendControlMovementMultiplier(botAI));
multipliers.push_back(new TeronGorefiendMarkedBotOnlyMoveToDieMultiplier(botAI));
multipliers.push_back(new TeronGorefiendSpiritsAttackOnlyShadowyConstructsMultiplier(botAI));
multipliers.push_back(new TeronGorefiendDisableAttackingConstructsMultiplier(botAI));
// Gurtogg Bloodboil
multipliers.push_back(new GurtoggBloodboilDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new GurtoggBloodboilControlMovementMultiplier(botAI));
// Reliquary of Souls
multipliers.push_back(new ReliquaryOfSoulsDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new ReliquaryOfSoulsDontWasteHealingMultiplier(botAI));
// Mother Shahraz
multipliers.push_back(new MotherShahrazDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new MotherShahrazControlMovementMultiplier(botAI));
multipliers.push_back(new MotherShahrazBotsWithFatalAttractionOnlyRunAwayMultiplier(botAI));
// Illidari Council
multipliers.push_back(new IllidariCouncilDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new IllidariCouncilDisableTankActionsMultiplier(botAI));
multipliers.push_back(new IllidariCouncilControlMovementMultiplier(botAI));
multipliers.push_back(new IllidariCouncilControlMisdirectionMultiplier(botAI));
multipliers.push_back(new IllidariCouncilDisableArcaneShotOnZerevorMultiplier(botAI));
multipliers.push_back(new IllidariCouncilDisableIceBlockMultiplier(botAI));
multipliers.push_back(new IllidariCouncilWaitForDpsMultiplier(botAI));
// Illidan Stormrage <The Betrayer>
multipliers.push_back(new IllidanStormrageDelayDpsCooldownsMultiplier(botAI));
multipliers.push_back(new IllidanStormrageControlTankActionsMultiplier(botAI));
multipliers.push_back(new IllidanStormrageDisableDefaultTargetingMultiplier(botAI));
multipliers.push_back(new IllidanStormrageControlNonTankMovementMultiplier(botAI));
multipliers.push_back(new IllidanStormrageUseEarthbindTotemMultiplier(botAI));
multipliers.push_back(new IllidanStormrageWaitForDpsMultiplier(botAI));
}

View File

@ -0,0 +1,22 @@
/*
* 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_RAIDBLACKTEMPLESTRATEGY_H_
#define _PLAYERBOT_RAIDBLACKTEMPLESTRATEGY_H_
#include "Strategy.h"
class RaidBlackTempleStrategy : public Strategy
{
public:
RaidBlackTempleStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::string const getName() override { return "blacktemple"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@ -0,0 +1,863 @@
/*
* 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 "RaidBlackTempleTriggers.h"
#include "AiFactory.h"
#include "Playerbots.h"
#include "RaidBlackTempleActions.h"
#include "RaidBlackTempleHelpers.h"
#include "RaidBossHelpers.h"
#include "SharedDefines.h"
using namespace BlackTempleHelpers;
// General
bool BlackTempleBotIsNotInCombatTrigger::IsActive()
{
return !bot->IsInCombat() && bot->GetMapId() == BLACK_TEMPLE_MAP_ID;
}
// High Warlord Naj'entus
bool HighWarlordNajentusPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* najentus = AI_VALUE2(Unit*, "find target", "high warlord naj'entus");
return najentus && najentus->GetHealthPct() > 95.0f;
}
bool HighWarlordNajentusBossEngagedByTanksTrigger::IsActive()
{
return botAI->IsTank(bot) &&
AI_VALUE2(Unit*, "find target", "high warlord naj'entus");
}
bool HighWarlordNajentusCastsNeedleSpinesTrigger::IsActive()
{
return botAI->IsRanged(bot) &&
AI_VALUE2(Unit*, "find target", "high warlord naj'entus");
}
bool HighWarlordNajentusPlayerIsImpaledTrigger::IsActive()
{
if (botAI->IsTank(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "high warlord naj'entus"))
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
Player* impaledPlayer = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || member == bot)
continue;
if (member->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_IMPALING_SPINE)))
{
impaledPlayer = member;
break;
}
}
Player* closestBot = nullptr;
float closestDist = std::numeric_limits<float>::max();
if (impaledPlayer)
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member == impaledPlayer ||
!GET_PLAYERBOT_AI(member) || botAI->IsTank(member))
{
continue;
}
float dist = member->GetDistance(impaledPlayer);
if (dist < closestDist)
{
closestDist = dist;
closestBot = member;
}
}
}
return closestBot == bot;
}
bool HighWarlordNajentusBossHasTidalShieldTrigger::IsActive()
{
Unit* najentus = AI_VALUE2(Unit*, "find target", "high warlord naj'entus");
if (!najentus || !najentus->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_TIDAL_SHIELD)))
{
return false;
}
return botAI->HasItemInInventory(
static_cast<uint32>(BlackTempleItems::ITEM_NAJENTUS_SPINE));
}
// Supremus
bool SupremusPullingBossOrChangingPhaseTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* supremus = AI_VALUE2(Unit*, "find target", "supremus");
if (!supremus)
return false;
auto it = supremusPhaseTimer.find(supremus->GetMap()->GetInstanceId());
if (it == supremusPhaseTimer.end())
return false;
const time_t now = time(nullptr);
const time_t elapsed = now - it->second;
// Active during first 10 seconds, or during 60-70, 120-130, etc.
return (elapsed < 10) || ((elapsed % 60) < 10 && elapsed >= 60);
}
bool SupremusBossEngagedByRangedTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
Unit* supremus = AI_VALUE2(Unit*, "find target", "supremus");
return supremus && !supremus->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SNARE_SELF));
}
bool SupremusBossIsFixatedOnBotTrigger::IsActive()
{
Unit* supremus = AI_VALUE2(Unit*, "find target", "supremus");
return supremus && supremus->GetVictim() == bot &&
supremus->HasAura(static_cast<uint32>(
BlackTempleSpells::SPELL_SNARE_SELF));
}
bool SupremusVolcanoIsNearbyTrigger::IsActive()
{
return AI_VALUE2(Unit*, "find target", "supremus") &&
HasSupremusVolcanoNearby(botAI, bot);
}
bool SupremusNeedToManagePhaseTimerTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "supremus"))
return false;
return IsMechanicTrackerBot(botAI, bot, BLACK_TEMPLE_MAP_ID);
}
// Shade of Akama
bool ShadeOfAkamaKillingChannelersStartsPhase2Trigger::IsActive()
{
if (!botAI->IsDps(bot) || !botAI->IsMelee(bot))
return false;
constexpr float searchRadius = 30.0f;
Unit* channeler = bot->FindNearestCreature(
static_cast<uint32>(BlackTempleNpcs::NPC_ASHTONGUE_CHANNELER),
searchRadius, true);
return channeler && !channeler->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
// Teron Gorefiend
bool TeronGorefiendPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* gorefiend =
AI_VALUE2(Unit*, "find target", "teron gorefiend");
return gorefiend && gorefiend->GetHealthPct() > 95.0f;
}
bool TeronGorefiendBossEngagedByTanksTrigger::IsActive()
{
return botAI->IsTank(bot) &&
AI_VALUE2(Unit*, "find target", "teron gorefiend");
}
bool TeronGorefiendBossEngagedByRangedTrigger::IsActive()
{
return botAI->IsRanged(bot) &&
AI_VALUE2(Unit*, "find target", "teron gorefiend");
}
bool TeronGorefiendBossIsCastingShadowOfDeathTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER && bot->getClass() != CLASS_MAGE &&
bot->getClass() != CLASS_PALADIN && bot->getClass() != CLASS_ROGUE)
{
return false;
}
Unit* gorefiend = AI_VALUE2(Unit*, "find target", "teron gorefiend");
if (!gorefiend)
return false;
if (botAI->HasAura("feign death", bot))
{
botAI->RemoveAura("feign death");
return true;
}
else if (botAI->HasAura("ice block", bot))
{
botAI->RemoveAura("ice block");
return true;
}
else if (!botAI->IsHeal(bot) && botAI->HasAura("divine shield", bot))
{
botAI->RemoveAura("divine shield");
return true;
}
if (!gorefiend->HasUnitState(UNIT_STATE_CASTING))
return false;
Spell* spell = gorefiend->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell || spell->m_spellInfo->Id !=
static_cast<uint32>(BlackTempleSpells::SPELL_SHADOW_OF_DEATH))
{
return false;
}
Unit* target = spell->m_targets.GetUnitTarget();
return target && target->GetGUID() == bot->GetGUID();
}
bool TeronGorefiendBotHasShadowOfDeathTrigger::IsActive()
{
Aura* aura = bot->GetAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SHADOW_OF_DEATH));
return aura && aura->GetDuration() < 12000;
}
bool TeronGorefiendBotTransformedIntoVengefulSpiritTrigger::IsActive()
{
return bot->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SPIRITUAL_VENGEANCE));
}
// Gurtogg Bloodboil
bool GurtoggBloodboilPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* gurtogg = AI_VALUE2(Unit*, "find target", "gurtogg bloodboil");
if (!gurtogg)
return false;
auto it = gurtoggPhaseTimer.find(gurtogg->GetMap()->GetInstanceId());
if (it == gurtoggPhaseTimer.end())
return false;
const time_t elapsed = std::time(nullptr) - it->second;
return elapsed < 10;
}
bool GurtoggBloodboilBossEngagedByTanksTrigger::IsActive()
{
if (!botAI->IsTank(bot))
return false;
Unit* gurtogg = AI_VALUE2(Unit*, "find target", "gurtogg bloodboil");
return gurtogg && !gurtogg->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_BOSS_FEL_RAGE));
}
bool GurtoggBloodboilBossCastsBloodboilTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
Unit* gurtogg = AI_VALUE2(Unit*, "find target", "gurtogg bloodboil");
return gurtogg && !gurtogg->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_BOSS_FEL_RAGE));
}
bool GurtoggBloodboilBotHasFelRageTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
Unit* gurtogg = AI_VALUE2(Unit*, "find target", "gurtogg bloodboil");
if (!gurtogg || !gurtogg->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_BOSS_FEL_RAGE)))
{
return false;
}
if (Group* group = bot->GetGroup())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->HasAura
(static_cast<uint32>(BlackTempleSpells::SPELL_PLAYER_FEL_RAGE)))
{
return true;
}
}
}
return false;
}
bool GurtoggBloodboilNeedToManagePhaseTimerTrigger::IsActive()
{
return AI_VALUE2(Unit*, "find target", "gurtogg bloodboil") &&
IsMechanicTrackerBot(botAI, bot, BLACK_TEMPLE_MAP_ID);
}
// Reliquary of Souls
bool ReliquaryOfSoulsAggroResetsUponPhaseChangeTrigger::IsActive()
{
return bot->getClass() == CLASS_HUNTER &&
AI_VALUE2(Unit*, "find target", "reliquary of the lost");
}
bool ReliquaryOfSoulsEssenceOfSufferingFixatesOnClosestTargetTrigger::IsActive()
{
return AI_VALUE2(Unit*, "find target", "essence of suffering");
}
bool ReliquaryOfSoulsEssenceOfSufferingDisablesHealingTrigger::IsActive()
{
if (!botAI->IsHeal(bot))
return false;
if (bot->getClass() == CLASS_PRIEST &&
AiFactory::GetPlayerSpecTab(bot) == PRIEST_TAB_DISCIPLINE)
{
return false;
}
return AI_VALUE2(Unit*, "find target", "essence of suffering");
}
bool ReliquaryOfSoulsEssenceOfDesireHasRuneShieldTrigger::IsActive()
{
if (bot->getClass() != CLASS_MAGE)
return false;
Unit* desire = AI_VALUE2(Unit*, "find target", "essence of desire");
return desire && desire->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_RUNE_SHIELD));
}
bool ReliquaryOfSoulsEssenceOfDesireCastingDeadenTrigger::IsActive()
{
if (!botAI->IsTank(bot) || bot->getClass() != CLASS_WARRIOR)
return false;
Unit* desire = AI_VALUE2(Unit*, "find target", "essence of desire");
if (!desire || !desire->HasUnitState(UNIT_STATE_CASTING))
return false;
Spell* spell = desire->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell || spell->m_spellInfo->Id !=
static_cast<uint32>(BlackTempleSpells::SPELL_DEADEN))
{
return false;
}
Unit* target = spell->m_targets.GetUnitTarget();
return target && target->GetGUID() == bot->GetGUID();
}
// Mother Shahraz
bool MotherShahrazPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* shahraz = AI_VALUE2(Unit*, "find target", "mother shahraz");
return shahraz && shahraz->GetHealthPct() > 95.0f;
}
bool MotherShahrazBossEngagedByTanksTrigger::IsActive()
{
if (!botAI->IsTank(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "mother shahraz"))
return false;
return !bot->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_FATAL_ATTRACTION));
}
bool MotherShahrazTanksArePositioningBossTrigger::IsActive()
{
if (!botAI->IsMelee(bot) || !botAI->IsDps(bot))
return false;
Unit* shahraz = AI_VALUE2(Unit*, "find target", "mother shahraz");
if (!shahraz || shahraz->GetHealthPct() < 90.0f)
return false;
TankPositionState tankState = GetShahrazTankPositionState(botAI, bot);
return tankState != TankPositionState::Positioned;
}
bool MotherShahrazSinisterBeamKnocksBackPlayersTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "mother shahraz"))
return false;
return !bot->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_FATAL_ATTRACTION));
}
bool MotherShahrazBotsAreLinkedByFatalAttractionTrigger::IsActive()
{
return bot->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_FATAL_ATTRACTION));
}
// Illidari Council
bool IllidariCouncilPullingBossesTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* gathios = AI_VALUE2(Unit*, "find target", "gathios the shatterer");
return gathios && gathios->GetHealthPct() > 95.0f;
}
bool IllidariCouncilGathiosEngagedByMainTankTrigger::IsActive()
{
return botAI->IsMainTank(bot) &&
AI_VALUE2(Unit*, "find target", "gathios the shatterer");
}
bool IllidariCouncilGathiosCastingJudgementOfCommandTrigger::IsActive()
{
if (bot->getClass() != CLASS_WARRIOR || !botAI->IsMainTank(bot))
return false;
Unit* gathios = AI_VALUE2(Unit*, "find target", "gathios the shatterer");
if (!gathios || !gathios->HasUnitState(UNIT_STATE_CASTING) || !gathios->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SEAL_OF_COMMAND)))
{
return false;
}
Spell* spell = gathios->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell || spell->m_spellInfo->Id !=
static_cast<uint32>(BlackTempleSpells::SPELL_JUDGEMENT))
{
return false;
}
Unit* target = spell->m_targets.GetUnitTarget();
return target && target->GetGUID() == bot->GetGUID();
}
bool IllidariCouncilMalandeEngagedByFirstAssistTankTrigger::IsActive()
{
return botAI->IsAssistTankOfIndex(bot, 0, false) &&
AI_VALUE2(Unit*, "find target", "lady malande");
}
bool IllidariCouncilDarkshadowEngagedBySecondAssistTankTrigger::IsActive()
{
if (!botAI->IsAssistTankOfIndex(bot, 1, false))
return false;
Unit* darkshadow = AI_VALUE2(Unit*, "find target", "veras darkshadow");
return darkshadow && !darkshadow->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_VANISH));
}
bool IllidariCouncilZerevorEngagedByMageTankTrigger::IsActive()
{
if (bot->getClass() != CLASS_MAGE || GetZerevorMageTank(bot) != bot)
return false;
return AI_VALUE2(Unit*, "find target", "high nethermancer zerevor");
}
bool IllidariCouncilMageTankNeedsDedicatedHealerTrigger::IsActive()
{
return botAI->IsAssistHealOfIndex(bot, 0, true) &&
AI_VALUE2(Unit*, "find target", "high nethermancer zerevor");
}
bool IllidariCouncilZerevorCastsDangerousAoesTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "high nethermancer zerevor"))
return false;
return !HasDangerousCouncilAura(bot);
}
bool IllidariCouncilPetsScrewUpThePullTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER && bot->getClass() != CLASS_WARLOCK)
return false;
Pet* pet = bot->GetPet();
if (!pet || !pet->IsAlive())
return false;
return AI_VALUE2(Unit*, "find target", "gathios the shatterer");
}
bool IllidariCouncilDeterminingDpsAssignmentsTrigger::IsActive()
{
if (botAI->IsHeal(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "gathios the shatterer"))
return false;
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, false) ||
GetZerevorMageTank(bot) == bot)
{
return false;
}
Unit* darkshadow = AI_VALUE2(Unit*, "find target", "veras darkshadow");
if (botAI->IsTank(bot) && botAI->IsAssistTankOfIndex(bot, 1, false) &&
darkshadow && !darkshadow->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_VANISH)))
{
return false;
}
return true;
}
bool IllidariCouncilNeedToManageDpsTimerTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "gathios the shatterer"))
return false;
return IsMechanicTrackerBot(
botAI, bot, BLACK_TEMPLE_MAP_ID, GetZerevorMageTank(bot));
}
// Illidan Stormrage <The Betrayer>
bool IllidanStormrageTankNeedsAggroTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
return illidan && illidan->GetHealth() > 1;
}
bool IllidanStormrageBossCastsFlameCrashInFrontOfMainTankTrigger::IsActive()
{
if (!botAI->IsMainTank(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan)
return false;
int phase = GetIllidanPhase(illidan);
return phase == 1 || phase == 3 || phase == 5;
}
bool IllidanStormrageBotHasParasiticShadowfiendTrigger::IsActive()
{
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1 ||
illidan->GetVictim() == bot)
{
return false;
}
int phase = GetIllidanPhase(illidan);
if (phase == 2 || phase == 4)
return false;
if (botAI->IsMainTank(bot))
return false;
if (phase == 5 && FindNearestTrap(botAI, bot))
return false;
Player* infected = GetBotWithParasiticShadowfiend(bot);
if (!infected)
return false;
if (infected == bot ||
(phase != 1 && bot->getClass() == CLASS_HUNTER))
{
return true;
}
return false;
}
bool IllidanStormrageParasiticShadowfiendsRunWildTrigger::IsActive()
{
if (bot->getClass() != CLASS_SHAMAN)
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1 || GetIllidanPhase(illidan) == 2)
return false;
ObjectGuid guid = bot->m_SummonSlot[SUMMON_SLOT_TOTEM_EARTH];
if (guid.IsEmpty())
return true;
Creature* totem = bot->GetMap()->GetCreature(guid);
return !totem || totem->GetDistance(bot) > 20.0f ||
totem->GetUInt32Value(UNIT_CREATED_BY_SPELL) !=
static_cast<uint32>(BlackTempleSpells::SPELL_EARTHBIND_TOTEM);
}
bool IllidanStormrageBossSummonedFlamesOfAzzinothTrigger::IsActive()
{
if (!botAI->IsAssistTankOfIndex(bot, 0, true) &&
!botAI->IsAssistTankOfIndex(bot, 1, true))
{
return false;
}
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
return illidan && GetIllidanPhase(illidan) == 2;
}
bool IllidanStormragePetsDieToFireTrigger::IsActive()
{
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return false;
Pet* pet = bot->GetPet();
return pet && pet->IsAlive();
}
bool IllidanStormrageGrateIsSafeFromFlamesTrigger::IsActive()
{
if (botAI->IsAssistTankOfIndex(bot, 0, true) ||
botAI->IsAssistTankOfIndex(bot, 1, true))
{
return false;
}
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
return illidan && GetIllidanPhase(illidan) == 2;
}
bool IllidanStormrageBotStruckByDarkBarrageTrigger::IsActive()
{
if (bot->getClass() != CLASS_MAGE && bot->getClass() != CLASS_PALADIN &&
bot->getClass() != CLASS_ROGUE)
{
return false;
}
if (!AI_VALUE2(Unit*, "find target", "illidan stormrage"))
return false;
if (botAI->HasAura("ice block", bot))
{
botAI->RemoveAura("ice block");
return true;
}
else if (!botAI->IsHeal(bot) && botAI->HasAura("divine shield", bot))
{
botAI->RemoveAura("divine shield");
return true;
}
return bot->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_DARK_BARRAGE));
}
bool IllidanStormrageBossIsPreparingToLandTrigger::IsActive()
{
if (botAI->IsMainTank(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
return illidan && GetIllidanPhase(illidan) == 0;
}
bool IllidanStormrageBossDealsSplashDamageTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->HasAura
(static_cast<uint32>(BlackTempleSpells::SPELL_CAGED)))
{
return false;
}
int phase = GetIllidanPhase(illidan);
if (phase == 4 && GetIllidanWarlockTank(bot) == bot)
return false;
return phase == 3 || phase == 4 || phase == 5;
}
bool IllidanStormrageThisExpansionHatesMeleeTrigger::IsActive()
{
if (!botAI->IsMelee(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
return illidan && GetIllidanPhase(illidan) == 4;
}
bool IllidanStormrageBossTransformsIntoDemonTrigger::IsActive()
{
if (bot->getClass() != CLASS_WARLOCK)
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || GetIllidanPhase(illidan) != 4)
return false;
return GetIllidanWarlockTank(bot) == bot;
}
bool IllidanStormrageBossSpawnsAddsTrigger::IsActive()
{
if (botAI->IsHeal(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return false;
if (botAI->IsTank(bot) && GetIllidanPhase(illidan) != 4)
return false;
return true;
}
bool IllidanStormrageMaievPlacedShadowTrapTrigger::IsActive()
{
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1 ||
GetIllidanPhase(illidan) != 5)
{
return false;
}
GameObject* trap = FindNearestTrap(botAI, bot);
if (!trap)
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
Player* closestBot = nullptr;
float closestDist = std::numeric_limits<float>::max();
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || illidan->GetVictim() == member ||
!GET_PLAYERBOT_AI(member) || botAI->IsMainTank(member))
{
continue;
}
float dist = member->GetDistance(trap);
if (dist < closestDist)
{
closestDist = dist;
closestBot = member;
}
}
return closestBot == bot;
}
bool IllidanStormrageNeedToManageDpsTimerAndRtiTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return false;
return IsMechanicTrackerBot(
botAI, bot, BLACK_TEMPLE_MAP_ID, GetIllidanWarlockTank(bot));
}
// Destroying hazards behind phases is not gated behind CheatMask
// The strategy simply cannot work without doing this
bool IllidanStormrageNeedToClearHazardsBetweenPhasesTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan || illidan->GetHealth() == 1)
return false;
int phase = GetIllidanPhase(illidan);
if (phase != 0 && phase != 2 && phase != 4)
return false;
return IsMechanicTrackerBot(
botAI, bot, BLACK_TEMPLE_MAP_ID, GetIllidanWarlockTank(bot));
}
bool IllidanStormrageCheatTrigger::IsActive()
{
if (!botAI->HasCheat(BotCheatMask::raid) || !botAI->IsDps(bot))
return false;
Unit* illidan = AI_VALUE2(Unit*, "find target", "illidan stormrage");
if (!illidan)
return false;
int phase = GetIllidanPhase(illidan);
return phase == 2 || phase == 4;
}

View File

@ -0,0 +1,518 @@
/*
* 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_RAIDBLACKTEMPLETRIGGERS_H
#define _PLAYERBOT_RAIDBLACKTEMPLETRIGGERS_H
#include "Trigger.h"
// General
class BlackTempleBotIsNotInCombatTrigger : public Trigger
{
public:
BlackTempleBotIsNotInCombatTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "black temple bot is not in combat") {}
bool IsActive() override;
};
// High Warlord Naj'entus
class HighWarlordNajentusPullingBossTrigger : public Trigger
{
public:
HighWarlordNajentusPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high warlord naj'entus pulling boss") {}
bool IsActive() override;
};
class HighWarlordNajentusBossEngagedByTanksTrigger : public Trigger
{
public:
HighWarlordNajentusBossEngagedByTanksTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high warlord naj'entus boss engaged by tanks") {}
bool IsActive() override;
};
class HighWarlordNajentusCastsNeedleSpinesTrigger : public Trigger
{
public:
HighWarlordNajentusCastsNeedleSpinesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high warlord naj'entus casts needle spines") {}
bool IsActive() override;
};
class HighWarlordNajentusPlayerIsImpaledTrigger : public Trigger
{
public:
HighWarlordNajentusPlayerIsImpaledTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high warlord naj'entus player is impaled") {}
bool IsActive() override;
};
class HighWarlordNajentusBossHasTidalShieldTrigger : public Trigger
{
public:
HighWarlordNajentusBossHasTidalShieldTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high warlord naj'entus boss has tidal shield") {}
bool IsActive() override;
};
// Supremus
class SupremusPullingBossOrChangingPhaseTrigger : public Trigger
{
public:
SupremusPullingBossOrChangingPhaseTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "supremus pulling boss or changing phase") {}
bool IsActive() override;
};
class SupremusBossEngagedByRangedTrigger : public Trigger
{
public:
SupremusBossEngagedByRangedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "supremus boss engaged by ranged") {}
bool IsActive() override;
};
class SupremusBossIsFixatedOnBotTrigger : public Trigger
{
public:
SupremusBossIsFixatedOnBotTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "supremus boss is fixated on bot") {}
bool IsActive() override;
};
class SupremusVolcanoIsNearbyTrigger : public Trigger
{
public:
SupremusVolcanoIsNearbyTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "supremus volcano is nearby") {}
bool IsActive() override;
};
class SupremusNeedToManagePhaseTimerTrigger : public Trigger
{
public:
SupremusNeedToManagePhaseTimerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "supremus need to manage phase timer") {}
bool IsActive() override;
};
// Shade of Akama
class ShadeOfAkamaKillingChannelersStartsPhase2Trigger : public Trigger
{
public:
ShadeOfAkamaKillingChannelersStartsPhase2Trigger(
PlayerbotAI* botAI) : Trigger(botAI, "shade of akama killing channelers starts phase 2") {}
bool IsActive() override;
};
// Teron Gorefiend
class TeronGorefiendPullingBossTrigger : public Trigger
{
public:
TeronGorefiendPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend pulling boss") {}
bool IsActive() override;
};
class TeronGorefiendBossEngagedByTanksTrigger : public Trigger
{
public:
TeronGorefiendBossEngagedByTanksTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend boss engaged by tanks") {}
bool IsActive() override;
};
class TeronGorefiendBossEngagedByRangedTrigger : public Trigger
{
public:
TeronGorefiendBossEngagedByRangedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend boss engaged by ranged") {}
bool IsActive() override;
};
class TeronGorefiendBossIsCastingShadowOfDeathTrigger : public Trigger
{
public:
TeronGorefiendBossIsCastingShadowOfDeathTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend boss is casting shadow of death") {}
bool IsActive() override;
};
class TeronGorefiendBotHasShadowOfDeathTrigger : public Trigger
{
public:
TeronGorefiendBotHasShadowOfDeathTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend bot has shadow of death") {}
bool IsActive() override;
};
class TeronGorefiendBotTransformedIntoVengefulSpiritTrigger : public Trigger
{
public:
TeronGorefiendBotTransformedIntoVengefulSpiritTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "teron gorefiend bot transformed into vengeful spirit") {}
bool IsActive() override;
};
// Gurtogg Bloodboil
class GurtoggBloodboilPullingBossTrigger : public Trigger
{
public:
GurtoggBloodboilPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "gurtogg bloodboil pulling boss") {}
bool IsActive() override;
};
class GurtoggBloodboilBossEngagedByTanksTrigger : public Trigger
{
public:
GurtoggBloodboilBossEngagedByTanksTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "gurtogg bloodboil boss engaged by tanks") {}
bool IsActive() override;
};
class GurtoggBloodboilBossCastsBloodboilTrigger : public Trigger
{
public:
GurtoggBloodboilBossCastsBloodboilTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "gurtogg bloodboil boss casts bloodboil") {}
bool IsActive() override;
};
class GurtoggBloodboilBotHasFelRageTrigger : public Trigger
{
public:
GurtoggBloodboilBotHasFelRageTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "gurtogg bloodboil bot has fel rage") {}
bool IsActive() override;
};
class GurtoggBloodboilNeedToManagePhaseTimerTrigger : public Trigger
{
public:
GurtoggBloodboilNeedToManagePhaseTimerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "gurtogg bloodboil need to manage phase timer") {}
bool IsActive() override;
};
// Reliquary of Souls
class ReliquaryOfSoulsAggroResetsUponPhaseChangeTrigger : public Trigger
{
public:
ReliquaryOfSoulsAggroResetsUponPhaseChangeTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "reliquary of souls aggro resets upon phase change") {}
bool IsActive() override;
};
class ReliquaryOfSoulsEssenceOfSufferingFixatesOnClosestTargetTrigger : public Trigger
{
public:
ReliquaryOfSoulsEssenceOfSufferingFixatesOnClosestTargetTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "reliquary of souls essence of suffering fixates on closest target") {}
bool IsActive() override;
};
class ReliquaryOfSoulsEssenceOfSufferingDisablesHealingTrigger : public Trigger
{
public:
ReliquaryOfSoulsEssenceOfSufferingDisablesHealingTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "reliquary of souls essence of suffering disables healing") {}
bool IsActive() override;
};
class ReliquaryOfSoulsEssenceOfDesireHasRuneShieldTrigger : public Trigger
{
public:
ReliquaryOfSoulsEssenceOfDesireHasRuneShieldTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "reliquary of souls essence of desire has rune shield") {}
bool IsActive() override;
};
class ReliquaryOfSoulsEssenceOfDesireCastingDeadenTrigger : public Trigger
{
public:
ReliquaryOfSoulsEssenceOfDesireCastingDeadenTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "reliquary of souls essence of desire casting deaden") {}
bool IsActive() override;
};
// Mother Shahraz
class MotherShahrazPullingBossTrigger : public Trigger
{
public:
MotherShahrazPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "mother shahraz pulling boss") {}
bool IsActive() override;
};
class MotherShahrazBossEngagedByTanksTrigger : public Trigger
{
public:
MotherShahrazBossEngagedByTanksTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "mother shahraz boss engaged by tanks") {}
bool IsActive() override;
};
class MotherShahrazTanksArePositioningBossTrigger : public Trigger
{
public:
MotherShahrazTanksArePositioningBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "mother shahraz tanks are positioning boss") {}
bool IsActive() override;
};
class MotherShahrazSinisterBeamKnocksBackPlayersTrigger : public Trigger
{
public:
MotherShahrazSinisterBeamKnocksBackPlayersTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "mother shahraz sinister beam knocks back players") {}
bool IsActive() override;
};
class MotherShahrazBotsAreLinkedByFatalAttractionTrigger : public Trigger
{
public:
MotherShahrazBotsAreLinkedByFatalAttractionTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "mother shahraz bots are linked by fatal attraction") {}
bool IsActive() override;
};
// Illidari Council
class IllidariCouncilPullingBossesTrigger : public Trigger
{
public:
IllidariCouncilPullingBossesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council pulling bosses") {}
bool IsActive() override;
};
class IllidariCouncilGathiosEngagedByMainTankTrigger : public Trigger
{
public:
IllidariCouncilGathiosEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council gathios engaged by main tank") {}
bool IsActive() override;
};
class IllidariCouncilGathiosCastingJudgementOfCommandTrigger : public Trigger
{
public:
IllidariCouncilGathiosCastingJudgementOfCommandTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council gathios casting judgement of command") {}
bool IsActive() override;
};
class IllidariCouncilMalandeEngagedByFirstAssistTankTrigger : public Trigger
{
public:
IllidariCouncilMalandeEngagedByFirstAssistTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council malande engaged by first assist tank") {}
bool IsActive() override;
};
class IllidariCouncilDarkshadowEngagedBySecondAssistTankTrigger : public Trigger
{
public:
IllidariCouncilDarkshadowEngagedBySecondAssistTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council darkshadow engaged by second assist tank") {}
bool IsActive() override;
};
class IllidariCouncilZerevorEngagedByMageTankTrigger : public Trigger
{
public:
IllidariCouncilZerevorEngagedByMageTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council zerevor engaged by mage tank") {}
bool IsActive() override;
};
class IllidariCouncilMageTankNeedsDedicatedHealerTrigger : public Trigger
{
public:
IllidariCouncilMageTankNeedsDedicatedHealerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council mage tank needs dedicated healer") {}
bool IsActive() override;
};
class IllidariCouncilZerevorCastsDangerousAoesTrigger : public Trigger
{
public:
IllidariCouncilZerevorCastsDangerousAoesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council zerevor casts dangerous aoes") {}
bool IsActive() override;
};
class IllidariCouncilPetsScrewUpThePullTrigger : public Trigger
{
public:
IllidariCouncilPetsScrewUpThePullTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council pets screw up the pull") {}
bool IsActive() override;
};
class IllidariCouncilNeedToManageDpsTimerTrigger : public Trigger
{
public:
IllidariCouncilNeedToManageDpsTimerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council need to manage dps timer") {}
bool IsActive() override;
};
class IllidariCouncilDeterminingDpsAssignmentsTrigger : public Trigger
{
public:
IllidariCouncilDeterminingDpsAssignmentsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidari council determining dps assignments") {}
bool IsActive() override;
};
// Illidan Stormrage <The Betrayer>
class IllidanStormrageTankNeedsAggroTrigger : public Trigger
{
public:
IllidanStormrageTankNeedsAggroTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage tank needs aggro") {}
bool IsActive() override;
};
class IllidanStormrageBossCastsFlameCrashInFrontOfMainTankTrigger : public Trigger
{
public:
IllidanStormrageBossCastsFlameCrashInFrontOfMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss casts flame crash in front of main tank") {}
bool IsActive() override;
};
class IllidanStormrageBotHasParasiticShadowfiendTrigger : public Trigger
{
public:
IllidanStormrageBotHasParasiticShadowfiendTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage bot has parasitic shadowfiend") {}
bool IsActive() override;
};
class IllidanStormrageParasiticShadowfiendsRunWildTrigger : public Trigger
{
public:
IllidanStormrageParasiticShadowfiendsRunWildTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage parasitic shadowfiends run wild") {}
bool IsActive() override;
};
class IllidanStormrageBossSummonedFlamesOfAzzinothTrigger : public Trigger
{
public:
IllidanStormrageBossSummonedFlamesOfAzzinothTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss summoned flames of azzinoth") {}
bool IsActive() override;
};
class IllidanStormragePetsDieToFireTrigger : public Trigger
{
public:
IllidanStormragePetsDieToFireTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage pets die to fire") {}
bool IsActive() override;
};
class IllidanStormrageGrateIsSafeFromFlamesTrigger : public Trigger
{
public:
IllidanStormrageGrateIsSafeFromFlamesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage grate is safe from flames") {}
bool IsActive() override;
};
class IllidanStormrageBotStruckByDarkBarrageTrigger : public Trigger
{
public:
IllidanStormrageBotStruckByDarkBarrageTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage bot struck by dark barrage") {}
bool IsActive() override;
};
class IllidanStormrageBossIsPreparingToLandTrigger : public Trigger
{
public:
IllidanStormrageBossIsPreparingToLandTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss is preparing to land") {}
bool IsActive() override;
};
class IllidanStormrageBossDealsSplashDamageTrigger : public Trigger
{
public:
IllidanStormrageBossDealsSplashDamageTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss deals splash damage") {}
bool IsActive() override;
};
class IllidanStormrageThisExpansionHatesMeleeTrigger : public Trigger
{
public:
IllidanStormrageThisExpansionHatesMeleeTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage this expansion hates melee") {}
bool IsActive() override;
};
class IllidanStormrageBossTransformsIntoDemonTrigger : public Trigger
{
public:
IllidanStormrageBossTransformsIntoDemonTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss transforms into demon") {}
bool IsActive() override;
};
class IllidanStormrageBossSpawnsAddsTrigger : public Trigger
{
public:
IllidanStormrageBossSpawnsAddsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage boss spawns adds") {}
bool IsActive() override;
};
class IllidanStormrageMaievPlacedShadowTrapTrigger : public Trigger
{
public:
IllidanStormrageMaievPlacedShadowTrapTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage maiev placed shadow trap") {}
bool IsActive() override;
};
class IllidanStormrageNeedToManageDpsTimerAndRtiTrigger : public Trigger
{
public:
IllidanStormrageNeedToManageDpsTimerAndRtiTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage need to manage dps timer and rti") {}
bool IsActive() override;
};
class IllidanStormrageNeedToClearHazardsBetweenPhasesTrigger : public Trigger
{
public:
IllidanStormrageNeedToClearHazardsBetweenPhasesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage need to clear hazards between phases") {}
bool IsActive() override;
};
class IllidanStormrageCheatTrigger : public Trigger
{
public:
IllidanStormrageCheatTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "illidan stormrage cheat") {}
bool IsActive() override;
};
#endif

View File

@ -0,0 +1,468 @@
/*
* 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 "RaidBlackTempleHelpers.h"
#include "Playerbots.h"
#include "RaidBossHelpers.h"
namespace BlackTempleHelpers
{
// High Warlord Naj'entus
const Position NAJENTUS_TANK_POSITION = { 438.515f, 772.436f, 11.931f };
// Supremus
std::unordered_map<uint32, time_t> supremusPhaseTimer;
bool HasSupremusVolcanoNearby(PlayerbotAI* botAI, Player* bot)
{
constexpr float searchRadius = 20.0f;
std::list<Creature*> creatureList;
bot->GetCreatureListWithEntryInGrid(
creatureList, static_cast<uint32>(
BlackTempleNpcs::NPC_SUPREMUS_VOLCANO), searchRadius);
for (Creature* creature : creatureList)
{
if (creature && creature->IsAlive())
return true;
}
return false;
}
// Shade of Akama
const Position AKAMA_CHANNELER_POSITION = { 467.851f, 401.622f, 118.538f };
std::unordered_set<ObjectGuid> hasReachedAkamaChannelerPosition;
// Teron Gorefiend
const Position GOREFIEND_TANK_POSITION = { 597.653f, 402.284f, 187.090f };
const Position GOREFIEND_DIE_POSITION = { 525.709f, 377.177f, 193.203f };
// Gurtogg Bloodboil
const Position GURTOGG_TANK_POSITION = { 735.987f, 272.451f, 063.554f };
const Position GURTOGG_RANGED_POSITION = { 762.265f, 277.183f, 063.781f };
const Position GURTOGG_SOAKER_POSITION = { 769.348f, 280.116f, 063.780f };
std::unordered_map<uint32, time_t> gurtoggPhaseTimer;
std::vector<std::vector<Player*>> GetGurtoggRangedRotationGroups(Player* bot)
{
Group* group = bot->GetGroup();
std::vector<Player*> rangedMembers;
std::vector<std::vector<Player*>> groups(3);
if (!group)
return groups;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive())
{
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
if (memberAI && memberAI->IsRanged(member))
rangedMembers.push_back(member);
}
}
for (size_t i = 0; i < rangedMembers.size(); ++i)
{
groups[i / 5].push_back(rangedMembers[i]);
if (groups[2].size() == 5)
break;
}
return groups;
}
int GetGurtoggActiveRotationGroup(Unit* gurtogg)
{
if (!gurtogg)
return -1;
auto it = gurtoggPhaseTimer.find(gurtogg->GetMap()->GetInstanceId());
if (it == gurtoggPhaseTimer.end())
return -1;
const time_t now = std::time(nullptr);
const time_t elapsed = now - it->second;
const int groupIndex = (elapsed % 30) / 10; // 3 groups, swapping every 10 seconds
return groupIndex;
}
// Mother Shahraz
const Position SHAHRAZ_TANK_POSITION = { 960.438f, 178.989f, 192.826f };
const Position SHAHRAZ_TRANSITION_POSITION = { 951.327f, 179.550f, 192.550f };
const Position SHAHRAZ_RANGED_POSITION = { 935.267f, 175.459f, 192.821f };
std::unordered_map<ObjectGuid, TankPositionState> shahrazTankStep;
TankPositionState GetShahrazTankPositionState(PlayerbotAI* botAI, Player* bot)
{
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank)
return TankPositionState::Unknown;
auto it = shahrazTankStep.find(mainTank->GetGUID());
if (it != shahrazTankStep.end())
return it->second;
return TankPositionState::Unknown;
}
// Illidari Council
const std::array<Position, 4> GATHIOS_TANK_POSITIONS = {{
{ 662.977f, 296.246f, 271.688f },
{ 636.238f, 283.719f, 271.629f },
{ 655.571f, 261.377f, 271.687f },
{ 673.789f, 274.139f, 271.689f }
}};
const Position ZEREVOR_TANK_POSITION = { 686.219f, 377.644f, 271.689f };
const std::array<Position, 2> ZEREVOR_HEALER_POSITIONS = {{
{ 661.385f, 351.219f, 271.690f },
{ 667.003f, 363.768f, 271.690f }
}};
const Position MALANDE_TANK_POSITION = { 690.590f, 299.790f, 277.443f };
std::unordered_map<uint32, time_t> councilDpsWaitTimer;
std::unordered_map<ObjectGuid, uint8> gathiosTankStep;
std::unordered_map<ObjectGuid, uint8> zerevorHealStep;
// (1) First priority is an assistant Mage (real player or bot)
// (2) If no assistant Mage, then look for any Mage bot
Player* GetZerevorMageTank(Player* bot)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
Player* fallbackMage = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE)
continue;
if (group->IsAssistant(member->GetGUID()))
return member;
if (!fallbackMage && GET_PLAYERBOT_AI(member))
fallbackMage = member;
}
return fallbackMage;
}
bool HasDangerousCouncilAura(Unit* unit)
{
static const std::array<uint32, 3> dangerousAuras =
{
static_cast<uint32>(BlackTempleSpells::SPELL_CONSECRATION),
static_cast<uint32>(BlackTempleSpells::SPELL_BLIZZARD),
static_cast<uint32>(BlackTempleSpells::SPELL_FLAMESTRIKE)
};
for (uint32 aura : dangerousAuras)
{
if (unit->HasAura(aura))
return true;
}
return false;
}
// Illidan Stormrage <The Betrayer>
const Position ILLIDAN_LANDING_POSITION = { 676.648f, 304.761f, 354.189f };
const Position ILLIDAN_N_GRATE_POSITION = { 682.100f, 306.000f, 353.192f };
const Position ILLIDAN_E_GRATE_POSITION = { 673.500f, 298.500f, 353.192f };
const Position ILLIDAN_W_GRATE_POSITION = { 672.400f, 312.500f, 353.192f };
const std::array<Position, 3> GRATE_POSITIONS = {{
ILLIDAN_N_GRATE_POSITION,
ILLIDAN_E_GRATE_POSITION,
ILLIDAN_W_GRATE_POSITION
}};
const Position ILLIDAN_E_GLAIVE_WAITING_POSITION = { 677.656f, 294.066f, 353.192f };
const std::array<Position, 7> E_GLAIVE_TANK_POSITIONS = {{
{ 683.000f, 295.000f, 354.000f },
{ 696.969f, 300.982f, 354.302f },
{ 691.112f, 287.461f, 354.363f },
{ 676.674f, 280.797f, 354.268f },
{ 664.414f, 284.834f, 354.271f },
{ 656.826f, 295.113f, 354.165f },
{ 665.000f, 304.000f, 354.000f }
}};
const Position ILLIDAN_W_GLAIVE_WAITING_POSITION = { 676.102f, 316.305f, 353.192f };
const std::array<Position, 7> W_GLAIVE_TANK_POSITIONS = {{
{ 697.208f, 313.475f, 354.234f },
{ 681.000f, 318.000f, 354.000f },
{ 664.000f, 307.000f, 354.000f },
{ 656.161f, 314.132f, 354.092f },
{ 665.080f, 326.905f, 354.128f },
{ 678.809f, 329.968f, 354.387f },
{ 690.889f, 324.277f, 354.204f }
}};
std::unordered_map<ObjectGuid, size_t> flameTankWaypointIndex;
std::unordered_map<ObjectGuid, ObjectGuid> illidanShadowTrapGuid;
std::unordered_map<ObjectGuid, Position> illidanShadowTrapDestination;
std::unordered_map<uint32, int> illidanLastPhase;
std::unordered_map<uint32, time_t> illidanBossDpsWaitTimer;
std::unordered_map<uint32, time_t> illidanFlameDpsWaitTimer;
std::unordered_map<uint32, ObjectGuid> eastFlameGuid;
std::unordered_map<uint32, ObjectGuid> westFlameGuid;
int GetIllidanPhase(Unit* illidan)
{
if (!illidan || illidan->GetHealth() == 1 || illidan->HasAura(
static_cast<uint32>(BlackTempleSpells::SPELL_SHADOW_PRISON)))
{
return -1;
}
// Transitioning from Phase 2 to Phase 3
float x, y, z;
illidan->GetMotionMaster()->GetDestination(x, y, z);
Position dest(x, y, z);
if ((dest.GetExactDist2d(ILLIDAN_LANDING_POSITION) < 0.2f ||
illidan->GetExactDist2d(ILLIDAN_LANDING_POSITION) < 0.2f) &&
illidan->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
return 0;
}
// Phase 2: Flying
if (illidan->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
return 2;
// Phase 1: Health > 65%
if (illidan->GetHealthPct() > 65.0f)
return 1;
// Phase 4: Demon Form
if (!illidan->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_CAGED)) &&
(illidan->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_DEMON_FORM)) ||
illidan->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_DEMON_TRANSFORM_1)) ||
illidan->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_DEMON_TRANSFORM_2)) ||
illidan->HasAura(static_cast<uint32>(BlackTempleSpells::SPELL_DEMON_TRANSFORM_3))))
{
return 4;
}
// Phase 3: Normal (ground, 65-30%, not demon)
if (illidan->GetHealthPct() > 30.0f)
return 3;
// Phase 5: Health <= 30%
if (illidan->GetHealthPct() <= 30.0f)
return 5;
return -1;
}
std::vector<Unit*> GetAllFlameCrashes(Player* bot)
{
std::vector<Unit*> flameCrashes;
std::list<Creature*> creatureList;
constexpr float searchRadius = 30.0f;
bot->GetCreatureListWithEntryInGrid(
creatureList, static_cast<uint32>(BlackTempleNpcs::NPC_FLAME_CRASH), searchRadius);
for (Creature* creature : creatureList)
{
if (creature && creature->IsAlive())
flameCrashes.push_back(creature);
}
return flameCrashes;
}
std::pair<Unit*, Unit*> GetFlamesOfAzzinoth(Player* bot)
{
Unit* eastFlame = nullptr;
Unit* westFlame = nullptr;
const uint32 instanceId = bot->GetMap()->GetInstanceId();
if (eastFlameGuid.find(instanceId) != eastFlameGuid.end())
{
if (Unit* unit = ObjectAccessor::GetUnit(*bot, eastFlameGuid[instanceId]))
{
if (unit->IsAlive())
eastFlame = unit;
}
}
if (westFlameGuid.find(instanceId) != westFlameGuid.end())
{
if (Unit* unit = ObjectAccessor::GetUnit(*bot, westFlameGuid[instanceId]))
{
if (unit->IsAlive())
westFlame = unit;
}
}
return { eastFlame, westFlame };
}
// (1) First priority is an assistant Warlock (real player or bot)
// (2) If no assistant Warlock, then look for any Warlock bot
Player* GetIllidanWarlockTank(Player* bot)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
Player* fallbackWarlock = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->getClass() != CLASS_WARLOCK)
continue;
if (group->IsAssistant(member->GetGUID()))
return member;
if (!fallbackWarlock && GET_PLAYERBOT_AI(member))
fallbackWarlock = member;
}
return fallbackWarlock;
}
bool HasParasiticShadowfiend(Player* member)
{
if (!member)
return false;
constexpr uint32 shadowfiendAura1 =
static_cast<uint32>(BlackTempleSpells::SPELL_PARASITIC_SHADOWFIEND_1);
constexpr uint32 shadowfiendAura2 =
static_cast<uint32>(BlackTempleSpells::SPELL_PARASITIC_SHADOWFIEND_2);
return member->HasAura(shadowfiendAura1) || member->HasAura(shadowfiendAura2);
}
// Get the first bot hunter that doesn't have Parasitic Shadowfiend
Player* GetIllidanTrapperHunter(Player* bot)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER &&
GET_PLAYERBOT_AI(member) && !HasParasiticShadowfiend(member))
{
return member;
}
}
return nullptr;
}
Player* GetBotWithParasiticShadowfiend(Player* bot)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
HasParasiticShadowfiend(member))
{
return member;
}
}
return nullptr;
}
EyeBlastDangerArea GetEyeBlastDangerArea(Player* bot)
{
constexpr float searchRadius = 100.0f;
std::list<Creature*> creatureList;
bot->GetCreatureListWithEntryInGrid(
creatureList, static_cast<uint32>(BlackTempleNpcs::NPC_ILLIDAN_DB_TARGET), searchRadius);
Creature* eyeBlastTrigger = nullptr;
for (Creature* creature : creatureList)
{
if (creature && creature->IsAlive())
{
eyeBlastTrigger = creature;
break;
}
}
if (!eyeBlastTrigger)
return {};
Position startPos = Position(eyeBlastTrigger->GetPositionX(), eyeBlastTrigger->GetPositionY(),
eyeBlastTrigger->GetPositionZ());
float destX, destY, destZ;
eyeBlastTrigger->GetMotionMaster()->GetDestination(destX, destY, destZ);
Position endPos(destX, destY, destZ);
if (startPos.GetExactDist2d(endPos) < 0.1f)
return {};
constexpr float eyeBlastWidth = 9.0f;
return { startPos, endPos, eyeBlastWidth };
}
bool IsPositionInEyeBlastDangerArea(const Position& pos, const EyeBlastDangerArea& area)
{
const float dx = area.end.GetPositionX() - area.start.GetPositionX();
const float dy = area.end.GetPositionY() - area.start.GetPositionY();
const float length = area.start.GetExactDist2d(area.end.GetPositionX(), area.end.GetPositionY());
if (length < 0.1f)
return false;
const float projectionFactor = (
(pos.GetPositionX() - area.start.GetPositionX()) * dx + (
pos.GetPositionY() - area.start.GetPositionY()) * dy) / (length * length);
const float clampedProjectionFactor = std::clamp(projectionFactor, 0.0f, 1.0f);
const float closestX = area.start.GetPositionX() + clampedProjectionFactor * dx;
const float closestY = area.start.GetPositionY() + clampedProjectionFactor * dy;
const float distToLine = pos.GetExactDist2d(closestX, closestY);
return distToLine < area.width;
}
GameObject* FindNearestTrap(PlayerbotAI* botAI, Player* bot)
{
GuidVector const& gos =
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest game objects")->Get();
GameObject* nearestTrap = nullptr;
for (ObjectGuid const& guid : gos)
{
GameObject* go = botAI->GetGameObject(guid);
if (go && go->isSpawned() &&
go->GetEntry() == static_cast<uint32>(BlackTempleObjects::GO_SHADOW_TRAP))
{
nearestTrap = go;
break;
}
}
return nearestTrap;
}
}

View File

@ -0,0 +1,216 @@
/*
* 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_RAIDBLACKTEMPLEHELPERS_H_
#define _PLAYERBOT_RAIDBLACKTEMPLEHELPERS_H_
#include <array>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "Common.h"
#include "ObjectGuid.h"
#include "Position.h"
class GameObject;
class Player;
class PlayerbotAI;
class Unit;
namespace BlackTempleHelpers
{
enum class BlackTempleSpells : uint32
{
// High Warlord Naj'entus
SPELL_IMPALING_SPINE = 39837,
SPELL_TIDAL_SHIELD = 39872,
// Supremus
SPELL_SNARE_SELF = 41922,
// Teron Gorefiend
SPELL_SHADOW_OF_DEATH = 40251,
SPELL_SPIRITUAL_VENGEANCE = 40268,
SPELL_SPIRIT_LANCE = 40157,
SPELL_SPIRIT_CHAINS = 40175,
SPELL_SPIRIT_VOLLEY = 40314,
SPELL_SPIRIT_STRIKE = 40325,
// Gurtogg Bloodboil
SPELL_BOSS_FEL_RAGE = 40594,
SPELL_PLAYER_FEL_RAGE = 40604,
SPELL_BLOODBOIL = 42005,
// Reliquary of Souls
SPELL_DEADEN = 41410,
SPELL_RUNE_SHIELD = 41431,
// Mother Shahraz
SPELL_FATAL_ATTRACTION = 41001,
// Gathios the Shatterer
SPELL_BLESSING_OF_PROTECTION = 41450,
SPELL_BLESSING_OF_SPELL_WARDING = 41451,
SPELL_JUDGEMENT = 41467,
SPELL_SEAL_OF_COMMAND = 41469,
SPELL_CONSECRATION = 41541,
// Veras Darkshadow
SPELL_VANISH = 41476,
// High Nethermancer Zerevor
SPELL_DAMPEN_MAGIC = 41478,
SPELL_FLAMESTRIKE = 41481,
SPELL_BLIZZARD = 41482,
// Illidan Stormrage <The Betrayer>
SPELL_DEMON_TRANSFORM_1 = 40511,
SPELL_DEMON_TRANSFORM_2 = 40398,
SPELL_DEMON_TRANSFORM_3 = 40510,
SPELL_DEMON_FORM = 40506,
SPELL_DARK_BARRAGE = 40585,
SPELL_SHADOW_PRISON = 40647,
SPELL_CAGED = 40695,
SPELL_PARASITIC_SHADOWFIEND_1 = 41917, // cast by Illidan (primary infection)
SPELL_PARASITIC_SHADOWFIEND_2 = 41914, // cast by Shadowfiend on contact (secondary infection)
// Hunter
SPELL_FROST_TRAP = 13809,
SPELL_MISDIRECTION = 35079,
// Shaman
SPELL_EARTHBIND_TOTEM = 2484,
};
enum class BlackTempleNpcs : uint32
{
// Supremus
NPC_SUPREMUS_VOLCANO = 23085,
// Shade of Akama
NPC_ASHTONGUE_CHANNELER = 23421,
// Teron Gorefiend
NPC_SHADOWY_CONSTRUCT = 23111,
// Illidan Stormrage <The Betrayer>
NPC_FLAME_OF_AZZINOTH = 22997,
NPC_DEMON_FIRE = 23069,
NPC_ILLIDAN_DB_TARGET = 23070,
NPC_BLAZE = 23259,
NPC_FLAME_CRASH = 23336,
NPC_SHADOW_DEMON = 23375,
NPC_PARASITIC_SHADOWFIEND = 23498,
};
enum class BlackTempleItems : uint32
{
// High Warlord Naj'entus
ITEM_NAJENTUS_SPINE = 32408,
};
enum class BlackTempleObjects : uint32
{
// High Warlord Naj'entus
GO_NAJENTUS_SPINE = 185584,
// Illidan Stormrage <The Betrayer>
GO_SHADOW_TRAP = 185916,
};
enum class TankPositionState : uint8
{
MovingToTransition = 0,
MovingToFinal = 1,
Positioned = 2,
Unknown = 255,
};
constexpr uint32 BLACK_TEMPLE_MAP_ID = 564;
// High Warlord Naj'entus
extern const Position NAJENTUS_TANK_POSITION;
// Supremus
extern std::unordered_map<uint32, time_t> supremusPhaseTimer;
bool HasSupremusVolcanoNearby(PlayerbotAI* botAI, Player* bot);
// Shade of Akama
extern const Position AKAMA_CHANNELER_POSITION;
extern std::unordered_set<ObjectGuid> hasReachedAkamaChannelerPosition;
// Teron Gorefiend
extern const Position GOREFIEND_TANK_POSITION;
extern const Position GOREFIEND_DIE_POSITION;
// Gurtogg Bloodboil
extern const Position GURTOGG_TANK_POSITION;
extern const Position GURTOGG_RANGED_POSITION;
extern const Position GURTOGG_SOAKER_POSITION;
extern std::unordered_map<uint32, time_t> gurtoggPhaseTimer;
std::vector<std::vector<Player*>> GetGurtoggRangedRotationGroups(Player* bot);
int GetGurtoggActiveRotationGroup(Unit* gurtogg);
// Mother Shahraz
extern const Position SHAHRAZ_TANK_POSITION;
extern const Position SHAHRAZ_TRANSITION_POSITION;
extern const Position SHAHRAZ_RANGED_POSITION;
extern std::unordered_map<ObjectGuid, TankPositionState> shahrazTankStep;
TankPositionState GetShahrazTankPositionState(PlayerbotAI* botAI, Player* bot);
// Illidari Council
constexpr float COUNCIL_FLOOR_Z_THRESHOLD = 270.000f;
extern const std::array<Position, 4> GATHIOS_TANK_POSITIONS;
extern const Position MALANDE_TANK_POSITION;
extern const Position ZEREVOR_TANK_POSITION;
extern const std::array<Position, 2> ZEREVOR_HEALER_POSITIONS;
extern std::unordered_map<uint32, time_t> councilDpsWaitTimer;
extern std::unordered_map<ObjectGuid, uint8> gathiosTankStep;
extern std::unordered_map<ObjectGuid, uint8> zerevorHealStep;
Player* GetZerevorMageTank(Player* bot);
bool HasDangerousCouncilAura(Unit* unit);
// Illidan Stormrage <The Betrayer>
extern const Position ILLIDAN_LANDING_POSITION;
extern const Position ILLIDAN_N_GRATE_POSITION;
extern const Position ILLIDAN_E_GRATE_POSITION;
extern const Position ILLIDAN_W_GRATE_POSITION;
extern const std::array<Position, 3> GRATE_POSITIONS;
extern const Position ILLIDAN_E_GLAIVE_WAITING_POSITION;
extern const std::array<Position, 7> E_GLAIVE_TANK_POSITIONS;
extern const Position ILLIDAN_W_GLAIVE_WAITING_POSITION;
extern const std::array<Position, 7> W_GLAIVE_TANK_POSITIONS;
extern std::unordered_map<ObjectGuid, size_t> flameTankWaypointIndex;
extern std::unordered_map<ObjectGuid, ObjectGuid> illidanShadowTrapGuid;
extern std::unordered_map<ObjectGuid, Position> illidanShadowTrapDestination;
extern std::unordered_map<uint32, int> illidanLastPhase;
extern std::unordered_map<uint32, time_t> illidanBossDpsWaitTimer;
extern std::unordered_map<uint32, time_t> illidanFlameDpsWaitTimer;
extern std::unordered_map<uint32, ObjectGuid> eastFlameGuid;
extern std::unordered_map<uint32, ObjectGuid> westFlameGuid;
int GetIllidanPhase(Unit* illidan);
std::vector<Unit*> GetAllFlameCrashes(Player* bot);
std::pair<Unit*, Unit*> GetFlamesOfAzzinoth(Player* bot);
Player* GetIllidanWarlockTank(Player* bot);
bool HasParasiticShadowfiend(Player* member);
Player* GetIllidanTrapperHunter(Player* bot);
Player* GetBotWithParasiticShadowfiend(Player* bot);
struct EyeBlastDangerArea
{
Position start;
Position end;
float width;
};
EyeBlastDangerArea GetEyeBlastDangerArea(Player* bot);
bool IsPositionInEyeBlastDangerArea(const Position& pos, const EyeBlastDangerArea& area);
GameObject* FindNearestTrap(PlayerbotAI* botAI, Player* bot);
}
#endif

View File

@ -19,7 +19,7 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, maulgar);
SetRtiTarget(botAI, "square", maulgar);
if (bot->GetVictim() != maulgar)
if (AI_VALUE(Unit*, "current target") != maulgar)
return Attack(maulgar);
if (maulgar->GetVictim() == bot)
@ -53,7 +53,7 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, olm);
SetRtiTarget(botAI, "circle", olm);
if (bot->GetVictim() != olm)
if (AI_VALUE(Unit*, "current target") != olm)
return Attack(olm);
if (olm->GetVictim() == bot)
@ -88,7 +88,7 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event /*event*
MarkTargetWithStar(bot, blindeye);
SetRtiTarget(botAI, "star", blindeye);
if (bot->GetVictim() != blindeye)
if (AI_VALUE(Unit*, "current target") != blindeye)
return Attack(blindeye);
if (blindeye->GetVictim() == bot)
@ -128,7 +128,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event /*event*/)
if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot))
return botAI->CastSpell("fire ward", bot);
if (bot->GetTarget() != krosh->GetGUID())
if (AI_VALUE(Unit*, "current target") != krosh)
return Attack(krosh);
if (krosh->GetVictim() == bot)
@ -175,7 +175,7 @@ bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event /*event*/)
MarkTargetWithDiamond(bot, kiggler);
SetRtiTarget(botAI, "diamond", kiggler);
if (bot->GetTarget() != kiggler->GetGUID())
if (AI_VALUE(Unit*, "current target") != kiggler)
return Attack(kiggler);
Position safePos;
@ -205,7 +205,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "star", blindeye);
if (bot->GetTarget() != blindeye->GetGUID())
if (AI_VALUE(Unit*, "current target") != blindeye)
return Attack(blindeye);
return false;
@ -226,7 +226,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "circle", olm);
if (bot->GetTarget() != olm->GetGUID())
if (AI_VALUE(Unit*, "current target") != olm)
return Attack(olm);
return false;
@ -247,7 +247,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "triangle", krosh);
if (bot->GetTarget() != krosh->GetGUID())
if (AI_VALUE(Unit*, "current target") != krosh)
return Attack(krosh);
return false;
@ -268,7 +268,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "diamond", kiggler);
if (bot->GetTarget() != kiggler->GetGUID())
if (AI_VALUE(Unit*, "current target") != kiggler)
return Attack(kiggler);
return false;
@ -289,7 +289,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "square", maulgar);
if (bot->GetTarget() != maulgar->GetGUID())
if (AI_VALUE(Unit*, "current target") != maulgar)
return Attack(maulgar);
}
@ -499,7 +499,7 @@ bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event /*event*/)
if (!gruul)
return false;
if (bot->GetVictim() != gruul)
if (AI_VALUE(Unit*, "current target") != gruul)
return Attack(gruul);
if (gruul->GetVictim() == bot)

View File

@ -15,6 +15,9 @@ using namespace GruulsLairHelpers;
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr)
return 1.0f;
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
return 0.0f;
@ -46,9 +49,8 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action)
{
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
Unit* target = AI_VALUE(Unit*, "current target");
if (krosh && target && target->GetGUID() == krosh->GetGUID() &&
if (krosh && AI_VALUE(Unit*, "current target") == krosh &&
dynamic_cast<CastArcaneShotAction*>(action))
return 0.0f;

View File

@ -97,7 +97,7 @@ bool RageWinterchillMainTankPositionBossAction::Execute(Event /*event*/)
if (!winterchill)
return false;
if (bot->GetVictim() != winterchill)
if (AI_VALUE(Unit*, "current target") != winterchill)
return Attack(winterchill);
if (winterchill->GetVictim() == bot)
@ -265,7 +265,7 @@ bool AnetheronMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, anetheron);
SetRtiTarget(botAI, "square", anetheron);
if (bot->GetVictim() != anetheron)
if (AI_VALUE(Unit*, "current target") != anetheron)
return Attack(anetheron);
if (anetheron->GetVictim() == bot)
@ -395,7 +395,7 @@ bool AnetheronFirstAssistTankPickUpInfernalsAction::Execute(Event /*event*/)
MarkTargetWithDiamond(bot, infernal);
SetRtiTarget(botAI, "diamond", infernal);
if (bot->GetVictim() != infernal)
if (AI_VALUE(Unit*, "current target") != infernal)
return Attack(infernal);
if ((infernoTarget && infernoTarget == bot) ||
@ -432,7 +432,7 @@ bool AnetheronAssignDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "square", anetheron);
if (bot->GetVictim() != anetheron)
if (AI_VALUE(Unit*, "current target") != anetheron)
return Attack(anetheron);
return false;
@ -455,7 +455,7 @@ bool AnetheronAssignDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "diamond", infernal);
if (bot->GetTarget() != infernal->GetGUID())
if (AI_VALUE(Unit*, "current target") != infernal)
return Attack(infernal);
}
}
@ -464,7 +464,7 @@ bool AnetheronAssignDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "square", anetheron);
if (bot->GetTarget() != anetheron->GetGUID())
if (AI_VALUE(Unit*, "current target") != anetheron)
return Attack(anetheron);
}
@ -500,7 +500,7 @@ bool KazrogalMainTankPositionBossAction::Execute(Event /*event*/)
if (!kazrogal)
return false;
if (bot->GetVictim() != kazrogal)
if (AI_VALUE(Unit*, "current target") != kazrogal)
return Attack(kazrogal);
if (kazrogal->GetVictim() == bot && bot->IsWithinMeleeRange(kazrogal))
@ -734,7 +734,7 @@ bool AzgalorMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithStar(bot, azgalor);
SetRtiTarget(botAI, "star", azgalor);
if (bot->GetVictim() != azgalor)
if (AI_VALUE(Unit*, "current target") != azgalor)
return Attack(azgalor);
if (azgalor->GetVictim() == bot && bot->IsWithinMeleeRange(azgalor))
@ -801,7 +801,7 @@ bool AzgalorDisperseRangedAction::Execute(Event /*event*/)
{
return FleePosition(doomguard->GetPosition(), safeDistFromDoomguard);
}
else if (!doomguard || bot->GetTarget() != doomguard->GetGUID())
else if (!doomguard || AI_VALUE(Unit*, "current target") != doomguard)
{
Unit* nearestPlayer = GetNearestPlayerInRadius(bot, safeDistFromPlayer);
if (nearestPlayer)
@ -848,7 +848,7 @@ bool AzgalorMeleeGetOutOfFireAndSwapTargetsAction::Execute(Event /*event*/)
}
}
if (bot->GetVictim() != desiredTarget || bot->GetTarget() != desiredTarget->GetGUID())
if (AI_VALUE(Unit*, "current target") != desiredTarget)
return Attack(desiredTarget);
return false;
@ -912,7 +912,7 @@ bool AzgalorFirstAssistTankPositionDoomguardAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, doomguard);
SetRtiTarget(botAI, "circle", doomguard);
if (bot->GetVictim() != doomguard)
if (AI_VALUE(Unit*, "current target") != doomguard)
return Attack(doomguard);
if (doomguard->GetVictim() == bot && bot->IsWithinMeleeRange(doomguard) &&
@ -963,7 +963,7 @@ bool AzgalorRangedDpsPrioritizeDoomguardsAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "circle", doomguard);
if (bot->GetTarget() != doomguard->GetGUID())
if (AI_VALUE(Unit*, "current target") != doomguard)
return Attack(doomguard);
}
}
@ -971,7 +971,7 @@ bool AzgalorRangedDpsPrioritizeDoomguardsAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", azgalor);
if (bot->GetTarget() != azgalor->GetGUID())
if (AI_VALUE(Unit*, "current target") != azgalor)
return Attack(azgalor);
}
@ -1007,7 +1007,7 @@ bool ArchimondeMoveBossToInitialPositionAction::Execute(Event /*event*/)
if (!archimonde)
return false;
if (bot->GetVictim() != archimonde)
if (AI_VALUE(Unit*, "current target") != archimonde)
return Attack(archimonde);
if (archimonde->GetVictim() == bot && bot->IsWithinMeleeRange(archimonde) &&

View File

@ -50,11 +50,9 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "star", attumenMounted);
if (bot->GetTarget() != attumenMounted->GetGUID())
{
bot->SetTarget(attumenMounted->GetGUID());
if (AI_VALUE(Unit*, "current target") != attumenMounted)
return Attack(attumenMounted);
}
}
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
{
@ -65,11 +63,8 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", midnight);
if (bot->GetTarget() != midnight->GetGUID())
{
bot->SetTarget(midnight->GetGUID());
if (AI_VALUE(Unit*, "current target") != midnight)
return Attack(midnight);
}
}
}
@ -90,7 +85,7 @@ bool AttumenTheHuntsmanSplitBossesAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, attumen);
SetRtiTarget(botAI, "square", attumen);
if (bot->GetVictim() != attumen)
if (AI_VALUE(Unit*, "current target") != attumen)
return Attack(attumen);
if (attumen->GetVictim() == bot && midnight->GetVictim() != bot)
@ -162,7 +157,7 @@ bool MoroesMainTankAttackBossAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, moroes);
SetRtiTarget(botAI, "circle", moroes);
if (bot->GetVictim() != moroes)
if (AI_VALUE(Unit*, "current target") != moroes)
return Attack(moroes);
return false;
@ -200,7 +195,7 @@ bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event /*event*/)
if (!maiden)
return false;
if (bot->GetVictim() != maiden)
if (AI_VALUE(Unit*, "current target") != maiden)
return Attack(maiden);
Unit* healer = nullptr;
@ -294,7 +289,7 @@ bool BigBadWolfPositionBossAction::Execute(Event /*event*/)
if (!wolf)
return false;
if (bot->GetVictim() != wolf)
if (AI_VALUE(Unit*, "current target") != wolf)
return Attack(wolf);
if (wolf->GetVictim() == bot)
@ -425,7 +420,7 @@ bool TheCuratorPositionBossAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, curator);
SetRtiTarget(botAI, "circle", curator);
if (bot->GetVictim() != curator)
if (AI_VALUE(Unit*, "current target") != curator)
return Attack(curator);
if (curator->GetVictim() == bot)
@ -1193,7 +1188,7 @@ bool PrinceMalchezaarMainTankMovementAction::Execute(Event /*event*/)
if (!malchezaar)
return false;
if (bot->GetVictim() != malchezaar)
if (AI_VALUE(Unit*, "current target") != malchezaar)
return Attack(malchezaar);
std::vector<Unit*> infernals = GetSpawnedInfernals(botAI);
@ -1261,7 +1256,7 @@ bool NightbaneGroundPhasePositionBossAction::Execute(Event /*event*/)
MarkTargetWithSkull(bot, nightbane);
if (bot->GetVictim() != nightbane)
if (AI_VALUE(Unit*, "current target") != nightbane)
return Attack(nightbane);
const ObjectGuid botGuid = bot->GetGUID();
@ -1400,8 +1395,7 @@ bool NightbaneFlightPhaseMovementAction::Execute(Event /*event*/)
MarkTargetWithMoon(bot, nightbane);
Unit* botTarget = botAI->GetUnit(bot->GetTarget());
if (botTarget && botTarget == nightbane)
if (AI_VALUE(Unit*, "current target") == nightbane)
{
bot->AttackStop();
bot->InterruptNonMeleeSpells(true);

View File

@ -61,7 +61,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event /*event*
SetRtiTarget(botAI, rtiName, currentTarget);
if (currentTarget && bot->GetVictim() != currentTarget)
if (currentTarget && AI_VALUE(Unit*, "current target") != currentTarget)
return Attack(currentTarget);
return false;
@ -76,7 +76,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event /*event*/)
MarkTargetWithDiamond(bot, channelerDiamond);
SetRtiTarget(botAI, "diamond", channelerDiamond);
if (bot->GetVictim() != channelerDiamond)
if (AI_VALUE(Unit*, "current target") != channelerDiamond)
return Attack(channelerDiamond);
if (channelerDiamond->GetVictim() == bot)
@ -109,7 +109,7 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event /*event*/
MarkTargetWithTriangle(bot, channelerTriangle);
SetRtiTarget(botAI, "triangle", channelerTriangle);
if (bot->GetVictim() != channelerTriangle)
if (AI_VALUE(Unit*, "current target") != channelerTriangle)
return Attack(channelerTriangle);
if (channelerTriangle->GetVictim() == bot)
@ -219,7 +219,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "square", channelerSquare);
if (bot->GetTarget() != channelerSquare->GetGUID())
if (AI_VALUE(Unit*, "current target") != channelerSquare)
return Attack(channelerSquare);
return false;
@ -230,7 +230,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", channelerStar);
if (bot->GetTarget() != channelerStar->GetGUID())
if (AI_VALUE(Unit*, "current target") != channelerStar)
return Attack(channelerStar);
return false;
@ -241,7 +241,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "circle", channelerCircle);
if (bot->GetTarget() != channelerCircle->GetGUID())
if (AI_VALUE(Unit*, "current target") != channelerCircle)
return Attack(channelerCircle);
return false;
@ -252,7 +252,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "diamond", channelerDiamond);
if (bot->GetTarget() != channelerDiamond->GetGUID())
if (AI_VALUE(Unit*, "current target") != channelerDiamond)
return Attack(channelerDiamond);
return false;
@ -263,7 +263,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "triangle", channelerTriangle);
if (bot->GetTarget() != channelerTriangle->GetGUID())
if (AI_VALUE(Unit*, "current target") != channelerTriangle)
return Attack(channelerTriangle);
return false;
@ -276,7 +276,7 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "cross", magtheridon);
if (bot->GetTarget() != magtheridon->GetGUID())
if (AI_VALUE(Unit*, "current target") != magtheridon)
return Attack(magtheridon);
}
@ -347,7 +347,7 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithCross(bot, magtheridon);
SetRtiTarget(botAI, "cross", magtheridon);
if (bot->GetVictim() != magtheridon)
if (AI_VALUE(Unit*, "current target") != magtheridon)
return Attack(magtheridon);
if (magtheridon->GetVictim() == bot)

View File

@ -12,6 +12,7 @@
#include "RaidSSCStrategy.h"
#include "RaidTempestKeepStrategy.h"
#include "RaidHyjalSummitStrategy.h"
#include "RaidBlackTempleStrategy.h"
#include "RaidZulAmanStrategy.h"
#include "RaidOsStrategy.h"
#include "RaidEoEStrategy.h"
@ -35,6 +36,7 @@ public:
creators["ssc"] = &RaidStrategyContext::ssc;
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
creators["hyjal"] = &RaidStrategyContext::hyjal;
creators["blacktemple"] = &RaidStrategyContext::blacktemple;
creators["zulaman"] = &RaidStrategyContext::zulaman;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
@ -55,6 +57,7 @@ private:
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
static Strategy* hyjal(PlayerbotAI* botAI) { return new RaidHyjalSummitStrategy(botAI); }
static Strategy* blacktemple(PlayerbotAI* botAI) { return new RaidBlackTempleStrategy(botAI); }
static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }

View File

@ -153,7 +153,7 @@ bool HydrossTheUnstablePositionFrostTankAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, hydross);
SetRtiTarget(botAI, "square", hydross);
if (bot->GetTarget() != hydross->GetGUID())
if (AI_VALUE(Unit*, "current target") != hydross)
return Attack(hydross);
if (hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross))
@ -233,7 +233,7 @@ bool HydrossTheUnstablePositionNatureTankAction::Execute(Event /*event*/)
MarkTargetWithTriangle(bot, hydross);
SetRtiTarget(botAI, "triangle", hydross);
if (bot->GetTarget() != hydross->GetGUID())
if (AI_VALUE(Unit*, "current target") != hydross)
return Attack(hydross);
if (hydross->GetVictim() == bot && bot->IsWithinMeleeRange(hydross))
@ -308,7 +308,7 @@ bool HydrossTheUnstablePrioritizeElementalAddsAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "skull", waterElemental);
if (bot->GetTarget() != waterElemental->GetGUID())
if (AI_VALUE(Unit*, "current target") != waterElemental)
return Attack(waterElemental);
}
else if (Unit* natureElemental = GetFirstAliveUnitByEntry(botAI, NPC_TAINTED_SPAWN_OF_HYDROSS))
@ -318,7 +318,7 @@ bool HydrossTheUnstablePrioritizeElementalAddsAction::Execute(Event /*event*/)
SetRtiTarget(botAI, "skull", natureElemental);
if (bot->GetTarget() != natureElemental->GetGUID())
if (AI_VALUE(Unit*, "current target") != natureElemental)
return Attack(natureElemental);
}
@ -518,7 +518,7 @@ bool TheLurkerBelowPositionMainTankAction::Execute(Event /*event*/)
if (!lurker)
return false;
if (bot->GetTarget() != lurker->GetGUID())
if (AI_VALUE(Unit*, "current target") != lurker)
return Attack(lurker);
const Position& position = LURKER_MAIN_TANK_POSITION;
@ -639,7 +639,7 @@ bool TheLurkerBelowTanksPickUpAddsAction::Execute(Event /*event*/)
MarkTargetWithIcon(bot, guardian, rtiIndices[i]);
SetRtiTarget(botAI, rtiNames[i], guardian);
if (bot->GetVictim() != guardian)
if (AI_VALUE(Unit*, "current target") != guardian)
return Attack(guardian);
}
}
@ -841,7 +841,7 @@ bool LeotherasTheBlindDestroyInnerDemonAction::Execute(Event /*event*/)
// Roles without a strategy need to affirmatively attack their Inner Demons
// Because DPS assist is disabled via multipliers
if (bot->GetTarget() != innerDemon->GetGUID())
if (AI_VALUE(Unit*, "current target") != innerDemon)
return Attack(innerDemon);
}
@ -978,7 +978,7 @@ bool LeotherasTheBlindFinalPhaseAssignDpsPriorityAction::Execute(Event /*event*/
MarkTargetWithStar(bot, leotherasHuman);
SetRtiTarget(botAI, "star", leotherasHuman);
if (bot->GetTarget() != leotherasHuman->GetGUID())
if (AI_VALUE(Unit*, "current target") != leotherasHuman)
return Attack(leotherasHuman);
Unit* leotherasDemon = GetPhase3LeotherasDemon(bot);
@ -1092,7 +1092,7 @@ bool FathomLordKarathressMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithTriangle(bot, karathress);
SetRtiTarget(botAI, "triangle", karathress);
if (bot->GetTarget() != karathress->GetGUID())
if (AI_VALUE(Unit*, "current target") != karathress)
return Attack(karathress);
if (karathress->GetVictim() == bot && bot->IsWithinMeleeRange(karathress))
@ -1128,7 +1128,7 @@ bool FathomLordKarathressFirstAssistTankPositionCaribdisAction::Execute(Event /*
MarkTargetWithDiamond(bot, caribdis);
SetRtiTarget(botAI, "diamond", caribdis);
if (bot->GetTarget() != caribdis->GetGUID())
if (AI_VALUE(Unit*, "current target") != caribdis)
return Attack(caribdis);
if (caribdis->GetVictim() == bot)
@ -1163,7 +1163,7 @@ bool FathomLordKarathressSecondAssistTankPositionSharkkisAction::Execute(Event /
MarkTargetWithStar(bot, sharkkis);
SetRtiTarget(botAI, "star", sharkkis);
if (bot->GetTarget() != sharkkis->GetGUID())
if (AI_VALUE(Unit*, "current target") != sharkkis)
return Attack(sharkkis);
if (sharkkis->GetVictim() == bot && bot->IsWithinMeleeRange(sharkkis))
@ -1198,7 +1198,7 @@ bool FathomLordKarathressThirdAssistTankPositionTidalvessAction::Execute(Event /
MarkTargetWithCircle(bot, tidalvess);
SetRtiTarget(botAI, "circle", tidalvess);
if (bot->GetTarget() != tidalvess->GetGUID())
if (AI_VALUE(Unit*, "current target") != tidalvess)
return Attack(tidalvess);
if (tidalvess->GetVictim() == bot && bot->IsWithinMeleeRange(tidalvess))
@ -1322,7 +1322,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithSkull(bot, totem);
SetRtiTarget(botAI, "skull", totem);
if (bot->GetTarget() != totem->GetGUID())
if (AI_VALUE(Unit*, "current target") != totem)
return Attack(totem);
// Direct movement order due to path between Sharkkis and totem sometimes being screwy
@ -1343,7 +1343,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, tidalvess);
SetRtiTarget(botAI, "circle", tidalvess);
if (bot->GetTarget() != tidalvess->GetGUID())
if (AI_VALUE(Unit*, "current target") != tidalvess)
return Attack(tidalvess);
return false;
@ -1363,7 +1363,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
position.GetPositionZ(), 8.0f, MovementPriority::MOVEMENT_COMBAT);
}
if (bot->GetTarget() != caribdis->GetGUID())
if (AI_VALUE(Unit*, "current target") != caribdis)
return Attack(caribdis);
return false;
@ -1376,7 +1376,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithStar(bot, sharkkis);
SetRtiTarget(botAI, "star", sharkkis);
if (bot->GetTarget() != sharkkis->GetGUID())
if (AI_VALUE(Unit*, "current target") != sharkkis)
return Attack(sharkkis);
return false;
@ -1389,7 +1389,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithCross(bot, fathomSporebat);
SetRtiTarget(botAI, "cross", fathomSporebat);
if (bot->GetTarget() != fathomSporebat->GetGUID())
if (AI_VALUE(Unit*, "current target") != fathomSporebat)
return Attack(fathomSporebat);
return false;
@ -1401,7 +1401,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, fathomLurker);
SetRtiTarget(botAI, "square", fathomLurker);
if (bot->GetTarget() != fathomLurker->GetGUID())
if (AI_VALUE(Unit*, "current target") != fathomLurker)
return Attack(fathomLurker);
return false;
@ -1414,7 +1414,7 @@ bool FathomLordKarathressAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithTriangle(bot, karathress);
SetRtiTarget(botAI, "triangle", karathress);
if (bot->GetTarget() != karathress->GetGUID())
if (AI_VALUE(Unit*, "current target") != karathress)
return Attack(karathress);
}
@ -1460,7 +1460,7 @@ bool MorogrimTidewalkerMoveBossToTankPositionAction::Execute(Event /*event*/)
if (!tidewalker)
return false;
if (bot->GetTarget() != tidewalker->GetGUID())
if (AI_VALUE(Unit*, "current target") != tidewalker)
return Attack(tidewalker);
if (tidewalker->GetVictim() == bot && bot->IsWithinMeleeRange(tidewalker))
@ -1610,7 +1610,7 @@ bool LadyVashjMainTankPositionBossAction::Execute(Event /*event*/)
if (!vashj)
return false;
if (bot->GetTarget() != vashj->GetGUID())
if (AI_VALUE(Unit*, "current target") != vashj)
return Attack(vashj);
if (vashj->GetVictim() == bot && bot->IsWithinMeleeRange(vashj))
@ -1929,7 +1929,8 @@ bool LadyVashjAssignPhase2AndPhase3DpsPriorityAction::Execute(Event /*event*/)
currentTarget = nullptr;
}
if (target && currentTarget != target && bot->GetTarget() != target->GetGUID())
if (target && currentTarget != target &&
AI_VALUE(Unit*, "current target") != target)
return Attack(target);
// If bots have wandered too far from the center, move them back
@ -1984,7 +1985,8 @@ bool LadyVashjTankAttackAndMoveAwayStriderAction::Execute(Event /*event*/)
if (!bot->HasAura(SPELL_FEAR_WARD))
bot->AddAura(SPELL_FEAR_WARD, bot);
if (botAI->IsAssistTankOfIndex(bot, 0, true) && bot->GetTarget() != strider->GetGUID())
if (botAI->IsAssistTankOfIndex(bot, 0, true) &&
AI_VALUE(Unit*, "current target") != strider)
return Attack(strider);
float currentDistance = bot->GetExactDist2d(vashj);
@ -2040,7 +2042,7 @@ bool LadyVashjTeleportToTaintedElementalAction::Execute(Event /*event*/)
tainted->GetPositionZ(), tainted->GetOrientation());
}
if (bot->GetTarget() != tainted->GetGUID())
if (AI_VALUE(Unit*, "current target") != tainted)
{
MarkTargetWithStar(bot, tainted);
SetRtiTarget(botAI, "star", tainted);

View File

@ -752,7 +752,7 @@ float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *ac
return 0.0f;
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
if (enchanted && bot->GetVictim() == enchanted &&
if (enchanted && AI_VALUE(Unit*, "current target") == enchanted &&
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
return 0.0f;
}
@ -772,7 +772,7 @@ float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *ac
dynamic_cast<FleeAction*>(action))
return 0.0f;
if (enchanted && bot->GetVictim() == enchanted &&
if (enchanted && AI_VALUE(Unit*, "current target") == enchanted &&
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
return 0.0f;
}

View File

@ -209,35 +209,24 @@ namespace SerpentShrineCavernHelpers
{
Unit* vashj =
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
if (!vashj)
return false;
Creature* vashjCreature = vashj->ToCreature();
return vashjCreature && vashjCreature->GetHealthPct() > 70.0f &&
vashjCreature->GetReactState() != REACT_PASSIVE;
return vashj && vashj->GetHealthPct() > 70.0f;
}
bool IsLadyVashjInPhase2(PlayerbotAI* botAI)
{
Unit* vashj =
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
if (!vashj)
return false;
Creature* vashjCreature = vashj->ToCreature();
return vashjCreature && vashjCreature->GetReactState() == REACT_PASSIVE;
return vashj && vashj->GetHealthPct() <= 70.0f && vashj->HasAura(SPELL_MAGIC_BARRIER);
}
bool IsLadyVashjInPhase3(PlayerbotAI* botAI)
{
Unit* vashj =
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
if (!vashj)
return false;
Creature* vashjCreature = vashj->ToCreature();
return vashjCreature && vashjCreature->GetHealthPct() <= 50.0f &&
vashjCreature->GetReactState() != REACT_PASSIVE;
return vashj && vashj->GetHealthPct() <= 70.0f && !vashj->HasAura(SPELL_MAGIC_BARRIER);
}
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI)

View File

@ -48,6 +48,7 @@ namespace SerpentShrineCavernHelpers
// Lady Vashj <Coilfang Matron>
SPELL_FEAR_WARD = 6346,
SPELL_MAGIC_BARRIER = 38112,
SPELL_POISON_BOLT = 38253,
SPELL_STATIC_CHARGE = 38280,
SPELL_ENTANGLE = 38316,

View File

@ -93,7 +93,7 @@ bool AlarBossTanksMoveBetweenPlatformsAction::PositionMainTank(
MovementPriority::MOVEMENT_COMBAT, true, false);
}
else if ((locationIndex == PLATFORM_0_IDX || locationIndex == PLATFORM_2_IDX) &&
bot->GetTarget() != alar->GetGUID())
AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
}
@ -116,7 +116,7 @@ bool AlarBossTanksMoveBetweenPlatformsAction::PositionAssistTank(
MovementPriority::MOVEMENT_COMBAT, true, false);
}
else if ((locationIndex == PLATFORM_1_IDX || locationIndex == PLATFORM_3_IDX) &&
bot->GetTarget() != alar->GetGUID())
AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
}
@ -152,7 +152,7 @@ bool AlarMeleeDpsMoveBetweenPlatformsAction::Execute(Event /*event*/)
MovementPriority::MOVEMENT_COMBAT, true, false);
}
if (bot->GetTarget() != alar->GetGUID())
if (AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
}
@ -227,7 +227,7 @@ bool AlarAssistTanksPickUpEmbersAction::HandlePhase1Embers(Unit* alar)
MarkTargetWithSquare(bot, ember);
SetRtiTarget(botAI, "square", ember);
if (bot->GetTarget() != ember->GetGUID())
if (AI_VALUE(Unit*, "current target") != ember)
return Attack(ember);
if (ember->GetVictim() == bot)
@ -286,7 +286,7 @@ bool AlarAssistTanksPickUpEmbersAction::HandlePhase2Embers()
if (firstEmber->GetVictim() != bot)
{
if (bot->GetTarget() != firstEmber->GetGUID())
if (AI_VALUE(Unit*, "current target") != firstEmber)
return Attack(firstEmber);
return botAI->DoSpecificAction("taunt spell", Event(), true);
@ -305,7 +305,7 @@ bool AlarAssistTanksPickUpEmbersAction::HandlePhase2Embers()
if (secondEmber->GetVictim() != bot)
{
if (bot->GetTarget() != secondEmber->GetGUID())
if (AI_VALUE(Unit*, "current target") != secondEmber)
return Attack(secondEmber);
return botAI->DoSpecificAction("taunt spell", Event(), true);
@ -336,7 +336,7 @@ bool AlarRangedDpsPrioritizeEmbersAction::Execute(Event /*event*/)
}
SetRtiTarget(botAI, "square", firstEmber);
if (bot->GetTarget() != firstEmber->GetGUID())
if (AI_VALUE(Unit*, "current target") != firstEmber)
return Attack(firstEmber);
}
else if (secondEmber)
@ -349,13 +349,13 @@ bool AlarRangedDpsPrioritizeEmbersAction::Execute(Event /*event*/)
}
SetRtiTarget(botAI, "circle", secondEmber);
if (bot->GetTarget() != secondEmber->GetGUID())
if (AI_VALUE(Unit*, "current target") != secondEmber)
return Attack(secondEmber);
}
else if (Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar"))
{
SetRtiTarget(botAI, "star", alar);
if (bot->GetTarget() != alar->GetGUID())
if (AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
}
@ -469,7 +469,7 @@ bool AlarSwapTanksOnBossAction::Execute(Event /*event*/)
if (alar->GetHealth() == alar->GetMaxHealth())
{
SetRtiTarget(botAI, "star", alar);
if (bot->GetTarget() != alar->GetGUID())
if (AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
}
@ -477,7 +477,7 @@ bool AlarSwapTanksOnBossAction::Execute(Event /*event*/)
if (secondEmberTank && secondEmberTank != bot)
{
SetRtiTarget(botAI, "star", alar);
if (bot->GetTarget() != alar->GetGUID())
if (AI_VALUE(Unit*, "current target") != alar)
return Attack(alar);
else if (alar->GetVictim() != bot)
return botAI->DoSpecificAction("taunt spell", Event(), true);
@ -555,7 +555,7 @@ bool AlarReturnToRoomCenterAction::Execute(Event /*event*/)
{
constexpr float distFromCenter = 45.0f;
const Position& center = ALAR_ROOM_CENTER;
if (bot->GetVictim() == nullptr &&
if (AI_VALUE(Unit*, "current target") == nullptr &&
bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY()) > distFromCenter)
{
return MoveInside(TEMPEST_KEEP_MAP_ID, center.GetPositionX(), center.GetPositionY(),
@ -887,7 +887,7 @@ bool HighAstromancerSolarianTargetSolariumPriestsAction::Execute(Event /*event*/
SetRtiTarget(botAI, "star", targetPriest);
}
if (bot->GetTarget() != targetPriest->GetGUID())
if (AI_VALUE(Unit*, "current target") != targetPriest)
return Attack(targetPriest);
return false;
@ -1047,7 +1047,7 @@ bool KaelthasSunstriderMainTankPositionSanguinarAction::Execute(Event /*event*/)
MarkTargetWithStar(bot, sanguinar);
SetRtiTarget(botAI, "star", sanguinar);
if (bot->GetTarget() != sanguinar->GetGUID())
if (AI_VALUE(Unit*, "current target") != sanguinar)
return Attack(sanguinar);
if (sanguinar->GetVictim() == bot && bot->IsWithinMeleeRange(sanguinar))
@ -1090,7 +1090,7 @@ bool KaelthasSunstriderWarlockTankPositionCapernianAction::Execute(Event /*event
MarkTargetWithCircle(bot, capernian);
SetRtiTarget(botAI, "circle", capernian);
if (bot->GetTarget() != capernian->GetGUID() &&
if (AI_VALUE(Unit*, "current target") != capernian &&
botAI->CanCastSpell("searing pain", capernian) &&
botAI->CastSpell("searing pain", capernian))
return true;
@ -1251,7 +1251,7 @@ bool KaelthasSunstriderFirstAssistTankPositionTelonicusAction::Execute(Event /*e
MarkTargetWithTriangle(bot, telonicus);
SetRtiTarget(botAI, "triangle", telonicus);
if (bot->GetTarget() != telonicus->GetGUID())
if (AI_VALUE(Unit*, "current target") != telonicus)
return Attack(telonicus);
if (telonicus->GetVictim() == bot && bot->IsWithinMeleeRange(telonicus))
@ -1331,7 +1331,7 @@ bool KaelthasSunstriderAssignAdvisorDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithSquare(bot, thaladred);
SetRtiTarget(botAI, "square", thaladred);
if (bot->GetTarget() != thaladred->GetGUID())
if (AI_VALUE(Unit*, "current target") != thaladred)
return Attack(thaladred);
return false;
@ -1346,7 +1346,7 @@ bool KaelthasSunstriderAssignAdvisorDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "circle", capernian);
if (bot->GetTarget() != capernian->GetGUID())
if (AI_VALUE(Unit*, "current target") != capernian)
return Attack(capernian);
return false;
@ -1360,7 +1360,7 @@ bool KaelthasSunstriderAssignAdvisorDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", sanguinar);
if (bot->GetTarget() != sanguinar->GetGUID())
if (AI_VALUE(Unit*, "current target") != sanguinar)
return Attack(sanguinar);
return false;
@ -1373,7 +1373,7 @@ bool KaelthasSunstriderAssignAdvisorDpsPriorityAction::Execute(Event /*event*/)
!telonicus->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
{
SetRtiTarget(botAI, "triangle", telonicus);
if (bot->GetTarget() != telonicus->GetGUID())
if (AI_VALUE(Unit*, "current target") != telonicus)
return Attack(telonicus);
// Melee DPS need to stay at max-ish melee range behind Telonicus to avoid bombs
@ -1461,7 +1461,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, staff);
SetRtiTarget(botAI, "skull", staff);
if (bot->GetTarget() != staff->GetGUID())
if (AI_VALUE(Unit*, "current target") != staff)
return Attack(staff);
}
// Priority 2: Cosmic Infuser (Skull)
@ -1470,7 +1470,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, mace);
SetRtiTarget(botAI, "skull", mace);
if (bot->GetTarget() != mace->GetGUID())
if (AI_VALUE(Unit*, "current target") != mace)
return Attack(mace);
}
// Priority 3: Warp Slicer (Skull)
@ -1479,7 +1479,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, sword);
SetRtiTarget(botAI, "skull", sword);
if (bot->GetTarget() != sword->GetGUID())
if (AI_VALUE(Unit*, "current target") != sword)
return Attack(sword);
}
// Priority 4: Infinity Blades (Skull)
@ -1488,7 +1488,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, dagger);
SetRtiTarget(botAI, "skull", dagger);
if (bot->GetTarget() != dagger->GetGUID())
if (AI_VALUE(Unit*, "current target") != dagger)
return Attack(dagger);
}
// Priority 5: Devastation - ranged only (Diamond--marked in other method by main tank)
@ -1496,7 +1496,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
{
SetRtiTarget(botAI, "diamond", axe);
if (bot->GetTarget() != axe->GetGUID())
if (AI_VALUE(Unit*, "current target") != axe)
return Attack(axe);
}
// Priority 6: Netherstrand Longbow (Skull)
@ -1505,7 +1505,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, longbow);
SetRtiTarget(botAI, "skull", longbow);
if (bot->GetTarget() != longbow->GetGUID())
if (AI_VALUE(Unit*, "current target") != longbow)
return Attack(longbow);
}
// Priority 7: Phaseshift Bulwark (Skull)
@ -1514,7 +1514,7 @@ bool KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction::Execute(Event /*e
MarkTargetWithSkull(bot, shield);
SetRtiTarget(botAI, "skull", shield);
if (bot->GetTarget() != shield->GetGUID())
if (AI_VALUE(Unit*, "current target") != shield)
return Attack(shield);
}
}
@ -1531,7 +1531,7 @@ bool KaelthasSunstriderMoveDevastationAwayAction::Execute(Event /*event*/)
MarkTargetWithDiamond(bot, axe);
SetRtiTarget(botAI, "diamond", axe);
if (bot->GetTarget() != axe->GetGUID())
if (AI_VALUE(Unit*, "current target") != axe)
return Attack(axe);
constexpr float safeDistance = 13.0f;
@ -1764,7 +1764,7 @@ bool KaelthasSunstriderMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithStar(bot, kaelthas);
SetRtiTarget(botAI, "star", kaelthas);
if (bot->GetTarget() != kaelthas->GetGUID())
if (AI_VALUE(Unit*, "current target") != kaelthas)
return Attack(kaelthas);
if (kaelthas->GetVictim() == bot && bot->IsWithinMeleeRange(kaelthas))
@ -1862,7 +1862,7 @@ bool KaelthasSunstriderHandlePhoenixesAndEggsAction::AssistTanksPickUpPhoenixes(
if (!targetPhoenix)
return false;
if (bot->GetTarget() != targetPhoenix->GetGUID())
if (AI_VALUE(Unit*, "current target") != targetPhoenix)
return Attack(targetPhoenix);
constexpr float safeDistance = 12.0f;
@ -1886,7 +1886,7 @@ bool KaelthasSunstriderHandlePhoenixesAndEggsAction::NonTanksDestroyEggsAndAvoid
MarkTargetWithDiamond(bot, phoenixEgg);
SetRtiTarget(botAI, "diamond", phoenixEgg);
if (bot->GetTarget() != phoenixEgg->GetGUID())
if (AI_VALUE(Unit*, "current target") != phoenixEgg)
return Attack(phoenixEgg);
}
}
@ -1991,7 +1991,7 @@ bool KaelthasSunstriderBreakThroughShockBarrierAction::Execute(Event /*event*/)
return botAI->CastSpell(spell, kaelthas);
}
}
else if (bot->GetTarget() != kaelthas->GetGUID())
else if (AI_VALUE(Unit*, "current target") != kaelthas)
{
SetRtiTarget(botAI, "star", kaelthas);
return Attack(kaelthas);

View File

@ -100,7 +100,7 @@ float AlarPhase2NoTankingIfArmorMeltedMultiplier::GetValue(Action* action)
return 1.0f;
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar || bot->GetTarget() != alar->GetGUID())
if (!alar || AI_VALUE(Unit*, "current target") != alar)
return 1.0f;
if (dynamic_cast<CastTauntAction*>(action) ||

View File

@ -56,7 +56,7 @@ bool AkilzonTanksPositionBossAction::Execute(Event /*event*/)
if (!akilzon)
return false;
if (bot->GetVictim() != akilzon)
if (AI_VALUE(Unit*, "current target") != akilzon)
return Attack(akilzon);
if (akilzon->GetVictim() == bot)
@ -168,7 +168,7 @@ bool NalorakkTanksPositionBossAction::MainTankPositionTrollForm(Unit* nalorakk)
{
if (!nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
{
if (bot->GetVictim() != nalorakk)
if (AI_VALUE(Unit*, "current target") != nalorakk)
return Attack(nalorakk);
if (nalorakk->GetVictim() != bot)
@ -198,7 +198,7 @@ bool NalorakkTanksPositionBossAction::FirstAssistTankPositionBearForm(Unit* nalo
{
if (nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
{
if (bot->GetVictim() != nalorakk)
if (AI_VALUE(Unit*, "current target") != nalorakk)
return Attack(nalorakk);
if (nalorakk->GetVictim() != bot)
@ -262,7 +262,7 @@ bool JanalaiTanksPositionBossAction::Execute(Event /*event*/)
if (!janalai)
return false;
if (bot->GetVictim() != janalai)
if (AI_VALUE(Unit*, "current target") != janalai)
return Attack(janalai);
if (janalai->GetVictim() == bot)
@ -409,7 +409,7 @@ bool HalazziMainTankPositionBossAction::Execute(Event /*event*/)
MarkTargetWithStar(bot, halazzi);
SetRtiTarget(botAI, "star", halazzi);
if (bot->GetVictim() != halazzi)
if (AI_VALUE(Unit*, "current target") != halazzi)
return Attack(halazzi);
if (halazzi->GetVictim() == bot)
@ -443,7 +443,7 @@ bool HalazziFirstAssistTankAttackSpiritLynxAction::Execute(Event /*event*/)
MarkTargetWithCircle(bot, lynx);
SetRtiTarget(botAI, "circle", lynx);
if (bot->GetVictim() != lynx)
if (AI_VALUE(Unit*, "current target") != lynx)
return Attack(lynx);
if (lynx->GetVictim() != bot)
@ -455,7 +455,7 @@ bool HalazziFirstAssistTankAttackSpiritLynxAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", halazzi);
if (bot->GetVictim() != halazzi)
if (AI_VALUE(Unit*, "current target") != halazzi)
return Attack(halazzi);
targetFound = true;
@ -492,7 +492,7 @@ bool HalazziAssignDpsPriorityAction::Execute(Event /*event*/)
MarkTargetWithSkull(bot, totem);
SetRtiTarget(botAI, "skull", totem);
if (bot->GetTarget() != totem->GetGUID())
if (AI_VALUE(Unit*, "current target") != totem)
return Attack(totem);
return false;
@ -503,7 +503,7 @@ bool HalazziAssignDpsPriorityAction::Execute(Event /*event*/)
{
SetRtiTarget(botAI, "star", halazzi);
if (bot->GetTarget() != halazzi->GetGUID())
if (AI_VALUE(Unit*, "current target") != halazzi)
return Attack(halazzi);
}
@ -602,7 +602,7 @@ bool HexLordMalacrassCastersStopAttackingAction::Execute(Event /*event*/)
!malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION)))
return false;
if (bot->GetVictim() == malacrass)
if (AI_VALUE(Unit*, "current target") == malacrass)
{
bot->AttackStop();
bot->InterruptNonMeleeSpells(true);
@ -657,7 +657,7 @@ bool ZuljinTanksPositionBossAction::Execute(Event /*event*/)
if (!zuljin)
return false;
if (bot->GetVictim() != zuljin)
if (AI_VALUE(Unit*, "current target") != zuljin)
return Attack(zuljin);
if (zuljin->GetVictim() == bot)

View File

@ -267,7 +267,7 @@ float HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier::GetValue(Act
return 1.0f;
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe ||
(bot->GetVictim() == malacrass &&
(AI_VALUE(Unit*, "current target") == malacrass &&
castSpellAction->getThreatType() == Action::ActionThreatType::Single))
return 0.0f;

View File

@ -12,6 +12,7 @@
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h"
#include "Ai/Raid/BlackTemple/RaidBlackTempleActionContext.h"
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
@ -36,6 +37,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new RaidSSCActionContext());
actionContexts.Add(new RaidTempestKeepActionContext());
actionContexts.Add(new RaidHyjalSummitActionContext());
actionContexts.Add(new RaidBlackTempleActionContext());
actionContexts.Add(new RaidZulAmanActionContext());
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext());

View File

@ -12,6 +12,7 @@
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h"
#include "Ai/Raid/BlackTemple/RaidBlackTempleTriggerContext.h"
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
@ -37,6 +38,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new RaidSSCTriggerContext());
triggerContexts.Add(new RaidTempestKeepTriggerContext());
triggerContexts.Add(new RaidHyjalSummitTriggerContext());
triggerContexts.Add(new RaidBlackTempleTriggerContext());
triggerContexts.Add(new RaidZulAmanTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());

View File

@ -1619,9 +1619,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
{
static const std::vector<std::string> allInstanceStrategies =
{
"aq20", "bwl", "karazhan", "gruulslair", "hyjal", "icc", "magtheridon",
"moltencore", "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar",
"voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos",
"aq20", "blacktemple", "bwl", "gruulslair", "hyjal", "icc", "karazhan",
"magtheridon", "moltencore", "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep",
"ulduar", "voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos",
"wotlk-gd", "wotlk-hol", "wotlk-hor", "wotlk-hos", "wotlk-nex", "wotlk-occ",
"wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-toc", "wotlk-uk", "wotlk-up",
"wotlk-vh", "zulaman"
@ -1669,6 +1669,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 558:
strategyName = "tbc-ac"; // Auchindoun: Auchenai Crypts
break;
case 564:
strategyName = "blacktemple"; // Black Temple
break;
case 565:
strategyName = "gruulslair"; // Gruul's Lair
break;
@ -2874,7 +2877,7 @@ bool PlayerbotAI::SayToParty(const std::string& msg)
bool PlayerbotAI::SayToRaid(const std::string& msg)
{
if (!bot->GetGroup() || bot->GetGroup()->isRaidGroup())
if (!bot->GetGroup() || !bot->GetGroup()->isRaidGroup())
return false;
WorldPacket data;
@ -5560,7 +5563,7 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, ELEMENTAL_SHARPENING_STONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
Item* stone = nullptr;

View File

@ -39,6 +39,8 @@ void PlayerbotRepository::Load(PlayerbotAI* botAI)
botAI->ChangeStrategy(value, BOT_STATE_DEAD);
} while (result->NextRow());
botAI->GetAiObjectContext()->GetUntypedValue("outfit list");
botAI->GetAiObjectContext()->Load(values);
}
}