mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-13 23:33:47 +00:00
Compare commits
19 Commits
force_buil
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6a6af1b4d | ||
|
|
610fdc16d7 | ||
|
|
c9c936d5c1 | ||
|
|
cfb2ed4bf3 | ||
|
|
e9e79ad696 | ||
|
|
3db2a5a193 | ||
|
|
8585f10f48 | ||
|
|
79fb3a5bbc | ||
|
|
6ed3f24ecb | ||
|
|
76b6df9ea3 | ||
|
|
026df0dabe | ||
|
|
b31bda85ee | ||
|
|
bebac60c51 | ||
|
|
52d4191b43 | ||
|
|
254055ff32 | ||
|
|
31765c77fa | ||
|
|
c86032f43b | ||
|
|
ba835250c8 | ||
|
|
8c2a27b9fe |
6
.github/workflows/check_pr_source.yml
vendored
6
.github/workflows/check_pr_source.yml
vendored
@@ -1,13 +1,15 @@
|
|||||||
name: Enforce test-staging → main
|
name: Enforce test-staging → master
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- master
|
||||||
|
- test-staging
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
require-test-staging:
|
require-test-staging:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
if: github.event.pull_request.base.ref == 'master'
|
||||||
steps:
|
steps:
|
||||||
- name: Ensure PR source is test-staging
|
- name: Ensure PR source is test-staging
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.github/workflows/code_style.yml
vendored
4
.github/workflows/code_style.yml
vendored
@@ -2,9 +2,9 @@ name: Codestyle
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "codestyle-${{ github.event.pull_request.number }}"
|
group: "codestyle-${{ github.event.pull_request.number }}"
|
||||||
|
|||||||
4
.github/workflows/core_build.yml
vendored
4
.github/workflows/core_build.yml
vendored
@@ -2,9 +2,9 @@ name: ubuntu-build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "core-build-${{ github.event.pull_request.number }}"
|
group: "core-build-${{ github.event.pull_request.number }}"
|
||||||
|
|||||||
4
.github/workflows/macos_build.yml
vendored
4
.github/workflows/macos_build.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: macos-build
|
name: macos-build
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "macos-build-${{ github.event.pull_request.number }}"
|
group: "macos-build-${{ github.event.pull_request.number }}"
|
||||||
|
|||||||
4
.github/workflows/windows_build.yml
vendored
4
.github/workflows/windows_build.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: windows-build
|
name: windows-build
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "master" ]
|
branches: [ "master", "test-staging" ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "windows-build-${{ github.event.pull_request.number }}"
|
group: "windows-build-${{ github.event.pull_request.number }}"
|
||||||
|
|||||||
@@ -66,38 +66,35 @@ Please answer the following:
|
|||||||
|
|
||||||
## Complexity & Impact
|
## Complexity & Impact
|
||||||
|
|
||||||
- Does this change add new decision branches?
|
Does this change add new decision branches?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain below**)
|
- - [ ] Yes (**explain below**)
|
||||||
|
|
||||||
- Does this change increase per-bot or per-tick processing?
|
Does this change increase per-bot or per-tick processing?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**describe and justify impact**)
|
- - [ ] Yes (**describe and justify impact**)
|
||||||
|
|
||||||
- Could this logic scale poorly under load?
|
|
||||||
- [ ] No
|
|
||||||
- [ ] Yes (**explain why**)
|
|
||||||
|
|
||||||
|
Could this logic scale poorly under load?
|
||||||
|
- - [ ] No
|
||||||
|
- - [ ] Yes (**explain why**)
|
||||||
---
|
---
|
||||||
|
|
||||||
## Defaults & Configuration
|
## Defaults & Configuration
|
||||||
|
|
||||||
- Does this change modify default bot behavior?
|
Does this change modify default bot behavior?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain why**)
|
- - [ ] Yes (**explain why**)
|
||||||
|
|
||||||
If this introduces more advanced or AI-heavy logic:
|
If this introduces more advanced or AI-heavy logic:
|
||||||
|
- - [ ] Lightweight mode remains the default
|
||||||
- [ ] Lightweight mode remains the default
|
- - [ ] More complex behavior is optional and thereby configurable
|
||||||
- [ ] More complex behavior is optional and thereby configurable
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## AI Assistance
|
## AI Assistance
|
||||||
|
|
||||||
- Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change?
|
Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain below**)
|
- - [ ] Yes (**explain below**)
|
||||||
|
|
||||||
If yes, please specify:
|
If yes, please specify:
|
||||||
|
|
||||||
@@ -114,10 +111,10 @@ about what they do and do not understand.
|
|||||||
|
|
||||||
## Final Checklist
|
## Final Checklist
|
||||||
|
|
||||||
- [ ] Stability is not compromised
|
- - [ ] Stability is not compromised
|
||||||
- [ ] Performance impact is understood, tested, and acceptable
|
- - [ ] Performance impact is understood, tested, and acceptable
|
||||||
- [ ] Added logic complexity is justified and explained
|
- - [ ] Added logic complexity is justified and explained
|
||||||
- [ ] Documentation updated if needed
|
- - [ ] Documentation updated if needed
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
71
README_CN.md
Normal file
71
README_CN.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
[English](README.md) | [Español](README_ES.md) | [中文](README_CN.md)
|
||||||
|
|
||||||
|
# 玩家机器人模块
|
||||||
|
|
||||||
|
欢迎使用AzerothCore的玩家机器人模块,这是一个基于IKE3玩家机器人的正在进行中的项目。这些玩家机器人利用实际的玩家数据,使您能够与您自己的替身进行交互,组建队伍,升级角色等等。
|
||||||
|
|
||||||
|
如果您遇到任何错误或出现崩溃,请您将它们报告为GitHub问题。您宝贵的反馈将帮助我们协作改进和增强这个项目。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
请注意,此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性,您必须使用我fork的自定义分支来编译它,可以在这里找到:[mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot)。
|
||||||
|
|
||||||
|
要安装此模块,请参考AzerothCore Wiki的详细说明:[AzerothCore安装指南](https://www.azerothcore.org/wiki/installation)。
|
||||||
|
|
||||||
|
我们提供了一个简单的方法来克隆该模块:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
|
||||||
|
cd azerothcore-wotlk/modules
|
||||||
|
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始与文档
|
||||||
|
|
||||||
|
要快速开始并了解一系列命令,您可以参考ike3原版playerbots的手册。该模块提供了大部分基本命令。您可以在此找到文档:[IKE3 Playerbots 文档](https://ike3.github.io/mangosbot-docs/)。请注意,在我们的模块中,您需要将文档中所有的 `.bot` 替换为 `.playerbot bot`。
|
||||||
|
|
||||||
|
请注意,由于项目仍在开发中,新添加的命令的文档目前尚不完善。
|
||||||
|
|
||||||
|
## 进展
|
||||||
|
|
||||||
|
该模块主要强调以下关键功能,并在这些领域实施了改进:
|
||||||
|
|
||||||
|
- **世界中的机器人(随机机器人):** 我们增强了随机机器人的行为,使它们更接近真实玩家的表现,从而创建了更真实的玩家服务器环境。
|
||||||
|
|
||||||
|
- **团队副本中的机器人:** 我们赋予机器人征服具有挑战性的团队副本内容的能力,通过为各种Boss实施特定策略,使团队副本更加吸引人。此外,我们增强了机器人在DPS、治疗和坦克等各种角色中的能力,确保它们有效地为团队的成功做出贡献。
|
||||||
|
|
||||||
|
- **战场中的机器人:** 机器人现在能够与真实玩家一起积极参与战场,为这些PvP场景增添了深度和刺激。
|
||||||
|
|
||||||
|
- **与机器人的交互:** 我们改进了真实玩家和机器人之间的交互,使玩家能够在与机器人伙伴合作时完成任务并升级多个角色。
|
||||||
|
|
||||||
|
- **玩家进阶路径:** 我们设计了一个改进的玩家进阶路径,辅以机器人,为玩家提供了一种替代且引人入胜的游戏体验。
|
||||||
|
|
||||||
|
- **稳定性:** 我们的努力主要集中在增强使用Playerbots模块时AzerothCore的整体稳定性。这些改进旨在防止服务器崩溃,并确保所有用户都能获得更流畅的体验。
|
||||||
|
|
||||||
|
- **配置:** 我们引入了一系列可配置的选项,以满足不同需求的玩家,从而提供更个性化的体验。
|
||||||
|
|
||||||
|
值得注意的是,随着我们继续改进项目,还有大量工作需要完成。我们欢迎每个人以不同的方式做出贡献。
|
||||||
|
|
||||||
|
## 插件
|
||||||
|
|
||||||
|
为了更好地控制机器人并简化命令的使用,您还可以使用我们的插件:[Unbot Addon](https://github.com/liyunfan1223/unbot-addon)。目前,该插件仅对简体中文客户端提供更好的支持。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
**机器人无法释放技能**
|
||||||
|
|
||||||
|
- 请确保必要的英文DBC文件(enUS)存在。
|
||||||
|
|
||||||
|
**编译错误**
|
||||||
|
|
||||||
|
- 我们支持Ubuntu、Windows和macOS。
|
||||||
|
|
||||||
|
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/mod-playerbots/mod-playerbots/actions)中查看构建状态。
|
||||||
|
|
||||||
|
- 如果最新的构建状态失败,请恢复到上一个提交。我们将尽快解决此问题。
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
该模块的代码来自[ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots)和[celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots)。我们衷心感谢@ZhengPeiRu21和@celguar对维护该模块的持续努力。
|
||||||
|
|
||||||
|
我们还要向所有为playerbot开发做出贡献的个人表示诚挚的感谢。您的奉献和努力对塑造这个项目至关重要,我们对您的贡献表示感谢。
|
||||||
@@ -558,7 +558,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
|||||||
# "mana" (bots have infinite mana)
|
# "mana" (bots have infinite mana)
|
||||||
# "power" (bots have infinite energy, rage, and runic power)
|
# "power" (bots have infinite energy, rage, and runic power)
|
||||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||||
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
|
# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar))
|
||||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
||||||
# Default: food, taxi, and raid are enabled
|
# Default: food, taxi, and raid are enabled
|
||||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||||
@@ -990,7 +990,7 @@ AiPlayerbot.ZoneBracket.3433 = 10,22
|
|||||||
AiPlayerbot.ZoneBracket.3525 = 10,21
|
AiPlayerbot.ZoneBracket.3525 = 10,21
|
||||||
|
|
||||||
# Classic WoW - High-level zones:
|
# Classic WoW - High-level zones:
|
||||||
# Deadwind Pass (Zone ID: 10 Default Min,Max: 19,33)
|
# Duskwood (Zone ID: 10 Default Min,Max: 19,33)
|
||||||
# Wetlands (Zone ID: 11 Default Min,Max: 21,30)
|
# Wetlands (Zone ID: 11 Default Min,Max: 21,30)
|
||||||
# Redridge Mountains (Zone ID: 44 Default Min,Max: 16,28)
|
# Redridge Mountains (Zone ID: 44 Default Min,Max: 16,28)
|
||||||
# Hillsbrad Foothills (Zone ID: 267 Default Min,Max: 20,34)
|
# Hillsbrad Foothills (Zone ID: 267 Default Min,Max: 20,34)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- Temporarily disables innodb_strict_mode for the session to allow the script to complete even if legacy table definitions contain InnoDB-incompatible attributes
|
||||||
|
SET SESSION innodb_strict_mode = 0;
|
||||||
|
|
||||||
|
-- Change the tables to InnoDB
|
||||||
|
ALTER TABLE playerbots_guild_names ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_names ENGINE=InnoDB;
|
||||||
|
|
||||||
|
-- Re-enables innodb_strict_mode
|
||||||
|
SET SESSION innodb_strict_mode = 1;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- Temporarily disables innodb_strict_mode for the session to allow the script to complete even if legacy table definitions contain InnoDB-incompatible attributes
|
||||||
|
SET SESSION innodb_strict_mode = 0;
|
||||||
|
|
||||||
|
-- Change the tables to InnoDB
|
||||||
|
ALTER TABLE playerbots_dungeon_suggestion_abbrevation ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_dungeon_suggestion_definition ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_dungeon_suggestion_strategy ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_equip_cache ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_item_info_cache ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_rarity_cache ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_rnditem_cache ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_tele_cache ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_travelnode ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_travelnode_link ENGINE=InnoDB;
|
||||||
|
ALTER TABLE playerbots_travelnode_path ENGINE=InnoDB;
|
||||||
|
|
||||||
|
-- Re-enables innodb_strict_mode
|
||||||
|
SET SESSION innodb_strict_mode = 1;
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
-- #########################################################
|
||||||
|
-- Playerbots - Add PVP / Arena texts for TellPvpAction
|
||||||
|
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
|
||||||
|
-- zhTW, esES, esMX, ruRU)
|
||||||
|
-- #########################################################
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- pvp_currency
|
||||||
|
-- [PVP] Arena points: %arena_points | Honor Points: %honor_points
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
SELECT
|
||||||
|
'pvp_currency',
|
||||||
|
'[PVP] Arena points: %arena_points | Honor Points: %honor_points',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'[PVP] 투기장 점수: %arena_points | 명예 점수: %honor_points',
|
||||||
|
-- frFR
|
||||||
|
'[PVP] Points d''arène : %arena_points | Points d''honneur : %honor_points',
|
||||||
|
-- deDE
|
||||||
|
'[PVP] Arenapunkte: %arena_points | Ehrenpunkte: %honor_points',
|
||||||
|
-- zhCN
|
||||||
|
'[PVP] 竞技场点数:%arena_points | 荣誉点数:%honor_points',
|
||||||
|
-- zhTW
|
||||||
|
'[PVP] 競技場點數:%arena_points | 榮譽點數:%honor_points',
|
||||||
|
-- esES
|
||||||
|
'[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points',
|
||||||
|
-- esMX
|
||||||
|
'[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points',
|
||||||
|
-- ruRU
|
||||||
|
'[PVP] Очки арены: %arena_points | Очки чести: %honor_points'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_currency'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- pvp_arena_team
|
||||||
|
-- [PVP] %bracket: <%team_name> (rating %team_rating)
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
SELECT
|
||||||
|
'pvp_arena_team',
|
||||||
|
'[PVP] %bracket: <%team_name> (rating %team_rating)',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'[PVP] %bracket: <%team_name> (평점 %team_rating)',
|
||||||
|
-- frFR
|
||||||
|
'[PVP] %bracket : <%team_name> (cote %team_rating)',
|
||||||
|
-- deDE
|
||||||
|
'[PVP] %bracket: <%team_name> (Wertung %team_rating)',
|
||||||
|
-- zhCN
|
||||||
|
'[PVP] %bracket: <%team_name> (评分 %team_rating)',
|
||||||
|
-- zhTW
|
||||||
|
'[PVP] %bracket: <%team_name> (評分 %team_rating)',
|
||||||
|
-- esES
|
||||||
|
'[PVP] %bracket: <%team_name> (índice %team_rating)',
|
||||||
|
-- esMX
|
||||||
|
'[PVP] %bracket: <%team_name> (índice %team_rating)',
|
||||||
|
-- ruRU
|
||||||
|
'[PVP] %bracket: <%team_name> (рейтинг %team_rating)'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_arena_team'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- pvp_no_arena_team
|
||||||
|
-- [PVP] I have no Arena Team.
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
SELECT
|
||||||
|
'pvp_no_arena_team',
|
||||||
|
'[PVP] I have no Arena Team.',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'[PVP] 투기장 팀이 없습니다.',
|
||||||
|
-- frFR
|
||||||
|
'[PVP] Je n''ai aucune équipe d''arène.',
|
||||||
|
-- deDE
|
||||||
|
'[PVP] Ich habe kein Arenateam.',
|
||||||
|
-- zhCN
|
||||||
|
'[PVP] 我没有竞技场战队。',
|
||||||
|
-- zhTW
|
||||||
|
'[PVP] 我沒有競技場隊伍。',
|
||||||
|
-- esES
|
||||||
|
'[PVP] No tengo equipo de arena.',
|
||||||
|
-- esMX
|
||||||
|
'[PVP] No tengo equipo de arena.',
|
||||||
|
-- ruRU
|
||||||
|
'[PVP] У меня нет команды арены.'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_no_arena_team'
|
||||||
|
);
|
||||||
@@ -328,7 +328,43 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
botAI->TellMaster(out);
|
botAI->TellMaster(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EquipUpgradesAction::Execute(Event event)
|
ItemIds EquipAction::SelectInventoryItemsToEquip()
|
||||||
|
{
|
||||||
|
CollectItemsVisitor visitor;
|
||||||
|
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||||
|
|
||||||
|
ItemIds items;
|
||||||
|
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||||
|
{
|
||||||
|
Item* item = *i;
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ItemTemplate const* itemTemplate = item->GetTemplate();
|
||||||
|
if (!itemTemplate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//TODO Expand to Glyphs and Gems, that can be placed in equipment
|
||||||
|
//Pre-filter non-equipable items
|
||||||
|
if (itemTemplate->InventoryType == INVTYPE_NON_EQUIP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||||
|
uint32 itemId = item->GetTemplate()->ItemId;
|
||||||
|
std::string itemUsageParam;
|
||||||
|
if (randomProperty != 0)
|
||||||
|
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||||
|
else
|
||||||
|
itemUsageParam = std::to_string(itemId);
|
||||||
|
|
||||||
|
ItemUsage usage = AI_VALUE2(ItemUsage, "item upgrade", itemUsageParam);
|
||||||
|
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||||
|
items.insert(itemId);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquipUpgradesTriggeredAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot))
|
if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||||
return false;
|
return false;
|
||||||
@@ -361,72 +397,18 @@ bool EquipUpgradesAction::Execute(Event event)
|
|||||||
p >> itemId;
|
p >> itemId;
|
||||||
|
|
||||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||||
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
if (item->InventoryType == INVTYPE_NON_EQUIP)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectItemsVisitor visitor;
|
ItemIds items = SelectInventoryItemsToEquip();
|
||||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
|
||||||
|
|
||||||
ItemIds items;
|
|
||||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
|
||||||
{
|
|
||||||
Item* item = *i;
|
|
||||||
if (!item)
|
|
||||||
break;
|
|
||||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
|
||||||
uint32 itemId = item->GetTemplate()->ItemId;
|
|
||||||
std::string itemUsageParam;
|
|
||||||
if (randomProperty != 0)
|
|
||||||
{
|
|
||||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemUsageParam = std::to_string(itemId);
|
|
||||||
}
|
|
||||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
|
||||||
|
|
||||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
|
||||||
{
|
|
||||||
items.insert(itemId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EquipItems(items);
|
EquipItems(items);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EquipUpgradeAction::Execute(Event event)
|
bool EquipUpgradeAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
CollectItemsVisitor visitor;
|
ItemIds items = SelectInventoryItemsToEquip();
|
||||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
|
||||||
|
|
||||||
ItemIds items;
|
|
||||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
|
||||||
{
|
|
||||||
Item* item = *i;
|
|
||||||
if (!item)
|
|
||||||
break;
|
|
||||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
|
||||||
uint32 itemId = item->GetTemplate()->ItemId;
|
|
||||||
std::string itemUsageParam;
|
|
||||||
if (randomProperty != 0)
|
|
||||||
{
|
|
||||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemUsageParam = std::to_string(itemId);
|
|
||||||
}
|
|
||||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
|
||||||
|
|
||||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
|
||||||
{
|
|
||||||
items.insert(itemId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EquipItems(items);
|
EquipItems(items);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "ChatHelper.h"
|
#include "ChatHelper.h"
|
||||||
#include "InventoryAction.h"
|
#include "InventoryAction.h"
|
||||||
|
#include "Item.h"
|
||||||
|
|
||||||
class FindItemVisitor;
|
class FindItemVisitor;
|
||||||
class Item;
|
class Item;
|
||||||
@@ -20,6 +21,7 @@ public:
|
|||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
void EquipItems(ItemIds ids);
|
void EquipItems(ItemIds ids);
|
||||||
|
ItemIds SelectInventoryItemsToEquip();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void EquipItem(FindItemVisitor* visitor);
|
void EquipItem(FindItemVisitor* visitor);
|
||||||
@@ -27,10 +29,10 @@ private:
|
|||||||
void EquipItem(Item* item);
|
void EquipItem(Item* item);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EquipUpgradesAction : public EquipAction
|
class EquipUpgradesTriggeredAction : public EquipAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EquipUpgradesAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {}
|
explicit EquipUpgradesTriggeredAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
@@ -38,7 +40,7 @@ public:
|
|||||||
class EquipUpgradeAction : public EquipAction
|
class EquipUpgradeAction : public EquipAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {}
|
explicit EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ bool LootRollAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (sPlayerbotAIConfig.lootRollLevel == 1)
|
else if (sPlayerbotAIConfig.lootRollLevel == 1)
|
||||||
{
|
{
|
||||||
|
// Level 1 = "greed" mode: bots greed on useful items but never need
|
||||||
|
// Only downgrade NEED to GREED, preserve GREED votes as-is
|
||||||
if (vote == NEED)
|
if (vote == NEED)
|
||||||
{
|
{
|
||||||
if (RollUniqueCheck(proto, bot))
|
if (RollUniqueCheck(proto, bot))
|
||||||
@@ -101,10 +103,6 @@ bool LootRollAction::Execute(Event event)
|
|||||||
vote = GREED;
|
vote = GREED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (vote == GREED)
|
|
||||||
{
|
|
||||||
vote = PASS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
switch (group->GetLootMethod())
|
switch (group->GetLootMethod())
|
||||||
{
|
{
|
||||||
|
|||||||
100
src/Ai/Base/Actions/TellPvpStatsAction.cpp
Normal file
100
src/Ai/Base/Actions/TellPvpStatsAction.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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 "TellPvpStatsAction.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "ArenaTeam.h"
|
||||||
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "Event.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "Language.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline char const* BracketName(uint8 slot)
|
||||||
|
{
|
||||||
|
switch (slot)
|
||||||
|
{
|
||||||
|
case ARENA_SLOT_2v2: return "2v2";
|
||||||
|
case ARENA_SLOT_3v3: return "3v3";
|
||||||
|
default: return "5v5"; // ARENA_SLOT_5v5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TellPvpStatsAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (!bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Prefer the actual chat sender (whisper / say / etc.) if available.
|
||||||
|
Player* requester = nullptr;
|
||||||
|
|
||||||
|
if (Unit* owner = event.getOwner())
|
||||||
|
requester = owner->ToPlayer();
|
||||||
|
|
||||||
|
// Fallback to master if event owner is not available.
|
||||||
|
if (!requester)
|
||||||
|
requester = GetMaster();
|
||||||
|
|
||||||
|
// If we still do not have a valid player to answer to, bail out.
|
||||||
|
if (!requester)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// PVP currencies
|
||||||
|
std::map<std::string, std::string> currencyPlaceholders;
|
||||||
|
currencyPlaceholders["%arena_points"] = std::to_string(bot->GetArenaPoints());
|
||||||
|
currencyPlaceholders["%honor_points"] = std::to_string(bot->GetHonorPoints());
|
||||||
|
|
||||||
|
std::string const currencyText = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pvp_currency",
|
||||||
|
"[PVP] Arena points: %arena_points | Honor Points: %honor_points",
|
||||||
|
currencyPlaceholders);
|
||||||
|
|
||||||
|
bot->Whisper(currencyText, LANG_UNIVERSAL, requester);
|
||||||
|
|
||||||
|
// Arena Teams by slot
|
||||||
|
bool anyTeam = false;
|
||||||
|
for (uint8 slot = 0; slot < MAX_ARENA_SLOT; ++slot)
|
||||||
|
{
|
||||||
|
uint32 const teamId = bot->GetArenaTeamId(slot);
|
||||||
|
if (!teamId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(teamId))
|
||||||
|
{
|
||||||
|
anyTeam = true;
|
||||||
|
std::map<std::string, std::string> placeholders;
|
||||||
|
placeholders["%bracket"] = BracketName(slot);
|
||||||
|
placeholders["%team_name"] = team->GetName();
|
||||||
|
placeholders["%team_rating"] = std::to_string(team->GetRating());
|
||||||
|
|
||||||
|
std::string const teamText = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pvp_arena_team",
|
||||||
|
"[PVP] %bracket: <%team_name> (rating %team_rating)",
|
||||||
|
placeholders);
|
||||||
|
|
||||||
|
bot->Whisper(teamText, LANG_UNIVERSAL, requester);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyTeam)
|
||||||
|
{
|
||||||
|
std::string const noTeamText = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pvp_no_arena_team",
|
||||||
|
"[PVP] I have no Arena Team.",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
|
||||||
|
bot->Whisper(noTeamText, LANG_UNIVERSAL, requester);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
20
src/Ai/Base/Actions/TellPvpStatsAction.h
Normal file
20
src/Ai/Base/Actions/TellPvpStatsAction.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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_TELLPVPSTATSACTION_H
|
||||||
|
#define _PLAYERBOT_TELLPVPSTATSACTION_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class TellPvpStatsAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TellPvpStatsAction(PlayerbotAI* botAI) : Action(botAI, "tell pvp stats") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -17,9 +17,9 @@ public:
|
|||||||
SummonAction(PlayerbotAI* botAI, std::string const name = "summon") : MovementAction(botAI, name) {}
|
SummonAction(PlayerbotAI* botAI, std::string const name = "summon") : MovementAction(botAI, name) {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
|
||||||
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
|
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
|
||||||
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
|
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
#include "TellItemCountAction.h"
|
#include "TellItemCountAction.h"
|
||||||
#include "TellLosAction.h"
|
#include "TellLosAction.h"
|
||||||
#include "TellReputationAction.h"
|
#include "TellReputationAction.h"
|
||||||
|
#include "TellPvpStatsAction.h"
|
||||||
#include "TellTargetAction.h"
|
#include "TellTargetAction.h"
|
||||||
#include "TradeAction.h"
|
#include "TradeAction.h"
|
||||||
#include "TrainerAction.h"
|
#include "TrainerAction.h"
|
||||||
@@ -97,6 +98,7 @@ public:
|
|||||||
creators["quests"] = &ChatActionContext::quests;
|
creators["quests"] = &ChatActionContext::quests;
|
||||||
creators["leave"] = &ChatActionContext::leave;
|
creators["leave"] = &ChatActionContext::leave;
|
||||||
creators["reputation"] = &ChatActionContext::reputation;
|
creators["reputation"] = &ChatActionContext::reputation;
|
||||||
|
creators["tell pvp stats"] = &ChatActionContext::tell_pvp_stats;
|
||||||
creators["log"] = &ChatActionContext::log;
|
creators["log"] = &ChatActionContext::log;
|
||||||
creators["los"] = &ChatActionContext::los;
|
creators["los"] = &ChatActionContext::los;
|
||||||
creators["rpg status"] = &ChatActionContext::rpg_status;
|
creators["rpg status"] = &ChatActionContext::rpg_status;
|
||||||
@@ -118,7 +120,7 @@ public:
|
|||||||
creators["use"] = &ChatActionContext::use;
|
creators["use"] = &ChatActionContext::use;
|
||||||
creators["item count"] = &ChatActionContext::item_count;
|
creators["item count"] = &ChatActionContext::item_count;
|
||||||
creators["equip"] = &ChatActionContext::equip;
|
creators["equip"] = &ChatActionContext::equip;
|
||||||
creators["equip upgrades"] = &ChatActionContext::equip_upgrades;
|
creators["equip upgrades"] = &ChatActionContext::equip_upgrade;
|
||||||
creators["unequip"] = &ChatActionContext::unequip;
|
creators["unequip"] = &ChatActionContext::unequip;
|
||||||
creators["sell"] = &ChatActionContext::sell;
|
creators["sell"] = &ChatActionContext::sell;
|
||||||
creators["buy"] = &ChatActionContext::buy;
|
creators["buy"] = &ChatActionContext::buy;
|
||||||
@@ -256,7 +258,6 @@ private:
|
|||||||
static Action* talents(PlayerbotAI* botAI) { return new ChangeTalentsAction(botAI); }
|
static Action* talents(PlayerbotAI* botAI) { return new ChangeTalentsAction(botAI); }
|
||||||
|
|
||||||
static Action* equip(PlayerbotAI* botAI) { return new EquipAction(botAI); }
|
static Action* equip(PlayerbotAI* botAI) { return new EquipAction(botAI); }
|
||||||
static Action* equip_upgrades(PlayerbotAI* botAI) { return new EquipUpgradesAction(botAI); }
|
|
||||||
static Action* unequip(PlayerbotAI* botAI) { return new UnequipAction(botAI); }
|
static Action* unequip(PlayerbotAI* botAI) { return new UnequipAction(botAI); }
|
||||||
static Action* sell(PlayerbotAI* botAI) { return new SellAction(botAI); }
|
static Action* sell(PlayerbotAI* botAI) { return new SellAction(botAI); }
|
||||||
static Action* buy(PlayerbotAI* botAI) { return new BuyAction(botAI); }
|
static Action* buy(PlayerbotAI* botAI) { return new BuyAction(botAI); }
|
||||||
@@ -279,6 +280,7 @@ private:
|
|||||||
static Action* quests(PlayerbotAI* botAI) { return new ListQuestsAction(botAI); }
|
static Action* quests(PlayerbotAI* botAI) { return new ListQuestsAction(botAI); }
|
||||||
static Action* leave(PlayerbotAI* botAI) { return new LeaveGroupAction(botAI); }
|
static Action* leave(PlayerbotAI* botAI) { return new LeaveGroupAction(botAI); }
|
||||||
static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); }
|
static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); }
|
||||||
|
static Action* tell_pvp_stats(PlayerbotAI* botAI) { return new TellPvpStatsAction(botAI); }
|
||||||
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
|
static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); }
|
||||||
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
|
static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); }
|
||||||
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
|
static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); }
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
creators["leave"] = &ChatTriggerContext::leave;
|
creators["leave"] = &ChatTriggerContext::leave;
|
||||||
creators["rep"] = &ChatTriggerContext::reputation;
|
creators["rep"] = &ChatTriggerContext::reputation;
|
||||||
creators["reputation"] = &ChatTriggerContext::reputation;
|
creators["reputation"] = &ChatTriggerContext::reputation;
|
||||||
|
creators["pvp stats"] = &ChatTriggerContext::pvp_stats;
|
||||||
creators["log"] = &ChatTriggerContext::log;
|
creators["log"] = &ChatTriggerContext::log;
|
||||||
creators["los"] = &ChatTriggerContext::los;
|
creators["los"] = &ChatTriggerContext::los;
|
||||||
creators["rpg status"] = &ChatTriggerContext::rpg_status;
|
creators["rpg status"] = &ChatTriggerContext::rpg_status;
|
||||||
@@ -224,6 +225,7 @@ private:
|
|||||||
static Trigger* stats(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "stats"); }
|
static Trigger* stats(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "stats"); }
|
||||||
static Trigger* leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "leave"); }
|
static Trigger* leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "leave"); }
|
||||||
static Trigger* reputation(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "reputation"); }
|
static Trigger* reputation(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "reputation"); }
|
||||||
|
static Trigger* pvp_stats(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pvp stats"); }
|
||||||
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
|
static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); }
|
||||||
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
|
static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); }
|
||||||
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
|
static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
PassTroughStrategy::InitTriggers(triggers);
|
PassTroughStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
|
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
|
||||||
|
triggers.push_back(new TriggerNode("pvp stats", { NextAction("tell pvp stats", relevance) }));
|
||||||
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
|
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
|
||||||
NextAction("query item usage", relevance) }));
|
NextAction("query item usage", relevance) }));
|
||||||
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
|
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
|
||||||
@@ -116,6 +117,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("stats");
|
supported.push_back("stats");
|
||||||
supported.push_back("leave");
|
supported.push_back("leave");
|
||||||
supported.push_back("reputation");
|
supported.push_back("reputation");
|
||||||
|
supported.push_back("tell pvp stats");
|
||||||
supported.push_back("log");
|
supported.push_back("log");
|
||||||
supported.push_back("los");
|
supported.push_back("los");
|
||||||
supported.push_back("rpg status");
|
supported.push_back("rpg status");
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
NextAction("query item usage", relevance),
|
NextAction("query item usage", relevance),
|
||||||
NextAction("equip upgrades", relevance) }));
|
NextAction("equip upgrades", relevance) }));
|
||||||
triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) }));
|
triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) }));
|
||||||
|
triggers.push_back(new TriggerNode("loot roll won", { NextAction("equip upgrades", relevance) }));
|
||||||
triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) }));
|
triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) }));
|
||||||
// triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) }));
|
// triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) }));
|
||||||
triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) }));
|
triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) }));
|
||||||
|
|||||||
@@ -19,19 +19,9 @@
|
|||||||
|
|
||||||
ItemUsage ItemUsageValue::Calculate()
|
ItemUsage ItemUsageValue::Calculate()
|
||||||
{
|
{
|
||||||
uint32 itemId = 0;
|
ParsedItemUsage const parsed = GetItemIdFromQualifier();
|
||||||
uint32 randomPropertyId = 0;
|
uint32 itemId = parsed.itemId;
|
||||||
size_t pos = qualifier.find(",");
|
uint32 randomPropertyId = parsed.randomPropertyId;
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
itemId = atoi(qualifier.substr(0, pos).c_str());
|
|
||||||
randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemId = atoi(qualifier.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!itemId)
|
if (!itemId)
|
||||||
return ITEM_USAGE_NONE;
|
return ITEM_USAGE_NONE;
|
||||||
|
|
||||||
@@ -142,96 +132,30 @@ ItemUsage ItemUsageValue::Calculate()
|
|||||||
|
|
||||||
// If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer
|
// If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer
|
||||||
if (isLootFromItem && botNeedsItemForQuest)
|
if (isLootFromItem && botNeedsItemForQuest)
|
||||||
{
|
|
||||||
return ITEM_USAGE_QUEST;
|
return ITEM_USAGE_QUEST;
|
||||||
}
|
|
||||||
|
|
||||||
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
|
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
|
||||||
if (!isSelfBot && masterNeedsItemForQuest)
|
if (!isSelfBot && masterNeedsItemForQuest)
|
||||||
{
|
|
||||||
return ITEM_USAGE_NONE;
|
return ITEM_USAGE_NONE;
|
||||||
}
|
|
||||||
|
|
||||||
// If the bot itself needs the item for a quest, allow looting
|
// If the bot itself needs the item for a quest, allow looting
|
||||||
if (botNeedsItemForQuest)
|
if (botNeedsItemForQuest)
|
||||||
{
|
|
||||||
return ITEM_USAGE_QUEST;
|
return ITEM_USAGE_QUEST;
|
||||||
}
|
|
||||||
|
|
||||||
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||||
{
|
{
|
||||||
if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR)
|
ItemUsage ammoUsage = QueryItemUsageForAmmo(proto);
|
||||||
{
|
if (ammoUsage != ITEM_USAGE_NONE)
|
||||||
Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
return ammoUsage;
|
||||||
uint32 requiredSubClass = 0;
|
|
||||||
|
|
||||||
if (rangedWeapon)
|
|
||||||
{
|
|
||||||
switch (rangedWeapon->GetTemplate()->SubClass)
|
|
||||||
{
|
|
||||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
|
||||||
requiredSubClass = ITEM_SUBCLASS_BULLET;
|
|
||||||
break;
|
|
||||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
|
||||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
|
||||||
requiredSubClass = ITEM_SUBCLASS_ARROW;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the item is the correct ammo type for the equipped ranged weapon
|
|
||||||
if (proto->SubClass == requiredSubClass)
|
|
||||||
{
|
|
||||||
float ammoCount = BetterStacks(proto, "ammo");
|
|
||||||
float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2
|
|
||||||
uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID);
|
|
||||||
|
|
||||||
// Check if the bot has an ammo type assigned
|
|
||||||
if (currentAmmoId == 0)
|
|
||||||
{
|
|
||||||
return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo
|
|
||||||
}
|
|
||||||
// Compare new ammo vs current equipped ammo
|
|
||||||
ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId);
|
|
||||||
if (currentAmmoProto)
|
|
||||||
{
|
|
||||||
uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2;
|
|
||||||
uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2;
|
|
||||||
|
|
||||||
if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition
|
|
||||||
{
|
|
||||||
return ITEM_USAGE_EQUIP;
|
|
||||||
}
|
|
||||||
if (newAmmoDPS < currentAmmoDPS) // New ammo is worse
|
|
||||||
{
|
|
||||||
return ITEM_USAGE_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure we have enough ammo in the inventory
|
|
||||||
if (ammoCount < requiredAmmo)
|
|
||||||
{
|
|
||||||
ammoCount += CurrentStacks(proto);
|
|
||||||
|
|
||||||
if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply
|
|
||||||
return ITEM_USAGE_AMMO;
|
|
||||||
else if (ammoCount < requiredAmmo + 1)
|
|
||||||
return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to add something like free bagspace or item value.
|
// Need to add something like free bagspace or item value.
|
||||||
if (proto->SellPrice > 0)
|
if (proto->SellPrice > 0)
|
||||||
{
|
{
|
||||||
if (proto->Quality >= ITEM_QUALITY_NORMAL && !isSoulbound)
|
if (proto->Quality >= ITEM_QUALITY_NORMAL && !isSoulbound)
|
||||||
{
|
|
||||||
return ITEM_USAGE_AH;
|
return ITEM_USAGE_AH;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
return ITEM_USAGE_VENDOR;
|
return ITEM_USAGE_VENDOR;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ITEM_USAGE_NONE;
|
return ITEM_USAGE_NONE;
|
||||||
@@ -480,6 +404,80 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
|||||||
return ITEM_USAGE_NONE;
|
return ITEM_USAGE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemUsage ItemUsageValue::QueryItemUsageForAmmo(ItemTemplate const* proto)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER || bot->getClass() != CLASS_ROGUE || bot->getClass() != CLASS_WARRIOR)
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
|
||||||
|
Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||||
|
uint32 requiredSubClass = 0;
|
||||||
|
|
||||||
|
if (rangedWeapon)
|
||||||
|
{
|
||||||
|
switch (rangedWeapon->GetTemplate()->SubClass)
|
||||||
|
{
|
||||||
|
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||||
|
requiredSubClass = ITEM_SUBCLASS_BULLET;
|
||||||
|
break;
|
||||||
|
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||||
|
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||||
|
requiredSubClass = ITEM_SUBCLASS_ARROW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the item is the correct ammo type for the equipped ranged weapon
|
||||||
|
if (proto->SubClass == requiredSubClass)
|
||||||
|
{
|
||||||
|
float ammoCount = BetterStacks(proto, "ammo");
|
||||||
|
float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2
|
||||||
|
uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID);
|
||||||
|
|
||||||
|
// Check if the bot has an ammo type assigned
|
||||||
|
if (currentAmmoId == 0)
|
||||||
|
return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo
|
||||||
|
// Compare new ammo vs current equipped ammo
|
||||||
|
ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId);
|
||||||
|
if (currentAmmoProto)
|
||||||
|
{
|
||||||
|
uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2;
|
||||||
|
uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2;
|
||||||
|
|
||||||
|
if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition
|
||||||
|
return ITEM_USAGE_EQUIP;
|
||||||
|
|
||||||
|
if (newAmmoDPS < currentAmmoDPS) // New ammo is worse
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
}
|
||||||
|
// Ensure we have enough ammo in the inventory
|
||||||
|
if (ammoCount < requiredAmmo)
|
||||||
|
{
|
||||||
|
ammoCount += CurrentStacks(proto);
|
||||||
|
|
||||||
|
if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply
|
||||||
|
return ITEM_USAGE_AMMO;
|
||||||
|
else if (ammoCount < requiredAmmo + 1)
|
||||||
|
return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsedItemUsage ItemUsageValue::GetItemIdFromQualifier()
|
||||||
|
{
|
||||||
|
ParsedItemUsage parsed;
|
||||||
|
|
||||||
|
size_t const pos = qualifier.find(",");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
parsed.itemId = atoi(qualifier.substr(0, pos).c_str());
|
||||||
|
parsed.randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parsed.itemId = atoi(qualifier.c_str());
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
// Return smaltest bag size equipped
|
// Return smaltest bag size equipped
|
||||||
uint32 ItemUsageValue::GetSmallestBagSize()
|
uint32 ItemUsageValue::GetSmallestBagSize()
|
||||||
{
|
{
|
||||||
@@ -913,3 +911,25 @@ std::string const ItemUsageValue::GetConsumableType(ItemTemplate const* proto, b
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemUsage ItemUpgradeValue::Calculate()
|
||||||
|
{
|
||||||
|
ParsedItemUsage parsed = GetItemIdFromQualifier();
|
||||||
|
uint32 itemId = parsed.itemId;
|
||||||
|
uint32 randomPropertyId = parsed.randomPropertyId;
|
||||||
|
if (!itemId)
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (!proto)
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
|
||||||
|
ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId);
|
||||||
|
if (equip != ITEM_USAGE_NONE)
|
||||||
|
return equip;
|
||||||
|
|
||||||
|
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||||
|
return QueryItemUsageForAmmo(proto);
|
||||||
|
|
||||||
|
return ITEM_USAGE_NONE;
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ class Player;
|
|||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
struct ItemTemplate;
|
struct ItemTemplate;
|
||||||
|
struct ParsedItemUsage
|
||||||
|
{
|
||||||
|
uint32 itemId = 0;
|
||||||
|
int32 randomPropertyId = 0;
|
||||||
|
};
|
||||||
enum ItemUsage : uint32
|
enum ItemUsage : uint32
|
||||||
{
|
{
|
||||||
ITEM_USAGE_NONE = 0,
|
ITEM_USAGE_NONE = 0,
|
||||||
@@ -42,8 +46,12 @@ public:
|
|||||||
|
|
||||||
ItemUsage Calculate() override;
|
ItemUsage Calculate() override;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
|
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
|
||||||
|
ItemUsage QueryItemUsageForAmmo(ItemTemplate const* proto);
|
||||||
|
ParsedItemUsage GetItemIdFromQualifier();
|
||||||
|
|
||||||
|
private:
|
||||||
uint32 GetSmallestBagSize();
|
uint32 GetSmallestBagSize();
|
||||||
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
|
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
|
||||||
bool IsItemNeededForSkill(ItemTemplate const* proto);
|
bool IsItemNeededForSkill(ItemTemplate const* proto);
|
||||||
@@ -61,4 +69,14 @@ public:
|
|||||||
static std::string const GetConsumableType(ItemTemplate const* proto, bool hasMana);
|
static std::string const GetConsumableType(ItemTemplate const* proto, bool hasMana);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ItemUpgradeValue : public ItemUsageValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ItemUpgradeValue(PlayerbotAI* botAI, std::string const name = "item upgrade") : ItemUsageValue(botAI, name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemUsage Calculate() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ std::vector<GuidPosition> ActiveQuestGiversValue::Calculate()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guidp.isDead())
|
if (!guidp.IsCreatureOrGOAccessible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
retQuestGivers.push_back(guidp);
|
retQuestGivers.push_back(guidp);
|
||||||
@@ -231,7 +231,7 @@ std::vector<GuidPosition> ActiveQuestTakersValue::Calculate()
|
|||||||
|
|
||||||
for (auto& guidp : entry.second)
|
for (auto& guidp : entry.second)
|
||||||
{
|
{
|
||||||
if (guidp.isDead())
|
if (!guidp.IsCreatureOrGOAccessible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
retQuestTakers.push_back(guidp);
|
retQuestTakers.push_back(guidp);
|
||||||
@@ -298,7 +298,7 @@ std::vector<GuidPosition> ActiveQuestObjectivesValue::Calculate()
|
|||||||
{
|
{
|
||||||
for (auto& guidp : entry.second)
|
for (auto& guidp : entry.second)
|
||||||
{
|
{
|
||||||
if (guidp.isDead())
|
if (!guidp.IsCreatureOrGOAccessible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
retQuestObjectives.push_back(guidp);
|
retQuestObjectives.push_back(guidp);
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ public:
|
|||||||
creators["formation"] = &ValueContext::formation;
|
creators["formation"] = &ValueContext::formation;
|
||||||
creators["stance"] = &ValueContext::stance;
|
creators["stance"] = &ValueContext::stance;
|
||||||
creators["item usage"] = &ValueContext::item_usage;
|
creators["item usage"] = &ValueContext::item_usage;
|
||||||
|
creators["item upgrade"] = &ValueContext::item_upgrade;
|
||||||
creators["speed"] = &ValueContext::speed;
|
creators["speed"] = &ValueContext::speed;
|
||||||
creators["last said"] = &ValueContext::last_said;
|
creators["last said"] = &ValueContext::last_said;
|
||||||
creators["last emote"] = &ValueContext::last_emote;
|
creators["last emote"] = &ValueContext::last_emote;
|
||||||
@@ -341,6 +342,7 @@ private:
|
|||||||
static UntypedValue* already_seen_players(PlayerbotAI* botAI) { return new AlreadySeenPlayersValue(botAI); }
|
static UntypedValue* already_seen_players(PlayerbotAI* botAI) { return new AlreadySeenPlayersValue(botAI); }
|
||||||
static UntypedValue* new_player_nearby(PlayerbotAI* botAI) { return new NewPlayerNearbyValue(botAI); }
|
static UntypedValue* new_player_nearby(PlayerbotAI* botAI) { return new NewPlayerNearbyValue(botAI); }
|
||||||
static UntypedValue* item_usage(PlayerbotAI* botAI) { return new ItemUsageValue(botAI); }
|
static UntypedValue* item_usage(PlayerbotAI* botAI) { return new ItemUsageValue(botAI); }
|
||||||
|
static UntypedValue* item_upgrade(PlayerbotAI* botAI) { return new ItemUpgradeValue(botAI); }
|
||||||
static UntypedValue* formation(PlayerbotAI* botAI) { return new FormationValue(botAI); }
|
static UntypedValue* formation(PlayerbotAI* botAI) { return new FormationValue(botAI); }
|
||||||
static UntypedValue* stance(PlayerbotAI* botAI) { return new StanceValue(botAI); }
|
static UntypedValue* stance(PlayerbotAI* botAI) { return new StanceValue(botAI); }
|
||||||
static UntypedValue* mana_save_level(PlayerbotAI* botAI) { return new ManaSaveLevelValue(botAI); }
|
static UntypedValue* mana_save_level(PlayerbotAI* botAI) { return new ManaSaveLevelValue(botAI); }
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details;
|
creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details;
|
||||||
|
|
||||||
creators["item push result"] = &WorldPacketTriggerContext::item_push_result;
|
creators["item push result"] = &WorldPacketTriggerContext::item_push_result;
|
||||||
|
creators["loot roll won"] = &WorldPacketTriggerContext::loot_roll_won;
|
||||||
creators["party command"] = &WorldPacketTriggerContext::party_command;
|
creators["party command"] = &WorldPacketTriggerContext::party_command;
|
||||||
creators["taxi done"] = &WorldPacketTriggerContext::taxi_done;
|
creators["taxi done"] = &WorldPacketTriggerContext::taxi_done;
|
||||||
creators["cast failed"] = &WorldPacketTriggerContext::cast_failed;
|
creators["cast failed"] = &WorldPacketTriggerContext::cast_failed;
|
||||||
@@ -92,6 +93,7 @@ private:
|
|||||||
static Trigger* taxi_done(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "taxi done"); }
|
static Trigger* taxi_done(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "taxi done"); }
|
||||||
static Trigger* party_command(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "party command"); }
|
static Trigger* party_command(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "party command"); }
|
||||||
static Trigger* item_push_result(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "item push result"); }
|
static Trigger* item_push_result(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "item push result"); }
|
||||||
|
static Trigger* loot_roll_won(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot roll won"); }
|
||||||
|
|
||||||
// quest
|
// quest
|
||||||
static Trigger* quest_update_add_kill(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add kill"); }
|
static Trigger* quest_update_add_kill(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add kill"); }
|
||||||
|
|||||||
@@ -41,6 +41,18 @@ public:
|
|||||||
std::string const GetTargetName() override { return "pet target"; }
|
std::string const GetTargetName() override { return "pet target"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CastUnendingBreathAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastUnendingBreathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "unending breath") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastUnendingBreathOnPartyAction : public BuffOnPartyAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastUnendingBreathOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "unending breath") {}
|
||||||
|
};
|
||||||
|
|
||||||
class CreateSoulShardAction : public Action
|
class CreateSoulShardAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
|
|||||||
triggers.push_back(new TriggerNode("too many soul shards", { NextAction("destroy soul shard", 60.0f) }));
|
triggers.push_back(new TriggerNode("too many soul shards", { NextAction("destroy soul shard", 60.0f) }));
|
||||||
triggers.push_back(new TriggerNode("soul link", { NextAction("soul link", 28.0f) }));
|
triggers.push_back(new TriggerNode("soul link", { NextAction("soul link", 28.0f) }));
|
||||||
triggers.push_back(new TriggerNode("demon armor", { NextAction("fel armor", 27.0f) }));
|
triggers.push_back(new TriggerNode("demon armor", { NextAction("fel armor", 27.0f) }));
|
||||||
|
triggers.push_back(new TriggerNode("unending breath", { NextAction("unending breath", 12.0f) }));
|
||||||
|
triggers.push_back(new TriggerNode("unending breath on party", { NextAction("unending breath on party", 11.0f) }));
|
||||||
triggers.push_back(new TriggerNode("no healthstone", { NextAction("create healthstone", 26.0f) }));
|
triggers.push_back(new TriggerNode("no healthstone", { NextAction("create healthstone", 26.0f) }));
|
||||||
triggers.push_back(new TriggerNode("no soulstone", { NextAction("create soulstone", 25.0f) }));
|
triggers.push_back(new TriggerNode("no soulstone", { NextAction("create soulstone", 25.0f) }));
|
||||||
triggers.push_back(new TriggerNode("life tap", { NextAction("life tap", 23.0f) }));
|
triggers.push_back(new TriggerNode("life tap", { NextAction("life tap", 23.0f) }));
|
||||||
|
|||||||
@@ -79,6 +79,16 @@ bool SoulLinkTrigger::IsActive()
|
|||||||
return !botAI->HasAura("soul link", target);
|
return !botAI->HasAura("soul link", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UnendingBreathTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnendingBreathOnPartyTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return BuffOnPartyTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
|
||||||
|
}
|
||||||
|
|
||||||
bool DemonicEmpowermentTrigger::IsActive()
|
bool DemonicEmpowermentTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Pet* pet = bot->GetPet();
|
Pet* pet = bot->GetPet();
|
||||||
|
|||||||
@@ -32,6 +32,20 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UnendingBreathTrigger : public BuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnendingBreathTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "unending breath", 5 * 2000) {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnendingBreathOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnendingBreathOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "unending breath on party", 2 * 2000) {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class OutOfSoulShardsTrigger : public Trigger
|
class OutOfSoulShardsTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ public:
|
|||||||
creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance;
|
creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance;
|
||||||
creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor;
|
creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor;
|
||||||
creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link;
|
creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link;
|
||||||
|
creators["unending breath"] = &WarlockTriggerFactoryInternal::unending_breath;
|
||||||
|
creators["unending breath on party"] = &WarlockTriggerFactoryInternal::unending_breath_on_party;
|
||||||
creators["no soul shard"] = &WarlockTriggerFactoryInternal::no_soul_shard;
|
creators["no soul shard"] = &WarlockTriggerFactoryInternal::no_soul_shard;
|
||||||
creators["too many soul shards"] = &WarlockTriggerFactoryInternal::too_many_soul_shards;
|
creators["too many soul shards"] = &WarlockTriggerFactoryInternal::too_many_soul_shards;
|
||||||
creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone;
|
creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone;
|
||||||
@@ -189,6 +191,8 @@ private:
|
|||||||
static Trigger* shadow_trance(PlayerbotAI* botAI) { return new ShadowTranceTrigger(botAI); }
|
static Trigger* shadow_trance(PlayerbotAI* botAI) { return new ShadowTranceTrigger(botAI); }
|
||||||
static Trigger* demon_armor(PlayerbotAI* botAI) { return new DemonArmorTrigger(botAI); }
|
static Trigger* demon_armor(PlayerbotAI* botAI) { return new DemonArmorTrigger(botAI); }
|
||||||
static Trigger* soul_link(PlayerbotAI* botAI) { return new SoulLinkTrigger(botAI); }
|
static Trigger* soul_link(PlayerbotAI* botAI) { return new SoulLinkTrigger(botAI); }
|
||||||
|
static Trigger* unending_breath(PlayerbotAI* botAI) { return new UnendingBreathTrigger(botAI); }
|
||||||
|
static Trigger* unending_breath_on_party(PlayerbotAI* botAI) { return new UnendingBreathOnPartyTrigger(botAI); }
|
||||||
static Trigger* no_soul_shard(PlayerbotAI* botAI) { return new OutOfSoulShardsTrigger(botAI); }
|
static Trigger* no_soul_shard(PlayerbotAI* botAI) { return new OutOfSoulShardsTrigger(botAI); }
|
||||||
static Trigger* too_many_soul_shards(PlayerbotAI* botAI) { return new TooManySoulShardsTrigger(botAI); }
|
static Trigger* too_many_soul_shards(PlayerbotAI* botAI) { return new TooManySoulShardsTrigger(botAI); }
|
||||||
static Trigger* HasHealthstone(PlayerbotAI* botAI) { return new HasHealthstoneTrigger(botAI); }
|
static Trigger* HasHealthstone(PlayerbotAI* botAI) { return new HasHealthstoneTrigger(botAI); }
|
||||||
@@ -240,6 +244,8 @@ public:
|
|||||||
creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor;
|
creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor;
|
||||||
creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin;
|
creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin;
|
||||||
creators["soul link"] = &WarlockAiObjectContextInternal::soul_link;
|
creators["soul link"] = &WarlockAiObjectContextInternal::soul_link;
|
||||||
|
creators["unending breath"] = &WarlockAiObjectContextInternal::unending_breath;
|
||||||
|
creators["unending breath on party"] = &WarlockAiObjectContextInternal::unending_breath_on_party;
|
||||||
creators["create soul shard"] = &WarlockAiObjectContextInternal::create_soul_shard;
|
creators["create soul shard"] = &WarlockAiObjectContextInternal::create_soul_shard;
|
||||||
creators["destroy soul shard"] = &WarlockAiObjectContextInternal::destroy_soul_shard;
|
creators["destroy soul shard"] = &WarlockAiObjectContextInternal::destroy_soul_shard;
|
||||||
creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone;
|
creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone;
|
||||||
@@ -313,6 +319,8 @@ private:
|
|||||||
static Action* demon_armor(PlayerbotAI* botAI) { return new CastDemonArmorAction(botAI); }
|
static Action* demon_armor(PlayerbotAI* botAI) { return new CastDemonArmorAction(botAI); }
|
||||||
static Action* demon_skin(PlayerbotAI* botAI) { return new CastDemonSkinAction(botAI); }
|
static Action* demon_skin(PlayerbotAI* botAI) { return new CastDemonSkinAction(botAI); }
|
||||||
static Action* soul_link(PlayerbotAI* botAI) { return new CastSoulLinkAction(botAI); }
|
static Action* soul_link(PlayerbotAI* botAI) { return new CastSoulLinkAction(botAI); }
|
||||||
|
static Action* unending_breath(PlayerbotAI* botAI) { return new CastUnendingBreathAction(botAI); }
|
||||||
|
static Action* unending_breath_on_party(PlayerbotAI* botAI) { return new CastUnendingBreathOnPartyAction(botAI); }
|
||||||
static Action* create_soul_shard(PlayerbotAI* botAI) { return new CreateSoulShardAction(botAI); }
|
static Action* create_soul_shard(PlayerbotAI* botAI) { return new CreateSoulShardAction(botAI); }
|
||||||
static Action* destroy_soul_shard(PlayerbotAI* botAI) { return new DestroySoulShardAction(botAI); }
|
static Action* destroy_soul_shard(PlayerbotAI* botAI) { return new DestroySoulShardAction(botAI); }
|
||||||
static Action* create_healthstone(PlayerbotAI* botAI) { return new CastCreateHealthstoneAction(botAI); }
|
static Action* create_healthstone(PlayerbotAI* botAI) { return new CastCreateHealthstoneAction(botAI); }
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ bool MountDrakeAction::Execute(Event event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Player*> players = botAI->GetPlayersInGroup();
|
std::vector<Player*> players = botAI->GetAllPlayersInGroup();
|
||||||
for (Player* player : players)
|
for (Player* player : players)
|
||||||
{
|
{
|
||||||
if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld())
|
if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "RaidGruulsLairHelpers.h"
|
#include "RaidGruulsLairHelpers.h"
|
||||||
#include "CreatureAI.h"
|
#include "CreatureAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
using namespace GruulsLairHelpers;
|
using namespace GruulsLairHelpers;
|
||||||
@@ -12,6 +13,8 @@ using namespace GruulsLairHelpers;
|
|||||||
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
if (!maulgar)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithSquare(bot, maulgar);
|
MarkTargetWithSquare(bot, maulgar);
|
||||||
SetRtiTarget(botAI, "square", maulgar);
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
@@ -21,31 +24,20 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
|||||||
|
|
||||||
if (maulgar->GetVictim() == bot)
|
if (maulgar->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
const Position& position = MAULGAR_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
|
|
||||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (distanceToTankPosition > maxDistance)
|
if (distanceToPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float dX = tankPosition.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(),
|
|
||||||
maulgar->GetPositionX() - bot->GetPositionX());
|
|
||||||
bot->SetFacingTo(orientation);
|
|
||||||
}
|
|
||||||
else if (!bot->IsWithinMeleeRange(maulgar))
|
|
||||||
{
|
|
||||||
return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(),
|
|
||||||
maulgar->GetPositionZ(), false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -55,6 +47,8 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
if (!olm)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithCircle(bot, olm);
|
MarkTargetWithCircle(bot, olm);
|
||||||
SetRtiTarget(botAI, "circle", olm);
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
@@ -64,29 +58,22 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
|||||||
|
|
||||||
if (olm->GetVictim() == bot)
|
if (olm->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
const Position& position = OLM_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
const float olmTankLeeway = 30.0f;
|
const float olmTankLeeway = 30.0f;
|
||||||
|
|
||||||
float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceOlmToPosition = olm->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (distanceOlmToTankPosition > olmTankLeeway)
|
if (distanceOlmToPosition > olmTankLeeway)
|
||||||
{
|
{
|
||||||
float dX = tankPosition.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceOlmToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceOlmToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!bot->IsWithinMeleeRange(olm))
|
|
||||||
{
|
|
||||||
return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(),
|
|
||||||
olm->GetPositionZ(), false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -95,6 +82,8 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
if (!blindeye)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithStar(bot, blindeye);
|
MarkTargetWithStar(bot, blindeye);
|
||||||
SetRtiTarget(botAI, "star", blindeye);
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
@@ -104,31 +93,20 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
|||||||
|
|
||||||
if (blindeye->GetVictim() == bot)
|
if (blindeye->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
const Position& position = BLINDEYE_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
|
|
||||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (distanceToTankPosition > maxDistance)
|
if (distanceToPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float dX = tankPosition.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(),
|
|
||||||
blindeye->GetPositionX() - bot->GetPositionX());
|
|
||||||
bot->SetFacingTo(orientation);
|
|
||||||
}
|
|
||||||
else if (!bot->IsWithinMeleeRange(blindeye))
|
|
||||||
{
|
|
||||||
return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(),
|
|
||||||
blindeye->GetPositionZ(), false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -138,6 +116,8 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
if (!krosh)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithTriangle(bot, krosh);
|
MarkTargetWithTriangle(bot, krosh);
|
||||||
SetRtiTarget(botAI, "triangle", krosh);
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
@@ -149,25 +129,22 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
return botAI->CastSpell("fire ward", bot);
|
return botAI->CastSpell("fire ward", bot);
|
||||||
|
|
||||||
if (bot->GetTarget() != krosh->GetGUID())
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
{
|
return Attack(krosh);
|
||||||
bot->SetSelection(krosh->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (krosh->GetVictim() == bot)
|
if (krosh->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
const Position& position = KROSH_TANK_POSITION;
|
||||||
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToKrosh = krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
const float minDistance = 16.0f;
|
const float minDistance = 16.0f;
|
||||||
const float maxDistance = 29.0f;
|
const float maxDistance = 29.0f;
|
||||||
const float tankPositionLeeway = 1.0f;
|
const float tankPositionLeeway = 1.0f;
|
||||||
|
|
||||||
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
||||||
{
|
{
|
||||||
if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway))
|
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), tankPositionLeeway))
|
||||||
{
|
{
|
||||||
return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),
|
||||||
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
||||||
@@ -179,7 +156,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,20 +169,19 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
if (!kiggler)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithDiamond(bot, kiggler);
|
MarkTargetWithDiamond(bot, kiggler);
|
||||||
SetRtiTarget(botAI, "diamond", kiggler);
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
if (bot->GetTarget() != kiggler->GetGUID())
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
{
|
return Attack(kiggler);
|
||||||
bot->SetSelection(kiggler->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,120 +192,105 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
// Target priority 1: Blindeye
|
// Target priority 1: Blindeye
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
if (blindeye && blindeye->IsAlive())
|
if (blindeye)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "star", blindeye);
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
|
|
||||||
if (bot->GetTarget() != blindeye->GetGUID())
|
if (bot->GetTarget() != blindeye->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(blindeye->GetGUID());
|
|
||||||
return Attack(blindeye);
|
return Attack(blindeye);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 2: Olm
|
// Target priority 2: Olm
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
if (olm && olm->IsAlive())
|
if (olm)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "circle", olm);
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
|
|
||||||
if (bot->GetTarget() != olm->GetGUID())
|
if (bot->GetTarget() != olm->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(olm->GetGUID());
|
|
||||||
return Attack(olm);
|
return Attack(olm);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 3a: Krosh (ranged only)
|
// Target priority 3a: Krosh (ranged only)
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
if (krosh && botAI->IsRanged(bot))
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "triangle", krosh);
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
|
|
||||||
if (bot->GetTarget() != krosh->GetGUID())
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(krosh->GetGUID());
|
|
||||||
return Attack(krosh);
|
return Attack(krosh);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 3b: Kiggler
|
// Target priority 3b: Kiggler
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
if (kiggler && kiggler->IsAlive())
|
if (kiggler)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "diamond", kiggler);
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
if (bot->GetTarget() != kiggler->GetGUID())
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(kiggler->GetGUID());
|
|
||||||
return Attack(kiggler);
|
return Attack(kiggler);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 4: Maulgar
|
// Target priority 4: Maulgar
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
if (maulgar && maulgar->IsAlive())
|
if (maulgar)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "square", maulgar);
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
|
|
||||||
if (bot->GetTarget() != maulgar->GetGUID())
|
if (bot->GetTarget() != maulgar->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(maulgar->GetGUID());
|
|
||||||
return Attack(maulgar);
|
return Attack(maulgar);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -338,22 +299,22 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
|||||||
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||||
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
const Position& center = MAULGAR_ROOM_CENTER;
|
||||||
const float maxDistanceFromFight = 50.0f;
|
const float maxDistanceFromCenter = 50.0f;
|
||||||
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
float distToCenter = bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY());
|
||||||
|
|
||||||
if (distToFight > maxDistanceFromFight)
|
if (distToCenter > maxDistanceFromCenter)
|
||||||
{
|
{
|
||||||
float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x);
|
float angle = atan2(bot->GetPositionY() - center.GetPositionY(), bot->GetPositionX() - center.GetPositionX());
|
||||||
float destX = fightCenter.x + 40.0f * cos(angle);
|
float destX = center.GetPositionX() + 40.0f * cos(angle);
|
||||||
float destY = fightCenter.y + 40.0f * sin(angle);
|
float destY = center.GetPositionY() + 40.0f * sin(angle);
|
||||||
float destZ = fightCenter.z;
|
float destZ = center.GetPositionZ();
|
||||||
|
|
||||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
bot->GetPositionZ(), destX, destY, destZ))
|
bot->GetPositionZ(), destX, destY, destZ))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +323,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,6 +334,8 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
if (!maulgar)
|
||||||
|
return false;
|
||||||
|
|
||||||
const float safeDistance = 10.0f;
|
const float safeDistance = 10.0f;
|
||||||
float distance = bot->GetExactDist2d(maulgar);
|
float distance = bot->GetExactDist2d(maulgar);
|
||||||
@@ -395,7 +358,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
bot->InterruptNonMeleeSpells(true);
|
||||||
return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,7 +402,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
|||||||
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||||
{
|
{
|
||||||
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
||||||
if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true))
|
if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker))
|
||||||
return botAI->CastSpell("banish", assignedFelStalker);
|
return botAI->CastSpell("banish", assignedFelStalker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,40 +491,33 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
|||||||
// Gruul the Dragonkiller Actions
|
// Gruul the Dragonkiller Actions
|
||||||
|
|
||||||
// Position in center of the room
|
// Position in center of the room
|
||||||
bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event)
|
bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
if (!gruul)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (bot->GetVictim() != gruul)
|
if (bot->GetVictim() != gruul)
|
||||||
return Attack(gruul);
|
return Attack(gruul);
|
||||||
|
|
||||||
if (gruul->GetVictim() == bot)
|
if (gruul->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
const Position& position = GRUUL_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 5.0f;
|
||||||
|
|
||||||
float dX = tankPosition.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (distanceToTankPosition > maxDistance)
|
if (distanceToTankPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float step = std::min(maxDistance, distanceToTankPosition);
|
float step = std::min(maxDistance, distanceToTankPosition);
|
||||||
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||||
const float moveZ = tankPosition.z;
|
const float moveZ = position.GetPositionZ();
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(),
|
|
||||||
gruul->GetPositionX() - bot->GetPositionX());
|
|
||||||
bot->SetFacingTo(orientation);
|
|
||||||
}
|
|
||||||
else if (!bot->IsWithinMeleeRange(gruul))
|
|
||||||
{
|
|
||||||
return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(),
|
|
||||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -579,16 +535,16 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
|||||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||||
|
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth())
|
if (gruul && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||||
{
|
{
|
||||||
initialPositions.clear();
|
initialPositions.erase(bot->GetGUID());
|
||||||
hasReachedInitialPosition.clear();
|
hasReachedInitialPosition.erase(bot->GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
const Position& position = GRUUL_TANK_POSITION;
|
||||||
const float centerX = tankPosition.x;
|
const float centerX = position.GetPositionX();
|
||||||
const float centerY = tankPosition.y;
|
const float centerY = position.GetPositionY();
|
||||||
float centerZ = bot->GetPositionZ();
|
const float centerZ = position.GetPositionZ();
|
||||||
const float minRadius = 25.0f;
|
const float minRadius = 25.0f;
|
||||||
const float maxRadius = 40.0f;
|
const float maxRadius = 40.0f;
|
||||||
|
|
||||||
@@ -642,7 +598,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
|||||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,10 +85,10 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
class GruulTheDragonkillerTanksPositionBossAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {};
|
GruulTheDragonkillerTanksPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller tanks position boss") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,18 +8,11 @@
|
|||||||
#include "HunterActions.h"
|
#include "HunterActions.h"
|
||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
#include "WarriorActions.h"
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
using namespace GruulsLairHelpers;
|
using namespace GruulsLairHelpers;
|
||||||
|
|
||||||
static bool IsChargeAction(Action* action)
|
|
||||||
{
|
|
||||||
return dynamic_cast<CastChargeAction*>(action) ||
|
|
||||||
dynamic_cast<CastInterceptAction*>(action) ||
|
|
||||||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
|
||||||
dynamic_cast<CastFeralChargeCatAction*>(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
||||||
@@ -38,12 +31,10 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
|
|||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||||
(!kiggler || !kiggler->IsAlive()) &&
|
!kiggler && !krosh && !olm && !blindeye)
|
||||||
(!krosh || !krosh->IsAlive()) &&
|
|
||||||
(!olm || !olm->IsAlive()) &&
|
|
||||||
(!blindeye || !blindeye->IsAlive()))
|
|
||||||
{
|
{
|
||||||
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
(dynamic_cast<MovementAction*>(action) &&
|
||||||
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
@@ -57,7 +48,8 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action
|
|||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
|
||||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast<CastArcaneShotAction*>(action))
|
if (krosh && target && target->GetGUID() == krosh->GetGUID() &&
|
||||||
|
dynamic_cast<CastArcaneShotAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
@@ -101,8 +93,9 @@ float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
|||||||
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
bot->HasAura(SPELL_GROUND_SLAM_2))
|
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||||
{
|
{
|
||||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
if ((dynamic_cast<MovementAction*>(action) &&
|
||||||
IsChargeAction(action))
|
!dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss;
|
creators["gruul the dragonkiller tanks position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_tanks_position_boss;
|
||||||
creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
||||||
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ private:
|
|||||||
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); }
|
static Action* gruul_the_dragonkiller_tanks_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerTanksPositionBossAction(botAI); }
|
||||||
static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
||||||
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ public:
|
|||||||
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank;
|
creators["gruul the dragonkiller boss engaged by tanks"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks;
|
||||||
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
creators["gruul the dragonkiller boss engaged by ranged"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged;
|
||||||
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ private:
|
|||||||
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); }
|
static Trigger* gruul_the_dragonkiller_boss_engaged_by_tanks(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); }
|
||||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); }
|
static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); }
|
||||||
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", {
|
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", {
|
||||||
NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) }));
|
NextAction("gruul the dragonkiller tanks position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", {
|
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by ranged", {
|
||||||
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
||||||
|
|||||||
@@ -10,35 +10,35 @@ bool HighKingMaulgarIsMainTankTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive();
|
return botAI->IsMainTank(bot) && maulgar;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
|
||||||
return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive();
|
return botAI->IsAssistTankOfIndex(bot, 0, false) && olm;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive();
|
return botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
return IsKroshMageTank(botAI, bot) && krosh;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
|
||||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive();
|
return IsKigglerMoonkinTank(botAI, bot) && kiggler;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||||
@@ -50,11 +50,11 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
|||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||||
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
!(botAI->IsMainTank(bot) && maulgar) &&
|
||||||
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
!(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) &&
|
||||||
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
!(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) &&
|
||||||
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
!(IsKroshMageTank(botAI, bot) && krosh) &&
|
||||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
!(IsKigglerMoonkinTank(botAI, bot) && kiggler);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||||
@@ -66,7 +66,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
return maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||||
!botAI->IsMainTank(bot);
|
!botAI->IsMainTank(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
||||||
|
|
||||||
return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK;
|
return felStalker && bot->getClass() == CLASS_WARLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||||
@@ -120,12 +120,12 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
|||||||
switch (hunterIndex)
|
switch (hunterIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
return olm && olm->GetHealthPct() > 98.0f &&
|
||||||
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
olmTank && botAI->CanCastSpell("misdirection", olmTank);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
return blindeye && blindeye->GetHealthPct() > 90.0f &&
|
||||||
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -136,25 +136,24 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
|||||||
|
|
||||||
// Gruul the Dragonkiller Triggers
|
// Gruul the Dragonkiller Triggers
|
||||||
|
|
||||||
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
return gruul && gruul->IsAlive() && botAI->IsMainTank(bot);
|
return gruul && botAI->IsTank(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive()
|
bool GruulTheDragonkillerBossEngagedByRangedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
return gruul && gruul->IsAlive() && botAI->IsRanged(bot);
|
return gruul && botAI->IsRanged(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
return gruul && gruul->IsAlive() &&
|
return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,17 +73,17 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {}
|
GruulTheDragonkillerBossEngagedByTanksTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {}
|
GruulTheDragonkillerBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,16 @@
|
|||||||
|
|
||||||
namespace GruulsLairHelpers
|
namespace GruulsLairHelpers
|
||||||
{
|
{
|
||||||
namespace GruulsLairLocations
|
// Olm does not chase properly due to the Core's caster movement issues
|
||||||
{
|
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
||||||
// Olm does not chase properly due to the Core's caster movement issues
|
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
||||||
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||||
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f };
|
||||||
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
const Position OLM_TANK_POSITION = { 87.485f, 234.942f, -3.635f };
|
||||||
const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f };
|
||||||
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f };
|
||||||
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f };
|
||||||
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f };
|
||||||
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
|
||||||
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
@@ -42,84 +39,43 @@ namespace GruulsLairHelpers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!target || !group)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
|
||||||
if (currentGuid != target->GetGUID())
|
|
||||||
{
|
|
||||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
|
||||||
{
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
|
||||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
|
||||||
|
|
||||||
if (currentRti != rtiName || currentTarget != target)
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
|
||||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* highestHpMage = nullptr;
|
// (1) First loop: Return the first assistant Mage (real player or bot)
|
||||||
uint32 highestHp = 0;
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (member->getClass() == CLASS_MAGE)
|
if (group->IsAssistant(member->GetGUID()))
|
||||||
|
return member == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (2) Fall back to bot Mage with highest HP
|
||||||
|
Player* highestHpMage = nullptr;
|
||||||
|
uint32 highestHp = 0;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
member->getClass() != CLASS_MAGE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 hp = member->GetMaxHealth();
|
||||||
|
if (!highestHpMage || hp > highestHp)
|
||||||
{
|
{
|
||||||
uint32 hp = member->GetMaxHealth();
|
highestHpMage = member;
|
||||||
if (!highestHpMage || hp > highestHp)
|
highestHp = hp;
|
||||||
{
|
|
||||||
highestHpMage = member;
|
|
||||||
highestHp = hp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (3) Return the found Mage tank, or nullptr if none found
|
||||||
return highestHpMage == bot;
|
return highestHpMage == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,30 +85,37 @@ namespace GruulsLairHelpers
|
|||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* highestHpMoonkin = nullptr;
|
// (1) First loop: Return the first assistant Moonkin (real player or bot)
|
||||||
uint32 highestHp = 0;
|
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (member->getClass() == CLASS_DRUID)
|
if (group->IsAssistant(member->GetGUID()) &&
|
||||||
|
AiFactory::GetPlayerSpecTab(member) == DRUID_TAB_BALANCE)
|
||||||
|
return member == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (2) Fall back to bot Moonkin with highest HP
|
||||||
|
Player* highestHpMoonkin = nullptr;
|
||||||
|
uint32 highestHp = 0;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID ||
|
||||||
|
!GET_PLAYERBOT_AI(member) || AiFactory::GetPlayerSpecTab(member) != DRUID_TAB_BALANCE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 hp = member->GetMaxHealth();
|
||||||
|
if (!highestHpMoonkin || hp > highestHp)
|
||||||
{
|
{
|
||||||
int tab = AiFactory::GetPlayerSpecTab(member);
|
highestHpMoonkin = member;
|
||||||
if (tab == DRUID_TAB_BALANCE)
|
highestHp = hp;
|
||||||
{
|
|
||||||
uint32 hp = member->GetMaxHealth();
|
|
||||||
if (!highestHpMoonkin || hp > highestHp)
|
|
||||||
{
|
|
||||||
highestHpMoonkin = member;
|
|
||||||
highestHp = hp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (3) Return the found Moonkin tank, or nullptr if none found
|
||||||
return highestHpMoonkin == bot;
|
return highestHpMoonkin == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,19 @@
|
|||||||
#define RAID_GRUULSLAIRHELPERS_H
|
#define RAID_GRUULSLAIRHELPERS_H
|
||||||
|
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace GruulsLairHelpers
|
namespace GruulsLairHelpers
|
||||||
{
|
{
|
||||||
enum GruulsLairSpells
|
enum GruulsLairSpells
|
||||||
{
|
{
|
||||||
// High King Maulgar
|
// High King Maulgar
|
||||||
SPELL_WHIRLWIND = 33238,
|
SPELL_WHIRLWIND = 33238,
|
||||||
|
|
||||||
// Krosh Firehand
|
// Krosh Firehand
|
||||||
SPELL_SPELL_SHIELD = 33054,
|
SPELL_SPELL_SHIELD = 33054,
|
||||||
|
|
||||||
// Hunter
|
// Hunter
|
||||||
SPELL_MISDIRECTION = 35079,
|
SPELL_MISDIRECTION = 35079,
|
||||||
|
|
||||||
// Warlock
|
|
||||||
SPELL_BANISH = 18647, // Rank 2
|
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
SPELL_GROUND_SLAM_1 = 33525,
|
SPELL_GROUND_SLAM_1 = 33525,
|
||||||
@@ -30,33 +26,20 @@ namespace GruulsLairHelpers
|
|||||||
NPC_WILD_FEL_STALKER = 18847,
|
NPC_WILD_FEL_STALKER = 18847,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32 GRUULS_LAIR_MAP_ID = 565;
|
||||||
|
|
||||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
|
||||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||||
|
|
||||||
struct Location
|
extern const Position MAULGAR_TANK_POSITION;
|
||||||
{
|
extern const Position OLM_TANK_POSITION;
|
||||||
float x, y, z;
|
extern const Position BLINDEYE_TANK_POSITION;
|
||||||
};
|
extern const Position KROSH_TANK_POSITION;
|
||||||
|
extern const Position MAULGAR_ROOM_CENTER;
|
||||||
namespace GruulsLairLocations
|
extern const Position GRUUL_TANK_POSITION;
|
||||||
{
|
|
||||||
extern const Location MaulgarTankPosition;
|
|
||||||
extern const Location OlmTankPosition;
|
|
||||||
extern const Location BlindeyeTankPosition;
|
|
||||||
extern const Location KroshTankPosition;
|
|
||||||
extern const Location MaulgarRoomCenter;
|
|
||||||
extern const Location GruulTankPosition;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PlayerbotTextMgr.h"
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace KarazhanHelpers;
|
using namespace KarazhanHelpers;
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
|||||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||||
if (attumenMounted)
|
if (attumenMounted)
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithStar(bot, attumenMounted);
|
MarkTargetWithStar(bot, attumenMounted);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "star", attumenMounted);
|
SetRtiTarget(botAI, "star", attumenMounted);
|
||||||
@@ -57,7 +58,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
|
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithStar(bot, midnight);
|
MarkTargetWithStar(bot, midnight);
|
||||||
|
|
||||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
@@ -180,7 +181,7 @@ bool MoroesMarkTargetAction::Execute(Event event)
|
|||||||
|
|
||||||
if (target)
|
if (target)
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithSkull(bot, target);
|
MarkTargetWithSkull(bot, target);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "skull", target);
|
SetRtiTarget(botAI, "skull", target);
|
||||||
@@ -405,7 +406,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event)
|
|||||||
if (!flare)
|
if (!flare)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithSkull(bot, flare);
|
MarkTargetWithSkull(bot, flare);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "skull", flare);
|
SetRtiTarget(botAI, "skull", flare);
|
||||||
@@ -469,11 +470,11 @@ bool TheCuratorSpreadRangedAction::Execute(Event event)
|
|||||||
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
||||||
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains");
|
Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS);
|
||||||
Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek");
|
Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK);
|
||||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
|
||||||
|
|
||||||
|
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||||
if (target)
|
if (target)
|
||||||
MarkTargetWithSkull(bot, target);
|
MarkTargetWithSkull(bot, target);
|
||||||
|
|
||||||
@@ -1007,7 +1008,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
||||||
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
||||||
|
|
||||||
if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||||
@@ -1018,7 +1019,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.erase(instanceId);
|
netherspiteDpsWaitTimer.erase(instanceId);
|
||||||
|
|
||||||
if (botAI->IsTank(bot))
|
if (botAI->IsTank(bot))
|
||||||
@@ -1029,7 +1030,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
||||||
|
|
||||||
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||||
@@ -1458,7 +1459,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (botAI->IsRanged(bot))
|
if (botAI->IsRanged(bot))
|
||||||
nightbaneRangedStep.erase(botGuid);
|
nightbaneRangedStep.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
nightbaneDpsWaitTimer.erase(instanceId);
|
nightbaneDpsWaitTimer.erase(instanceId);
|
||||||
}
|
}
|
||||||
// Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer
|
// Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer
|
||||||
@@ -1466,7 +1467,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
nightbaneRainOfBonesHit.erase(botGuid);
|
nightbaneRainOfBonesHit.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
||||||
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
||||||
@@ -1482,7 +1483,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (botAI->IsRanged(bot))
|
if (botAI->IsRanged(bot))
|
||||||
nightbaneRangedStep.erase(botGuid);
|
nightbaneRangedStep.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
nightbaneDpsWaitTimer.erase(instanceId);
|
nightbaneDpsWaitTimer.erase(instanceId);
|
||||||
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PriestActions.h"
|
#include "PriestActions.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
#include "RogueActions.h"
|
#include "RogueActions.h"
|
||||||
#include "ShamanActions.h"
|
#include "ShamanActions.h"
|
||||||
@@ -242,6 +243,9 @@ float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
|||||||
|
|
||||||
if (bot->HasAura(SPELL_ENFEEBLE))
|
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||||
{
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
if (dynamic_cast<MovementAction*>(action) &&
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace KarazhanHelpers;
|
using namespace KarazhanHelpers;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
|||||||
|
|
||||||
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||||
@@ -110,7 +111,7 @@ bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
|||||||
|
|
||||||
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||||
@@ -126,7 +127,7 @@ bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
|||||||
|
|
||||||
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||||
@@ -178,7 +179,7 @@ bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
|||||||
|
|
||||||
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||||
@@ -202,7 +203,7 @@ bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive()
|
|||||||
// Exclusion of Banish is so the player may Banish elementals if they wish
|
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||||
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||||
@@ -279,7 +280,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive()
|
|||||||
|
|
||||||
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot))
|
if (!botAI->IsTank(bot) && !IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace KarazhanHelpers
|
namespace KarazhanHelpers
|
||||||
{
|
{
|
||||||
@@ -52,75 +51,6 @@ namespace KarazhanHelpers
|
|||||||
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||||
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
||||||
|
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
|
||||||
{
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
|
||||||
if (currentGuid != target->GetGUID())
|
|
||||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
|
||||||
{
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
|
||||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
|
||||||
|
|
||||||
if (currentRti != rtiName || currentTarget != target)
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
|
||||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only one bot is needed to set/reset instance-wide timers
|
|
||||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
|
||||||
{
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
|
||||||
return member == bot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||||
{
|
{
|
||||||
for (Unit* unit : units)
|
for (Unit* unit : units)
|
||||||
@@ -132,44 +62,6 @@ namespace KarazhanHelpers
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
|
||||||
{
|
|
||||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
|
||||||
for (auto const& npcGuid : npcs)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(npcGuid);
|
|
||||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
|
||||||
{
|
|
||||||
Unit* nearestPlayer = nullptr;
|
|
||||||
float nearestDistance = radius;
|
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member || !member->IsAlive() || member == bot)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float distance = bot->GetExactDist2d(member);
|
|
||||||
if (distance < nearestDistance)
|
|
||||||
{
|
|
||||||
nearestDistance = distance;
|
|
||||||
nearestPlayer = member;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nearestPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ namespace KarazhanHelpers
|
|||||||
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
||||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
NPC_TERESTIAN_ILLHOOF = 15688,
|
||||||
|
NPC_DEMON_CHAINS = 17248,
|
||||||
|
NPC_KILREK = 17229,
|
||||||
|
|
||||||
// Shade of Aran
|
// Shade of Aran
|
||||||
NPC_CONJURED_ELEMENTAL = 17167,
|
NPC_CONJURED_ELEMENTAL = 17167,
|
||||||
|
|
||||||
@@ -74,8 +79,8 @@ namespace KarazhanHelpers
|
|||||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32 KARAZHAN_MAP_ID = 532;
|
constexpr uint32 KARAZHAN_MAP_ID = 532;
|
||||||
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
constexpr float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||||
|
|
||||||
// Attumen the Huntsman
|
// Attumen the Huntsman
|
||||||
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||||
@@ -105,17 +110,7 @@ namespace KarazhanHelpers
|
|||||||
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
||||||
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
||||||
|
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
|
||||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
|
||||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
|
||||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
|
||||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
|
||||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
using namespace MagtheridonHelpers;
|
||||||
|
|
||||||
@@ -14,46 +15,45 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
MarkTargetWithSquare(bot, channelerSquare);
|
MarkTargetWithSquare(bot, channelerSquare);
|
||||||
|
|
||||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||||
if (channelerStar && channelerStar->IsAlive())
|
if (channelerStar)
|
||||||
MarkTargetWithStar(bot, channelerStar);
|
MarkTargetWithStar(bot, channelerStar);
|
||||||
|
|
||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
if (channelerCircle && channelerCircle->IsAlive())
|
if (channelerCircle)
|
||||||
MarkTargetWithCircle(bot, channelerCircle);
|
MarkTargetWithCircle(bot, channelerCircle);
|
||||||
|
|
||||||
// After first three channelers are dead, wait for Magtheridon to activate
|
// After first three channelers are dead, wait for Magtheridon to activate
|
||||||
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
|
if (!channelerSquare && !channelerStar && !channelerCircle)
|
||||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
|
||||||
(!channelerCircle || !channelerCircle->IsAlive()))
|
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
|
const Position& position = WAITING_FOR_MAGTHERIDON_POSITION;
|
||||||
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
|
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), 2.0f))
|
||||||
{
|
{
|
||||||
return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, position.GetPositionX(), position.GetPositionY(),
|
||||||
|
position.GetPositionZ(), false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bot->SetFacingTo(position.orientation);
|
bot->SetFacingTo(position.GetOrientation());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* currentTarget = nullptr;
|
Creature* currentTarget = nullptr;
|
||||||
std::string rtiName;
|
std::string rtiName;
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
{
|
{
|
||||||
currentTarget = channelerSquare;
|
currentTarget = channelerSquare;
|
||||||
rtiName = "square";
|
rtiName = "square";
|
||||||
}
|
}
|
||||||
else if (channelerStar && channelerStar->IsAlive())
|
else if (channelerStar)
|
||||||
{
|
{
|
||||||
currentTarget = channelerStar;
|
currentTarget = channelerStar;
|
||||||
rtiName = "star";
|
rtiName = "star";
|
||||||
}
|
}
|
||||||
else if (channelerCircle && channelerCircle->IsAlive())
|
else if (channelerCircle)
|
||||||
{
|
{
|
||||||
currentTarget = channelerCircle;
|
currentTarget = channelerCircle;
|
||||||
rtiName = "circle";
|
rtiName = "circle";
|
||||||
@@ -70,7 +70,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
|||||||
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
if (!channelerDiamond || !channelerDiamond->IsAlive())
|
if (!channelerDiamond)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MarkTargetWithDiamond(bot, channelerDiamond);
|
MarkTargetWithDiamond(bot, channelerDiamond);
|
||||||
@@ -81,18 +81,18 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
|||||||
|
|
||||||
if (channelerDiamond->GetVictim() == bot)
|
if (channelerDiamond->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
|
const Position& position = NW_CHANNELER_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
|
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
if (distanceToPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float dX = position.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
|||||||
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
if (!channelerTriangle || !channelerTriangle->IsAlive())
|
if (!channelerTriangle)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MarkTargetWithTriangle(bot, channelerTriangle);
|
MarkTargetWithTriangle(bot, channelerTriangle);
|
||||||
@@ -114,18 +114,18 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
|||||||
|
|
||||||
if (channelerTriangle->GetVictim() == bot)
|
if (channelerTriangle->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
|
const Position& position = NE_CHANNELER_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
|
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
if (distanceToPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float dX = position.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
|||||||
switch (hunterIndex)
|
switch (hunterIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if (mainTank && channelerStar && channelerStar->IsAlive() &&
|
if (mainTank && channelerStar &&
|
||||||
channelerStar->GetVictim() != mainTank)
|
channelerStar->GetVictim() != mainTank)
|
||||||
{
|
{
|
||||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
@@ -190,7 +190,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
|
if (mainTank && channelerCircle &&
|
||||||
channelerCircle->GetVictim() != mainTank)
|
channelerCircle->GetVictim() != mainTank)
|
||||||
{
|
{
|
||||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
@@ -215,90 +215,69 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
// Listed in order of priority
|
// Listed in order of priority
|
||||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "square", channelerSquare);
|
SetRtiTarget(botAI, "square", channelerSquare);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerSquare->GetGUID())
|
if (bot->GetTarget() != channelerSquare->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerSquare->GetGUID());
|
|
||||||
return Attack(channelerSquare);
|
return Attack(channelerSquare);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||||
if (channelerStar && channelerStar->IsAlive())
|
if (channelerStar)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "star", channelerStar);
|
SetRtiTarget(botAI, "star", channelerStar);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerStar->GetGUID())
|
if (bot->GetTarget() != channelerStar->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerStar->GetGUID());
|
|
||||||
return Attack(channelerStar);
|
return Attack(channelerStar);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
if (channelerCircle && channelerCircle->IsAlive())
|
if (channelerCircle)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "circle", channelerCircle);
|
SetRtiTarget(botAI, "circle", channelerCircle);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerCircle->GetGUID())
|
if (bot->GetTarget() != channelerCircle->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerCircle->GetGUID());
|
|
||||||
return Attack(channelerCircle);
|
return Attack(channelerCircle);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
if (channelerDiamond && channelerDiamond->IsAlive())
|
if (channelerDiamond)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerDiamond->GetGUID());
|
|
||||||
return Attack(channelerDiamond);
|
return Attack(channelerDiamond);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
if (channelerTriangle && channelerTriangle->IsAlive())
|
if (channelerTriangle)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerTriangle->GetGUID());
|
|
||||||
return Attack(channelerTriangle);
|
return Attack(channelerTriangle);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
||||||
(!channelerSquare || !channelerSquare->IsAlive()) &&
|
!channelerSquare && !channelerStar && !channelerCircle &&
|
||||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
!channelerDiamond && !channelerTriangle)
|
||||||
(!channelerCircle || !channelerCircle->IsAlive()) &&
|
|
||||||
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
|
|
||||||
(!channelerTriangle || !channelerTriangle->IsAlive()))
|
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "cross", magtheridon);
|
SetRtiTarget(botAI, "cross", magtheridon);
|
||||||
|
|
||||||
if (bot->GetTarget() != magtheridon->GetGUID())
|
if (bot->GetTarget() != magtheridon->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(magtheridon->GetGUID());
|
|
||||||
return Attack(magtheridon);
|
return Attack(magtheridon);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -343,15 +322,15 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
|
|||||||
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
||||||
{
|
{
|
||||||
Unit* assignedAbyssal = abyssals[warlockIndex];
|
Unit* assignedAbyssal = abyssals[warlockIndex];
|
||||||
if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true))
|
if (!botAI->HasAura("banish", assignedAbyssal) && botAI->CanCastSpell("banish", assignedAbyssal))
|
||||||
return botAI->CastSpell("banish", assignedAbyssal);
|
return botAI->CastSpell("banish", assignedAbyssal);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
||||||
{
|
{
|
||||||
Unit* excessAbyssal = abyssals[i];
|
Unit* excessAbyssal = abyssals[i];
|
||||||
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
|
if (!botAI->HasAura("banish", excessAbyssal) && !botAI->HasAura("fear", excessAbyssal) &&
|
||||||
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
|
botAI->CanCastSpell("fear", excessAbyssal))
|
||||||
return botAI->CastSpell("fear", excessAbyssal);
|
return botAI->CastSpell("fear", excessAbyssal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,22 +352,20 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event)
|
|||||||
|
|
||||||
if (magtheridon->GetVictim() == bot)
|
if (magtheridon->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
|
const Position& position = MAGTHERIDON_TANK_POSITION;
|
||||||
const float maxDistance = 2.0f;
|
const float maxDistance = 2.0f;
|
||||||
|
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
if (distanceToPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float dX = position.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bot->SetFacingTo(position.orientation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -440,13 +417,13 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isHealer = botAI->IsHeal(bot);
|
bool isHealer = botAI->IsHeal(bot);
|
||||||
const Location& center = isHealer
|
const Position& center = isHealer
|
||||||
? MagtheridonsLairLocations::HealerSpreadPosition
|
? HEALER_SPREAD_POSITION
|
||||||
: MagtheridonsLairLocations::RangedSpreadPosition;
|
: RANGED_SPREAD_POSITION;
|
||||||
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
||||||
float centerX = center.x;
|
float centerX = center.GetPositionX();
|
||||||
float centerY = center.y;
|
float centerY = center.GetPositionY();
|
||||||
float centerZ = bot->GetPositionZ();
|
float centerZ = center.GetPositionZ();
|
||||||
const float radiusBuffer = 3.0f;
|
const float radiusBuffer = 3.0f;
|
||||||
|
|
||||||
if (!initialPositions.count(bot->GetGUID()))
|
if (!initialPositions.count(bot->GetGUID()))
|
||||||
@@ -479,7 +456,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
|
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||||
@@ -499,7 +476,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
bot->InterruptNonMeleeSpells(false);
|
||||||
return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, centerZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -593,7 +570,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
bot->InterruptNonMeleeSpells(true);
|
||||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +580,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
|||||||
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||||
float fallbackZ = bot->GetPositionZ();
|
float fallbackZ = bot->GetPositionZ();
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +615,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu
|
|||||||
|
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
bot->InterruptNonMeleeSpells(true);
|
||||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,14 +640,14 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
|||||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||||
bool lastBlastNova = lastBlastNovaState[instanceId];
|
bool lastBlastNova = lastBlastNovaState[instanceId];
|
||||||
|
|
||||||
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
|
if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
blastNovaTimer[instanceId] = now;
|
blastNovaTimer[instanceId] = now;
|
||||||
|
|
||||||
lastBlastNovaState[instanceId] = blastNovaActive;
|
lastBlastNovaState[instanceId] = blastNovaActive;
|
||||||
|
|
||||||
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
spreadWaitTimer.try_emplace(instanceId, now);
|
spreadWaitTimer.try_emplace(instanceId, now);
|
||||||
blastNovaTimer.try_emplace(instanceId, now);
|
blastNovaTimer.try_emplace(instanceId, now);
|
||||||
@@ -679,11 +656,12 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MagtheridonSpreadRangedAction::initialPositions.clear();
|
ObjectGuid guid = bot->GetGUID();
|
||||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
|
MagtheridonSpreadRangedAction::initialPositions.erase(guid);
|
||||||
botToCubeAssignment.clear();
|
MagtheridonSpreadRangedAction::hasReachedInitialPosition.erase(guid);
|
||||||
|
botToCubeAssignment.erase(guid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
spreadWaitTimer.erase(instanceId);
|
spreadWaitTimer.erase(instanceId);
|
||||||
blastNovaTimer.erase(instanceId);
|
blastNovaTimer.erase(instanceId);
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
|
||||||
|
|
||||||
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -85,8 +83,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
||||||
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
||||||
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
|
bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo);
|
||||||
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
|
bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "WarlockActions.h"
|
#include "WarlockActions.h"
|
||||||
|
#include "WipeAction.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
using namespace MagtheridonHelpers;
|
||||||
|
|
||||||
@@ -24,10 +25,10 @@ float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
|
|||||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||||
if (it != botToCubeAssignment.end())
|
if (it != botToCubeAssignment.end())
|
||||||
{
|
{
|
||||||
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
if (dynamic_cast<WipeAction*>(action))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
else if (!dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,28 +42,31 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
|
|||||||
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
const uint8 dpsWaitSeconds = 6;
|
const uint8 dpsWaitSeconds = 6;
|
||||||
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||||
if (it == dpsWaitTimer.end() ||
|
if (it == dpsWaitTimer.end() ||
|
||||||
(time(nullptr) - it->second) < dpsWaitSeconds)
|
(time(nullptr) - it->second) < dpsWaitSeconds)
|
||||||
{
|
{
|
||||||
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
|
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No tank assist for offtanks during the channeler phase
|
|
||||||
// So they don't try to pull channelers from each other or the main tank
|
|
||||||
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
|
||||||
if (!magtheridon)
|
if (!magtheridon)
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
||||||
dynamic_cast<TankAssistAction*>(action))
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
|
|||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
||||||
channelerDiamond && channelerDiamond->IsAlive();
|
channelerDiamond;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||||
@@ -27,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
|||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
||||||
channelerTriangle && channelerTriangle->IsAlive();
|
channelerTriangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||||
@@ -38,8 +38,7 @@ bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
|||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
||||||
((channelerStar && channelerStar->IsAlive()) ||
|
(channelerStar || channelerCircle);
|
||||||
(channelerCircle && channelerCircle->IsAlive()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||||
@@ -51,12 +50,11 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
|||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
|
|
||||||
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
||||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
|
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond) ||
|
||||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
|
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (channeler && channeler->IsAlive()) || (magtheridon &&
|
return channeler || (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
||||||
@@ -84,10 +82,8 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
|
|||||||
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
|
||||||
|
|
||||||
return magtheridon && botAI->IsRanged(bot) &&
|
return magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && botAI->IsRanged(bot);
|
||||||
!(channeler && channeler->IsAlive());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||||
@@ -122,7 +118,5 @@ bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
|||||||
|
|
||||||
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
return AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
|
|
||||||
return magtheridon;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
#include "RaidMagtheridonHelpers.h"
|
#include "RaidMagtheridonHelpers.h"
|
||||||
#include "Creature.h"
|
#include "Creature.h"
|
||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
#include "GroupReference.h"
|
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
namespace MagtheridonHelpers
|
namespace MagtheridonHelpers
|
||||||
{
|
{
|
||||||
namespace MagtheridonsLairLocations
|
const Position WAITING_FOR_MAGTHERIDON_POSITION = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||||
{
|
const Position MAGTHERIDON_TANK_POSITION = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||||
const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
|
const Position NW_CHANNELER_TANK_POSITION = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||||
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
|
const Position NE_CHANNELER_TANK_POSITION = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||||
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
|
const Position RANGED_SPREAD_POSITION = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||||
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
|
const Position HEALER_SPREAD_POSITION = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||||
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
|
|
||||||
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify channelers by their database GUIDs
|
// Identify channelers by their database GUIDs
|
||||||
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
||||||
@@ -29,63 +25,11 @@ namespace MagtheridonHelpers
|
|||||||
if (it == map->GetCreatureBySpawnIdStore().end())
|
if (it == map->GetCreatureBySpawnIdStore().end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return it->second;
|
Creature* channeler = it->second;
|
||||||
}
|
if (!channeler->IsAlive())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
return channeler;
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!target || !group)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
|
||||||
if (currentGuid != target->GetGUID())
|
|
||||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
|
||||||
{
|
|
||||||
if (!target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
|
||||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
|
||||||
|
|
||||||
if (currentRti != rtiName || currentTarget != target)
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
|
||||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
|
||||||
{
|
|
||||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
||||||
@@ -208,19 +152,4 @@ namespace MagtheridonHelpers
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
|
||||||
{
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
|
||||||
return member == bot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace MagtheridonHelpers
|
namespace MagtheridonHelpers
|
||||||
{
|
{
|
||||||
@@ -19,10 +18,6 @@ namespace MagtheridonHelpers
|
|||||||
SPELL_BLAST_NOVA = 30616,
|
SPELL_BLAST_NOVA = 30616,
|
||||||
SPELL_SHADOW_GRASP = 30410,
|
SPELL_SHADOW_GRASP = 30410,
|
||||||
|
|
||||||
// Warlock
|
|
||||||
SPELL_BANISH = 18647,
|
|
||||||
SPELL_FEAR = 6215,
|
|
||||||
|
|
||||||
// Hunter
|
// Hunter
|
||||||
SPELL_MISDIRECTION = 35079,
|
SPELL_MISDIRECTION = 35079,
|
||||||
};
|
};
|
||||||
@@ -38,6 +33,7 @@ namespace MagtheridonHelpers
|
|||||||
GO_BLAZE = 181832,
|
GO_BLAZE = 181832,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32 MAGTHERIDON_MAP_ID = 544;
|
||||||
constexpr uint32 SOUTH_CHANNELER = 90978;
|
constexpr uint32 SOUTH_CHANNELER = 90978;
|
||||||
constexpr uint32 WEST_CHANNELER = 90979;
|
constexpr uint32 WEST_CHANNELER = 90979;
|
||||||
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
||||||
@@ -45,31 +41,14 @@ namespace MagtheridonHelpers
|
|||||||
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
||||||
|
|
||||||
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
|
||||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
|
||||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
|
||||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
|
||||||
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
||||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
|
||||||
|
|
||||||
struct Location
|
extern const Position WAITING_FOR_MAGTHERIDON_POSITION;
|
||||||
{
|
extern const Position MAGTHERIDON_TANK_POSITION;
|
||||||
float x, y, z, orientation;
|
extern const Position NW_CHANNELER_TANK_POSITION;
|
||||||
};
|
extern const Position NE_CHANNELER_TANK_POSITION;
|
||||||
|
extern const Position RANGED_SPREAD_POSITION;
|
||||||
namespace MagtheridonsLairLocations
|
extern const Position HEALER_SPREAD_POSITION;
|
||||||
{
|
|
||||||
extern const Location WaitingForMagtheridonPosition;
|
|
||||||
extern const Location MagtheridonTankPosition;
|
|
||||||
extern const Location NWChannelerTankPosition;
|
|
||||||
extern const Location NEChannelerTankPosition;
|
|
||||||
extern const Location RangedSpreadPosition;
|
|
||||||
extern const Location HealerSpreadPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CubeInfo
|
struct CubeInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
#include "BossAuraActions.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidMcActions.h"
|
#include "RaidMcActions.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
#include "BossAuraTriggers.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidMcTriggers.h"
|
#include "RaidMcTriggers.h"
|
||||||
|
|
||||||
|
|||||||
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
|
|
||||||
|
// Functions to mark targets with raid target icons
|
||||||
|
// Note that these functions do not allow the player to change the icon during the encounter
|
||||||
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||||
|
if (currentGuid != target->GetGUID())
|
||||||
|
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For bots to set their raid target icon to the specified icon on the specified target
|
||||||
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||||
|
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||||
|
|
||||||
|
if (currentRti != rtiName || currentTarget != target)
|
||||||
|
{
|
||||||
|
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||||
|
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first alive DPS bot in the specified instance map, excluding any specified bot
|
||||||
|
// Intended for purposes of storing and erasing timers and trackers in associative containers
|
||||||
|
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member->GetMapId() != mapId ||
|
||||||
|
!GET_PLAYERBOT_AI(member) || !botAI->IsDps(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member != exclude)
|
||||||
|
return member == bot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first matching alive unit from a cell search of nearby npcs
|
||||||
|
// More responsive than "find target," but performance cost is much higher
|
||||||
|
// Re: using the third parameter (false by default), some units are never considered
|
||||||
|
// to be in combat (e.g., totems)
|
||||||
|
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat)
|
||||||
|
{
|
||||||
|
auto const& npcs =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||||
|
for (auto const& npcGuid : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(npcGuid);
|
||||||
|
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||||
|
{
|
||||||
|
if (!requireInCombat || unit->IsInCombat())
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the nearest alive player (human or bot) within the specified radius
|
||||||
|
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||||
|
{
|
||||||
|
Unit* nearestPlayer = nullptr;
|
||||||
|
float nearestDistance = radius;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == bot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distance = bot->GetExactDist2d(member);
|
||||||
|
if (distance < nearestDistance)
|
||||||
|
{
|
||||||
|
nearestDistance = distance;
|
||||||
|
nearestPlayer = member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearestPlayer;
|
||||||
|
}
|
||||||
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||||
|
#define _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||||
|
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||||
|
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||||
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||||
|
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
|
||||||
|
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false);
|
||||||
|
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "RaidKarazhanStrategy.h"
|
#include "RaidKarazhanStrategy.h"
|
||||||
#include "RaidMagtheridonStrategy.h"
|
#include "RaidMagtheridonStrategy.h"
|
||||||
#include "RaidGruulsLairStrategy.h"
|
#include "RaidGruulsLairStrategy.h"
|
||||||
|
#include "RaidSSCStrategy.h"
|
||||||
#include "RaidOsStrategy.h"
|
#include "RaidOsStrategy.h"
|
||||||
#include "RaidEoEStrategy.h"
|
#include "RaidEoEStrategy.h"
|
||||||
#include "RaidVoAStrategy.h"
|
#include "RaidVoAStrategy.h"
|
||||||
@@ -26,10 +27,11 @@ public:
|
|||||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||||
|
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||||
creators["voa"] = &RaidStrategyContext::voa;
|
creators["voa"] = &RaidStrategyContext::voa;
|
||||||
creators["uld"] = &RaidStrategyContext::uld;
|
creators["ulduar"] = &RaidStrategyContext::ulduar;
|
||||||
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
||||||
creators["icc"] = &RaidStrategyContext::icc;
|
creators["icc"] = &RaidStrategyContext::icc;
|
||||||
}
|
}
|
||||||
@@ -41,11 +43,12 @@ private:
|
|||||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||||
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||||
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||||
|
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||||
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
|
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
|
||||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
static Strategy* ulduar(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||||
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCACTIONS_H
|
||||||
|
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
// General
|
||||||
|
|
||||||
|
class SerpentShrineCavernEraseTimersAndTrackersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SerpentShrineCavernEraseTimersAndTrackersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "serpent shrine cavern erase timers and trackers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class UnderbogColossusEscapeToxicPoolAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnderbogColossusEscapeToxicPoolAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "underbog colossus escape toxic pool") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GreyheartTidecallerMarkWaterElementalTotemAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GreyheartTidecallerMarkWaterElementalTotemAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "greyheart tidecaller mark water elemental totem") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
class HydrossTheUnstablePositionFrostTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstablePositionFrostTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable position frost tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstablePositionNatureTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstablePositionNatureTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable position nature tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstablePrioritizeElementalAddsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstablePrioritizeElementalAddsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable prioritize elemental adds") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableFrostPhaseSpreadOutAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableFrostPhaseSpreadOutAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable frost phase spread out") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableMisdirectBossToTankAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableMisdirectBossToTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable misdirect boss to tank") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool TryMisdirectToFrostTank(Unit* hydross, Group* group);
|
||||||
|
bool TryMisdirectToNatureTank(Unit* hydross, Group* group);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableStopDpsUponPhaseChangeAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable stop dps upon phase change") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableManageTimersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableManageTimersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hydross the unstable manage timers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
class TheLurkerBelowRunAroundBehindBossAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowRunAroundBehindBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the lurker below run around behind boss") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowPositionMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowPositionMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the lurker below position main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowSpreadRangedInArcAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowSpreadRangedInArcAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the lurker below spread ranged in arc") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowTanksPickUpAddsAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowTanksPickUpAddsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the lurker below tanks pick up adds") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowManageSpoutTimerAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowManageSpoutTimerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the lurker below manage spout timer") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
class LeotherasTheBlindTargetSpellbindersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindTargetSpellbindersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind target spellbinders") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindPositionRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindPositionRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind position ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindDemonFormTankAttackBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindDemonFormTankAttackBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind demon form tank attack boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindMeleeTanksDontAttackDemonFormAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee tanks don't attack demon form") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindRunAwayFromWhirlwindAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindRunAwayFromWhirlwindAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind run away from whirlwind") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindMeleeDpsRunAwayFromBossAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindMeleeDpsRunAwayFromBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee dps run away from boss") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindDestroyInnerDemonAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindDestroyInnerDemonAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind destroy inner demon") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool HandleFeralTankStrategy(Unit* innerDemon);
|
||||||
|
bool HandleHealerStrategy(Unit* innerDemon);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindFinalPhaseAssignDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind final phase assign dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindMisdirectBossToDemonFormTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindMisdirectBossToDemonFormTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind misdirect boss to demon form tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindManageDpsWaitTimersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindManageDpsWaitTimersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "leotheras the blind manage dps wait timers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
class FathomLordKarathressMainTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressMainTankPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress main tank position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressFirstAssistTankPositionCaribdisAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressFirstAssistTankPositionCaribdisAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress first assist tank position caribdis") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressSecondAssistTankPositionSharkkisAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressSecondAssistTankPositionSharkkisAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress second assist tank position sharkkis") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressThirdAssistTankPositionTidalvessAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressThirdAssistTankPositionTidalvessAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress third assist tank position tidalvess") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressPositionCaribdisTankHealerAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressPositionCaribdisTankHealerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress position caribdis tank healer") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressMisdirectBossesToTanksAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressMisdirectBossesToTanksAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress misdirect bosses to tanks") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressAssignDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressAssignDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress assign dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressManageDpsTimerAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressManageDpsTimerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress manage dps timer") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
class MorogrimTidewalkerMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerMoveBossToTankPositionAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerMoveBossToTankPositionAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker move boss to tank position") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool MoveToPhase1TankPosition(Unit* tidewalker);
|
||||||
|
bool MoveToPhase2TankPosition(Unit* tidewalker);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerPhase2RepositionRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerPhase2RepositionRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker phase 2 reposition ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
class LadyVashjMainTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjMainTankPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj main tank position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjPhase1SpreadRangedInArcAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjPhase1SpreadRangedInArcAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj phase 1 spread ranged in arc") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjSetGroundingTotemInMainTankGroupAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjSetGroundingTotemInMainTankGroupAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj set grounding totem in main tank group") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjStaticChargeMoveAwayFromGroupAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjStaticChargeMoveAwayFromGroupAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj static charge move away from group") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjAssignPhase2AndPhase3DpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjAssignPhase2AndPhase3DpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj assign phase 2 and phase 3 dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjMisdirectStriderToFirstAssistTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjMisdirectStriderToFirstAssistTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect strider to first assist tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjTankAttackAndMoveAwayStriderAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjTankAttackAndMoveAwayStriderAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj tank attack and move away strider") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjTeleportToTaintedElementalAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjTeleportToTaintedElementalAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj teleport to tainted elemental") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjLootTaintedCoreAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjLootTaintedCoreAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj loot tainted core") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjPassTheTaintedCoreAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjPassTheTaintedCoreAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "lady vashj pass the tainted core") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool LineUpFirstCorePasser(Player* designatedLooter, Unit* closestTrigger);
|
||||||
|
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
||||||
|
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||||
|
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||||
|
bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||||
|
bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||||
|
bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||||
|
bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||||
|
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
||||||
|
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjDestroyTaintedCoreAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjDestroyTaintedCoreAction(PlayerbotAI* botAI, std::string const name = "lady vashj destroy tainted core") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjEraseCorePassingTrackersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjEraseCorePassingTrackersAction(PlayerbotAI* botAI, std::string const name = "lady vashj erase core passing trackers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjAvoidToxicSporesAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
static std::vector<Unit*> GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Position FindSafestNearbyPosition(const std::vector<Unit*>& spores, const Position& position, float maxRadius, float hazardRadius);
|
||||||
|
bool IsPathSafeFromSpores(const Position& start, const Position& end, const std::vector<Unit*>& spores, float hazardRadius);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjUseFreeActionAbilitiesAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjUseFreeActionAbilitiesAction(PlayerbotAI* botAI, std::string const name = "lady vashj use free action abilities") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,799 @@
|
|||||||
|
#include "RaidSSCMultipliers.h"
|
||||||
|
#include "RaidSSCActions.h"
|
||||||
|
#include "RaidSSCHelpers.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "DestroyItemAction.h"
|
||||||
|
#include "DKActions.h"
|
||||||
|
#include "DruidActions.h"
|
||||||
|
#include "DruidBearActions.h"
|
||||||
|
#include "DruidCatActions.h"
|
||||||
|
#include "DruidShapeshiftActions.h"
|
||||||
|
#include "FollowActions.h"
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "LootAction.h"
|
||||||
|
#include "MageActions.h"
|
||||||
|
#include "PaladinActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
|
#include "RogueActions.h"
|
||||||
|
#include "ShamanActions.h"
|
||||||
|
#include "WarlockActions.h"
|
||||||
|
#include "WarriorActions.h"
|
||||||
|
#include "WipeAction.h"
|
||||||
|
|
||||||
|
using namespace SerpentShrineCavernHelpers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_TOXIC_POOL))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
|
if (!hydross)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||||
|
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
(dynamic_cast<AttackAction*>(action) &&
|
||||||
|
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
||||||
|
!dynamic_cast<HydrossTheUnstablePositionNatureTankAction*>(action)))
|
||||||
|
{
|
||||||
|
if ((botAI->IsMainTank(bot) && hydross->HasAura(SPELL_CORRUPTION)) ||
|
||||||
|
(botAI->IsAssistTankOfIndex(bot, 0, true) && !hydross->HasAura(SPELL_CORRUPTION)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
|
if (!hydross)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* waterElemental = AI_VALUE2(Unit*, "find target", "pure spawn of hydross");
|
||||||
|
Unit* natureElemental = AI_VALUE2(Unit*, "find target", "tainted spawn of hydross");
|
||||||
|
if (botAI->IsAssistTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||||
|
(waterElemental || natureElemental))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<HydrossTheUnstableMisdirectBossToTankAction*>(action))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const uint32 instanceId = hydross->GetMap()->GetInstanceId();
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
constexpr uint8 phaseChangeWaitSeconds = 1;
|
||||||
|
constexpr uint8 dpsWaitSeconds = 5;
|
||||||
|
|
||||||
|
if (!hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsMainTank(bot))
|
||||||
|
{
|
||||||
|
auto itDps = hydrossFrostDpsWaitTimer.find(instanceId);
|
||||||
|
auto itPhase = hydrossChangeToFrostPhaseTimer.find(instanceId);
|
||||||
|
|
||||||
|
bool justChanged = (itDps == hydrossFrostDpsWaitTimer.end() ||
|
||||||
|
(now - itDps->second) < dpsWaitSeconds);
|
||||||
|
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
||||||
|
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||||
|
|
||||||
|
if (justChanged || aboutToChange)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
|
{
|
||||||
|
auto itDps = hydrossNatureDpsWaitTimer.find(instanceId);
|
||||||
|
auto itPhase = hydrossChangeToNaturePhaseTimer.find(instanceId);
|
||||||
|
|
||||||
|
bool justChanged = (itDps == hydrossNatureDpsWaitTimer.end() ||
|
||||||
|
(now - itDps->second) < dpsWaitSeconds);
|
||||||
|
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
||||||
|
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||||
|
|
||||||
|
if (justChanged || aboutToChange)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
float TheLurkerBelowStayAwayFromSpoutMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||||
|
if (it != lurkerSpoutTimer.end() && it->second > now)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<AttackAction*>(action) &&
|
||||||
|
!dynamic_cast<TheLurkerBelowRunAroundBehindBossAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "the lurker below"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable tank assist during Submerge only if there are 3 or more tanks in the raid
|
||||||
|
float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
uint8 tankCount = 0;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (botAI->IsTank(member))
|
||||||
|
++tankCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tankCount >= 3)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||||
|
if (!leotherasHuman)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||||
|
(leotherasHuman->HasAura(SPELL_WHIRLWIND) ||
|
||||||
|
leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<AttackAction*>(action) &&
|
||||||
|
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) || bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (GetPhase2LeotherasDemon(botAI) && dynamic_cast<AttackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast<CastBerserkAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||||
|
dynamic_cast<DpsAssistAction*>(action) ||
|
||||||
|
dynamic_cast<CastHealingSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastCureSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CurePartyMemberAction*>(action) ||
|
||||||
|
dynamic_cast<CastBuffSpellAction*>(action) ||
|
||||||
|
dynamic_cast<ResurrectPartyMemberAction*>(action) ||
|
||||||
|
dynamic_cast<PartyMemberActionNameSupport*>(action) ||
|
||||||
|
dynamic_cast<CastBearFormAction*>(action) ||
|
||||||
|
dynamic_cast<CastDireBearFormAction*>(action) ||
|
||||||
|
dynamic_cast<CastTreeFormAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!GetPhase2LeotherasDemon(botAI))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||||
|
if (chaosBlast && chaosBlast->GetStackAmount() >= 5)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
|
if (!leotheras)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<LeotherasTheBlindMisdirectBossToDemonFormTankAction*>(action))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const uint32 instanceId = leotheras->GetMap()->GetInstanceId();
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
||||||
|
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||||
|
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI);
|
||||||
|
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||||
|
!leotherasPhase3Demon)
|
||||||
|
{
|
||||||
|
if (botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto it = leotherasHumanFormDpsWaitTimer.find(instanceId);
|
||||||
|
if (it == leotherasHumanFormDpsWaitTimer.end() ||
|
||||||
|
(now - it->second) < dpsWaitSecondsPhase1)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
||||||
|
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI);
|
||||||
|
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
||||||
|
if (leotherasPhase2Demon)
|
||||||
|
{
|
||||||
|
if (demonFormTank && demonFormTank == bot)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!demonFormTank && botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto it = leotherasDemonFormDpsWaitTimer.find(instanceId);
|
||||||
|
if (it == leotherasDemonFormDpsWaitTimer.end() ||
|
||||||
|
(now - it->second) < dpsWaitSecondsPhase2)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8 dpsWaitSecondsPhase3 = 8;
|
||||||
|
if (leotherasPhase3Demon)
|
||||||
|
{
|
||||||
|
if ((demonFormTank && demonFormTank == bot) || botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto it = leotherasFinalPhaseDpsWaitTimer.find(instanceId);
|
||||||
|
if (it == leotherasFinalPhaseDpsWaitTimer.end() ||
|
||||||
|
(now - it->second) < dpsWaitSecondsPhase3)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use Bloodlust/Heroism during the Channeler phase
|
||||||
|
float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_SHAMAN)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
|
if (leotheras && leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||||
|
dynamic_cast<CastBloodlustAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
float FathomLordKarathressDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<AvoidAoeAction*>(action) ||
|
||||||
|
dynamic_cast<CastTauntAction*>(action) ||
|
||||||
|
dynamic_cast<CastChallengingShoutAction*>(action) ||
|
||||||
|
dynamic_cast<CastThunderClapAction*>(action) ||
|
||||||
|
dynamic_cast<CastShockwaveAction*>(action) ||
|
||||||
|
dynamic_cast<CastCleaveAction*>(action) ||
|
||||||
|
dynamic_cast<CastGrowlAction*>(action) ||
|
||||||
|
dynamic_cast<CastSwipeAction*>(action) ||
|
||||||
|
dynamic_cast<CastHandOfReckoningAction*>(action) ||
|
||||||
|
dynamic_cast<CastAvengersShieldAction*>(action) ||
|
||||||
|
dynamic_cast<CastConsecrationAction*>(action) ||
|
||||||
|
dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||||
|
dynamic_cast<CastDeathAndDecayAction*>(action) ||
|
||||||
|
dynamic_cast<CastPestilenceAction*>(action) ||
|
||||||
|
dynamic_cast<CastBloodBoilAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsDps(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||||
|
{
|
||||||
|
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||||
|
{
|
||||||
|
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FathomLordKarathressWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||||
|
if (!karathress)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<FathomLordKarathressMisdirectBossesToTanksAction*>(action))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
constexpr uint8 dpsWaitSeconds = 12;
|
||||||
|
|
||||||
|
auto it = karathressDpsWaitTimer.find(karathress->GetMap()->GetInstanceId());
|
||||||
|
if (it == karathressDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
// Use Bloodlust/Heroism after the first Murloc spawn
|
||||||
|
float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_SHAMAN)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "tidewalker lurker"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||||
|
dynamic_cast<CastBloodlustAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||||
|
if (!tidewalker)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (tidewalker->GetHealthPct() < 25.0f)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
// Wait until phase 3 to use Bloodlust/Heroism
|
||||||
|
// Don't use other major cooldowns in Phase 1, either
|
||||||
|
float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->getClass() == CLASS_SHAMAN)
|
||||||
|
{
|
||||||
|
if (IsLadyVashjInPhase3(botAI))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botAI->IsDps(bot) && IsLadyVashjInPhase1(botAI))
|
||||||
|
{
|
||||||
|
if (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) ||
|
||||||
|
dynamic_cast<UseTrinketAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||||
|
IsLadyVashjInPhase1(botAI))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot) || !bot->HasAura(SPELL_STATIC_CHARGE))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bots should not loot the core with normal looting logic
|
||||||
|
float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<LootAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||||
|
!IsLadyVashjInPhase2(botAI))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<WipeAction*>(action) ||
|
||||||
|
dynamic_cast<DestroyItemAction*>(action) ||
|
||||||
|
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||||
|
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||||
|
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||||
|
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||||
|
|
||||||
|
auto hasCore = [](Player* player)
|
||||||
|
{
|
||||||
|
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasCore(bot))
|
||||||
|
{
|
||||||
|
if (!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot == designatedLooter)
|
||||||
|
{
|
||||||
|
if (!hasCore(bot))
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
else if (bot == firstCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||||
|
hasCore(fourthCorePasser))
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
else if (bot == secondCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
else if (bot == thirdCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(fourthCorePasser))
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
else if (bot != fourthCorePasser)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||||
|
(bot == firstCorePasser || bot == secondCorePasser))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AnyRecentCoreInInventory(group, botAI))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All of phases 2 and 3 require a custom movement and targeting system
|
||||||
|
// So the standard target selection system must be disabled
|
||||||
|
float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (IsLadyVashjInPhase2(botAI))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||||
|
dynamic_cast<TankAssistAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||||
|
if (enchanted && bot->GetVictim() == enchanted)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsLadyVashjInPhase3(botAI))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<DpsAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||||
|
Unit* strider = AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||||
|
Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite");
|
||||||
|
if (enchanted || strider || elite)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (enchanted && bot->GetVictim() == enchanted)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class UnderbogColossusEscapeToxicPoolMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnderbogColossusEscapeToxicPoolMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "underbog colossus escape toxic pool") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
class HydrossTheUnstableDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableDisableTankActionsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable wait for dps") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableControlMisdirectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableControlMisdirectionMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable control misdirection") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
class TheLurkerBelowStayAwayFromSpoutMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowStayAwayFromSpoutMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below stay away from spout") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowMaintainRangedSpreadMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowMaintainRangedSpreadMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below maintain ranged spread") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowDisableTankAssistMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowDisableTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below disable tank assist") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
class LeotherasTheBlindAvoidWhirlwindMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindAvoidWhirlwindMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind avoid whirlwind") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindDisableTankActionsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind melee dps avoid chaos blast") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindFocusOnInnerDemonMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindFocusOnInnerDemonMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind focus on inner demon") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind wait for dps") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind delay bloodlust and heroism") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
class FathomLordKarathressDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressDisableTankActionsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressDisableAoeMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressDisableAoeMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable aoe") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressControlMisdirectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressControlMisdirectionMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress control misdirection") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress wait for dps") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress caribdis tank healer maintain position") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
class MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker delay bloodlust and heroism") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerDisableTankActionsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerMaintainPhase2StackingMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerMaintainPhase2StackingMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker maintain phase2 stacking") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
class LadyVashjDelayCooldownsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjDelayCooldownsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj delay cooldowns") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjMaintainPhase1RangedSpreadMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj maintain phase1 ranged spread") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjStaticChargeStayAwayFromGroupMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjStaticChargeStayAwayFromGroupMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj static charge stay away from group") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjDoNotLootTheTaintedCoreMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjDoNotLootTheTaintedCoreMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj do not loot the tainted core") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjCorePassersPrioritizePositioningMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjCorePassersPrioritizePositioningMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj core passers prioritize positioning") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjDisableAutomaticTargetingAndMovementModifier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjDisableAutomaticTargetingAndMovementModifier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj disable automatic targeting and movement") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidSSCActions.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
|
class RaidSSCActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidSSCActionContext()
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
creators["serpent shrine cavern erase timers and trackers"] =
|
||||||
|
&RaidSSCActionContext::serpent_shrine_cavern_erase_timers_and_trackers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
creators["underbog colossus escape toxic pool"] =
|
||||||
|
&RaidSSCActionContext::underbog_colossus_escape_toxic_pool;
|
||||||
|
|
||||||
|
creators["greyheart tidecaller mark water elemental totem"] =
|
||||||
|
&RaidSSCActionContext::greyheart_tidecaller_mark_water_elemental_totem;
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
creators["hydross the unstable position frost tank"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_position_frost_tank;
|
||||||
|
|
||||||
|
creators["hydross the unstable position nature tank"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_position_nature_tank;
|
||||||
|
|
||||||
|
creators["hydross the unstable prioritize elemental adds"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_prioritize_elemental_adds;
|
||||||
|
|
||||||
|
creators["hydross the unstable frost phase spread out"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_frost_phase_spread_out;
|
||||||
|
|
||||||
|
creators["hydross the unstable misdirect boss to tank"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_misdirect_boss_to_tank;
|
||||||
|
|
||||||
|
creators["hydross the unstable stop dps upon phase change"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_stop_dps_upon_phase_change;
|
||||||
|
|
||||||
|
creators["hydross the unstable manage timers"] =
|
||||||
|
&RaidSSCActionContext::hydross_the_unstable_manage_timers;
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
creators["the lurker below run around behind boss"] =
|
||||||
|
&RaidSSCActionContext::the_lurker_below_run_around_behind_boss;
|
||||||
|
|
||||||
|
creators["the lurker below position main tank"] =
|
||||||
|
&RaidSSCActionContext::the_lurker_below_position_main_tank;
|
||||||
|
|
||||||
|
creators["the lurker below spread ranged in arc"] =
|
||||||
|
&RaidSSCActionContext::the_lurker_below_spread_ranged_in_arc;
|
||||||
|
|
||||||
|
creators["the lurker below tanks pick up adds"] =
|
||||||
|
&RaidSSCActionContext::the_lurker_below_tanks_pick_up_adds;
|
||||||
|
|
||||||
|
creators["the lurker below manage spout timer"] =
|
||||||
|
&RaidSSCActionContext::the_lurker_below_manage_spout_timer;
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
creators["leotheras the blind target spellbinders"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_target_spellbinders;
|
||||||
|
|
||||||
|
creators["leotheras the blind demon form tank attack boss"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_demon_form_tank_attack_boss;
|
||||||
|
|
||||||
|
creators["leotheras the blind melee tanks don't attack demon form"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_melee_tanks_dont_attack_demon_form;
|
||||||
|
|
||||||
|
creators["leotheras the blind position ranged"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_position_ranged;
|
||||||
|
|
||||||
|
creators["leotheras the blind run away from whirlwind"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_run_away_from_whirlwind;
|
||||||
|
|
||||||
|
creators["leotheras the blind melee dps run away from boss"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_melee_dps_run_away_from_boss;
|
||||||
|
|
||||||
|
creators["leotheras the blind destroy inner demon"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_destroy_inner_demon;
|
||||||
|
|
||||||
|
creators["leotheras the blind final phase assign dps priority"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_final_phase_assign_dps_priority;
|
||||||
|
|
||||||
|
creators["leotheras the blind misdirect boss to demon form tank"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_misdirect_boss_to_demon_form_tank;
|
||||||
|
|
||||||
|
creators["leotheras the blind manage dps wait timers"] =
|
||||||
|
&RaidSSCActionContext::leotheras_the_blind_manage_dps_wait_timers;
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
creators["fathom-lord karathress main tank position boss"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_main_tank_position_boss;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress first assist tank position caribdis"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_first_assist_tank_position_caribdis;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress second assist tank position sharkkis"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_second_assist_tank_position_sharkkis;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress third assist tank position tidalvess"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_third_assist_tank_position_tidalvess;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress position caribdis tank healer"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_position_caribdis_tank_healer;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress misdirect bosses to tanks"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_misdirect_bosses_to_tanks;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress assign dps priority"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_assign_dps_priority;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress manage dps timer"] =
|
||||||
|
&RaidSSCActionContext::fathom_lord_karathress_manage_dps_timer;
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
creators["morogrim tidewalker misdirect boss to main tank"] =
|
||||||
|
&RaidSSCActionContext::morogrim_tidewalker_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["morogrim tidewalker move boss to tank position"] =
|
||||||
|
&RaidSSCActionContext::morogrim_tidewalker_move_boss_to_tank_position;
|
||||||
|
|
||||||
|
creators["morogrim tidewalker phase 2 reposition ranged"] =
|
||||||
|
&RaidSSCActionContext::morogrim_tidewalker_phase_2_reposition_ranged;
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
creators["lady vashj main tank position boss"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_main_tank_position_boss;
|
||||||
|
|
||||||
|
creators["lady vashj phase 1 spread ranged in arc"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_phase_1_spread_ranged_in_arc;
|
||||||
|
|
||||||
|
creators["lady vashj set grounding totem in main tank group"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_set_grounding_totem_in_main_tank_group;
|
||||||
|
|
||||||
|
creators["lady vashj static charge move away from group"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_static_charge_move_away_from_group;
|
||||||
|
|
||||||
|
creators["lady vashj misdirect boss to main tank"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["lady vashj assign phase 2 and phase 3 dps priority"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_assign_phase_2_and_phase_3_dps_priority;
|
||||||
|
|
||||||
|
creators["lady vashj misdirect strider to first assist tank"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_misdirect_strider_to_first_assist_tank;
|
||||||
|
|
||||||
|
creators["lady vashj tank attack and move away strider"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_tank_attack_and_move_away_strider;
|
||||||
|
|
||||||
|
creators["lady vashj loot tainted core"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_loot_tainted_core;
|
||||||
|
|
||||||
|
creators["lady vashj teleport to tainted elemental"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_teleport_to_tainted_elemental;
|
||||||
|
|
||||||
|
creators["lady vashj pass the tainted core"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_pass_the_tainted_core;
|
||||||
|
|
||||||
|
creators["lady vashj destroy tainted core"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_destroy_tainted_core;
|
||||||
|
|
||||||
|
creators["lady vashj erase core passing trackers"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_erase_core_passing_trackers;
|
||||||
|
|
||||||
|
creators["lady vashj avoid toxic spores"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
||||||
|
|
||||||
|
creators["lady vashj use free action abilities"] =
|
||||||
|
&RaidSSCActionContext::lady_vashj_use_free_action_abilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// General
|
||||||
|
static Action* serpent_shrine_cavern_erase_timers_and_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new SerpentShrineCavernEraseTimersAndTrackersAction(botAI); }
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
static Action* underbog_colossus_escape_toxic_pool(
|
||||||
|
PlayerbotAI* botAI) { return new UnderbogColossusEscapeToxicPoolAction(botAI); }
|
||||||
|
|
||||||
|
static Action* greyheart_tidecaller_mark_water_elemental_totem(
|
||||||
|
PlayerbotAI* botAI) { return new GreyheartTidecallerMarkWaterElementalTotemAction(botAI); }
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
static Action* hydross_the_unstable_position_frost_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionFrostTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_position_nature_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionNatureTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_prioritize_elemental_adds(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstablePrioritizeElementalAddsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_frost_phase_spread_out(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableFrostPhaseSpreadOutAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_misdirect_boss_to_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableMisdirectBossToTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_stop_dps_upon_phase_change(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableStopDpsUponPhaseChangeAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hydross_the_unstable_manage_timers(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableManageTimersAction(botAI); }
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
static Action* the_lurker_below_run_around_behind_boss(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowRunAroundBehindBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_lurker_below_position_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowPositionMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_lurker_below_spread_ranged_in_arc(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowSpreadRangedInArcAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_lurker_below_tanks_pick_up_adds(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowTanksPickUpAddsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_lurker_below_manage_spout_timer(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowManageSpoutTimerAction(botAI); }
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
static Action* leotheras_the_blind_target_spellbinders(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindTargetSpellbindersAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_demon_form_tank_attack_boss(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankAttackBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_melee_tanks_dont_attack_demon_form(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_position_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindPositionRangedAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_run_away_from_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindRunAwayFromWhirlwindAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_melee_dps_run_away_from_boss(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeDpsRunAwayFromBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_destroy_inner_demon(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindDestroyInnerDemonAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_misdirect_boss_to_demon_form_tank(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindMisdirectBossToDemonFormTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_final_phase_assign_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* leotheras_the_blind_manage_dps_wait_timers(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindManageDpsWaitTimersAction(botAI); }
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
static Action* fathom_lord_karathress_main_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressMainTankPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_first_assist_tank_position_caribdis(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressFirstAssistTankPositionCaribdisAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_second_assist_tank_position_sharkkis(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressSecondAssistTankPositionSharkkisAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_third_assist_tank_position_tidalvess(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressThirdAssistTankPositionTidalvessAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_position_caribdis_tank_healer(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressPositionCaribdisTankHealerAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_misdirect_bosses_to_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressMisdirectBossesToTanksAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_assign_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressAssignDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* fathom_lord_karathress_manage_dps_timer(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressManageDpsTimerAction(botAI); }
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
static Action* morogrim_tidewalker_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* morogrim_tidewalker_move_boss_to_tank_position(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerMoveBossToTankPositionAction(botAI); }
|
||||||
|
|
||||||
|
static Action* morogrim_tidewalker_phase_2_reposition_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerPhase2RepositionRangedAction(botAI); }
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
static Action* lady_vashj_main_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjMainTankPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_phase_1_spread_ranged_in_arc(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjPhase1SpreadRangedInArcAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_set_grounding_totem_in_main_tank_group(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjSetGroundingTotemInMainTankGroupAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_static_charge_move_away_from_group(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjStaticChargeMoveAwayFromGroupAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_assign_phase_2_and_phase_3_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjAssignPhase2AndPhase3DpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_misdirect_strider_to_first_assist_tank(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjMisdirectStriderToFirstAssistTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_tank_attack_and_move_away_strider(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjTankAttackAndMoveAwayStriderAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_teleport_to_tainted_elemental(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjTeleportToTaintedElementalAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_loot_tainted_core(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjLootTaintedCoreAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_pass_the_tainted_core(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjPassTheTaintedCoreAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_destroy_tainted_core(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_erase_core_passing_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjEraseCorePassingTrackersAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_avoid_toxic_spores(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); }
|
||||||
|
|
||||||
|
static Action* lady_vashj_use_free_action_abilities(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjUseFreeActionAbilitiesAction(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidSSCTriggers.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
class RaidSSCTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidSSCTriggerContext()
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
creators["serpent shrine cavern bot is not in combat"] =
|
||||||
|
&RaidSSCTriggerContext::serpent_shrine_cavern_bot_is_not_in_combat;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
creators["underbog colossus spawned toxic pool after death"] =
|
||||||
|
&RaidSSCTriggerContext::underbog_colossus_spawned_toxic_pool_after_death;
|
||||||
|
|
||||||
|
creators["greyheart tidecaller water elemental totem spawned"] =
|
||||||
|
&RaidSSCTriggerContext::greyheart_tidecaller_water_elemental_totem_spawned;
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
creators["hydross the unstable bot is frost tank"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_frost_tank;
|
||||||
|
|
||||||
|
creators["hydross the unstable bot is nature tank"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_nature_tank;
|
||||||
|
|
||||||
|
creators["hydross the unstable elementals spawned"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_elementals_spawned;
|
||||||
|
|
||||||
|
creators["hydross the unstable danger from water tombs"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_danger_from_water_tombs;
|
||||||
|
|
||||||
|
creators["hydross the unstable tank needs aggro upon phase change"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_tank_needs_aggro_upon_phase_change;
|
||||||
|
|
||||||
|
creators["hydross the unstable aggro resets upon phase change"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_aggro_resets_upon_phase_change;
|
||||||
|
|
||||||
|
creators["hydross the unstable need to manage timers"] =
|
||||||
|
&RaidSSCTriggerContext::hydross_the_unstable_need_to_manage_timers;
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
creators["the lurker below spout is active"] =
|
||||||
|
&RaidSSCTriggerContext::the_lurker_below_spout_is_active;
|
||||||
|
|
||||||
|
creators["the lurker below boss is active for main tank"] =
|
||||||
|
&RaidSSCTriggerContext::the_lurker_below_boss_is_active_for_main_tank;
|
||||||
|
|
||||||
|
creators["the lurker below boss casts geyser"] =
|
||||||
|
&RaidSSCTriggerContext::the_lurker_below_boss_casts_geyser;
|
||||||
|
|
||||||
|
creators["the lurker below boss is submerged"] =
|
||||||
|
&RaidSSCTriggerContext::the_lurker_below_boss_is_submerged;
|
||||||
|
|
||||||
|
creators["the lurker below need to prepare timer for spout"] =
|
||||||
|
&RaidSSCTriggerContext::the_lurker_below_need_to_prepare_timer_for_spout;
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
creators["leotheras the blind boss is inactive"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_boss_is_inactive;
|
||||||
|
|
||||||
|
creators["leotheras the blind boss transformed into demon form"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_boss_transformed_into_demon_form;
|
||||||
|
|
||||||
|
creators["leotheras the blind only warlock should tank demon form"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_only_warlock_should_tank_demon_form;
|
||||||
|
|
||||||
|
creators["leotheras the blind boss engaged by ranged"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_boss_engaged_by_ranged;
|
||||||
|
|
||||||
|
creators["leotheras the blind boss channeling whirlwind"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_boss_channeling_whirlwind;
|
||||||
|
|
||||||
|
creators["leotheras the blind bot has too many chaos blast stacks"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_bot_has_too_many_chaos_blast_stacks;
|
||||||
|
|
||||||
|
creators["leotheras the blind inner demon has awakened"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_inner_demon_has_awakened;
|
||||||
|
|
||||||
|
creators["leotheras the blind entered final phase"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_entered_final_phase;
|
||||||
|
|
||||||
|
creators["leotheras the blind demon form tank needs aggro"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_demon_form_tank_needs_aggro;
|
||||||
|
|
||||||
|
creators["leotheras the blind boss wipes aggro upon phase change"] =
|
||||||
|
&RaidSSCTriggerContext::leotheras_the_blind_boss_wipes_aggro_upon_phase_change;
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
creators["fathom-lord karathress boss engaged by main tank"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress caribdis engaged by first assist tank"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_engaged_by_first_assist_tank;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress sharkkis engaged by second assist tank"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress tidalvess engaged by third assist tank"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress caribdis tank needs dedicated healer"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_tank_needs_dedicated_healer;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress pulling bosses"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_pulling_bosses;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress determining kill order"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_determining_kill_order;
|
||||||
|
|
||||||
|
creators["fathom-lord karathress tanks need to establish aggro"] =
|
||||||
|
&RaidSSCTriggerContext::fathom_lord_karathress_tanks_need_to_establish_aggro;
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
creators["morogrim tidewalker boss engaged by main tank"] =
|
||||||
|
&RaidSSCTriggerContext::morogrim_tidewalker_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["morogrim tidewalker pulling boss"] =
|
||||||
|
&RaidSSCTriggerContext::morogrim_tidewalker_pulling_boss;
|
||||||
|
|
||||||
|
creators["morogrim tidewalker water globules are incoming"] =
|
||||||
|
&RaidSSCTriggerContext::morogrim_tidewalker_water_globules_are_incoming;
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
creators["lady vashj boss engaged by main tank"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["lady vashj boss engaged by ranged in phase 1"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_ranged_in_phase_1;
|
||||||
|
|
||||||
|
creators["lady vashj casts shock blast on highest aggro"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_casts_shock_blast_on_highest_aggro;
|
||||||
|
|
||||||
|
creators["lady vashj bot has static charge"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_bot_has_static_charge;
|
||||||
|
|
||||||
|
creators["lady vashj pulling boss in phase 1 and phase 3"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_pulling_boss_in_phase_1_and_phase_3;
|
||||||
|
|
||||||
|
creators["lady vashj adds spawn in phase 2 and phase 3"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_adds_spawn_in_phase_2_and_phase_3;
|
||||||
|
|
||||||
|
creators["lady vashj coilfang strider is approaching"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_coilfang_strider_is_approaching;
|
||||||
|
|
||||||
|
creators["lady vashj tainted elemental cheat"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_tainted_elemental_cheat;
|
||||||
|
|
||||||
|
creators["lady vashj tainted core was looted"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_tainted_core_was_looted;
|
||||||
|
|
||||||
|
creators["lady vashj tainted core is unusable"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_tainted_core_is_unusable;
|
||||||
|
|
||||||
|
creators["lady vashj need to reset core passing trackers"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_need_to_reset_core_passing_trackers;
|
||||||
|
|
||||||
|
creators["lady vashj toxic sporebats are spewing poison clouds"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_toxic_sporebats_are_spewing_poison_clouds;
|
||||||
|
|
||||||
|
creators["lady vashj bot is entangled in toxic spores or static charge"] =
|
||||||
|
&RaidSSCTriggerContext::lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// General
|
||||||
|
static Trigger* serpent_shrine_cavern_bot_is_not_in_combat(
|
||||||
|
PlayerbotAI* botAI) { return new SerpentShrineCavernBotIsNotInCombatTrigger(botAI); }
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
static Trigger* underbog_colossus_spawned_toxic_pool_after_death(
|
||||||
|
PlayerbotAI* botAI) { return new UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* greyheart_tidecaller_water_elemental_totem_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new GreyheartTidecallerWaterElementalTotemSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
static Trigger* hydross_the_unstable_bot_is_frost_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsFrostTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_bot_is_nature_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsNatureTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_elementals_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableElementalsSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_danger_from_water_tombs(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableDangerFromWaterTombsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_tank_needs_aggro_upon_phase_change(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_aggro_resets_upon_phase_change(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hydross_the_unstable_need_to_manage_timers(
|
||||||
|
PlayerbotAI* botAI) { return new HydrossTheUnstableNeedToManageTimersTrigger(botAI); }
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
static Trigger* the_lurker_below_spout_is_active(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowSpoutIsActiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_lurker_below_boss_is_active_for_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsActiveForMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_lurker_below_boss_casts_geyser(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowBossCastsGeyserTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_lurker_below_boss_is_submerged(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsSubmergedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_lurker_below_need_to_prepare_timer_for_spout(
|
||||||
|
PlayerbotAI* botAI) { return new TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(botAI); }
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
static Trigger* leotheras_the_blind_boss_is_inactive(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBossIsInactiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_boss_transformed_into_demon_form(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBossTransformedIntoDemonFormTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_only_warlock_should_tank_demon_form(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_boss_engaged_by_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBossEngagedByRangedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_boss_channeling_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBossChannelingWhirlwindTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_bot_has_too_many_chaos_blast_stacks(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_inner_demon_has_awakened(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindInnerDemonHasAwakenedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_entered_final_phase(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindEnteredFinalPhaseTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_demon_form_tank_needs_aggro(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankNeedsAggro(botAI); }
|
||||||
|
|
||||||
|
static Trigger* leotheras_the_blind_boss_wipes_aggro_upon_phase_change(
|
||||||
|
PlayerbotAI* botAI) { return new LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(botAI); }
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
static Trigger* fathom_lord_karathress_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_caribdis_engaged_by_first_assist_tank(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_caribdis_tank_needs_dedicated_healer(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_pulling_bosses(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressPullingBossesTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_determining_kill_order(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressDeterminingKillOrderTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* fathom_lord_karathress_tanks_need_to_establish_aggro(
|
||||||
|
PlayerbotAI* botAI) { return new FathomLordKarathressTanksNeedToEstablishAggroTrigger(botAI); }
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
static Trigger* morogrim_tidewalker_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* morogrim_tidewalker_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* morogrim_tidewalker_water_globules_are_incoming(
|
||||||
|
PlayerbotAI* botAI) { return new MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(botAI); }
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
static Trigger* lady_vashj_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_boss_engaged_by_ranged_in_phase_1(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByRangedInPhase1Trigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_casts_shock_blast_on_highest_aggro(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjCastsShockBlastOnHighestAggroTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_bot_has_static_charge(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjBotHasStaticChargeTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_pulling_boss_in_phase_1_and_phase_3(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjPullingBossInPhase1AndPhase3Trigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_adds_spawn_in_phase_2_and_phase_3(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjAddsSpawnInPhase2AndPhase3Trigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_coilfang_strider_is_approaching(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjCoilfangStriderIsApproachingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_tainted_elemental_cheat(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjTaintedElementalCheatTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_tainted_core_was_looted(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreWasLootedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_tainted_core_is_unusable(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_need_to_reset_core_passing_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjNeedToResetCorePassingTrackersTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge(
|
||||||
|
PlayerbotAI* botAI) { return new LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#include "RaidSSCStrategy.h"
|
||||||
|
#include "RaidSSCMultipliers.h"
|
||||||
|
|
||||||
|
void RaidSSCStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
triggers.push_back(new TriggerNode("serpent shrine cavern bot is not in combat", {
|
||||||
|
NextAction("serpent shrine cavern erase timers and trackers", ACTION_EMERGENCY + 11) }));
|
||||||
|
|
||||||
|
// Trash Mobs
|
||||||
|
triggers.push_back(new TriggerNode("underbog colossus spawned toxic pool after death", {
|
||||||
|
NextAction("underbog colossus escape toxic pool", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("greyheart tidecaller water elemental totem spawned", {
|
||||||
|
NextAction("greyheart tidecaller mark water elemental totem", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable bot is frost tank", {
|
||||||
|
NextAction("hydross the unstable position frost tank", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable bot is nature tank", {
|
||||||
|
NextAction("hydross the unstable position nature tank", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable elementals spawned", {
|
||||||
|
NextAction("hydross the unstable prioritize elemental adds", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable danger from water tombs", {
|
||||||
|
NextAction("hydross the unstable frost phase spread out", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable tank needs aggro upon phase change", {
|
||||||
|
NextAction("hydross the unstable misdirect boss to tank", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable aggro resets upon phase change", {
|
||||||
|
NextAction("hydross the unstable stop dps upon phase change", ACTION_EMERGENCY + 9) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hydross the unstable need to manage timers", {
|
||||||
|
NextAction("hydross the unstable manage timers", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
triggers.push_back(new TriggerNode("the lurker below spout is active", {
|
||||||
|
NextAction("the lurker below run around behind boss", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("the lurker below boss is active for main tank", {
|
||||||
|
NextAction("the lurker below position main tank", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("the lurker below boss casts geyser", {
|
||||||
|
NextAction("the lurker below spread ranged in arc", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("the lurker below boss is submerged", {
|
||||||
|
NextAction("the lurker below tanks pick up adds", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("the lurker below need to prepare timer for spout", {
|
||||||
|
NextAction("the lurker below manage spout timer", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind boss is inactive", {
|
||||||
|
NextAction("leotheras the blind target spellbinders", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind boss transformed into demon form", {
|
||||||
|
NextAction("leotheras the blind demon form tank attack boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind only warlock should tank demon form", {
|
||||||
|
NextAction("leotheras the blind melee tanks don't attack demon form", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind boss engaged by ranged", {
|
||||||
|
NextAction("leotheras the blind position ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind boss channeling whirlwind", {
|
||||||
|
NextAction("leotheras the blind run away from whirlwind", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind bot has too many chaos blast stacks", {
|
||||||
|
NextAction("leotheras the blind melee dps run away from boss", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind inner demon has awakened", {
|
||||||
|
NextAction("leotheras the blind destroy inner demon", ACTION_EMERGENCY + 7) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind entered final phase", {
|
||||||
|
NextAction("leotheras the blind final phase assign dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind demon form tank needs aggro", {
|
||||||
|
NextAction("leotheras the blind misdirect boss to demon form tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("leotheras the blind boss wipes aggro upon phase change", {
|
||||||
|
NextAction("leotheras the blind manage dps wait timers", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress boss engaged by main tank", {
|
||||||
|
NextAction("fathom-lord karathress main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis engaged by first assist tank", {
|
||||||
|
NextAction("fathom-lord karathress first assist tank position caribdis", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress sharkkis engaged by second assist tank", {
|
||||||
|
NextAction("fathom-lord karathress second assist tank position sharkkis", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress tidalvess engaged by third assist tank", {
|
||||||
|
NextAction("fathom-lord karathress third assist tank position tidalvess", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis tank needs dedicated healer", {
|
||||||
|
NextAction("fathom-lord karathress position caribdis tank healer", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress pulling bosses", {
|
||||||
|
NextAction("fathom-lord karathress misdirect bosses to tanks", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress determining kill order", {
|
||||||
|
NextAction("fathom-lord karathress assign dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("fathom-lord karathress tanks need to establish aggro", {
|
||||||
|
NextAction("fathom-lord karathress manage dps timer", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
triggers.push_back(new TriggerNode("morogrim tidewalker boss engaged by main tank", {
|
||||||
|
NextAction("morogrim tidewalker move boss to tank position", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("morogrim tidewalker water globules are incoming", {
|
||||||
|
NextAction("morogrim tidewalker phase 2 reposition ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("morogrim tidewalker pulling boss", {
|
||||||
|
NextAction("morogrim tidewalker misdirect boss to main tank", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj boss engaged by main tank", {
|
||||||
|
NextAction("lady vashj main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj boss engaged by ranged in phase 1", {
|
||||||
|
NextAction("lady vashj phase 1 spread ranged in arc", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj casts shock blast on highest aggro", {
|
||||||
|
NextAction("lady vashj set grounding totem in main tank group", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj bot has static charge", {
|
||||||
|
NextAction("lady vashj static charge move away from group", ACTION_EMERGENCY + 7) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj pulling boss in phase 1 and phase 3", {
|
||||||
|
NextAction("lady vashj misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj tainted elemental cheat", {
|
||||||
|
NextAction("lady vashj teleport to tainted elemental", ACTION_EMERGENCY + 10),
|
||||||
|
NextAction("lady vashj loot tainted core", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj tainted core was looted", {
|
||||||
|
NextAction("lady vashj pass the tainted core", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", {
|
||||||
|
NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj need to reset core passing trackers", {
|
||||||
|
NextAction("lady vashj erase core passing trackers", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj adds spawn in phase 2 and phase 3", {
|
||||||
|
NextAction("lady vashj assign phase 2 and phase 3 dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj coilfang strider is approaching", {
|
||||||
|
NextAction("lady vashj misdirect strider to first assist tank", ACTION_EMERGENCY + 2),
|
||||||
|
NextAction("lady vashj tank attack and move away strider", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj toxic sporebats are spewing poison clouds", {
|
||||||
|
NextAction("lady vashj avoid toxic spores", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("lady vashj bot is entangled in toxic spores or static charge", {
|
||||||
|
NextAction("lady vashj use free action abilities", ACTION_EMERGENCY + 7) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaidSSCStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
// Trash Mobs
|
||||||
|
multipliers.push_back(new UnderbogColossusEscapeToxicPoolMultiplier(botAI));
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
multipliers.push_back(new HydrossTheUnstableDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HydrossTheUnstableWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HydrossTheUnstableControlMisdirectionMultiplier(botAI));
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
multipliers.push_back(new TheLurkerBelowStayAwayFromSpoutMultiplier(botAI));
|
||||||
|
multipliers.push_back(new TheLurkerBelowMaintainRangedSpreadMultiplier(botAI));
|
||||||
|
multipliers.push_back(new TheLurkerBelowDisableTankAssistMultiplier(botAI));
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
multipliers.push_back(new LeotherasTheBlindAvoidWhirlwindMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LeotherasTheBlindDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LeotherasTheBlindFocusOnInnerDemonMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LeotherasTheBlindWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
multipliers.push_back(new FathomLordKarathressDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new FathomLordKarathressDisableAoeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new FathomLordKarathressControlMisdirectionMultiplier(botAI));
|
||||||
|
multipliers.push_back(new FathomLordKarathressWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(botAI));
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
multipliers.push_back(new MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
multipliers.push_back(new MorogrimTidewalkerDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new MorogrimTidewalkerMaintainPhase2StackingMultiplier(botAI));
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjCorePassersPrioritizePositioningMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjDisableAutomaticTargetingAndMovementModifier(botAI));
|
||||||
|
}
|
||||||
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||||
|
#define _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||||
|
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class RaidSSCStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidSSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "ssc"; }
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
#include "RaidSSCTriggers.h"
|
||||||
|
#include "RaidSSCHelpers.h"
|
||||||
|
#include "RaidSSCActions.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "Corpse.h"
|
||||||
|
#include "LootObjectStack.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
using namespace SerpentShrineCavernHelpers;
|
||||||
|
|
||||||
|
// General
|
||||||
|
bool SerpentShrineCavernBotIsNotInCombatTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return !bot->IsInCombat();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trash Mobs
|
||||||
|
|
||||||
|
bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_TOXIC_POOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsDps(bot) &&
|
||||||
|
GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||||
|
botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||||
|
botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
|
if (hydross && hydross->GetHealthPct() < 10.0f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") &&
|
||||||
|
!AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) &&
|
||||||
|
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsRanged(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->getClass() == CLASS_HUNTER &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->getClass() != CLASS_HUNTER &&
|
||||||
|
!botAI->IsHeal(bot) &&
|
||||||
|
!botAI->IsMainTank(bot) &&
|
||||||
|
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||||
|
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
bool TheLurkerBelowSpoutIsActiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||||
|
return it != lurkerSpoutTimer.end() && it->second > now;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||||
|
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||||
|
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TheLurkerBelowBossCastsGeyserTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||||
|
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||||
|
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger will be active only if there are at least 3 tanks in the raid
|
||||||
|
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = nullptr;
|
||||||
|
Player* firstAssistTank = nullptr;
|
||||||
|
Player* secondAssistTank = nullptr;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!mainTank && memberAI->IsMainTank(member))
|
||||||
|
mainTank = member;
|
||||||
|
else if (!firstAssistTank && memberAI->IsAssistTankOfIndex(member, 0, true))
|
||||||
|
firstAssistTank = member;
|
||||||
|
else if (!secondAssistTank && memberAI->IsAssistTankOfIndex(member, 1, true))
|
||||||
|
secondAssistTank = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot == mainTank || bot == firstAssistTank || bot == secondAssistTank;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "the lurker below") &&
|
||||||
|
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBossIsInactiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "greyheart spellbinder");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (GetLeotherasDemonFormTank(bot) != bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetActiveLeotherasDemon(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsRanged(bot) || !botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!GetLeotherasDemonFormTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetPhase2LeotherasDemon(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
|
if (!leotheras)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !leotheras->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||||
|
!leotheras->HasAura(SPELL_WHIRLWIND) &&
|
||||||
|
!leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsTank(bot) && botAI->IsMelee(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
|
if (!leotheras || leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
||||||
|
leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||||
|
if (!chaosBlast || chaosBlast->GetStackAmount() < 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetPhase2LeotherasDemon(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_INSIDIOUS_WHISPER) &&
|
||||||
|
GetLeotherasDemonFormTank(bot) != bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsHeal(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (GetLeotherasDemonFormTank(bot) == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetPhase3LeotherasDemon(botAI) &&
|
||||||
|
GetLeotherasHuman(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "leotheras the blind") &&
|
||||||
|
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||||
|
botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") &&
|
||||||
|
botAI->IsAssistTankOfIndex(bot, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") &&
|
||||||
|
botAI->IsAssistTankOfIndex(bot, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") &&
|
||||||
|
botAI->IsAssistTankOfIndex(bot, 2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||||
|
if (!caribdis)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* firstAssistTank = nullptr;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (botAI->IsAssistTankOfIndex(member, 0, false))
|
||||||
|
{
|
||||||
|
firstAssistTank = member;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstAssistTank;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||||
|
return karathress && karathress->GetHealthPct() > 98.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsHeal(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsDps(bot))
|
||||||
|
return true;
|
||||||
|
else if (botAI->IsAssistTankOfIndex(bot, 0, false))
|
||||||
|
return !AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||||
|
else if (botAI->IsAssistTankOfIndex(bot, 1, false))
|
||||||
|
return !AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis");
|
||||||
|
else if (botAI->IsAssistTankOfIndex(bot, 2, false))
|
||||||
|
return !AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess");
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||||
|
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
bool MorogrimTidewalkerPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||||
|
return tidewalker && tidewalker->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") &&
|
||||||
|
botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||||
|
return tidewalker && tidewalker->GetHealthPct() < 25.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||||
|
!IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsRanged(bot) && IsLadyVashjInPhase1(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_SHAMAN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||||
|
IsLadyVashjInPhase2(botAI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsMainTankInSameSubgroup(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjPullingBossInPhase1AndPhase3Trigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||||
|
if (!vashj)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (vashj->GetHealthPct() <= 100.0f && vashj->GetHealthPct() > 90.0f) ||
|
||||||
|
(!vashj->HasUnitState(UNIT_STATE_ROOT) && vashj->GetHealthPct() <= 50.0f &&
|
||||||
|
vashj->GetHealthPct() > 40.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjAddsSpawnInPhase2AndPhase3Trigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsHeal(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||||
|
!IsLadyVashjInPhase1(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjCoilfangStriderIsApproachingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool taintedPresent = false;
|
||||||
|
Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||||
|
if (taintedUnit)
|
||||||
|
taintedPresent = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||||
|
for (auto const& guid : corpses)
|
||||||
|
{
|
||||||
|
LootObject loot(bot, guid);
|
||||||
|
WorldObject* object = loot.GetWorldObject(bot);
|
||||||
|
if (!object)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Creature* creature = object->ToCreature())
|
||||||
|
{
|
||||||
|
if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||||
|
{
|
||||||
|
taintedPresent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!taintedPresent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (GetDesignatedCoreLooter(group, botAI) == bot &&
|
||||||
|
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||||
|
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||||
|
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||||
|
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||||
|
|
||||||
|
auto hasCore = [](Player* player) -> bool
|
||||||
|
{
|
||||||
|
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bot == designatedLooter)
|
||||||
|
{
|
||||||
|
if (!hasCore(bot))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (bot == firstCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||||
|
hasCore(fourthCorePasser))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (bot == secondCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (bot == thirdCorePasser)
|
||||||
|
{
|
||||||
|
if (hasCore(fourthCorePasser))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (bot != fourthCorePasser)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (AnyRecentCoreInInventory(group, botAI))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// First and second passers move to positions as soon as the elemental appears
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||||
|
(bot == firstCorePasser || bot == secondCorePasser))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||||
|
if (!vashj)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsLadyVashjInPhase2(botAI))
|
||||||
|
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* coreHandlers[] =
|
||||||
|
{
|
||||||
|
GetDesignatedCoreLooter(group, botAI),
|
||||||
|
GetFirstTaintedCorePasser(group, botAI),
|
||||||
|
GetSecondTaintedCorePasser(group, botAI),
|
||||||
|
GetThirdTaintedCorePasser(group, botAI),
|
||||||
|
GetFourthTaintedCorePasser(group, botAI)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||||
|
{
|
||||||
|
for (Player* coreHandler : coreHandlers)
|
||||||
|
{
|
||||||
|
if (coreHandler && bot == coreHandler)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjNeedToResetCorePassingTrackersTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||||
|
if (!vashj || IsLadyVashjInPhase2(botAI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr) ||
|
||||||
|
GetDesignatedCoreLooter(group, botAI) == bot ||
|
||||||
|
GetFirstTaintedCorePasser(group, botAI) == bot ||
|
||||||
|
GetSecondTaintedCorePasser(group, botAI) == bot ||
|
||||||
|
GetThirdTaintedCorePasser(group, botAI) == bot ||
|
||||||
|
GetFourthTaintedCorePasser(group, botAI) == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return IsLadyVashjInPhase3(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->HasAura(SPELL_ENTANGLE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (botAI->IsMelee(member))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
// General
|
||||||
|
|
||||||
|
class SerpentShrineCavernBotIsNotInCombatTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SerpentShrineCavernBotIsNotInCombatTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "serpent shrine cavern bot is not in combat") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class UnderbogColossusSpawnedToxicPoolAfterDeathTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "underbog colossus spawned toxic pool after death") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GreyheartTidecallerWaterElementalTotemSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GreyheartTidecallerWaterElementalTotemSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "greyheart tidecaller water elemental totem spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
class HydrossTheUnstableBotIsFrostTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableBotIsFrostTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is frost tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableBotIsNatureTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableBotIsNatureTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is nature tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableElementalsSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableElementalsSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable elementals spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableDangerFromWaterTombsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableDangerFromWaterTombsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable danger from water tombs") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable tank needs aggro upon phase change") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableAggroResetsUponPhaseChangeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable aggro resets upon phase change") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HydrossTheUnstableNeedToManageTimersTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HydrossTheUnstableNeedToManageTimersTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable need to manage timers") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
class TheLurkerBelowSpoutIsActiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowSpoutIsActiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below spout is active") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowBossIsActiveForMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowBossIsActiveForMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is active for main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowBossCastsGeyserTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowBossCastsGeyserTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss casts geyser") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowBossIsSubmergedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowBossIsSubmergedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is submerged") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheLurkerBelowNeedToPrepareTimerForSpoutTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below need to prepare timer for spout") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
class LeotherasTheBlindBossIsInactiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBossIsInactiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss is inactive") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind only warlock should tank demon form") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindBossTransformedIntoDemonFormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBossTransformedIntoDemonFormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss transformed into demon form") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindBossEngagedByRangedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBossEngagedByRangedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss engaged by ranged") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindBossChannelingWhirlwindTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBossChannelingWhirlwindTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss channeling whirlwind") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind bot has too many chaos blast stacks") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindInnerDemonHasAwakenedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindInnerDemonHasAwakenedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind inner demon has awakened") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindEnteredFinalPhaseTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindEnteredFinalPhaseTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind entered final phase") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindDemonFormTankNeedsAggro : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindDemonFormTankNeedsAggro(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind demon form tank needs aggro") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss wipes aggro upon phase change") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
class FathomLordKarathressBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis engaged by first assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress sharkkis engaged by second assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tidalvess engaged by third assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis tank needs dedicated healer") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressPullingBossesTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressPullingBossesTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress pulling bosses") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressDeterminingKillOrderTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressDeterminingKillOrderTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress determining kill order") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FathomLordKarathressTanksNeedToEstablishAggroTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FathomLordKarathressTanksNeedToEstablishAggroTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tanks need to establish aggro") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
class MorogrimTidewalkerPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MorogrimTidewalkerWaterGlobulesAreIncomingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker water globules are incoming") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
class LadyVashjBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjBossEngagedByRangedInPhase1Trigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjBossEngagedByRangedInPhase1Trigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by ranged in phase 1") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjCastsShockBlastOnHighestAggroTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjCastsShockBlastOnHighestAggroTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj casts shock blast on highest aggro") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjBotHasStaticChargeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjBotHasStaticChargeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot has static charge") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjPullingBossInPhase1AndPhase3Trigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjPullingBossInPhase1AndPhase3Trigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj pulling boss in phase 1 and phase 3") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjAddsSpawnInPhase2AndPhase3Trigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjAddsSpawnInPhase2AndPhase3Trigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj adds spawn in phase 2 and phase 3") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjCoilfangStriderIsApproachingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjCoilfangStriderIsApproachingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj coilfang strider is approaching") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjTaintedElementalCheatTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjTaintedElementalCheatTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted elemental cheat") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjTaintedCoreWasLootedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjTaintedCoreWasLootedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core was looted") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjTaintedCoreIsUnusableTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjTaintedCoreIsUnusableTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core is unusable") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjNeedToResetCorePassingTrackersTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjNeedToResetCorePassingTrackersTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj need to reset core passing trackers") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj toxic sporebats are spewing poison clouds") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot is entangled in toxic spores or static charge") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
#include "RaidSSCHelpers.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "Creature.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
namespace SerpentShrineCavernHelpers
|
||||||
|
{
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
|
const Position HYDROSS_FROST_TANK_POSITION = { -236.669f, -358.352f, -0.828f };
|
||||||
|
const Position HYDROSS_NATURE_TANK_POSITION = { -225.471f, -327.790f, -3.682f };
|
||||||
|
|
||||||
|
std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||||
|
std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||||
|
std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||||
|
std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||||
|
|
||||||
|
bool HasMarkOfHydrossAt100Percent(Player* bot)
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_MARK_OF_HYDROSS_100) ||
|
||||||
|
bot->HasAura(SPELL_MARK_OF_HYDROSS_250) ||
|
||||||
|
bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasNoMarkOfHydross(Player* bot)
|
||||||
|
{
|
||||||
|
return !bot->HasAura(SPELL_MARK_OF_HYDROSS_10) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_HYDROSS_25) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_HYDROSS_50) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_HYDROSS_100) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_HYDROSS_250) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasMarkOfCorruptionAt100Percent(Player* bot)
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) ||
|
||||||
|
bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) ||
|
||||||
|
bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasNoMarkOfCorruption(Player* bot)
|
||||||
|
{
|
||||||
|
return !bot->HasAura(SPELL_MARK_OF_CORRUPTION_10) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_25) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_50) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) &&
|
||||||
|
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
|
||||||
|
const Position LURKER_MAIN_TANK_POSITION = { 23.706f, -406.038f, -19.686f };
|
||||||
|
|
||||||
|
std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||||
|
std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||||
|
|
||||||
|
bool IsLurkerCastingSpout(Unit* lurker)
|
||||||
|
{
|
||||||
|
if (!lurker || !lurker->HasUnitState(UNIT_STATE_CASTING))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Spell* currentSpell = lurker->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
|
if (!currentSpell)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 spellId = currentSpell->m_spellInfo->Id;
|
||||||
|
bool isSpout = spellId == SPELL_SPOUT_VISUAL;
|
||||||
|
|
||||||
|
return isSpout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
|
||||||
|
std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||||
|
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||||
|
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||||
|
|
||||||
|
Unit* GetLeotherasHuman(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
auto const& npcs =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||||
|
for (auto const& guid : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||||
|
unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS))
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
auto const& npcs =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||||
|
for (auto const& guid : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||||
|
unit->HasAura(SPELL_METAMORPHOSIS))
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
auto const& npcs =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||||
|
for (auto const& guid : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS)
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Unit* phase2 = GetPhase2LeotherasDemon(botAI);
|
||||||
|
Unit* phase3 = GetPhase3LeotherasDemon(botAI);
|
||||||
|
return phase2 ? phase2 : phase3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetLeotherasDemonFormTank(Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// (1) First loop: Return the first assistant Warlock (real player or bot)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (2) Fall back to first found bot Warlock
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
member->getClass() != CLASS_WARLOCK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (3) Return nullptr if none found
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
|
const Position KARATHRESS_TANK_POSITION = { 474.403f, -531.118f, -7.548f };
|
||||||
|
const Position TIDALVESS_TANK_POSITION = { 511.282f, -501.162f, -13.158f };
|
||||||
|
const Position SHARKKIS_TANK_POSITION = { 508.057f, -541.109f, -10.133f };
|
||||||
|
const Position CARIBDIS_TANK_POSITION = { 464.462f, -475.820f, -13.158f };
|
||||||
|
const Position CARIBDIS_HEALER_POSITION = { 466.203f, -503.201f, -13.158f };
|
||||||
|
const Position CARIBDIS_RANGED_DPS_POSITION = { 463.197f, -501.190f, -13.158f };
|
||||||
|
|
||||||
|
std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
|
||||||
|
const Position TIDEWALKER_PHASE_1_TANK_POSITION = { 410.925f, -741.916f, -7.146f };
|
||||||
|
const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT = { 407.035f, -759.479f, -7.168f };
|
||||||
|
const Position TIDEWALKER_PHASE_2_TANK_POSITION = { 446.571f, -767.155f, -7.144f };
|
||||||
|
const Position TIDEWALKER_PHASE_2_RANGED_POSITION = { 432.595f, -766.288f, -7.145f };
|
||||||
|
|
||||||
|
std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||||
|
std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
|
||||||
|
const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.985f };
|
||||||
|
|
||||||
|
std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||||
|
std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||||
|
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||||
|
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||||
|
std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||||
|
std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||||
|
|
||||||
|
bool IsMainTankInSameSubgroup(Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group || !group->isRaidGroup())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 botSubGroup = group->GetMemberGroup(bot->GetGUID());
|
||||||
|
if (botSubGroup >= MAX_RAID_SUBGROUPS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || member == bot || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member))
|
||||||
|
{
|
||||||
|
if (memberAI->IsMainTank(member))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLadyVashjInPhase1(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() > 70.0f &&
|
||||||
|
vashjCreature->GetReactState() != REACT_PASSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!unit || !unit->IsAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 entry = unit->GetEntry();
|
||||||
|
|
||||||
|
if (IsLadyVashjInPhase2(botAI))
|
||||||
|
{
|
||||||
|
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||||
|
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER;
|
||||||
|
}
|
||||||
|
else if (IsLadyVashjInPhase3(botAI))
|
||||||
|
{
|
||||||
|
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||||
|
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER ||
|
||||||
|
entry == NPC_TOXIC_SPOREBAT || entry == NPC_LADY_VASHJ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds)
|
||||||
|
{
|
||||||
|
Unit* vashj =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||||
|
if (!vashj)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (group)
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32 instanceId = vashj->GetMap()->GetInstanceId();
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
|
auto it = lastCoreInInventoryTime.find(instanceId);
|
||||||
|
if (it != lastCoreInInventoryTime.end())
|
||||||
|
{
|
||||||
|
if ((now - it->second) <= static_cast<time_t>(graceSeconds))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* leader = nullptr;
|
||||||
|
ObjectGuid leaderGuid = group->GetLeaderGUID();
|
||||||
|
if (!leaderGuid.IsEmpty())
|
||||||
|
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
||||||
|
|
||||||
|
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||||
|
return leader;
|
||||||
|
|
||||||
|
Player* fallback = leader;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == leader)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memberAI->IsMelee(member) && memberAI->IsDps(member))
|
||||||
|
return member;
|
||||||
|
|
||||||
|
if (!fallback && memberAI->IsRangedDps(member))
|
||||||
|
fallback = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback ? fallback : leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == designatedLooter)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
botAI->IsTank(member) || member == designatedLooter)
|
||||||
|
continue;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||||
|
member == firstCorePasser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
botAI->IsTank(member) || member == designatedLooter ||
|
||||||
|
member == firstCorePasser)
|
||||||
|
continue;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||||
|
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||||
|
member == firstCorePasser || member == secondCorePasser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
botAI->IsTank(member) || member == designatedLooter ||
|
||||||
|
member == firstCorePasser || member == secondCorePasser)
|
||||||
|
continue;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||||
|
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||||
|
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||||
|
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||||
|
member == firstCorePasser || member == secondCorePasser ||
|
||||||
|
member == thirdCorePasser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
|
if (!memberAI)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||||
|
botAI->IsTank(member) || member == designatedLooter ||
|
||||||
|
member == firstCorePasser || member == secondCorePasser ||
|
||||||
|
member == thirdCorePasser)
|
||||||
|
continue;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS =
|
||||||
|
{
|
||||||
|
47482, // NW
|
||||||
|
47483, // NE
|
||||||
|
47484, // SE
|
||||||
|
47485 // SW
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the positions of all active Shield Generators by their database GUIDs
|
||||||
|
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||||
|
Map* map, const std::vector<uint32>& generatorDbGuids)
|
||||||
|
{
|
||||||
|
std::vector<GeneratorInfo> generators;
|
||||||
|
if (!map)
|
||||||
|
return generators;
|
||||||
|
|
||||||
|
for (uint32 dbGuid : generatorDbGuids)
|
||||||
|
{
|
||||||
|
auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid);
|
||||||
|
if (bounds.first == bounds.second)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GameObject* go = bounds.first->second;
|
||||||
|
if (!go)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (go->GetGoState() != GO_STATE_READY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GeneratorInfo info;
|
||||||
|
info.guid = go->GetGUID();
|
||||||
|
info.x = go->GetPositionX();
|
||||||
|
info.y = go->GetPositionY();
|
||||||
|
info.z = go->GetPositionZ();
|
||||||
|
generators.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generators;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the nearest active Shield Generator to the bot
|
||||||
|
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
||||||
|
// which depawn after use
|
||||||
|
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
||||||
|
{
|
||||||
|
if (!reference)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::list<Creature*> triggers;
|
||||||
|
constexpr float searchRange = 150.0f;
|
||||||
|
reference->GetCreatureListWithEntryInGrid(
|
||||||
|
triggers, NPC_WORLD_INVISIBLE_TRIGGER, searchRange);
|
||||||
|
|
||||||
|
Creature* nearest = nullptr;
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
for (Creature* creature : triggers)
|
||||||
|
{
|
||||||
|
if (!creature->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = reference->GetDistance(creature);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
nearest = creature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||||
|
Player* bot, const std::vector<GeneratorInfo>& generators)
|
||||||
|
{
|
||||||
|
if (!bot || generators.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const GeneratorInfo* nearest = nullptr;
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
for (auto const& gen : generators)
|
||||||
|
{
|
||||||
|
float dist = bot->GetExactDist(gen.x, gen.y, gen.z);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
nearest = &gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
}
|
||||||
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||||
|
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
namespace SerpentShrineCavernHelpers
|
||||||
|
{
|
||||||
|
enum SerpentShrineCavernSpells
|
||||||
|
{
|
||||||
|
// Trash Mobs
|
||||||
|
SPELL_TOXIC_POOL = 38718,
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
SPELL_MARK_OF_HYDROSS_10 = 38215,
|
||||||
|
SPELL_MARK_OF_HYDROSS_25 = 38216,
|
||||||
|
SPELL_MARK_OF_HYDROSS_50 = 38217,
|
||||||
|
SPELL_MARK_OF_HYDROSS_100 = 38218,
|
||||||
|
SPELL_MARK_OF_HYDROSS_250 = 38231,
|
||||||
|
SPELL_MARK_OF_HYDROSS_500 = 40584,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_10 = 38219,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_25 = 38220,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_50 = 38221,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_100 = 38222,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_250 = 38230,
|
||||||
|
SPELL_MARK_OF_CORRUPTION_500 = 40583,
|
||||||
|
SPELL_CORRUPTION = 37961,
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
SPELL_SPOUT_VISUAL = 37431,
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
SPELL_LEOTHERAS_BANISHED = 37546,
|
||||||
|
SPELL_WHIRLWIND = 37640,
|
||||||
|
SPELL_WHIRLWIND_CHANNEL = 37641,
|
||||||
|
SPELL_METAMORPHOSIS = 37673,
|
||||||
|
SPELL_CHAOS_BLAST = 37675,
|
||||||
|
SPELL_INSIDIOUS_WHISPER = 37676,
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
SPELL_FEAR_WARD = 6346,
|
||||||
|
SPELL_POISON_BOLT = 38253,
|
||||||
|
SPELL_STATIC_CHARGE = 38280,
|
||||||
|
SPELL_ENTANGLE = 38316,
|
||||||
|
|
||||||
|
// Druid
|
||||||
|
SPELL_CAT_FORM = 768,
|
||||||
|
SPELL_BEAR_FORM = 5487,
|
||||||
|
SPELL_DIRE_BEAR_FORM = 9634,
|
||||||
|
SPELL_TREE_OF_LIFE = 33891,
|
||||||
|
|
||||||
|
// Hunter
|
||||||
|
SPELL_MISDIRECTION = 35079,
|
||||||
|
|
||||||
|
// Mage
|
||||||
|
SPELL_SLOW = 31589,
|
||||||
|
|
||||||
|
// Shaman
|
||||||
|
SPELL_GROUNDING_TOTEM_EFFECT = 8178,
|
||||||
|
|
||||||
|
// Warlock
|
||||||
|
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
||||||
|
|
||||||
|
// Item
|
||||||
|
SPELL_HEAVY_NETHERWEAVE_NET = 31368,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SerpentShrineCavernNPCs
|
||||||
|
{
|
||||||
|
// Trash Mobs
|
||||||
|
NPC_WATER_ELEMENTAL_TOTEM = 22236,
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
NPC_PURE_SPAWN_OF_HYDROSS = 22035,
|
||||||
|
NPC_TAINTED_SPAWN_OF_HYDROSS = 22036,
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
NPC_COILFANG_GUARDIAN = 21873,
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
NPC_LEOTHERAS_THE_BLIND = 21215,
|
||||||
|
NPC_GREYHEART_SPELLBINDER = 21806,
|
||||||
|
NPC_INNER_DEMON = 21857,
|
||||||
|
NPC_SHADOW_OF_LEOTHERAS = 21875,
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
NPC_SPITFIRE_TOTEM = 22091,
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
NPC_WORLD_INVISIBLE_TRIGGER = 12999,
|
||||||
|
NPC_LADY_VASHJ = 21212,
|
||||||
|
NPC_ENCHANTED_ELEMENTAL = 21958,
|
||||||
|
NPC_TAINTED_ELEMENTAL = 22009,
|
||||||
|
NPC_COILFANG_ELITE = 22055,
|
||||||
|
NPC_COILFANG_STRIDER = 22056,
|
||||||
|
NPC_TOXIC_SPOREBAT = 22140,
|
||||||
|
NPC_SPORE_DROP_TRIGGER = 22207,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SerpentShrineCavernItems
|
||||||
|
{
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
ITEM_TAINTED_CORE = 31088,
|
||||||
|
|
||||||
|
// Tailoring
|
||||||
|
ITEM_HEAVY_NETHERWEAVE_NET = 24269,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint32 SSC_MAP_ID = 548;
|
||||||
|
|
||||||
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
extern const Position HYDROSS_FROST_TANK_POSITION;
|
||||||
|
extern const Position HYDROSS_NATURE_TANK_POSITION;
|
||||||
|
extern std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||||
|
extern std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||||
|
extern std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||||
|
extern std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||||
|
bool HasMarkOfHydrossAt100Percent(Player* bot);
|
||||||
|
bool HasNoMarkOfHydross(Player* bot);
|
||||||
|
bool HasMarkOfCorruptionAt100Percent(Player* bot);
|
||||||
|
bool HasNoMarkOfCorruption(Player* bot);
|
||||||
|
|
||||||
|
// The Lurker Below
|
||||||
|
extern const Position LURKER_MAIN_TANK_POSITION;
|
||||||
|
extern std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||||
|
extern std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||||
|
bool IsLurkerCastingSpout(Unit* lurker);
|
||||||
|
|
||||||
|
// Leotheras the Blind
|
||||||
|
extern std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||||
|
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||||
|
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||||
|
Unit* GetLeotherasHuman(PlayerbotAI* botAI);
|
||||||
|
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI);
|
||||||
|
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI);
|
||||||
|
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI);
|
||||||
|
Player* GetLeotherasDemonFormTank(Player* bot);
|
||||||
|
|
||||||
|
// Fathom-Lord Karathress
|
||||||
|
extern const Position KARATHRESS_TANK_POSITION;
|
||||||
|
extern const Position TIDALVESS_TANK_POSITION;
|
||||||
|
extern const Position SHARKKIS_TANK_POSITION;
|
||||||
|
extern const Position CARIBDIS_TANK_POSITION;
|
||||||
|
extern const Position CARIBDIS_HEALER_POSITION;
|
||||||
|
extern const Position CARIBDIS_RANGED_DPS_POSITION;
|
||||||
|
extern std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||||
|
|
||||||
|
// Morogrim Tidewalker
|
||||||
|
extern const Position TIDEWALKER_PHASE_1_TANK_POSITION;
|
||||||
|
extern const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT;
|
||||||
|
extern const Position TIDEWALKER_PHASE_2_TANK_POSITION;
|
||||||
|
extern const Position TIDEWALKER_PHASE_2_RANGED_POSITION;
|
||||||
|
extern std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||||
|
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||||
|
|
||||||
|
// Lady Vashj <Coilfang Matron>
|
||||||
|
constexpr float VASHJ_PLATFORM_Z = 42.985f;
|
||||||
|
extern const Position VASHJ_PLATFORM_CENTER_POSITION;
|
||||||
|
extern std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||||
|
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||||
|
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||||
|
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||||
|
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||||
|
extern std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||||
|
bool IsMainTankInSameSubgroup(Player* bot);
|
||||||
|
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
||||||
|
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
||||||
|
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
||||||
|
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
||||||
|
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3);
|
||||||
|
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI);
|
||||||
|
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||||
|
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||||
|
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||||
|
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||||
|
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
||||||
|
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
||||||
|
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||||
|
Map* map, const std::vector<uint32>& generatorDbGuids);
|
||||||
|
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference);
|
||||||
|
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||||
|
Player* bot, const std::vector<GeneratorInfo>& generators);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
#include "ObjectDefines.h"
|
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
@@ -19,11 +18,9 @@
|
|||||||
#include "Position.h"
|
#include "Position.h"
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "RaidUlduarScripts.h"
|
#include "RaidUlduarScripts.h"
|
||||||
#include "RaidUlduarStrategy.h"
|
|
||||||
#include "RtiValue.h"
|
#include "RtiValue.h"
|
||||||
#include "ScriptedCreature.h"
|
#include "ScriptedCreature.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "SharedDefines.h"
|
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include <RtiTargetValue.h>
|
#include <RtiTargetValue.h>
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#include "RaidUlduarMultipliers.h"
|
|
||||||
|
|
||||||
#include "ChooseTargetActions.h"
|
|
||||||
#include "DKActions.h"
|
|
||||||
#include "DruidActions.h"
|
|
||||||
#include "DruidBearActions.h"
|
|
||||||
#include "FollowActions.h"
|
|
||||||
#include "GenericActions.h"
|
|
||||||
#include "GenericSpellActions.h"
|
|
||||||
#include "HunterActions.h"
|
|
||||||
#include "MageActions.h"
|
|
||||||
#include "MovementActions.h"
|
|
||||||
#include "PaladinActions.h"
|
|
||||||
#include "PriestActions.h"
|
|
||||||
#include "RaidUlduarActions.h"
|
|
||||||
#include "ReachTargetActions.h"
|
|
||||||
#include "RogueActions.h"
|
|
||||||
#include "ScriptedCreature.h"
|
|
||||||
#include "ShamanActions.h"
|
|
||||||
#include "UseMeetingStoneAction.h"
|
|
||||||
#include "WarriorActions.h"
|
|
||||||
|
|
||||||
float FlameLeviathanMultiplier::GetValue(Action* action)
|
|
||||||
{
|
|
||||||
// if (dynamic_cast<FleeAction*>(action))
|
|
||||||
// return 0.0f;
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
#ifndef _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
|
||||||
#define _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
|
||||||
|
|
||||||
#include "Multiplier.h"
|
|
||||||
#include "Ai/Raid/Ulduar/RaidUlduarBossHelper.h"
|
|
||||||
|
|
||||||
class FlameLeviathanMultiplier : public Multiplier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FlameLeviathanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flame leviathan") {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual float GetValue(Action* action);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
|
||||||
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include <cmath>
|
|
||||||
#include <ctime>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#include "AiObject.h"
|
|
||||||
#include "AiObjectContext.h"
|
|
||||||
#include "EventMap.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "ObjectGuid.h"
|
|
||||||
#include "Player.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "ScriptedCreature.h"
|
|
||||||
#include "SharedDefines.h"
|
|
||||||
|
|
||||||
const uint32 ULDUAR_MAP_ID = 603;
|
|
||||||
|
|
||||||
class RazorscaleBossHelper : public AiObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Enums and constants specific to Razorscale
|
|
||||||
enum RazorscaleUnits : uint32
|
|
||||||
{
|
|
||||||
UNIT_RAZORSCALE = 33186,
|
|
||||||
UNIT_DARK_RUNE_SENTINEL = 33846,
|
|
||||||
UNIT_DARK_RUNE_WATCHER = 33453,
|
|
||||||
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
|
||||||
UNIT_DEVOURING_FLAME = 34188,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RazorscaleGameObjects : uint32
|
|
||||||
{
|
|
||||||
GO_RAZORSCALE_HARPOON_1 = 194519,
|
|
||||||
GO_RAZORSCALE_HARPOON_2 = 194541,
|
|
||||||
GO_RAZORSCALE_HARPOON_3 = 194542,
|
|
||||||
GO_RAZORSCALE_HARPOON_4 = 194543,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RazorscaleSpells : uint32
|
|
||||||
{
|
|
||||||
SPELL_CHAIN_1 = 49679,
|
|
||||||
SPELL_CHAIN_2 = 49682,
|
|
||||||
SPELL_CHAIN_3 = 49683,
|
|
||||||
SPELL_CHAIN_4 = 49684,
|
|
||||||
SPELL_SENTINEL_WHIRLWIND = 63806,
|
|
||||||
SPELL_STUN_AURA = 62794,
|
|
||||||
SPELL_FUSEARMOR = 64771
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
|
||||||
|
|
||||||
// Constants for arena parameters
|
|
||||||
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
|
||||||
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
|
||||||
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
|
||||||
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
|
||||||
|
|
||||||
// Harpoon cooldown (seconds)
|
|
||||||
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
|
||||||
|
|
||||||
// Structure for harpoon data
|
|
||||||
struct HarpoonData
|
|
||||||
{
|
|
||||||
uint32 gameObjectEntry;
|
|
||||||
uint32 chainSpellId;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
|
||||||
: AiObject(botAI), _boss(nullptr) {}
|
|
||||||
|
|
||||||
bool UpdateBossAI();
|
|
||||||
Unit* GetBoss() const;
|
|
||||||
|
|
||||||
bool IsGroundPhase() const;
|
|
||||||
bool IsFlyingPhase() const;
|
|
||||||
|
|
||||||
bool IsHarpoonFired(uint32 chainSpellId) const;
|
|
||||||
static bool IsHarpoonReady(GameObject* harpoonGO);
|
|
||||||
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
|
||||||
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
|
||||||
|
|
||||||
static const std::vector<HarpoonData>& GetHarpoonData();
|
|
||||||
|
|
||||||
void AssignRolesBasedOnHealth();
|
|
||||||
bool AreRolesAssigned() const;
|
|
||||||
bool CanSwapRoles() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Unit* _boss;
|
|
||||||
|
|
||||||
// A map to track the last role swap *per bot* by their GUID
|
|
||||||
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
|
||||||
|
|
||||||
// The cooldown that applies to every bot
|
|
||||||
static const std::time_t _roleSwapCooldown = 10;
|
|
||||||
|
|
||||||
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
|
||||||
};
|
|
||||||
|
|
||||||
// template <class BossAiType>
|
|
||||||
// class GenericBossHelper : public AiObject
|
|
||||||
// {
|
|
||||||
// public:
|
|
||||||
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
|
||||||
// virtual bool UpdateBossAI()
|
|
||||||
// {
|
|
||||||
// if (!bot->IsInCombat())
|
|
||||||
// {
|
|
||||||
// _unit = nullptr;
|
|
||||||
// }
|
|
||||||
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
|
||||||
// {
|
|
||||||
// _unit = nullptr;
|
|
||||||
// }
|
|
||||||
// if (!_unit)
|
|
||||||
// {
|
|
||||||
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
|
||||||
// if (!_unit)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// _target = _unit->ToCreature();
|
|
||||||
// if (!_target)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
|
||||||
// if (!_ai)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// _event_map = &_ai->events;
|
|
||||||
// if (!_event_map)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (!_event_map)
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// _timer = _event_map->GetTimer();
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// virtual void Reset()
|
|
||||||
// {
|
|
||||||
// _unit = nullptr;
|
|
||||||
// _target = nullptr;
|
|
||||||
// _ai = nullptr;
|
|
||||||
// _event_map = nullptr;
|
|
||||||
// _timer = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected:
|
|
||||||
// std::string _name;
|
|
||||||
// Unit* _unit = nullptr;
|
|
||||||
// Creature* _target = nullptr;
|
|
||||||
// BossAiType* _ai = nullptr;
|
|
||||||
// EventMap* _event_map = nullptr;
|
|
||||||
// uint32 _timer = 0;
|
|
||||||
// };
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "RaidUlduarStrategy.h"
|
#include "RaidUlduarStrategy.h"
|
||||||
|
|
||||||
#include "RaidUlduarMultipliers.h"
|
|
||||||
|
|
||||||
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@@ -316,8 +314,3 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
"yogg-saron phase 3 positioning trigger",
|
"yogg-saron phase 3 positioning trigger",
|
||||||
{ NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) }));
|
{ NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
|
||||||
{
|
|
||||||
multipliers.push_back(new FlameLeviathanMultiplier(botAI));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "Multiplier.h"
|
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
|
|
||||||
class RaidUlduarStrategy : public Strategy
|
class RaidUlduarStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||||
virtual std::string const getName() override { return "uld"; }
|
virtual std::string const getName() override { return "ulduar"; }
|
||||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1634,7 +1634,7 @@ bool VezaxShadowCrashTrigger::IsActive()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return botAI->HasAura(SPELL_SHADOW_CRASH, bot);
|
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
||||||
|
|||||||
@@ -3,187 +3,9 @@
|
|||||||
|
|
||||||
#include "EventMap.h"
|
#include "EventMap.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "Trigger.h"
|
#include "Trigger.h"
|
||||||
|
|
||||||
enum UlduarIDs
|
|
||||||
{
|
|
||||||
// Iron Assembly
|
|
||||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
|
||||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
|
||||||
SPELL_OVERLOAD_10_MAN = 61869,
|
|
||||||
SPELL_OVERLOAD_25_MAN = 63481,
|
|
||||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
|
||||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
|
||||||
SPELL_RUNE_OF_POWER = 64320,
|
|
||||||
|
|
||||||
// Kologarn
|
|
||||||
NPC_RIGHT_ARM = 32934,
|
|
||||||
NPC_RUBBLE = 33768,
|
|
||||||
SPELL_CRUNCH_ARMOR = 64002,
|
|
||||||
|
|
||||||
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
|
||||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
|
||||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
|
||||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
|
||||||
|
|
||||||
// Hodir
|
|
||||||
NPC_SNOWPACKED_ICICLE = 33174,
|
|
||||||
NPC_TOASTY_FIRE = 33342,
|
|
||||||
SPELL_FLASH_FREEZE = 61968,
|
|
||||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
|
||||||
|
|
||||||
// Freya
|
|
||||||
NPC_SNAPLASHER = 32916,
|
|
||||||
NPC_STORM_LASHER = 32919,
|
|
||||||
NPC_DETONATING_LASHER = 32918,
|
|
||||||
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
|
||||||
NPC_ANCIENT_CONSERVATOR = 33203,
|
|
||||||
NPC_HEALTHY_SPORE = 33215,
|
|
||||||
NPC_EONARS_GIFT = 33228,
|
|
||||||
GOBJECT_NATURE_BOMB = 194902,
|
|
||||||
|
|
||||||
// Thorim
|
|
||||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
|
||||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
|
||||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
|
||||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
|
||||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
|
||||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
|
||||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
|
||||||
NPC_DARK_RUNE_EVOKER = 32878,
|
|
||||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
|
||||||
NPC_DARK_RUNE_COMMONER = 32904,
|
|
||||||
NPC_IRON_RING_GUARD = 32874,
|
|
||||||
NPC_RUNIC_COLOSSUS = 32872,
|
|
||||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
|
||||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
|
||||||
NPC_IRON_HONOR_GUARD = 32875,
|
|
||||||
SPELL_UNBALANCING_STRIKE = 62130,
|
|
||||||
|
|
||||||
// Mimiron
|
|
||||||
NPC_LEVIATHAN_MKII = 33432,
|
|
||||||
NPC_VX001 = 33651,
|
|
||||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
|
||||||
NPC_BOMB_BOT = 33836,
|
|
||||||
NPC_ROCKET_STRIKE_N = 34047,
|
|
||||||
NPC_ASSAULT_BOT = 34057,
|
|
||||||
NPC_PROXIMITY_MINE = 34362,
|
|
||||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
|
||||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
|
||||||
SPELL_SPINNING_UP = 63414,
|
|
||||||
SPELL_SHOCK_BLAST = 63631,
|
|
||||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
|
||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
|
||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
|
||||||
|
|
||||||
// General Vezax
|
|
||||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
|
||||||
SPELL_SHADOW_CRASH = 63277,
|
|
||||||
|
|
||||||
// Yogg-Saron
|
|
||||||
ACTION_ILLUSION_DRAGONS = 1,
|
|
||||||
ACTION_ILLUSION_ICECROWN = 2,
|
|
||||||
ACTION_ILLUSION_STORMWIND = 3,
|
|
||||||
NPC_GUARDIAN_OF_YS = 33136,
|
|
||||||
NPC_YOGG_SARON = 33288,
|
|
||||||
NPC_OMINOUS_CLOUD = 33292,
|
|
||||||
NPC_RUBY_CONSORT = 33716,
|
|
||||||
NPC_AZURE_CONSORT = 33717,
|
|
||||||
NPC_BRONZE_CONSORT = 33718,
|
|
||||||
NPC_EMERALD_CONSORT = 33719,
|
|
||||||
NPC_OBSIDIAN_CONSORT = 33720,
|
|
||||||
NPC_ALEXTRASZA = 33536,
|
|
||||||
NPC_MALYGOS_ILLUSION = 33535,
|
|
||||||
NPC_NELTHARION = 33523,
|
|
||||||
NPC_YSERA = 33495,
|
|
||||||
GO_DRAGON_SOUL = 194462,
|
|
||||||
NPC_SARA_PHASE_1 = 33134,
|
|
||||||
NPC_LICH_KING_ILLUSION = 33441,
|
|
||||||
NPC_IMMOLATED_CHAMPION = 33442,
|
|
||||||
NPC_SUIT_OF_ARMOR = 33433,
|
|
||||||
NPC_GARONA = 33436,
|
|
||||||
NPC_KING_LLANE = 33437,
|
|
||||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
|
||||||
NPC_INFLUENCE_TENTACLE = 33943,
|
|
||||||
NPC_DEATH_ORB = 33882,
|
|
||||||
NPC_BRAIN = 33890,
|
|
||||||
NPC_CRUSHER_TENTACLE = 33966,
|
|
||||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
|
||||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
|
||||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
|
||||||
NPC_LAUGHING_SKULL = 33990,
|
|
||||||
NPC_SANITY_WELL = 33991,
|
|
||||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
|
||||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
|
||||||
SPELL_SANITY = 63050,
|
|
||||||
SPELL_BRAIN_LINK = 63802,
|
|
||||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
|
||||||
SPELL_SHADOW_BARRIER = 63894,
|
|
||||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
|
||||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
|
||||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
|
||||||
SPELL_TELEPORT_BACK = 63992,
|
|
||||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
|
||||||
SPELL_INDUCE_MADNESS = 64059,
|
|
||||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
|
||||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
|
||||||
|
|
||||||
// Buffs
|
|
||||||
SPELL_FROST_TRAP = 13809
|
|
||||||
};
|
|
||||||
|
|
||||||
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
|
||||||
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
|
||||||
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
|
||||||
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
|
||||||
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
|
||||||
|
|
||||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
|
||||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
|
||||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
|
||||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
|
||||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
|
||||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
|
||||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
|
||||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
|
||||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Flame Levi
|
// Flame Levi
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include "ChatHelper.h"
|
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
@@ -9,6 +8,44 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
|
|
||||||
|
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||||
|
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||||
|
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||||
|
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||||
|
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||||
|
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||||
|
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||||
|
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
||||||
|
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
||||||
|
|
||||||
// Prevent harpoon spam
|
// Prevent harpoon spam
|
||||||
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
||||||
// Prevent role assignment spam
|
// Prevent role assignment spam
|
||||||
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||||
|
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "EventMap.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ScriptedCreature.h"
|
||||||
|
|
||||||
|
constexpr uint32 ULDUAR_MAP_ID = 603;
|
||||||
|
|
||||||
|
enum UlduarIDs
|
||||||
|
{
|
||||||
|
// Iron Assembly
|
||||||
|
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||||
|
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||||
|
SPELL_OVERLOAD_10_MAN = 61869,
|
||||||
|
SPELL_OVERLOAD_25_MAN = 63481,
|
||||||
|
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||||
|
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||||
|
SPELL_RUNE_OF_POWER = 64320,
|
||||||
|
|
||||||
|
// Kologarn
|
||||||
|
NPC_RIGHT_ARM = 32934,
|
||||||
|
NPC_RUBBLE = 33768,
|
||||||
|
SPELL_CRUNCH_ARMOR = 64002,
|
||||||
|
|
||||||
|
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
||||||
|
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||||
|
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||||
|
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||||
|
|
||||||
|
// Hodir
|
||||||
|
NPC_SNOWPACKED_ICICLE = 33174,
|
||||||
|
NPC_TOASTY_FIRE = 33342,
|
||||||
|
SPELL_FLASH_FREEZE = 61968,
|
||||||
|
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||||
|
|
||||||
|
// Freya
|
||||||
|
NPC_SNAPLASHER = 32916,
|
||||||
|
NPC_STORM_LASHER = 32919,
|
||||||
|
NPC_DETONATING_LASHER = 32918,
|
||||||
|
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
||||||
|
NPC_ANCIENT_CONSERVATOR = 33203,
|
||||||
|
NPC_HEALTHY_SPORE = 33215,
|
||||||
|
NPC_EONARS_GIFT = 33228,
|
||||||
|
GOBJECT_NATURE_BOMB = 194902,
|
||||||
|
|
||||||
|
// Thorim
|
||||||
|
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||||
|
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||||
|
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||||
|
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||||
|
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||||
|
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||||
|
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||||
|
NPC_DARK_RUNE_EVOKER = 32878,
|
||||||
|
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||||
|
NPC_DARK_RUNE_COMMONER = 32904,
|
||||||
|
NPC_IRON_RING_GUARD = 32874,
|
||||||
|
NPC_RUNIC_COLOSSUS = 32872,
|
||||||
|
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||||
|
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||||
|
NPC_IRON_HONOR_GUARD = 32875,
|
||||||
|
SPELL_UNBALANCING_STRIKE = 62130,
|
||||||
|
|
||||||
|
// Mimiron
|
||||||
|
NPC_LEVIATHAN_MKII = 33432,
|
||||||
|
NPC_VX001 = 33651,
|
||||||
|
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||||
|
NPC_BOMB_BOT = 33836,
|
||||||
|
NPC_ROCKET_STRIKE_N = 34047,
|
||||||
|
NPC_ASSAULT_BOT = 34057,
|
||||||
|
NPC_PROXIMITY_MINE = 34362,
|
||||||
|
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||||
|
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||||
|
SPELL_SPINNING_UP = 63414,
|
||||||
|
SPELL_SHOCK_BLAST = 63631,
|
||||||
|
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||||
|
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||||
|
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||||
|
|
||||||
|
// General Vezax
|
||||||
|
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||||
|
SPELL_VEZAX_SHADOW_CRASH = 63277,
|
||||||
|
|
||||||
|
// Yogg-Saron
|
||||||
|
ACTION_ILLUSION_DRAGONS = 1,
|
||||||
|
ACTION_ILLUSION_ICECROWN = 2,
|
||||||
|
ACTION_ILLUSION_STORMWIND = 3,
|
||||||
|
NPC_GUARDIAN_OF_YS = 33136,
|
||||||
|
NPC_YOGG_SARON = 33288,
|
||||||
|
NPC_OMINOUS_CLOUD = 33292,
|
||||||
|
NPC_RUBY_CONSORT = 33716,
|
||||||
|
NPC_AZURE_CONSORT = 33717,
|
||||||
|
NPC_BRONZE_CONSORT = 33718,
|
||||||
|
NPC_EMERALD_CONSORT = 33719,
|
||||||
|
NPC_OBSIDIAN_CONSORT = 33720,
|
||||||
|
NPC_ALEXTRASZA = 33536,
|
||||||
|
NPC_MALYGOS_ILLUSION = 33535,
|
||||||
|
NPC_NELTHARION = 33523,
|
||||||
|
NPC_YSERA = 33495,
|
||||||
|
GO_DRAGON_SOUL = 194462,
|
||||||
|
NPC_SARA_PHASE_1 = 33134,
|
||||||
|
NPC_LICH_KING_ILLUSION = 33441,
|
||||||
|
NPC_IMMOLATED_CHAMPION = 33442,
|
||||||
|
NPC_SUIT_OF_ARMOR = 33433,
|
||||||
|
NPC_GARONA = 33436,
|
||||||
|
NPC_KING_LLANE = 33437,
|
||||||
|
NPC_DEATHSWORN_ZEALOT = 33567,
|
||||||
|
NPC_INFLUENCE_TENTACLE = 33943,
|
||||||
|
NPC_DEATH_ORB = 33882,
|
||||||
|
NPC_BRAIN = 33890,
|
||||||
|
NPC_CRUSHER_TENTACLE = 33966,
|
||||||
|
NPC_CONSTRICTOR_TENTACLE = 33983,
|
||||||
|
NPC_CORRUPTOR_TENTACLE = 33985,
|
||||||
|
NPC_IMMORTAL_GUARDIAN = 33988,
|
||||||
|
NPC_LAUGHING_SKULL = 33990,
|
||||||
|
NPC_SANITY_WELL = 33991,
|
||||||
|
NPC_DESCEND_INTO_MADNESS = 34072,
|
||||||
|
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
||||||
|
SPELL_SANITY = 63050,
|
||||||
|
SPELL_BRAIN_LINK = 63802,
|
||||||
|
SPELL_MALADY_OF_THE_MIND = 63830,
|
||||||
|
SPELL_SHADOW_BARRIER = 63894,
|
||||||
|
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
||||||
|
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
||||||
|
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
||||||
|
SPELL_TELEPORT_BACK = 63992,
|
||||||
|
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
||||||
|
SPELL_INDUCE_MADNESS = 64059,
|
||||||
|
SPELL_LUNATIC_GAZE_YS = 64163,
|
||||||
|
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
||||||
|
|
||||||
|
// Buffs
|
||||||
|
SPELL_FROST_TRAP = 13809
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||||
|
constexpr float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||||
|
constexpr float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||||
|
constexpr float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||||
|
constexpr float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
||||||
|
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
||||||
|
|
||||||
|
extern const Position ULDUAR_THORIM_NEAR_ARENA_CENTER;
|
||||||
|
extern const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2;
|
||||||
|
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3;
|
||||||
|
extern const Position ULDUAR_THORIM_JUMP_END_POINT;
|
||||||
|
extern const Position ULDUAR_THORIM_PHASE2_TANK_SPOT;
|
||||||
|
extern const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT;
|
||||||
|
extern const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT;
|
||||||
|
extern const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT;
|
||||||
|
extern const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT;
|
||||||
|
extern const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_MIDDLE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT;
|
||||||
|
extern const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT;
|
||||||
|
|
||||||
|
class RazorscaleBossHelper : public AiObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Enums and constants specific to Razorscale
|
||||||
|
enum RazorscaleUnits : uint32
|
||||||
|
{
|
||||||
|
UNIT_RAZORSCALE = 33186,
|
||||||
|
UNIT_DARK_RUNE_SENTINEL = 33846,
|
||||||
|
UNIT_DARK_RUNE_WATCHER = 33453,
|
||||||
|
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
||||||
|
UNIT_DEVOURING_FLAME = 34188,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RazorscaleGameObjects : uint32
|
||||||
|
{
|
||||||
|
GO_RAZORSCALE_HARPOON_1 = 194519,
|
||||||
|
GO_RAZORSCALE_HARPOON_2 = 194541,
|
||||||
|
GO_RAZORSCALE_HARPOON_3 = 194542,
|
||||||
|
GO_RAZORSCALE_HARPOON_4 = 194543,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RazorscaleSpells : uint32
|
||||||
|
{
|
||||||
|
SPELL_CHAIN_1 = 49679,
|
||||||
|
SPELL_CHAIN_2 = 49682,
|
||||||
|
SPELL_CHAIN_3 = 49683,
|
||||||
|
SPELL_CHAIN_4 = 49684,
|
||||||
|
SPELL_SENTINEL_WHIRLWIND = 63806,
|
||||||
|
SPELL_STUN_AURA = 62794,
|
||||||
|
SPELL_FUSEARMOR = 64771
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
||||||
|
|
||||||
|
// Constants for arena parameters
|
||||||
|
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
||||||
|
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
||||||
|
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
||||||
|
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
||||||
|
|
||||||
|
// Harpoon cooldown (seconds)
|
||||||
|
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
||||||
|
|
||||||
|
// Structure for harpoon data
|
||||||
|
struct HarpoonData
|
||||||
|
{
|
||||||
|
uint32 gameObjectEntry;
|
||||||
|
uint32 chainSpellId;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
||||||
|
: AiObject(botAI), _boss(nullptr) {}
|
||||||
|
|
||||||
|
bool UpdateBossAI();
|
||||||
|
Unit* GetBoss() const;
|
||||||
|
|
||||||
|
bool IsGroundPhase() const;
|
||||||
|
bool IsFlyingPhase() const;
|
||||||
|
|
||||||
|
bool IsHarpoonFired(uint32 chainSpellId) const;
|
||||||
|
static bool IsHarpoonReady(GameObject* harpoonGO);
|
||||||
|
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
||||||
|
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
||||||
|
|
||||||
|
static const std::vector<HarpoonData>& GetHarpoonData();
|
||||||
|
|
||||||
|
void AssignRolesBasedOnHealth();
|
||||||
|
bool AreRolesAssigned() const;
|
||||||
|
bool CanSwapRoles() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Unit* _boss;
|
||||||
|
|
||||||
|
// A map to track the last role swap *per bot* by their GUID
|
||||||
|
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
||||||
|
|
||||||
|
// The cooldown that applies to every bot
|
||||||
|
static const std::time_t _roleSwapCooldown = 10;
|
||||||
|
|
||||||
|
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
||||||
|
};
|
||||||
|
|
||||||
|
// template <class BossAiType>
|
||||||
|
// class GenericBossHelper : public AiObject
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||||
|
// virtual bool UpdateBossAI()
|
||||||
|
// {
|
||||||
|
// if (!bot->IsInCombat())
|
||||||
|
// {
|
||||||
|
// _unit = nullptr;
|
||||||
|
// }
|
||||||
|
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||||
|
// {
|
||||||
|
// _unit = nullptr;
|
||||||
|
// }
|
||||||
|
// if (!_unit)
|
||||||
|
// {
|
||||||
|
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
||||||
|
// if (!_unit)
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// _target = _unit->ToCreature();
|
||||||
|
// if (!_target)
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||||
|
// if (!_ai)
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// _event_map = &_ai->events;
|
||||||
|
// if (!_event_map)
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (!_event_map)
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// _timer = _event_map->GetTimer();
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// virtual void Reset()
|
||||||
|
// {
|
||||||
|
// _unit = nullptr;
|
||||||
|
// _target = nullptr;
|
||||||
|
// _ai = nullptr;
|
||||||
|
// _event_map = nullptr;
|
||||||
|
// _timer = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected:
|
||||||
|
// std::string _name;
|
||||||
|
// Unit* _unit = nullptr;
|
||||||
|
// Creature* _target = nullptr;
|
||||||
|
// BossAiType* _ai = nullptr;
|
||||||
|
// EventMap* _event_map = nullptr;
|
||||||
|
// uint32 _timer = 0;
|
||||||
|
// };
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H
|
#define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H
|
||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
#include "BossAuraActions.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidVoAActions.h"
|
#include "RaidVoAActions.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
#include "BossAuraTriggers.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidVoATriggers.h"
|
#include "RaidVoATriggers.h"
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include "PaladinAiObjectContext.h"
|
#include "PaladinAiObjectContext.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PriestAiObjectContext.h"
|
#include "PriestAiObjectContext.h"
|
||||||
#include "RaidUlduarActionContext.h"
|
|
||||||
#include "RaidUlduarTriggerContext.h"
|
|
||||||
#include "RogueAiObjectContext.h"
|
#include "RogueAiObjectContext.h"
|
||||||
#include "ShamanAiObjectContext.h"
|
#include "ShamanAiObjectContext.h"
|
||||||
#include "SharedValueContext.h"
|
#include "SharedValueContext.h"
|
||||||
@@ -43,12 +41,16 @@
|
|||||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||||
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||||
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||||
|
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||||
|
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||||
@@ -115,6 +117,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
|||||||
actionContexts.Add(new RaidKarazhanActionContext());
|
actionContexts.Add(new RaidKarazhanActionContext());
|
||||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||||
|
actionContexts.Add(new RaidSSCActionContext());
|
||||||
actionContexts.Add(new RaidOsActionContext());
|
actionContexts.Add(new RaidOsActionContext());
|
||||||
actionContexts.Add(new RaidEoEActionContext());
|
actionContexts.Add(new RaidEoEActionContext());
|
||||||
actionContexts.Add(new RaidVoAActionContext());
|
actionContexts.Add(new RaidVoAActionContext());
|
||||||
@@ -149,6 +152,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
|||||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||||
triggerContexts.Add(new RaidOsTriggerContext());
|
triggerContexts.Add(new RaidOsTriggerContext());
|
||||||
triggerContexts.Add(new RaidEoETriggerContext());
|
triggerContexts.Add(new RaidEoETriggerContext());
|
||||||
triggerContexts.Add(new RaidVoATriggerContext());
|
triggerContexts.Add(new RaidVoATriggerContext());
|
||||||
|
|||||||
@@ -1799,10 +1799,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
{
|
{
|
||||||
for (uint32 itemId : sRandomItemMgr.GetCachedEquipments(requiredLevel, inventoryType))
|
for (uint32 itemId : sRandomItemMgr.GetCachedEquipments(requiredLevel, inventoryType))
|
||||||
{
|
{
|
||||||
if (itemId == 46978) // shaman earth ring totem
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint32 skipProb = 25;
|
uint32 skipProb = 25;
|
||||||
if (urand(1, 100) <= skipProb)
|
if (urand(1, 100) <= skipProb)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
|
|||||||
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended");
|
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response");
|
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result");
|
botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result");
|
||||||
|
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_ROLL_WON, "loot roll won");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command");
|
botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_LEVELUP_INFO, "levelup");
|
botOutgoingPacketHandlers.AddHandler(SMSG_LEVELUP_INFO, "levelup");
|
||||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOG_XPGAIN, "xpgain");
|
botOutgoingPacketHandlers.AddHandler(SMSG_LOG_XPGAIN, "xpgain");
|
||||||
@@ -892,6 +893,7 @@ bool PlayerbotAI::IsAllowedCommand(std::string const text)
|
|||||||
unsecuredCommands.insert("invite");
|
unsecuredCommands.insert("invite");
|
||||||
unsecuredCommands.insert("leave");
|
unsecuredCommands.insert("leave");
|
||||||
unsecuredCommands.insert("lfg");
|
unsecuredCommands.insert("lfg");
|
||||||
|
unsecuredCommands.insert("pvp stats");
|
||||||
unsecuredCommands.insert("rpg status");
|
unsecuredCommands.insert("rpg status");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1553,6 +1555,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 544:
|
case 544:
|
||||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||||
break;
|
break;
|
||||||
|
case 548:
|
||||||
|
strategyName = "ssc"; // Serpentshrine Cavern
|
||||||
|
break;
|
||||||
case 565:
|
case 565:
|
||||||
strategyName = "gruulslair"; // Gruul's Lair
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
break;
|
break;
|
||||||
@@ -1584,7 +1589,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
strategyName = "wotlk-hol"; // Halls of Lightning
|
strategyName = "wotlk-hol"; // Halls of Lightning
|
||||||
break;
|
break;
|
||||||
case 603:
|
case 603:
|
||||||
strategyName = "uld"; // Ulduar
|
strategyName = "ulduar"; // Ulduar
|
||||||
break;
|
break;
|
||||||
case 604:
|
case 604:
|
||||||
strategyName = "wotlk-gd"; // Gundrak
|
strategyName = "wotlk-gd"; // Gundrak
|
||||||
@@ -2579,7 +2584,7 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
std::vector<Player*> PlayerbotAI::GetRealPlayersInGroup()
|
||||||
{
|
{
|
||||||
std::vector<Player*> members;
|
std::vector<Player*> members;
|
||||||
|
|
||||||
@@ -2606,6 +2611,30 @@ std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
|||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Player*> PlayerbotAI::GetAllPlayersInGroup()
|
||||||
|
{
|
||||||
|
std::vector<Player*> members;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
|
||||||
|
if (!group)
|
||||||
|
return members;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
|
if (!member)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
members.push_back(ref->GetSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::SayToGuild(const std::string& msg)
|
bool PlayerbotAI::SayToGuild(const std::string& msg)
|
||||||
{
|
{
|
||||||
if (msg.empty())
|
if (msg.empty())
|
||||||
@@ -2714,9 +2743,9 @@ bool PlayerbotAI::SayToParty(const std::string& msg)
|
|||||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_PARTY, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
ChatHandler::BuildChatPacket(data, CHAT_MSG_PARTY, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
||||||
bot->GetName());
|
bot->GetName());
|
||||||
|
|
||||||
for (auto reciever : GetPlayersInGroup())
|
for (auto receiver : GetRealPlayersInGroup())
|
||||||
{
|
{
|
||||||
ServerFacade::instance().SendPacket(reciever, &data);
|
ServerFacade::instance().SendPacket(receiver, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -2731,9 +2760,9 @@ bool PlayerbotAI::SayToRaid(const std::string& msg)
|
|||||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
||||||
bot->GetName());
|
bot->GetName());
|
||||||
|
|
||||||
for (auto reciever : GetPlayersInGroup())
|
for (auto receiver : GetRealPlayersInGroup())
|
||||||
{
|
{
|
||||||
ServerFacade::instance().SendPacket(reciever, &data);
|
ServerFacade::instance().SendPacket(receiver, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -446,7 +446,8 @@ public:
|
|||||||
GameObject* GetGameObject(ObjectGuid guid);
|
GameObject* GetGameObject(ObjectGuid guid);
|
||||||
// static GameObject* GetGameObject(GameObjectData const* gameObjectData);
|
// static GameObject* GetGameObject(GameObjectData const* gameObjectData);
|
||||||
WorldObject* GetWorldObject(ObjectGuid guid);
|
WorldObject* GetWorldObject(ObjectGuid guid);
|
||||||
std::vector<Player*> GetPlayersInGroup();
|
std::vector<Player*> GetAllPlayersInGroup();
|
||||||
|
std::vector<Player*> GetRealPlayersInGroup();
|
||||||
const AreaTableEntry* GetCurrentArea();
|
const AreaTableEntry* GetCurrentArea();
|
||||||
const AreaTableEntry* GetCurrentZone();
|
const AreaTableEntry* GetCurrentZone();
|
||||||
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
||||||
|
|||||||
@@ -1784,7 +1784,7 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
|
|||||||
zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle
|
zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle
|
||||||
|
|
||||||
// Classic WoW - High - level zones
|
// Classic WoW - High - level zones
|
||||||
zone2LevelBracket[10] = {19, 33}; // Deadwind Pass
|
zone2LevelBracket[10] = {19, 33}; // Duskwood
|
||||||
zone2LevelBracket[11] = {21, 30}; // Wetlands
|
zone2LevelBracket[11] = {21, 30}; // Wetlands
|
||||||
zone2LevelBracket[44] = {16, 28}; // Redridge Mountains
|
zone2LevelBracket[44] = {16, 28}; // Redridge Mountains
|
||||||
zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills
|
zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills
|
||||||
|
|||||||
@@ -2256,10 +2256,13 @@ void RandomItemMgr::BuildEquipCacheNew()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (itemId == 22784)
|
|
||||||
{ // Sunwell Orb
|
// Unobtainable or unusable items
|
||||||
|
if (itemId == 12468 || // Chilton Wand
|
||||||
|
itemId == 22784 || // Sunwell Orb
|
||||||
|
itemId == 46978) // Totem of the Earthen Ring
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -881,107 +881,6 @@ std::vector<GameObjectData const*> WorldPosition::getGameObjectsNear(float radiu
|
|||||||
return worker.GetResult();
|
return worker.GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* GuidPosition::GetCreature()
|
|
||||||
{
|
|
||||||
if (!*this)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (loadedFromDB)
|
|
||||||
{
|
|
||||||
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
|
|
||||||
if (creatureBounds.first != creatureBounds.second)
|
|
||||||
return creatureBounds.second->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMap()->GetCreature(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* GuidPosition::GetUnit()
|
|
||||||
{
|
|
||||||
if (!*this)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (loadedFromDB)
|
|
||||||
{
|
|
||||||
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
|
|
||||||
if (creatureBounds.first != creatureBounds.second)
|
|
||||||
return creatureBounds.second->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsPlayer())
|
|
||||||
return ObjectAccessor::FindPlayer(*this);
|
|
||||||
|
|
||||||
if (IsPet())
|
|
||||||
return getMap()->GetPet(*this);
|
|
||||||
|
|
||||||
return GetCreature();
|
|
||||||
}
|
|
||||||
|
|
||||||
GameObject* GuidPosition::GetGameObject()
|
|
||||||
{
|
|
||||||
if (!*this)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (loadedFromDB)
|
|
||||||
{
|
|
||||||
auto gameobjectBounds = getMap()->GetGameObjectBySpawnIdStore().equal_range(GetCounter());
|
|
||||||
if (gameobjectBounds.first != gameobjectBounds.second)
|
|
||||||
return gameobjectBounds.second->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMap()->GetGameObject(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Player* GuidPosition::GetPlayer()
|
|
||||||
{
|
|
||||||
if (!*this)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (IsPlayer())
|
|
||||||
return ObjectAccessor::FindPlayer(*this);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GuidPosition::isDead()
|
|
||||||
{
|
|
||||||
if (!getMap())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!getMap()->IsGridLoaded(getX(), getY()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (IsUnit() && GetUnit() && GetUnit()->IsInWorld() && GetUnit()->IsAlive())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (IsGameObject() && GetGameObject() && GetGameObject()->IsInWorld())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {}
|
|
||||||
|
|
||||||
GuidPosition::GuidPosition(CreatureData const& creData)
|
|
||||||
: ObjectGuid(HighGuid::Unit, creData.id1, creData.spawnId),
|
|
||||||
WorldPosition(creData.mapid, creData.posX, creData.posY, creData.posZ, creData.orientation)
|
|
||||||
{
|
|
||||||
loadedFromDB = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidPosition::GuidPosition(GameObjectData const& goData)
|
|
||||||
: ObjectGuid(HighGuid::GameObject, goData.id),
|
|
||||||
WorldPosition(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)
|
|
||||||
{
|
|
||||||
loadedFromDB = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreatureTemplate const* GuidPosition::GetCreatureTemplate()
|
CreatureTemplate const* GuidPosition::GetCreatureTemplate()
|
||||||
{
|
{
|
||||||
return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr;
|
return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr;
|
||||||
@@ -1000,7 +899,7 @@ WorldObject* GuidPosition::GetWorldObject()
|
|||||||
switch (GetHigh())
|
switch (GetHigh())
|
||||||
{
|
{
|
||||||
case HighGuid::Player:
|
case HighGuid::Player:
|
||||||
return ObjectAccessor::FindPlayer(*this);
|
return GetPlayer();
|
||||||
case HighGuid::Transport:
|
case HighGuid::Transport:
|
||||||
case HighGuid::Mo_Transport:
|
case HighGuid::Mo_Transport:
|
||||||
case HighGuid::GameObject:
|
case HighGuid::GameObject:
|
||||||
@@ -1021,8 +920,93 @@ WorldObject* GuidPosition::GetWorldObject()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameObject* GuidPosition::GetGameObject()
|
||||||
|
{
|
||||||
|
if (!*this)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (loadedFromDB)
|
||||||
|
return ObjectAccessor::GetSpawnedGameObjectByDBGUID(GetMapId(), GetCounter());
|
||||||
|
|
||||||
|
return getMap()->GetGameObject(*this); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GuidPosition::GetUnit()
|
||||||
|
{
|
||||||
|
if (!*this)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (IsPlayer())
|
||||||
|
return GetPlayer();
|
||||||
|
|
||||||
|
if (IsPet())
|
||||||
|
return getMap()->GetPet(*this);
|
||||||
|
|
||||||
|
return GetCreature();
|
||||||
|
}
|
||||||
|
|
||||||
|
Creature* GuidPosition::GetCreature()
|
||||||
|
{
|
||||||
|
if (!*this)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (loadedFromDB)
|
||||||
|
return ObjectAccessor::GetSpawnedCreatureByDBGUID(GetMapId(), GetCounter());
|
||||||
|
|
||||||
|
return getMap()->GetCreature(*this); // fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GuidPosition::GetPlayer()
|
||||||
|
{
|
||||||
|
if (!*this)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (IsPlayer())
|
||||||
|
return ObjectAccessor::FindPlayer(*this);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; }
|
bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; }
|
||||||
|
|
||||||
|
bool GuidPosition::IsCreatureOrGOAccessible()
|
||||||
|
{
|
||||||
|
Map* map = getMap();
|
||||||
|
if (!map || !map->IsGridLoaded(GetPositionX(), GetPositionY()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (IsCreature())
|
||||||
|
{
|
||||||
|
Creature* creature = GetCreature();
|
||||||
|
if (creature && creature->IsInWorld() && creature->IsAlive())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (IsGameObject())
|
||||||
|
{
|
||||||
|
GameObject* go = GetGameObject();
|
||||||
|
if (go && go->IsInWorld())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {}
|
||||||
|
|
||||||
|
GuidPosition::GuidPosition(CreatureData const& creData)
|
||||||
|
: ObjectGuid(HighGuid::Unit, creData.id1, creData.spawnId),
|
||||||
|
WorldPosition(creData.mapid, creData.posX, creData.posY, creData.posZ, creData.orientation)
|
||||||
|
{
|
||||||
|
loadedFromDB = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuidPosition::GuidPosition(GameObjectData const& goData)
|
||||||
|
: ObjectGuid(HighGuid::GameObject, goData.id),
|
||||||
|
WorldPosition(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)
|
||||||
|
{
|
||||||
|
loadedFromDB = true;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<WorldPosition*> TravelDestination::getPoints(bool ignoreFull)
|
std::vector<WorldPosition*> TravelDestination::getPoints(bool ignoreFull)
|
||||||
{
|
{
|
||||||
if (ignoreFull)
|
if (ignoreFull)
|
||||||
|
|||||||
@@ -416,14 +416,13 @@ public:
|
|||||||
GameObjectTemplate const* GetGameObjectTemplate();
|
GameObjectTemplate const* GetGameObjectTemplate();
|
||||||
|
|
||||||
WorldObject* GetWorldObject();
|
WorldObject* GetWorldObject();
|
||||||
Creature* GetCreature();
|
|
||||||
Unit* GetUnit();
|
|
||||||
GameObject* GetGameObject();
|
GameObject* GetGameObject();
|
||||||
|
Unit* GetUnit();
|
||||||
|
Creature* GetCreature();
|
||||||
Player* GetPlayer();
|
Player* GetPlayer();
|
||||||
|
|
||||||
bool HasNpcFlag(NPCFlags flag);
|
bool HasNpcFlag(NPCFlags flag);
|
||||||
|
bool IsCreatureOrGOAccessible(); // For loaded grids check if the creature/gameobject is in world + alive
|
||||||
bool isDead(); // For loaded grids check if the unit/object is unloaded/dead.
|
|
||||||
|
|
||||||
operator bool() const { return !IsEmpty(); }
|
operator bool() const { return !IsEmpty(); }
|
||||||
bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
|
bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
|
||||||
|
|||||||
@@ -14,9 +14,11 @@
|
|||||||
#include "PlayerbotOperation.h"
|
#include "PlayerbotOperation.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "PlayerbotMgr.h"
|
#include "PlayerbotMgr.h"
|
||||||
#include "PlayerbotRepository.h"
|
#include "PlayerbotRepository.h"
|
||||||
#include "RandomPlayerbotMgr.h"
|
#include "RandomPlayerbotMgr.h"
|
||||||
|
#include "UseMeetingStoneAction.h"
|
||||||
#include "WorldSession.h"
|
#include "WorldSession.h"
|
||||||
#include "WorldSessionMgr.h"
|
#include "WorldSessionMgr.h"
|
||||||
|
|
||||||
@@ -74,6 +76,15 @@ public:
|
|||||||
if (group->AddMember(target))
|
if (group->AddMember(target))
|
||||||
{
|
{
|
||||||
LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName());
|
LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName());
|
||||||
|
if (sPlayerbotAIConfig.summonWhenGroup && target->GetDistance(bot) > sPlayerbotAIConfig.sightDistance)
|
||||||
|
{
|
||||||
|
PlayerbotAI* targetAI = sPlayerbotsMgr.GetPlayerbotAI(target);
|
||||||
|
if (targetAI)
|
||||||
|
{
|
||||||
|
SummonAction summonAction(targetAI, "group summon");
|
||||||
|
summonAction.Teleport(bot, target, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user