mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
7 Commits
f00fe15ff1
...
6db44b5296
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db44b5296 | ||
|
|
45aa6f2f8e | ||
|
|
bbd9d3e37a | ||
|
|
b172e88010 | ||
|
|
91eac70ca2 | ||
|
|
c7ac849fbe | ||
|
|
e27b429908 |
@ -61,27 +61,12 @@ any impact on performance, you may skip these question. If necessary, a maintain
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Messages to Translate
|
|
||||||
<!--
|
|
||||||
Bot messages have to be translatable, but you don't need to do the translations here. You only need to make sure
|
|
||||||
the message is in a translatable format, and list in the table the message_key and the default English message.
|
|
||||||
Search for GetBotTextOrDefault in the codebase for examples.
|
|
||||||
-->
|
|
||||||
- Does this change add bot messages to translate?
|
|
||||||
- - [ ] No
|
|
||||||
- - [ ] Yes (**list messages in the table**)
|
|
||||||
|
|
||||||
| Message key | Default message |
|
|
||||||
| --------------- | ------------------ |
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
|
|
||||||
## AI Assistance
|
## AI Assistance
|
||||||
<!--
|
<!--
|
||||||
AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor.
|
AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor.
|
||||||
We expect contributors to be honest about what they do and do not understand.
|
We expect contributors to be honest about what they do and do not understand.
|
||||||
-->
|
-->
|
||||||
- Was AI assistance used while working on this change?
|
Was AI assistance used while working on this change?
|
||||||
- - [ ] No
|
- - [ ] No
|
||||||
- - [ ] Yes (**explain below**)
|
- - [ ] Yes (**explain below**)
|
||||||
<!--
|
<!--
|
||||||
@ -92,12 +77,25 @@ If yes, please specify:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TRANSLATIONS:
|
||||||
|
Anything new that the bots say in chat must be in a translatable format. This is done using GetBotTextOrDefault,
|
||||||
|
which you can search for in the codebase to find examples. Your code needs to have English as the default fallback,
|
||||||
|
while the full translations need to be in an SQL update file. The languages in the file are the nine language
|
||||||
|
options supported by AzerothCore: English, Korean, French, German, Chinese, Taiwanese, Spanish, Spanish Mexico, and
|
||||||
|
Russian. See data/sql/playerbots/updates/2025_12_27_ai_playerbot_fishing_text.sql as an example of a translation SQL
|
||||||
|
update, whose content are called within the codebase at src/strategy/actions/FishingAction.cpp
|
||||||
|
-->
|
||||||
|
|
||||||
## Final Checklist
|
## 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.
|
||||||
|
- - [ ] Any new bot dialogue lines are translated.
|
||||||
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
||||||
|
|
||||||
## Notes for Reviewers
|
## Notes for Reviewers
|
||||||
<!-- Anything else that's helpful to review or test your pull request. -->
|
<!-- Anything else that's helpful to review or test your pull request. -->
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,104 @@
|
|||||||
|
-- #########################################################
|
||||||
|
-- Playerbots - Add texts for SetWaitForAttackTimeAction
|
||||||
|
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
|
||||||
|
-- zhTW, esES, esMX, ruRU)
|
||||||
|
-- #########################################################
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN ('wait_for_attack_provide_time', 'wait_for_attack_invalid_time', 'wait_for_attack_time_set');
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('wait_for_attack_provide_time', 'wait_for_attack_invalid_time', 'wait_for_attack_time_set');
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_provide_time
|
||||||
|
-- Please provide a time to set (in seconds)
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1740,
|
||||||
|
'wait_for_attack_provide_time',
|
||||||
|
'Please provide a time to set (in seconds)',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'설정할 시간을 입력해 주세요 (초 단위)',
|
||||||
|
-- frFR
|
||||||
|
'Veuillez indiquer un temps à définir (en secondes)',
|
||||||
|
-- deDE
|
||||||
|
'Bitte gib eine Zeit an (in Sekunden)',
|
||||||
|
-- zhCN
|
||||||
|
'请提供要设置的时间(以秒为单位)',
|
||||||
|
-- zhTW
|
||||||
|
'請提供要設定的時間(以秒為單位)',
|
||||||
|
-- esES
|
||||||
|
'Por favor, indica un tiempo a establecer (en segundos)',
|
||||||
|
-- esMX
|
||||||
|
'Por favor, indica un tiempo a establecer (en segundos)',
|
||||||
|
-- ruRU
|
||||||
|
'Пожалуйста, укажите время (в секундах)');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_provide_time', 100);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_invalid_time
|
||||||
|
-- Please provide valid time to set (in seconds) between 0 and 99
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1741,
|
||||||
|
'wait_for_attack_invalid_time',
|
||||||
|
'Please provide valid time to set (in seconds) between 0 and 99',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'0에서 99 사이의 유효한 시간을 입력해 주세요 (초 단위)',
|
||||||
|
-- frFR
|
||||||
|
'Veuillez indiquer un temps valide (en secondes) entre 0 et 99',
|
||||||
|
-- deDE
|
||||||
|
'Bitte gib eine gültige Zeit an (in Sekunden) zwischen 0 und 99',
|
||||||
|
-- zhCN
|
||||||
|
'请提供有效的时间(以秒为单位),范围为 0 到 99',
|
||||||
|
-- zhTW
|
||||||
|
'請提供有效的時間(以秒為單位),範圍為 0 到 99',
|
||||||
|
-- esES
|
||||||
|
'Por favor, indica un tiempo válido (en segundos) entre 0 y 99',
|
||||||
|
-- esMX
|
||||||
|
'Por favor, indica un tiempo válido (en segundos) entre 0 y 99',
|
||||||
|
-- ruRU
|
||||||
|
'Пожалуйста, укажите допустимое время (в секундах) от 0 до 99');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_invalid_time', 100);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_time_set
|
||||||
|
-- Wait for attack time set to %new_time seconds
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1742,
|
||||||
|
'wait_for_attack_time_set',
|
||||||
|
'Wait for attack time set to %new_time seconds',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'공격 대기 시간이 %new_time초로 설정되었습니다',
|
||||||
|
-- frFR
|
||||||
|
'Temps d''attente avant l''attaque défini à %new_time secondes',
|
||||||
|
-- deDE
|
||||||
|
'Wartezeit vor dem Angriff auf %new_time Sekunden gesetzt',
|
||||||
|
-- zhCN
|
||||||
|
'等待攻击时间已设置为 %new_time 秒',
|
||||||
|
-- zhTW
|
||||||
|
'等待攻擊時間已設定為 %new_time 秒',
|
||||||
|
-- esES
|
||||||
|
'Tiempo de espera para atacar establecido en %new_time segundos',
|
||||||
|
-- esMX
|
||||||
|
'Tiempo de espera para atacar establecido en %new_time segundos',
|
||||||
|
-- ruRU
|
||||||
|
'Время ожидания атаки установлено на %new_time секунд');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_time_set', 100);
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
-- Translations for additional logout related messages
|
||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN ('bot_not_your_master', 'bot_rndbot_no_logout');
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('bot_not_your_master', 'bot_rndbot_no_logout');
|
||||||
|
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1740,
|
||||||
|
'bot_not_your_master',
|
||||||
|
"You are not my master!",
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
"당신은 내 주인이 아닙니다!",
|
||||||
|
-- frFR
|
||||||
|
"Tu n'es pas mon maître !",
|
||||||
|
-- deDE
|
||||||
|
"Du bist nicht mein Meister!",
|
||||||
|
-- zhCN
|
||||||
|
"你不是我的主人!",
|
||||||
|
-- zhTW
|
||||||
|
"你不是我的主人!",
|
||||||
|
-- esES
|
||||||
|
"¡No eres mi amo!",
|
||||||
|
-- esMX
|
||||||
|
"¡No eres mi amo!",
|
||||||
|
-- ruRU
|
||||||
|
"Ты не мой хозяин!");
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('bot_not_your_master', 100);
|
||||||
|
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1741,
|
||||||
|
'bot_rndbot_no_logout',
|
||||||
|
"You can't command me to logout!",
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
"당신은 나에게 로그아웃을 명령할 수 없습니다!",
|
||||||
|
-- frFR
|
||||||
|
"Tu ne peux pas m'ordonner de me déconnecter !",
|
||||||
|
-- deDE
|
||||||
|
"Du kannst mir nicht befehlen, mich auszuloggen!",
|
||||||
|
-- zhCN
|
||||||
|
"你不能命令我下线!",
|
||||||
|
-- zhTW
|
||||||
|
"你不能命令我登出!",
|
||||||
|
-- esES
|
||||||
|
"¡No puedes ordenarme que cierre sesión!",
|
||||||
|
-- esMX
|
||||||
|
"¡No puedes ordenarme que cierre sesión!",
|
||||||
|
-- ruRU
|
||||||
|
"Ты не можешь приказать мне выйти из игры!");
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('bot_rndbot_no_logout', 100);
|
||||||
@ -65,6 +65,7 @@
|
|||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
#include "FishingAction.h"
|
#include "FishingAction.h"
|
||||||
#include "CancelChannelAction.h"
|
#include "CancelChannelAction.h"
|
||||||
|
#include "WaitForAttackAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -164,6 +165,7 @@ public:
|
|||||||
creators["blood fury"] = &ActionContext::blood_fury;
|
creators["blood fury"] = &ActionContext::blood_fury;
|
||||||
creators["berserking"] = &ActionContext::berserking;
|
creators["berserking"] = &ActionContext::berserking;
|
||||||
creators["every man for himself"] = &ActionContext::every_man_for_himself;
|
creators["every man for himself"] = &ActionContext::every_man_for_himself;
|
||||||
|
creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken;
|
||||||
creators["use trinket"] = &ActionContext::use_trinket;
|
creators["use trinket"] = &ActionContext::use_trinket;
|
||||||
creators["auto talents"] = &ActionContext::auto_talents;
|
creators["auto talents"] = &ActionContext::auto_talents;
|
||||||
creators["auto share quest"] = &ActionContext::auto_share_quest;
|
creators["auto share quest"] = &ActionContext::auto_share_quest;
|
||||||
@ -263,6 +265,7 @@ public:
|
|||||||
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
|
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
|
||||||
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
|
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
|
||||||
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
|
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
|
||||||
|
creators["wait for attack keep safe distance"] = &ActionContext::wait_for_attack_keep_safe_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -359,6 +362,7 @@ private:
|
|||||||
static Action* blood_fury(PlayerbotAI* botAI) { return new CastBloodFuryAction(botAI); }
|
static Action* blood_fury(PlayerbotAI* botAI) { return new CastBloodFuryAction(botAI); }
|
||||||
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
|
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
|
||||||
static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); }
|
static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); }
|
||||||
|
static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); }
|
||||||
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
|
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
|
||||||
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
||||||
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
|
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
|
||||||
@ -458,6 +462,7 @@ private:
|
|||||||
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
|
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
|
||||||
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
|
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
|
||||||
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
|
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
|
||||||
|
static Action* wait_for_attack_keep_safe_distance(PlayerbotAI* ai) { return new WaitForAttackKeepSafeDistanceAction(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
bool AttackAction::Execute(Event /*event*/)
|
bool AttackAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -164,6 +165,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
|
|
||||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
|
|
||||||
|
if (!WaitForAttackStrategy::ShouldWait(botAI))
|
||||||
bot->Attack(target, shouldMelee);
|
bot->Attack(target, shouldMelee);
|
||||||
/* prevent pet dead immediately in group */
|
/* prevent pet dead immediately in group */
|
||||||
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
|
||||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Group.h"
|
|
||||||
#include "Chat.h"
|
|
||||||
#include "Language.h"
|
|
||||||
|
|
||||||
class Player;
|
|
||||||
class PlayerbotAI;
|
|
||||||
|
|
||||||
namespace ai::buff
|
|
||||||
{
|
|
||||||
|
|
||||||
// Build an aura qualifier "single + greater" to avoid double-buffing
|
|
||||||
std::string MakeAuraQualifierForBuff(std::string const& name);
|
|
||||||
|
|
||||||
// Returns the group spell name for a given single-target buff.
|
|
||||||
// If no group equivalent exists, returns "".
|
|
||||||
std::string GroupVariantFor(std::string const& name);
|
|
||||||
|
|
||||||
// Checks if the bot has the required reagents to cast a spell (by its spellId).
|
|
||||||
// Returns false if the spellId is invalid.
|
|
||||||
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
|
||||||
|
|
||||||
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
|
||||||
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
|
||||||
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
|
||||||
// (if provided) to notify the party/raid.
|
|
||||||
std::string UpgradeToGroupIfAppropriate(
|
|
||||||
Player* bot,
|
|
||||||
PlayerbotAI* botAI,
|
|
||||||
std::string const& baseName,
|
|
||||||
bool announceOnMissing = false,
|
|
||||||
std::function<void(std::string const&)> announce = {}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ai::chat {
|
|
||||||
inline std::function<void(std::string const&)> MakeGroupAnnouncer(Player* me)
|
|
||||||
{
|
|
||||||
return [me](std::string const& msg)
|
|
||||||
{
|
|
||||||
if (Group* g = me->GetGroup())
|
|
||||||
{
|
|
||||||
WorldPacket data;
|
|
||||||
ChatMsg type = g->isRaidGroup() ? CHAT_MSG_RAID : CHAT_MSG_PARTY;
|
|
||||||
ChatHandler::BuildChatPacket(data, type, LANG_UNIVERSAL, me, /*receiver=*/nullptr, msg.c_str());
|
|
||||||
g->BroadcastPacket(&data, true, -1, me->GetGUID());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
me->Say(msg, LANG_UNIVERSAL);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,10 +17,11 @@
|
|||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
#include "GenericBuffUtils.h"
|
#include "Ai/Base/Util/GenericBuffUtils.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
|
||||||
using ai::buff::MakeAuraQualifierForBuff;
|
using ai::buff::MakeAuraQualifierForBuff;
|
||||||
|
using ai::spell::HasSpellOrCategoryCooldown;
|
||||||
|
|
||||||
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
||||||
@ -320,7 +321,7 @@ bool CastEveryManForHimselfAction::isPossible()
|
|||||||
if (!bot->HasSpell(spellId))
|
if (!bot->HasSpell(spellId))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->HasSpellCooldown(spellId))
|
if (HasSpellOrCategoryCooldown(bot, spellId))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -328,11 +329,36 @@ bool CastEveryManForHimselfAction::isPossible()
|
|||||||
|
|
||||||
bool CastEveryManForHimselfAction::isUseful()
|
bool CastEveryManForHimselfAction::isUseful()
|
||||||
{
|
{
|
||||||
return bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
|
return (bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_ROOT) ||
|
bot->HasAuraType(SPELL_AURA_MOD_ROOT) ||
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) ||
|
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) ||
|
||||||
bot->HasAuraType(SPELL_AURA_MOD_CHARM);
|
bot->HasAuraType(SPELL_AURA_MOD_CHARM))
|
||||||
|
&& CastSpellAction::isUseful();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastWillOfTheForsakenAction::isPossible()
|
||||||
|
{
|
||||||
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||||
|
if (!spellId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!bot->HasSpell(spellId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HasSpellOrCategoryCooldown(bot, spellId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastWillOfTheForsakenAction::isUseful()
|
||||||
|
{
|
||||||
|
return (bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
||||||
|
bot->HasAuraType(SPELL_AURA_MOD_CHARM) ||
|
||||||
|
bot->HasAuraType(SPELL_AURA_AOE_CHARM) ||
|
||||||
|
bot->HasAuraWithMechanic(1 << MECHANIC_SLEEP))
|
||||||
|
&& CastSpellAction::isUseful();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UseTrinketAction::Execute(Event /*event*/)
|
bool UseTrinketAction::Execute(Event /*event*/)
|
||||||
|
|||||||
@ -294,6 +294,16 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CastWillOfTheForsakenAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastWillOfTheForsakenAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "will of the forsaken") {}
|
||||||
|
|
||||||
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
|
bool isPossible() override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
class UseTrinketAction : public Action
|
class UseTrinketAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
166
src/Ai/Base/Actions/WaitForAttackAction.cpp
Normal file
166
src/Ai/Base/Actions/WaitForAttackAction.cpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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 "WaitForAttackAction.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
WorldPosition GetBestPoint(AiObjectContext* context, Player* bot, Unit* target,
|
||||||
|
float minDistance, float maxDistance)
|
||||||
|
{
|
||||||
|
WorldPosition botPosition(bot);
|
||||||
|
WorldPosition targetPosition(target);
|
||||||
|
|
||||||
|
int8 startDir = urand(0, 1) * 2 - 1;
|
||||||
|
float const radiansIncrement = (5.0f / 180.0f) * static_cast<float>(M_PI);
|
||||||
|
float startAngle = targetPosition.getAngleTo(botPosition) +
|
||||||
|
frand(0.0f, radiansIncrement) * startDir;
|
||||||
|
float distance = frand(minDistance, maxDistance);
|
||||||
|
|
||||||
|
GuidVector enemies = AI_VALUE(GuidVector, "possible targets no los");
|
||||||
|
|
||||||
|
for (float tryAngle = 0.0f; tryAngle < static_cast<float>(M_PI); tryAngle += radiansIncrement)
|
||||||
|
{
|
||||||
|
for (int8 tryDir = -1; tryAngle && tryDir < 1; tryDir += 2)
|
||||||
|
{
|
||||||
|
float pointAngle = startAngle + tryAngle * startDir * tryDir;
|
||||||
|
|
||||||
|
float x = targetPosition.GetPositionX() + distance * cos(pointAngle);
|
||||||
|
float y = targetPosition.GetPositionY() + distance * sin(pointAngle);
|
||||||
|
float z = targetPosition.GetPositionZ() + 1.0f;
|
||||||
|
|
||||||
|
WorldPosition point(targetPosition.GetMapId(), x, y, z);
|
||||||
|
point.setZ(point.getHeight());
|
||||||
|
|
||||||
|
// Check line of sight to target
|
||||||
|
if (!target->IsWithinLOS(point.GetPositionX(), point.GetPositionY(),
|
||||||
|
point.GetPositionZ() + bot->GetCollisionHeight()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if enemies are close to this point
|
||||||
|
bool enemyClose = false;
|
||||||
|
for (ObjectGuid const& enemyGUID : enemies)
|
||||||
|
{
|
||||||
|
Unit* enemy = ObjectAccessor::GetUnit(*bot, enemyGUID);
|
||||||
|
if (enemy && enemy->IsWithinLOSInMap(bot) && enemy->IsHostileTo(bot))
|
||||||
|
{
|
||||||
|
float enemyAttackRange = enemy->GetCombatReach() + ATTACK_DISTANCE;
|
||||||
|
WorldPosition enemyPos(enemy);
|
||||||
|
if (enemyPos.sqDistance(point) <= (enemyAttackRange * enemyAttackRange))
|
||||||
|
{
|
||||||
|
enemyClose = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enemyClose)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if bot can path to this point
|
||||||
|
if (!botPosition.canPathTo(point, bot))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return botPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool WaitForAttackKeepSafeDistanceAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
|
||||||
|
// If our target is moving towards a stationary unit, use that unit as anchor
|
||||||
|
if (target && !target->IsStopped())
|
||||||
|
{
|
||||||
|
ObjectGuid targetGuid = target->GetTarget();
|
||||||
|
if (targetGuid)
|
||||||
|
{
|
||||||
|
Unit* targetsTarget = ObjectAccessor::GetUnit(*target, targetGuid);
|
||||||
|
if (targetsTarget && targetsTarget->IsStopped())
|
||||||
|
target = targetsTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target && target->IsAlive())
|
||||||
|
{
|
||||||
|
float safeDistance = std::max(
|
||||||
|
target->GetCombatReach() + ATTACK_DISTANCE,
|
||||||
|
WaitForAttackStrategy::GetSafeDistance());
|
||||||
|
float safeDistanceThreshold = WaitForAttackStrategy::GetSafeDistanceThreshold();
|
||||||
|
|
||||||
|
WorldPosition bestPoint = GetBestPoint(context, bot, target,
|
||||||
|
safeDistance - safeDistanceThreshold, safeDistance);
|
||||||
|
|
||||||
|
if (bestPoint)
|
||||||
|
return MoveTo(bestPoint.GetMapId(), bestPoint.GetPositionX(),
|
||||||
|
bestPoint.GetPositionY(), bestPoint.GetPositionZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetWaitForAttackTimeAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
std::string newTimeStr = event.getParam();
|
||||||
|
|
||||||
|
if (newTimeStr.empty())
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_provide_time",
|
||||||
|
"Please provide a time to set (in seconds)",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::all_of(newTimeStr.begin(), newTimeStr.end(), ::isdigit))
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_invalid_time",
|
||||||
|
"Please provide valid time to set (in seconds) between 0 and 99",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newTime = std::stoi(newTimeStr);
|
||||||
|
if (newTime < 0 || newTime > 99)
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_invalid_time",
|
||||||
|
"Please provide valid time to set (in seconds) between 0 and 99",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->GetValue<uint8>("wait for attack time")->Set(static_cast<uint8>(newTime));
|
||||||
|
|
||||||
|
std::map<std::string, std::string> placeholders;
|
||||||
|
placeholders["%new_time"] = std::to_string(newTime);
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_time_set",
|
||||||
|
"Wait for attack time set to %new_time seconds",
|
||||||
|
placeholders);
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
31
src/Ai/Base/Actions/WaitForAttackAction.h
Normal file
31
src/Ai/Base/Actions/WaitForAttackAction.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKACTION_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKACTION_H
|
||||||
|
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackKeepSafeDistanceAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackKeepSafeDistanceAction(PlayerbotAI* botAI)
|
||||||
|
: MovementAction(botAI, "wait for attack keep safe distance") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetWaitForAttackTimeAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SetWaitForAttackTimeAction(PlayerbotAI* botAI)
|
||||||
|
: Action(botAI, "wait for attack time") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -84,6 +84,7 @@
|
|||||||
#include "TellGlyphsAction.h"
|
#include "TellGlyphsAction.h"
|
||||||
#include "EquipGlyphsAction.h"
|
#include "EquipGlyphsAction.h"
|
||||||
#include "PetsAction.h"
|
#include "PetsAction.h"
|
||||||
|
#include "WaitForAttackAction.h"
|
||||||
|
|
||||||
class ChatActionContext : public NamedObjectContext<Action>
|
class ChatActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -199,6 +200,7 @@ public:
|
|||||||
creators["pet"] = &ChatActionContext::pet;
|
creators["pet"] = &ChatActionContext::pet;
|
||||||
creators["pet attack"] = &ChatActionContext::pet_attack;
|
creators["pet attack"] = &ChatActionContext::pet_attack;
|
||||||
creators["roll"] = &ChatActionContext::roll_action;
|
creators["roll"] = &ChatActionContext::roll_action;
|
||||||
|
creators["wait for attack time"] = &ChatActionContext::wait_for_attack_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -311,6 +313,7 @@ private:
|
|||||||
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
|
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
|
||||||
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
|
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
|
static Action* wait_for_attack_time(PlayerbotAI* botAI) { return new SetWaitForAttackTimeAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -145,6 +145,7 @@ public:
|
|||||||
creators["pet"] = &ChatTriggerContext::pet;
|
creators["pet"] = &ChatTriggerContext::pet;
|
||||||
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
||||||
creators["roll"] = &ChatTriggerContext::roll_action;
|
creators["roll"] = &ChatTriggerContext::roll_action;
|
||||||
|
creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -269,6 +270,7 @@ private:
|
|||||||
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
||||||
static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); }
|
static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); }
|
||||||
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
||||||
|
static Trigger* wait_for_attack_time(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wait for attack time"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -199,4 +199,5 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||||
supported.push_back("pet");
|
supported.push_back("pet");
|
||||||
supported.push_back("pet attack");
|
supported.push_back("pet attack");
|
||||||
|
supported.push_back("wait for attack time");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,9 @@ void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
|
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
||||||
|
|||||||
93
src/Ai/Base/Strategy/WaitForAttackStrategy.cpp
Normal file
93
src/Ai/Base/Strategy/WaitForAttackStrategy.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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 "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
void WaitForAttackStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"wait for attack safe distance",
|
||||||
|
{
|
||||||
|
NextAction("wait for attack keep safe distance", ACTION_RAID)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForAttackStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new WaitForAttackMultiplier(botAI));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaitForAttackStrategy::ShouldWait(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("wait for attack", BOT_STATE_COMBAT))
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
if (bot->GetGroup() && botAI->HasRealPlayerMaster())
|
||||||
|
{
|
||||||
|
// Don't wait if the current target is an enemy player
|
||||||
|
Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||||
|
if (target && target->IsPlayer())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
|
time_t combatStartTime = context->GetValue<time_t>("combat start time")->Get();
|
||||||
|
|
||||||
|
if (bot->IsInCombat())
|
||||||
|
{
|
||||||
|
if (combatStartTime == 0)
|
||||||
|
{
|
||||||
|
combatStartTime = time(nullptr);
|
||||||
|
context->GetValue<time_t>("combat start time")->Set(combatStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t elapsedTime = time(nullptr) - combatStartTime;
|
||||||
|
return elapsedTime < GetWaitTime(botAI);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (combatStartTime != 0)
|
||||||
|
context->GetValue<time_t>("combat start time")->Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WaitForAttackStrategy::GetWaitTime(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return botAI->GetAiObjectContext()->GetValue<uint8>("wait for attack time")->Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
float WaitForAttackStrategy::GetSafeDistance()
|
||||||
|
{
|
||||||
|
return sPlayerbotAIConfig.spellDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float WaitForAttackMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
std::string const& actionName = action->getName();
|
||||||
|
|
||||||
|
if (actionName != "wait for attack keep safe distance" &&
|
||||||
|
actionName != "dps assist" &&
|
||||||
|
actionName != "set facing" &&
|
||||||
|
actionName != "pull my target" &&
|
||||||
|
actionName != "pull rti target" &&
|
||||||
|
actionName != "pull start" &&
|
||||||
|
actionName != "pull action" &&
|
||||||
|
actionName != "pull end")
|
||||||
|
{
|
||||||
|
return WaitForAttackStrategy::ShouldWait(botAI) ? 0.0f : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
39
src/Ai/Base/Strategy/WaitForAttackStrategy.h
Normal file
39
src/Ai/Base/Strategy/WaitForAttackStrategy.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_WAITFORATTACKSTRATEGY_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKSTRATEGY_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "wait for attack"; }
|
||||||
|
|
||||||
|
static bool ShouldWait(PlayerbotAI* botAI);
|
||||||
|
static uint8 GetWaitTime(PlayerbotAI* botAI);
|
||||||
|
static float GetSafeDistance();
|
||||||
|
static float GetSafeDistanceThreshold() { return 2.5f; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaitForAttackMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "wait for attack") {}
|
||||||
|
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -50,6 +50,7 @@
|
|||||||
#include "TravelStrategy.h"
|
#include "TravelStrategy.h"
|
||||||
#include "UseFoodStrategy.h"
|
#include "UseFoodStrategy.h"
|
||||||
#include "UsePotionsStrategy.h"
|
#include "UsePotionsStrategy.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
#include "WorldPacketHandlerStrategy.h"
|
#include "WorldPacketHandlerStrategy.h"
|
||||||
|
|
||||||
class StrategyContext : public NamedObjectContext<Strategy>
|
class StrategyContext : public NamedObjectContext<Strategy>
|
||||||
@ -124,6 +125,7 @@ public:
|
|||||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||||
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
||||||
creators["master fishing"] = &StrategyContext::master_fishing;
|
creators["master fishing"] = &StrategyContext::master_fishing;
|
||||||
|
creators["wait for attack"] = &StrategyContext::wait_for_attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -195,6 +197,7 @@ private:
|
|||||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||||
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
||||||
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
||||||
|
static Strategy* wait_for_attack(PlayerbotAI* botAI) { return new WaitForAttackStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||||
|
|||||||
@ -473,6 +473,14 @@ bool LossOfControlTrigger::IsActive()
|
|||||||
bot->HasAuraType(SPELL_AURA_MOD_CHARM);
|
bot->HasAuraType(SPELL_AURA_MOD_CHARM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FearCharmSleepTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
|
||||||
|
bot->HasAuraType(SPELL_AURA_MOD_CHARM) ||
|
||||||
|
bot->HasAuraType(SPELL_AURA_AOE_CHARM) ||
|
||||||
|
bot->HasAuraWithMechanic(1 << MECHANIC_SLEEP);
|
||||||
|
}
|
||||||
|
|
||||||
bool HasAuraStackTrigger::IsActive()
|
bool HasAuraStackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
||||||
|
|||||||
@ -754,6 +754,14 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FearCharmSleepTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FearCharmSleepTrigger(PlayerbotAI* botAI) : Trigger(botAI, "fear charm sleep", 1) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class IsSwimmingTrigger : public Trigger
|
class IsSwimmingTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
49
src/Ai/Base/Trigger/WaitForAttackTriggers.h
Normal file
49
src/Ai/Base/Trigger/WaitForAttackTriggers.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKTRIGGERS_H
|
||||||
|
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
#include "Trigger.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackSafeDistanceTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackSafeDistanceTrigger(PlayerbotAI* botAI)
|
||||||
|
: Trigger(botAI, "wait for attack safe distance") {}
|
||||||
|
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
if (!WaitForAttackStrategy::ShouldWait(botAI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do not move if stay strategy is set
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do not move if currently being targeted
|
||||||
|
if (!bot->getAttackers().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float safeDistance = WaitForAttackStrategy::GetSafeDistance();
|
||||||
|
float safeDistanceThreshold = WaitForAttackStrategy::GetSafeDistanceThreshold();
|
||||||
|
float distanceToTarget = ServerFacade::instance().GetDistance2d(bot, target);
|
||||||
|
|
||||||
|
return (distanceToTarget > (safeDistance + safeDistanceThreshold)) ||
|
||||||
|
(distanceToTarget < (safeDistance - safeDistanceThreshold));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#include "RtiTriggers.h"
|
#include "RtiTriggers.h"
|
||||||
#include "StuckTriggers.h"
|
#include "StuckTriggers.h"
|
||||||
#include "TravelTriggers.h"
|
#include "TravelTriggers.h"
|
||||||
|
#include "WaitForAttackTriggers.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ public:
|
|||||||
|
|
||||||
creators["generic boost"] = &TriggerContext::generic_boost;
|
creators["generic boost"] = &TriggerContext::generic_boost;
|
||||||
creators["loss of control"] = &TriggerContext::loss_of_control;
|
creators["loss of control"] = &TriggerContext::loss_of_control;
|
||||||
|
creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep;
|
||||||
|
|
||||||
creators["protect party member"] = &TriggerContext::protect_party_member;
|
creators["protect party member"] = &TriggerContext::protect_party_member;
|
||||||
|
|
||||||
@ -231,6 +233,7 @@ public:
|
|||||||
creators["can fish"] = &TriggerContext::can_fish;
|
creators["can fish"] = &TriggerContext::can_fish;
|
||||||
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
||||||
creators["new pet"] = &TriggerContext::new_pet;
|
creators["new pet"] = &TriggerContext::new_pet;
|
||||||
|
creators["wait for attack safe distance"] = &TriggerContext::wait_for_attack_safe_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -365,6 +368,7 @@ private:
|
|||||||
}
|
}
|
||||||
static Trigger* generic_boost(PlayerbotAI* botAI) { return new GenericBoostTrigger(botAI); }
|
static Trigger* generic_boost(PlayerbotAI* botAI) { return new GenericBoostTrigger(botAI); }
|
||||||
static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); }
|
static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); }
|
||||||
|
static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); }
|
||||||
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
|
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new PartyMemberCriticalHealthTrigger(botAI);
|
return new PartyMemberCriticalHealthTrigger(botAI);
|
||||||
@ -434,6 +438,7 @@ private:
|
|||||||
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
|
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
|
||||||
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }
|
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }
|
||||||
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
||||||
|
static Trigger* wait_for_attack_safe_distance(PlayerbotAI* ai) { return new WaitForAttackSafeDistanceTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -143,3 +143,28 @@ namespace ai::buff
|
|||||||
return castName;
|
return castName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ai::spell
|
||||||
|
{
|
||||||
|
bool HasSpellOrCategoryCooldown(Player* bot, uint32 spellId)
|
||||||
|
{
|
||||||
|
if (bot->HasSpellCooldown(spellId))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (!spellInfo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32 category = spellInfo->GetCategory();
|
||||||
|
if (!category)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto const& [cooldownSpellId, cooldown] : bot->GetSpellCooldownMap())
|
||||||
|
{
|
||||||
|
if (cooldown.category == category && bot->GetSpellCooldownDelay(cooldownSpellId) > 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/Ai/Base/Util/GenericBuffUtils.h
Normal file
68
src/Ai/Base/Util/GenericBuffUtils.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
#include "Language.h"
|
||||||
|
|
||||||
|
class Player;
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
namespace ai::buff
|
||||||
|
{
|
||||||
|
|
||||||
|
// Build an aura qualifier "single + greater" to avoid double-buffing
|
||||||
|
std::string MakeAuraQualifierForBuff(std::string const& name);
|
||||||
|
|
||||||
|
// Returns the group spell name for a given single-target buff.
|
||||||
|
// If no group equivalent exists, returns "".
|
||||||
|
std::string GroupVariantFor(std::string const& name);
|
||||||
|
|
||||||
|
// Checks if the bot has the required reagents to cast a spell (by its spellId).
|
||||||
|
// Returns false if the spellId is invalid.
|
||||||
|
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
||||||
|
|
||||||
|
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
||||||
|
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
||||||
|
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
||||||
|
// (if provided) to notify the party/raid.
|
||||||
|
std::string UpgradeToGroupIfAppropriate(
|
||||||
|
Player* bot,
|
||||||
|
PlayerbotAI* botAI,
|
||||||
|
std::string const& baseName,
|
||||||
|
bool announceOnMissing = false,
|
||||||
|
std::function<void(std::string const&)> announce = {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ai::spell
|
||||||
|
{
|
||||||
|
bool HasSpellOrCategoryCooldown(Player* bot, uint32 spellId);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ai::chat {
|
||||||
|
inline std::function<void(std::string const&)> MakeGroupAnnouncer(Player* me)
|
||||||
|
{
|
||||||
|
return [me](std::string const& msg)
|
||||||
|
{
|
||||||
|
if (Group* g = me->GetGroup())
|
||||||
|
{
|
||||||
|
WorldPacket data;
|
||||||
|
ChatMsg type = g->isRaidGroup() ? CHAT_MSG_RAID : CHAT_MSG_PARTY;
|
||||||
|
ChatHandler::BuildChatPacket(data, type, LANG_UNIVERSAL, me, /*receiver=*/nullptr, msg.c_str());
|
||||||
|
g->BroadcastPacket(&data, true, -1, me->GetGUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
me->Say(msg, LANG_UNIVERSAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Ai/Base/Value/WaitForAttackTimeValue.h
Normal file
25
src/Ai/Base/Value/WaitForAttackTimeValue.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKTIMEVALUE_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKTIMEVALUE_H
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackTimeValue : public ManualSetValue<uint8>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackTimeValue(PlayerbotAI* botAI) : ManualSetValue<uint8>(botAI, 10, "wait for attack time") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CombatStartTimeValue : public ManualSetValue<time_t>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CombatStartTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0, "combat start time") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -91,6 +91,7 @@
|
|||||||
#include "ThreatValues.h"
|
#include "ThreatValues.h"
|
||||||
#include "TradeValues.h"
|
#include "TradeValues.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
#include "WaitForAttackTimeValue.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -322,6 +323,8 @@ public:
|
|||||||
creators["can fish"] = &ValueContext::can_fish;
|
creators["can fish"] = &ValueContext::can_fish;
|
||||||
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
||||||
creators["fishing spot"] = &ValueContext::fishing_spot;
|
creators["fishing spot"] = &ValueContext::fishing_spot;
|
||||||
|
creators["wait for attack time"] = &ValueContext::wait_for_attack_time;
|
||||||
|
creators["combat start time"] = &ValueContext::combat_start_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -578,6 +581,8 @@ private:
|
|||||||
{
|
{
|
||||||
return new ManualSetValue<bool>(ai, false, "custom_glyphs");
|
return new ManualSetValue<bool>(ai, false, "custom_glyphs");
|
||||||
}
|
}
|
||||||
|
static UntypedValue* wait_for_attack_time(PlayerbotAI* ai) { return new WaitForAttackTimeValue(ai); }
|
||||||
|
static UntypedValue* combat_start_time(PlayerbotAI* ai) { return new CombatStartTimeValue(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -112,6 +112,7 @@ public:
|
|||||||
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
|
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
|
||||||
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
|
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
|
||||||
creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check;
|
creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check;
|
||||||
|
creators["no healer dps strategy"] = &DruidTriggerFactoryInternal::no_healer_dps_strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -149,6 +150,7 @@ private:
|
|||||||
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); }
|
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); }
|
||||||
static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); }
|
static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); }
|
||||||
static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); }
|
static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); }
|
||||||
|
static Trigger* no_healer_dps_strategy(PlayerbotAI* ai) { return new NoHealerDpsStrategyTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
|
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
|
||||||
|
|||||||
@ -149,10 +149,10 @@ void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("healer should attack",
|
new TriggerNode("healer should attack",
|
||||||
{
|
{
|
||||||
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
|
NextAction("cancel tree form", ACTION_DEFAULT + 0.4f),
|
||||||
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
|
NextAction("moonfire", ACTION_DEFAULT + 0.3f),
|
||||||
NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
NextAction("wrath", ACTION_DEFAULT + 0.2f),
|
||||||
NextAction("starfire", ACTION_DEFAULT),
|
NextAction("starfire", ACTION_DEFAULT + 0.1f),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,10 @@ void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
GenericDruidStrategy::InitTriggers(triggers);
|
GenericDruidStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
|
// no healer dps strategy
|
||||||
|
triggers.push_back(new TriggerNode("no healer dps strategy",
|
||||||
|
{ NextAction("tree form", ACTION_DEFAULT) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"party member to heal out of spell range",
|
"party member to heal out of spell range",
|
||||||
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
|
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
|
||||||
|
|||||||
@ -280,4 +280,15 @@ protected:
|
|||||||
static const std::set<uint32> HURRICANE_SPELL_IDS;
|
static const std::set<uint32> HURRICANE_SPELL_IDS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NoHealerDpsStrategyTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoHealerDpsStrategyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no healer dps strategy") {}
|
||||||
|
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
return !botAI->HasStrategy("healer dps", BOT_STATE_COMBAT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -107,6 +107,8 @@ bool SwitchToMeleeTrigger::IsActive()
|
|||||||
ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f));
|
ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Valid targets for "Improved Tracking".
|
||||||
|
// Optional/Utility targets (uncomment for selfbot).
|
||||||
bool NoTrackTrigger::IsActive()
|
bool NoTrackTrigger::IsActive()
|
||||||
{
|
{
|
||||||
std::vector<std::string> track_list = {
|
std::vector<std::string> track_list = {
|
||||||
@ -115,8 +117,13 @@ bool NoTrackTrigger::IsActive()
|
|||||||
"track dragonkin",
|
"track dragonkin",
|
||||||
"track elementals",
|
"track elementals",
|
||||||
"track giants",
|
"track giants",
|
||||||
"track hidden",
|
"track humanoids",
|
||||||
"track humanoids"
|
"track undead",
|
||||||
|
// "track hidden",
|
||||||
|
// "find herbs",
|
||||||
|
// "find minerals",
|
||||||
|
// "find fish",
|
||||||
|
// "find treasure",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto &track: track_list)
|
for (auto &track: track_list)
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
||||||
#include "GenericBuffUtils.h"
|
#include "Ai/Base/Util/GenericBuffUtils.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "RaidNaxxStrategy.h"
|
#include "RaidNaxxStrategy.h"
|
||||||
#include "RaidSSCStrategy.h"
|
#include "RaidSSCStrategy.h"
|
||||||
#include "RaidTempestKeepStrategy.h"
|
#include "RaidTempestKeepStrategy.h"
|
||||||
|
#include "RaidZulAmanStrategy.h"
|
||||||
#include "RaidOsStrategy.h"
|
#include "RaidOsStrategy.h"
|
||||||
#include "RaidEoEStrategy.h"
|
#include "RaidEoEStrategy.h"
|
||||||
#include "RaidVoAStrategy.h"
|
#include "RaidVoAStrategy.h"
|
||||||
@ -32,6 +33,7 @@ public:
|
|||||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||||
|
creators["zulaman"] = &RaidStrategyContext::zulaman;
|
||||||
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;
|
||||||
@ -50,6 +52,7 @@ private:
|
|||||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||||
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
||||||
|
static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(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); }
|
||||||
|
|||||||
@ -116,8 +116,8 @@ public:
|
|||||||
creators["kael'thas sunstrider assign legendary weapon dps priority"] =
|
creators["kael'thas sunstrider assign legendary weapon dps priority"] =
|
||||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_legendary_weapon_dps_priority;
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_legendary_weapon_dps_priority;
|
||||||
|
|
||||||
creators["kael'thas sunstrider main tank move devastation away"] =
|
creators["kael'thas sunstrider move devastation away"] =
|
||||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_move_devastation_away;
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_move_devastation_away;
|
||||||
|
|
||||||
creators["kael'thas sunstrider loot legendary weapons"] =
|
creators["kael'thas sunstrider loot legendary weapons"] =
|
||||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons;
|
&RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons;
|
||||||
@ -255,7 +255,7 @@ private:
|
|||||||
static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority(
|
static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority(
|
||||||
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(botAI); }
|
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
static Action* kaelthas_sunstrider_main_tank_move_devastation_away(
|
static Action* kaelthas_sunstrider_move_devastation_away(
|
||||||
PlayerbotAI* botAI) { return new KaelthasSunstriderMoveDevastationAwayAction(botAI); }
|
PlayerbotAI* botAI) { return new KaelthasSunstriderMoveDevastationAwayAction(botAI); }
|
||||||
|
|
||||||
static Action* kaelthas_sunstrider_loot_legendary_weapons(
|
static Action* kaelthas_sunstrider_loot_legendary_weapons(
|
||||||
|
|||||||
@ -105,7 +105,7 @@ void RaidTempestKeepStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
NextAction("kael'thas sunstrider assign legendary weapon dps priority", ACTION_RAID + 1) }));
|
NextAction("kael'thas sunstrider assign legendary weapon dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary axe casts whirlwind", {
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary axe casts whirlwind", {
|
||||||
NextAction("kael'thas sunstrider main tank move devastation away", ACTION_EMERGENCY + 1) }));
|
NextAction("kael'thas sunstrider move devastation away", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are dead and lootable", {
|
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are dead and lootable", {
|
||||||
NextAction("kael'thas sunstrider loot legendary weapons", ACTION_RAID) }));
|
NextAction("kael'thas sunstrider loot legendary weapons", ACTION_RAID) }));
|
||||||
|
|||||||
745
src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.cpp
Normal file
745
src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.cpp
Normal file
@ -0,0 +1,745 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RaidZulAmanActions.h"
|
||||||
|
#include "RaidZulAmanHelpers.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
using namespace ZulAmanHelpers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
bool AmanishiMedicineManMarkWardAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (Unit* protectiveWard = GetFirstAliveUnitByEntry(
|
||||||
|
botAI, static_cast<uint32>(ZulAmanNPCs::NPC_AMANI_PROTECTIVE_WARD)))
|
||||||
|
{
|
||||||
|
MarkTargetWithSkull(bot, protectiveWard);
|
||||||
|
}
|
||||||
|
else if (Unit* healingWard = GetFirstAliveUnitByEntry(
|
||||||
|
botAI, static_cast<uint32>(ZulAmanNPCs::NPC_AMANI_HEALING_WARD)))
|
||||||
|
{
|
||||||
|
MarkTargetWithSkull(bot, healingWard);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
bool AkilzonMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon");
|
||||||
|
if (!akilzon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", akilzon))
|
||||||
|
return botAI->CastSpell("steady shot", akilzon);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonTanksPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon");
|
||||||
|
if (!akilzon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != akilzon)
|
||||||
|
return Attack(akilzon);
|
||||||
|
|
||||||
|
if (akilzon->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Position& position = AKILZON_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonSpreadRangedAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
constexpr float minDistance = 13.0f;
|
||||||
|
constexpr uint32 minInterval = 1000;
|
||||||
|
if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance))
|
||||||
|
return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonMoveToEyeOfTheStormAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Player* target = GetElectricalStormTarget(bot);
|
||||||
|
if (!target && !botAI->IsMainTank(bot))
|
||||||
|
target = GetGroupMainTank(botAI, bot);
|
||||||
|
|
||||||
|
if (target && bot->GetExactDist2d(target) > 2.0f)
|
||||||
|
{
|
||||||
|
botAI->Reset();
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, target->GetPositionX(), target->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonManageElectricalStormTimerAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
const uint32 instanceId = bot->GetMap()->GetInstanceId();
|
||||||
|
|
||||||
|
Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon");
|
||||||
|
if (akilzon)
|
||||||
|
{
|
||||||
|
auto [it, inserted] = akilzonStormTimer.try_emplace(instanceId, now);
|
||||||
|
return inserted;
|
||||||
|
}
|
||||||
|
else if (!bot->IsInCombat() && !akilzon && akilzonStormTimer.erase(instanceId) > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
bool NalorakkMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
if (!nalorakk)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", nalorakk))
|
||||||
|
return botAI->CastSpell("steady shot", nalorakk);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkTanksPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
if (!nalorakk)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return MainTankPositionTrollForm(nalorakk);
|
||||||
|
else
|
||||||
|
return FirstAssistTankPositionBearForm(nalorakk);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkTanksPositionBossAction::MainTankPositionTrollForm(Unit* nalorakk)
|
||||||
|
{
|
||||||
|
if (!nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() != nalorakk)
|
||||||
|
return Attack(nalorakk);
|
||||||
|
|
||||||
|
if (nalorakk->GetVictim() != bot)
|
||||||
|
return botAI->DoSpecificAction("taunt spell", Event(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Position& position = NALORAKK_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkTanksPositionBossAction::FirstAssistTankPositionBearForm(Unit* nalorakk)
|
||||||
|
{
|
||||||
|
if (nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() != nalorakk)
|
||||||
|
return Attack(nalorakk);
|
||||||
|
|
||||||
|
if (nalorakk->GetVictim() != bot)
|
||||||
|
return botAI->DoSpecificAction("taunt spell", Event(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Position& position = NALORAKK_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkSpreadRangedAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
constexpr float minDistance = 11.0f;
|
||||||
|
constexpr uint32 minInterval = 1000;
|
||||||
|
if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance))
|
||||||
|
return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
bool JanalaiMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai");
|
||||||
|
if (!janalai)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", janalai))
|
||||||
|
return botAI->CastSpell("steady shot", janalai);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiTanksPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai");
|
||||||
|
if (!janalai)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != janalai)
|
||||||
|
return Attack(janalai);
|
||||||
|
|
||||||
|
if (janalai->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Position& position = JANALAI_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiSpreadRangedInCircleAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<Player*> rangedMembers;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !botAI->IsRanged(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rangedMembers.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangedMembers.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto findIt = std::find(rangedMembers.begin(), rangedMembers.end(), bot);
|
||||||
|
size_t botIndex =
|
||||||
|
(findIt != rangedMembers.end()) ? std::distance(rangedMembers.begin(), findIt) : 0;
|
||||||
|
size_t count = rangedMembers.size();
|
||||||
|
if (count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
constexpr float radius = 15.0f;
|
||||||
|
float angle = (count == 1) ? 0.0f :
|
||||||
|
(2.0f * M_PI * static_cast<float>(botIndex) / static_cast<float>(count));
|
||||||
|
|
||||||
|
float targetX = JANALAI_TANK_POSITION.GetPositionX() + radius * std::cos(angle);
|
||||||
|
float targetY = JANALAI_TANK_POSITION.GetPositionY() + radius * std::sin(angle);
|
||||||
|
|
||||||
|
if (bot->GetExactDist2d(targetX, targetY) > 2.0f)
|
||||||
|
{
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, targetX, targetY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiAvoidFireBombsAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
auto const& bombs = GetAllHazardTriggers(
|
||||||
|
bot, static_cast<uint32>(ZulAmanNPCs::NPC_FIRE_BOMB), 50.0f);
|
||||||
|
|
||||||
|
if (bombs.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
constexpr float hazardRadius = 5.0f;
|
||||||
|
bool inDanger = false;
|
||||||
|
for (Unit* bomb : bombs)
|
||||||
|
{
|
||||||
|
if (bot->GetDistance2d(bomb) < hazardRadius)
|
||||||
|
{
|
||||||
|
inDanger = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inDanger)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Position& janalaiCenter = JANALAI_TANK_POSITION;
|
||||||
|
constexpr float safeZoneRadius = 17.0f;
|
||||||
|
|
||||||
|
Position safestPos =
|
||||||
|
FindSafestNearbyPosition(bot, bombs, janalaiCenter, safeZoneRadius, hazardRadius, false);
|
||||||
|
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, safestPos.GetPositionX(), safestPos.GetPositionY(),
|
||||||
|
bot->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiMarkAmanishiHatchersAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
auto [hatcherLow, hatcherHigh] = GetAmanishiHatcherPair(botAI);
|
||||||
|
|
||||||
|
if (hatcherLow && hatcherHigh && hatcherHigh != hatcherLow)
|
||||||
|
{
|
||||||
|
MarkTargetWithSkull(bot, hatcherLow);
|
||||||
|
MarkTargetWithMoon(bot, hatcherHigh);
|
||||||
|
SetRtiTarget(botAI, "skull", hatcherLow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
bool HalazziMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
if (!halazzi)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", halazzi))
|
||||||
|
return botAI->CastSpell("steady shot", halazzi);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziMainTankPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
if (!halazzi)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MarkTargetWithStar(bot, halazzi);
|
||||||
|
SetRtiTarget(botAI, "star", halazzi);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != halazzi)
|
||||||
|
return Attack(halazzi);
|
||||||
|
|
||||||
|
if (halazzi->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Position& position = HALAZZI_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziFirstAssistTankAttackSpiritLynxAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
bool targetFound = false;
|
||||||
|
|
||||||
|
if (Unit* lynx = AI_VALUE2(Unit*, "find target", "spirit of the lynx"))
|
||||||
|
{
|
||||||
|
MarkTargetWithCircle(bot, lynx);
|
||||||
|
SetRtiTarget(botAI, "circle", lynx);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != lynx)
|
||||||
|
return Attack(lynx);
|
||||||
|
|
||||||
|
if (lynx->GetVictim() != bot)
|
||||||
|
return botAI->DoSpecificAction("taunt spell", Event(), true);
|
||||||
|
|
||||||
|
targetFound = true;
|
||||||
|
}
|
||||||
|
else if (Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi"))
|
||||||
|
{
|
||||||
|
SetRtiTarget(botAI, "star", halazzi);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != halazzi)
|
||||||
|
return Attack(halazzi);
|
||||||
|
|
||||||
|
targetFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetFound)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Position& position = HALAZZI_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziAssignDpsPriorityAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
// Target priority 1: Corrupted Lightning Totems
|
||||||
|
if (Unit* totem = GetFirstAliveUnitByEntry(
|
||||||
|
botAI, static_cast<uint32>(ZulAmanNPCs::NPC_CORRUPTED_LIGHTNING_TOTEM)))
|
||||||
|
{
|
||||||
|
MarkTargetWithSkull(bot, totem);
|
||||||
|
SetRtiTarget(botAI, "skull", totem);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != totem->GetGUID())
|
||||||
|
return Attack(totem);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target priority 2: Halazzi
|
||||||
|
if (Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi"))
|
||||||
|
{
|
||||||
|
SetRtiTarget(botAI, "star", halazzi);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != halazzi->GetGUID())
|
||||||
|
return Attack(halazzi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't attack the Lynx
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
bool HexLordMalacrassMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
if (!malacrass)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", malacrass))
|
||||||
|
return botAI->CastSpell("steady shot", malacrass);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassAssignDpsPriorityAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
static constexpr uint32 priorityEntries[] =
|
||||||
|
{
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_LORD_RAADAN),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_ALYSON_ANTILLE),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_KORAGG),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_DARKHEART),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_FENSTALKER),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_GAZAKROTH),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_THURG),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_SLITHER),
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_HEX_LORD_MALACRASS)
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const& targets =
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get();
|
||||||
|
|
||||||
|
Unit* priorityTarget = nullptr;
|
||||||
|
|
||||||
|
for (uint32 entry : priorityEntries)
|
||||||
|
{
|
||||||
|
for (auto const& guid : targets)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||||
|
{
|
||||||
|
priorityTarget = unit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorityTarget)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priorityTarget)
|
||||||
|
{
|
||||||
|
MarkTargetWithSkull(bot, priorityTarget);
|
||||||
|
SetRtiTarget(botAI, "skull", priorityTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassRunAwayFromWhirlwindAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass"))
|
||||||
|
{
|
||||||
|
float currentDistance = bot->GetDistance2d(malacrass);
|
||||||
|
constexpr float safeDistance = 9.0f;
|
||||||
|
if (currentDistance < safeDistance)
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return MoveAway(malacrass, safeDistance - currentDistance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassCastersStopAttackingAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
if (!malacrass ||
|
||||||
|
!malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == malacrass)
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassMoveAwayFromFreezingTrapAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
GameObject* trapGo = bot->FindNearestGameObject(
|
||||||
|
static_cast<uint32>(ZulAmanObjects::GO_FREEZING_TRAP), 20.0f, true);
|
||||||
|
|
||||||
|
if (!trapGo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float currentDistance = bot->GetDistance2d(trapGo);
|
||||||
|
constexpr float safeDistance = 6.0f;
|
||||||
|
constexpr uint32 minInterval = 0;
|
||||||
|
if (currentDistance < safeDistance)
|
||||||
|
return FleePosition(trapGo->GetPosition(), safeDistance, minInterval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
bool ZuljinMisdirectBossToMainTankAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
|
if (!mainTank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
|
return botAI->CastSpell("misdirection", mainTank);
|
||||||
|
|
||||||
|
if (bot->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_MISDIRECTION)) &&
|
||||||
|
botAI->CanCastSpell("steady shot", zuljin))
|
||||||
|
return botAI->CastSpell("steady shot", zuljin);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinTanksPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != zuljin)
|
||||||
|
return Attack(zuljin);
|
||||||
|
|
||||||
|
if (zuljin->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Position& position = ZULJIN_TANK_POSITION;
|
||||||
|
float distToPosition =
|
||||||
|
bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
|
if (distToPosition > 2.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(10.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinRunAwayFromWhirlwindAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin"))
|
||||||
|
{
|
||||||
|
float currentDistance = bot->GetExactDist2d(zuljin);
|
||||||
|
constexpr float safeDistance = 10.0f;
|
||||||
|
if (currentDistance < safeDistance)
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return MoveAway(zuljin, safeDistance - currentDistance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinAvoidCyclonesAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
auto const& cyclones = GetAllHazardTriggers(
|
||||||
|
bot, static_cast<uint32>(ZulAmanNPCs::NPC_FEATHER_VORTEX), 50.0f);
|
||||||
|
|
||||||
|
if (cyclones.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
constexpr float hazardRadius = 6.0f;
|
||||||
|
bool inDanger = false;
|
||||||
|
for (Unit* cyclone : cyclones)
|
||||||
|
{
|
||||||
|
if (bot->GetDistance2d(cyclone) < hazardRadius)
|
||||||
|
{
|
||||||
|
inDanger = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inDanger)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Position& zuljinCenter = ZULJIN_TANK_POSITION;
|
||||||
|
constexpr float safeZoneRadius = 30.0f;
|
||||||
|
|
||||||
|
Position safestPos =
|
||||||
|
FindSafestNearbyPosition(bot, cyclones, zuljinCenter, safeZoneRadius, hazardRadius, true);
|
||||||
|
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return MoveTo(ZULAMAN_MAP_ID, safestPos.GetPositionX(), safestPos.GetPositionY(),
|
||||||
|
bot->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinSpreadRangedAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
constexpr float minDistance = 6.0f;
|
||||||
|
constexpr uint32 minInterval = 1000;
|
||||||
|
if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance))
|
||||||
|
return FleePosition(nearestPlayer->GetPosition(), minDistance, minInterval);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
253
src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.h
Normal file
253
src/Ai/Raid/ZulAman/Action/RaidZulAmanActions.h
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_RAIDZULAMANACTIONS_H
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANACTIONS_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class AmanishiMedicineManMarkWardAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AmanishiMedicineManMarkWardAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "amani'shi medicine man mark ward") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
class AkilzonMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "akil'zon misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonTanksPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonTanksPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "akil'zon tanks position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonSpreadRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "akil'zon spread ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonMoveToEyeOfTheStormAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonMoveToEyeOfTheStormAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "akil'zon move to eye of the storm") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonManageElectricalStormTimerAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonManageElectricalStormTimerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "akil'zon manage electrical storm timer") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
class NalorakkMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nalorakk misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalorakkTanksPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkTanksPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nalorakk tanks position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool MainTankPositionTrollForm(Unit* nalorakk);
|
||||||
|
bool FirstAssistTankPositionBearForm(Unit* nalorakk);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalorakkSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkSpreadRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nalorakk spread ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
class JanalaiMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "jan'alai misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiTanksPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiTanksPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "jan'alai tanks position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiSpreadRangedInCircleAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiSpreadRangedInCircleAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "jan'alai spread ranged in circle") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiAvoidFireBombsAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiAvoidFireBombsAction(PlayerbotAI* botAI, std::string const name = "jan'alai avoid fire bombs") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiMarkAmanishiHatchersAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiMarkAmanishiHatchersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "jan'alai mark amani'shi hatchers") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
class HalazziMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "halazzi misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziMainTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziMainTankPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "halazzi main tank position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziFirstAssistTankAttackSpiritLynxAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziFirstAssistTankAttackSpiritLynxAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "halazzi first assist tank attack spirit lynx") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziAssignDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziAssignDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "halazzi assign dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
class HexLordMalacrassMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hex lord malacrass misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassAssignDpsPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassAssignDpsPriorityAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hex lord malacrass assign dps priority") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassRunAwayFromWhirlwindAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassRunAwayFromWhirlwindAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hex lord malacrass run away from whirlwind") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassCastersStopAttackingAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassCastersStopAttackingAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hex lord malacrass casters stop attacking") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassMoveAwayFromFreezingTrapAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassMoveAwayFromFreezingTrapAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "hex lord malacrass move away from freezing trap") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
class ZuljinMisdirectBossToMainTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinMisdirectBossToMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "zul'jin misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinTanksPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinTanksPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "zul'jin tanks position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinRunAwayFromWhirlwindAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinRunAwayFromWhirlwindAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "zul'jin run away from whirlwind") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinAvoidCyclonesAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinAvoidCyclonesAction(PlayerbotAI* botAI, std::string const name = "zul'jin avoid cyclones") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinSpreadRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "zul'jin spread ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
382
src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp
Normal file
382
src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RaidZulAmanMultipliers.h"
|
||||||
|
#include "RaidZulAmanActions.h"
|
||||||
|
#include "RaidZulAmanHelpers.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "DKActions.h"
|
||||||
|
#include "DruidBearActions.h"
|
||||||
|
#include "FollowActions.h"
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "MageActions.h"
|
||||||
|
#include "PaladinActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "PriestActions.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
|
#include "RogueActions.h"
|
||||||
|
#include "ShamanActions.h"
|
||||||
|
#include "WarlockActions.h"
|
||||||
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
|
using namespace ZulAmanHelpers;
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
float AkilzonDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "akil'zon"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AkilzonStayInEyeOfTheStormMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "akil'zon") /* ||
|
||||||
|
!GetElectricalStormTarget(bot)*/)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId());
|
||||||
|
if (it == akilzonStormTimer.end() ||
|
||||||
|
!IsInStormWindow(it->second, std::time(nullptr)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<SetBehindTargetAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
float NalorakkDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
if (!nalorakk)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankFaceAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
bool shouldTankBoss = false;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot) &&
|
||||||
|
!nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
|
||||||
|
shouldTankBoss = true;
|
||||||
|
|
||||||
|
if (botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||||
|
nalorakk->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_BEARFORM)))
|
||||||
|
shouldTankBoss = true;
|
||||||
|
|
||||||
|
if (!shouldTankBoss &&
|
||||||
|
(dynamic_cast<TankAssistAction*>(action) ||
|
||||||
|
dynamic_cast<CastTauntAction*>(action) ||
|
||||||
|
dynamic_cast<CastGrowlAction*>(action) ||
|
||||||
|
dynamic_cast<CastHandOfReckoningAction*>(action) ||
|
||||||
|
dynamic_cast<CastDarkCommandAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NalorakkControlMisdirectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "nalorakk"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
float JanalaiDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankFaceAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot) &&
|
||||||
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (botAI->IsAssistTank(bot) &&
|
||||||
|
!GetFirstAliveUnitByEntry(
|
||||||
|
botAI, static_cast<uint32>(ZulAmanNPCs::NPC_AMANI_DRAGONHAWK_HATCHLING)) &&
|
||||||
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float JanalaiDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float JanalaiStayAwayFromFireBombsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!HasFireBombNearby(botAI, bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float JanalaiDoNotCrowdControlHatchersMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "amani'shi hatcher"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastCrowdControlSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastPolymorphAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float JanalaiDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_SHAMAN)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "amani dragonhawk hatchling"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
float HalazziDisableTankActionsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "halazzi"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankFaceAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != nullptr &&
|
||||||
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HalazziControlMisdirectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "halazzi"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
float HexLordMalacrassAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
if (!malacrass ||
|
||||||
|
!malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_WHIRLWIND)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsCaster(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
if (!malacrass ||
|
||||||
|
!malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto castSpellAction = dynamic_cast<CastSpellAction*>(action);
|
||||||
|
if (!castSpellAction)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe ||
|
||||||
|
(bot->GetVictim() == malacrass &&
|
||||||
|
castSpellAction->getThreatType() == Action::ActionThreatType::Single))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_PRIEST &&
|
||||||
|
bot->getClass() != CLASS_PALADIN &&
|
||||||
|
bot->getClass() != CLASS_WARLOCK)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "hex lord malacrass"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
bool hasUnstableAffliction = false;
|
||||||
|
for (GroupReference* ref = bot->GetGroup()->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_UNSTABLE_AFFLICTION)))
|
||||||
|
{
|
||||||
|
hasUnstableAffliction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasUnstableAffliction)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastDevourMagicCleanseAction*>(action) ||
|
||||||
|
dynamic_cast<CastDispelMagicAction*>(action) ||
|
||||||
|
dynamic_cast<CastDispelMagicOnPartyAction*>(action) ||
|
||||||
|
dynamic_cast<CastMassDispelAction*>(action) ||
|
||||||
|
dynamic_cast<CastPurgeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
float ZuljinDisableTankFaceMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin ||
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<TankFaceAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ZuljinAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin ||
|
||||||
|
!zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_ZULJIN_WHIRLWIND)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ZuljinDisableAvoidAoeMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin ||
|
||||||
|
!zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ZuljinDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_SHAMAN)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin ||
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE)))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
167
src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.h
Normal file
167
src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
class AkilzonDisableCombatFormationMoveMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonDisableCombatFormationMoveMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "akil'zon disable combat formation move") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonStayInEyeOfTheStormMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonStayInEyeOfTheStormMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "akil'zon stay in eye of the storm") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
class NalorakkDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "nalorakk disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalorakkControlMisdirectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkControlMisdirectionMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "nalorakk control misdirection") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
class JanalaiDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "jan'alai disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiDisableCombatFormationMoveMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiDisableCombatFormationMoveMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "jan'alai disable combat formation move") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiStayAwayFromFireBombsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiStayAwayFromFireBombsMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "jan'alai stay away from fire bombs") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiDoNotCrowdControlHatchersMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiDoNotCrowdControlHatchersMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "jan'alai do not crowd control hatchers") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiDelayBloodlustAndHeroismMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "jan'alai delay bloodlust and heroism") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
class HalazziDisableTankActionsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziDisableTankActionsMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "halazzi disable tank actions") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziControlMisdirectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziControlMisdirectionMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "halazzi control misdirection") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
class HexLordMalacrassAvoidWhirlwindMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "hex lord malacrass avoid whirlwind") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "hex lord malacrass do not dispel unstable affliction") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "hex lord malacrass stop attacking during spell reflection") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
class ZuljinDisableTankFaceMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinDisableTankFaceMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "zul'jin disable tank face") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinAvoidWhirlwindMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "zul'jin avoid whirlwind") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinDisableAvoidAoeMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinDisableAvoidAoeMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "zul'jin disable avoid aoe") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinDelayBloodlustAndHeroismMultiplier(PlayerbotAI* botAI) : Multiplier(
|
||||||
|
botAI, "zul'jin delay bloodlust and heroism") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
202
src/Ai/Raid/ZulAman/RaidZulAmanActionContext.h
Normal file
202
src/Ai/Raid/ZulAman/RaidZulAmanActionContext.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidZulAmanActions.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
|
class RaidZulAmanActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidZulAmanActionContext()
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
creators["amani'shi medicine man mark ward"] =
|
||||||
|
&RaidZulAmanActionContext::amanishi_medicine_man_mark_ward;
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
creators["akil'zon misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::akilzon_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["akil'zon tanks position boss"] =
|
||||||
|
&RaidZulAmanActionContext::akilzon_tanks_position_boss;
|
||||||
|
|
||||||
|
creators["akil'zon spread ranged"] =
|
||||||
|
&RaidZulAmanActionContext::akilzon_spread_ranged;
|
||||||
|
|
||||||
|
creators["akil'zon move to eye of the storm"] =
|
||||||
|
&RaidZulAmanActionContext::akilzon_move_to_eye_of_the_storm;
|
||||||
|
|
||||||
|
creators["akil'zon manage electrical storm timer"] =
|
||||||
|
&RaidZulAmanActionContext::akilzon_manage_electrical_storm_timer;
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
creators["nalorakk misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::nalorakk_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["nalorakk tanks position boss"] =
|
||||||
|
&RaidZulAmanActionContext::nalorakk_tanks_position_boss;
|
||||||
|
|
||||||
|
creators["nalorakk spread ranged"] =
|
||||||
|
&RaidZulAmanActionContext::nalorakk_spread_ranged;
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
creators["jan'alai misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::janalai_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["jan'alai tanks position boss"] =
|
||||||
|
&RaidZulAmanActionContext::janalai_tanks_position_boss;
|
||||||
|
|
||||||
|
creators["jan'alai spread ranged in circle"] =
|
||||||
|
&RaidZulAmanActionContext::janalai_spread_ranged_in_circle;
|
||||||
|
|
||||||
|
creators["jan'alai avoid fire bombs"] =
|
||||||
|
&RaidZulAmanActionContext::janalai_avoid_fire_bombs;
|
||||||
|
|
||||||
|
creators["jan'alai mark amani'shi hatchers"] =
|
||||||
|
&RaidZulAmanActionContext::janalai_mark_amanishi_hatchers;
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
creators["halazzi misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::halazzi_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["halazzi main tank position boss"] =
|
||||||
|
&RaidZulAmanActionContext::halazzi_main_tank_position_boss;
|
||||||
|
|
||||||
|
creators["halazzi first assist tank attack spirit lynx"] =
|
||||||
|
&RaidZulAmanActionContext::halazzi_first_assist_tank_attack_spirit_lynx;
|
||||||
|
|
||||||
|
creators["halazzi assign dps priority"] =
|
||||||
|
&RaidZulAmanActionContext::halazzi_assign_dps_priority;
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
creators["hex lord malacrass misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::hex_lord_malacrass_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["hex lord malacrass assign dps priority"] =
|
||||||
|
&RaidZulAmanActionContext::hex_lord_malacrass_assign_dps_priority;
|
||||||
|
|
||||||
|
creators["hex lord malacrass run away from whirlwind"] =
|
||||||
|
&RaidZulAmanActionContext::hex_lord_malacrass_run_away_from_whirlwind;
|
||||||
|
|
||||||
|
creators["hex lord malacrass casters stop attacking"] =
|
||||||
|
&RaidZulAmanActionContext::hex_lord_malacrass_casters_stop_attacking;
|
||||||
|
|
||||||
|
creators["hex lord malacrass move away from freezing trap"] =
|
||||||
|
&RaidZulAmanActionContext::hex_lord_malacrass_move_away_from_freezing_trap;
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
creators["zul'jin misdirect boss to main tank"] =
|
||||||
|
&RaidZulAmanActionContext::zuljin_misdirect_boss_to_main_tank;
|
||||||
|
|
||||||
|
creators["zul'jin tanks position boss"] =
|
||||||
|
&RaidZulAmanActionContext::zuljin_tanks_position_boss;
|
||||||
|
|
||||||
|
creators["zul'jin run away from whirlwind"] =
|
||||||
|
&RaidZulAmanActionContext::zuljin_run_away_from_whirlwind;
|
||||||
|
|
||||||
|
creators["zul'jin avoid cyclones"] =
|
||||||
|
&RaidZulAmanActionContext::zuljin_avoid_cyclones;
|
||||||
|
|
||||||
|
creators["zul'jin spread ranged"] =
|
||||||
|
&RaidZulAmanActionContext::zuljin_spread_ranged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Trash
|
||||||
|
static Action* amanishi_medicine_man_mark_ward(
|
||||||
|
PlayerbotAI* botAI) { return new AmanishiMedicineManMarkWardAction(botAI); }
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
static Action* akilzon_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* akilzon_tanks_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonTanksPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* akilzon_spread_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonSpreadRangedAction(botAI); }
|
||||||
|
|
||||||
|
static Action* akilzon_move_to_eye_of_the_storm(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonMoveToEyeOfTheStormAction(botAI); }
|
||||||
|
|
||||||
|
static Action* akilzon_manage_electrical_storm_timer(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonManageElectricalStormTimerAction(botAI); }
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
static Action* nalorakk_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nalorakk_tanks_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkTanksPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nalorakk_spread_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkSpreadRangedAction(botAI); }
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
static Action* janalai_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* janalai_tanks_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiTanksPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* janalai_spread_ranged_in_circle(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiSpreadRangedInCircleAction(botAI); }
|
||||||
|
|
||||||
|
static Action* janalai_avoid_fire_bombs(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiAvoidFireBombsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* janalai_mark_amanishi_hatchers(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiMarkAmanishiHatchersAction(botAI); }
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
static Action* halazzi_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* halazzi_main_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziMainTankPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* halazzi_first_assist_tank_attack_spirit_lynx(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziFirstAssistTankAttackSpiritLynxAction(botAI); }
|
||||||
|
|
||||||
|
static Action* halazzi_assign_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziAssignDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
static Action* hex_lord_malacrass_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hex_lord_malacrass_assign_dps_priority(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassAssignDpsPriorityAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hex_lord_malacrass_run_away_from_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassRunAwayFromWhirlwindAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hex_lord_malacrass_casters_stop_attacking(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassCastersStopAttackingAction(botAI); }
|
||||||
|
|
||||||
|
static Action* hex_lord_malacrass_move_away_from_freezing_trap(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassMoveAwayFromFreezingTrapAction(botAI); }
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
static Action* zuljin_misdirect_boss_to_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinMisdirectBossToMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* zuljin_tanks_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinTanksPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* zuljin_run_away_from_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinRunAwayFromWhirlwindAction(botAI); }
|
||||||
|
|
||||||
|
static Action* zuljin_avoid_cyclones(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinAvoidCyclonesAction(botAI); }
|
||||||
|
|
||||||
|
static Action* zuljin_spread_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinSpreadRangedAction(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
206
src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h
Normal file
206
src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidZulAmanTriggers.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
class RaidZulAmanTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidZulAmanTriggerContext()
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
creators["amani'shi medicine man summoned ward"] =
|
||||||
|
&RaidZulAmanTriggerContext::amanishi_medicine_man_summoned_ward;
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
creators["akil'zon pulling boss"] =
|
||||||
|
&RaidZulAmanTriggerContext::akilzon_pulling_boss;
|
||||||
|
|
||||||
|
creators["akil'zon boss engaged by tanks"] =
|
||||||
|
&RaidZulAmanTriggerContext::akilzon_boss_engaged_by_tanks;
|
||||||
|
|
||||||
|
creators["akil'zon boss casts static disruption"] =
|
||||||
|
&RaidZulAmanTriggerContext::akilzon_boss_casts_static_disruption;
|
||||||
|
|
||||||
|
creators["akil'zon electrical storm incoming"] =
|
||||||
|
&RaidZulAmanTriggerContext::akilzon_electrical_storm_incoming;
|
||||||
|
|
||||||
|
creators["akil'zon bots need to prepare for electrical storm"] =
|
||||||
|
&RaidZulAmanTriggerContext::akilzon_bots_need_to_prepare_for_electrical_storm;
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
creators["nalorakk pulling boss"] =
|
||||||
|
&RaidZulAmanTriggerContext::nalorakk_pulling_boss;
|
||||||
|
|
||||||
|
creators["nalorakk boss casts surge"] =
|
||||||
|
&RaidZulAmanTriggerContext::nalorakk_boss_casts_surge;
|
||||||
|
|
||||||
|
creators["nalorakk boss switches forms"] =
|
||||||
|
&RaidZulAmanTriggerContext::nalorakk_boss_switches_forms;
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
creators["jan'alai pulling boss"] =
|
||||||
|
&RaidZulAmanTriggerContext::janalai_pulling_boss;
|
||||||
|
|
||||||
|
creators["jan'alai boss engaged by tanks"] =
|
||||||
|
&RaidZulAmanTriggerContext::janalai_boss_engaged_by_tanks;
|
||||||
|
|
||||||
|
creators["jan'alai boss casts flame breath"] =
|
||||||
|
&RaidZulAmanTriggerContext::janalai_boss_casts_flame_breath;
|
||||||
|
|
||||||
|
creators["jan'alai boss summoning fire bombs"] =
|
||||||
|
&RaidZulAmanTriggerContext::janalai_boss_summoning_fire_bombs;
|
||||||
|
|
||||||
|
creators["jan'alai amani'shi hatchers spawned"] =
|
||||||
|
&RaidZulAmanTriggerContext::janalai_amanishi_hatchers_spawned;
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
creators["halazzi pulling boss"] =
|
||||||
|
&RaidZulAmanTriggerContext::halazzi_pulling_boss;
|
||||||
|
|
||||||
|
creators["halazzi boss engaged by main tank"] =
|
||||||
|
&RaidZulAmanTriggerContext::halazzi_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["halazzi boss summons spirit lynx"] =
|
||||||
|
&RaidZulAmanTriggerContext::halazzi_boss_summons_spirit_lynx;
|
||||||
|
|
||||||
|
creators["halazzi determining dps target"] =
|
||||||
|
&RaidZulAmanTriggerContext::halazzi_determining_dps_target;
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
creators["hex lord malacrass pulling boss"] =
|
||||||
|
&RaidZulAmanTriggerContext::hex_lord_malacrass_pulling_boss;
|
||||||
|
|
||||||
|
creators["hex lord malacrass determining kill order"] =
|
||||||
|
&RaidZulAmanTriggerContext::hex_lord_malacrass_determining_kill_order;
|
||||||
|
|
||||||
|
creators["hex lord malacrass boss is channeling whirlwind"] =
|
||||||
|
&RaidZulAmanTriggerContext::hex_lord_malacrass_boss_is_channeling_whirlwind;
|
||||||
|
|
||||||
|
creators["hex lord malacrass boss has spell reflection"] =
|
||||||
|
&RaidZulAmanTriggerContext::hex_lord_malacrass_boss_has_spell_reflection;
|
||||||
|
|
||||||
|
creators["hex lord malacrass boss placed freezing trap"] =
|
||||||
|
&RaidZulAmanTriggerContext::hex_lord_malacrass_boss_placed_freezing_trap;
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
creators["zul'jin main tank needs aggro upon pull or phase change"] =
|
||||||
|
&RaidZulAmanTriggerContext::zuljin_main_tank_needs_aggro_upon_pull_or_phase_change;
|
||||||
|
|
||||||
|
creators["zul'jin boss engaged by tanks"] =
|
||||||
|
&RaidZulAmanTriggerContext::zuljin_boss_engaged_by_tanks;
|
||||||
|
|
||||||
|
creators["zul'jin boss is channeling whirlwind in troll form"] =
|
||||||
|
&RaidZulAmanTriggerContext::zuljin_boss_is_channeling_whirlwind_in_troll_form;
|
||||||
|
|
||||||
|
creators["zul'jin boss is summoning cyclones in eagle form"] =
|
||||||
|
&RaidZulAmanTriggerContext::zuljin_boss_is_summoning_cyclones_in_eagle_form;
|
||||||
|
|
||||||
|
creators["zul'jin boss casts aoe abilities in dragonhawk form"] =
|
||||||
|
&RaidZulAmanTriggerContext::zuljin_boss_casts_aoe_abilities_in_dragonhawk_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Trash
|
||||||
|
static Trigger* amanishi_medicine_man_summoned_ward(
|
||||||
|
PlayerbotAI* botAI) { return new AmanishiMedicineManSummonedWardTrigger(botAI); }
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
static Trigger* akilzon_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* akilzon_boss_engaged_by_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonBossEngagedByTanksTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* akilzon_boss_casts_static_disruption(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonBossCastsStaticDisruptionTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* akilzon_electrical_storm_incoming(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonElectricalStormIncomingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* akilzon_bots_need_to_prepare_for_electrical_storm(
|
||||||
|
PlayerbotAI* botAI) { return new AkilzonBotsNeedToPrepareForElectricalStormTrigger(botAI); }
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
static Trigger* nalorakk_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nalorakk_boss_casts_surge(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkBossCastsSurgeTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nalorakk_boss_switches_forms(
|
||||||
|
PlayerbotAI* botAI) { return new NalorakkBossSwitchesFormsTrigger(botAI); }
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
static Trigger* janalai_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* janalai_boss_engaged_by_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiBossEngagedByTanksTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* janalai_boss_casts_flame_breath(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiBossCastsFlameBreathTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* janalai_boss_summoning_fire_bombs(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiBossSummoningFireBombsTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* janalai_amanishi_hatchers_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new JanalaiAmanishiHatchersSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
static Trigger* halazzi_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* halazzi_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* halazzi_boss_summons_spirit_lynx(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziBossSummonsSpiritLynxTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* halazzi_determining_dps_target(
|
||||||
|
PlayerbotAI* botAI) { return new HalazziDeterminingDpsTargetTrigger(botAI); }
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
static Trigger* hex_lord_malacrass_pulling_boss(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassPullingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hex_lord_malacrass_determining_kill_order(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassDeterminingKillOrderTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hex_lord_malacrass_boss_is_channeling_whirlwind(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassBossIsChannelingWhirlwindTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hex_lord_malacrass_boss_has_spell_reflection(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassBossHasSpellReflectionTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* hex_lord_malacrass_boss_placed_freezing_trap(
|
||||||
|
PlayerbotAI* botAI) { return new HexLordMalacrassBossPlacedFreezingTrapTrigger(botAI); }
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
static Trigger* zuljin_boss_engaged_by_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinBossEngagedByTanksTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* zuljin_main_tank_needs_aggro_upon_pull_or_phase_change(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* zuljin_boss_is_channeling_whirlwind_in_troll_form(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinBossIsChannelingWhirlwindInTrollFormTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* zuljin_boss_is_summoning_cyclones_in_eagle_form(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinBossIsSummoningCyclonesInEagleFormTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* zuljin_boss_casts_aoe_abilities_in_dragonhawk_form(
|
||||||
|
PlayerbotAI* botAI) { return new ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
134
src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.cpp
Normal file
134
src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.cpp
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RaidZulAmanStrategy.h"
|
||||||
|
#include "RaidZulAmanMultipliers.h"
|
||||||
|
|
||||||
|
void RaidZulAmanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
triggers.push_back(new TriggerNode("amani'shi medicine man summoned ward", {
|
||||||
|
NextAction("amani'shi medicine man mark ward", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
triggers.push_back(new TriggerNode("akil'zon pulling boss", {
|
||||||
|
NextAction("akil'zon misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("akil'zon boss engaged by main tank", {
|
||||||
|
NextAction("akil'zon main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("akil'zon boss casts static disruption", {
|
||||||
|
NextAction("akil'zon spread ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("akil'zon electrical storm incoming", {
|
||||||
|
NextAction("akil'zon move to eye of the storm", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("akil'zon bots need to prepare for electrical storm", {
|
||||||
|
NextAction("akil'zon manage electrical storm timer", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
triggers.push_back(new TriggerNode("nalorakk pulling boss", {
|
||||||
|
NextAction("nalorakk misdirect boss to main tank", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("nalorakk boss switches forms", {
|
||||||
|
NextAction("nalorakk tanks position boss", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("nalorakk boss casts surge", {
|
||||||
|
NextAction("nalorakk spread ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
triggers.push_back(new TriggerNode("jan'alai pulling boss", {
|
||||||
|
NextAction("jan'alai misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("jan'alai boss engaged by main tank", {
|
||||||
|
NextAction("jan'alai main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("jan'alai boss casts flame breath", {
|
||||||
|
NextAction("jan'alai spread ranged in circle", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("jan'alai boss summoning fire bombs", {
|
||||||
|
NextAction("jan'alai avoid fire bombs", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("jan'alai amani'shi hatchers spawned", {
|
||||||
|
NextAction("jan'alai mark amani'shi hatchers", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
triggers.push_back(new TriggerNode("halazzi pulling boss", {
|
||||||
|
NextAction("halazzi misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("halazzi boss engaged by main tank", {
|
||||||
|
NextAction("halazzi main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("halazzi boss summons spirit lynx", {
|
||||||
|
NextAction("halazzi first assist tank attack spirit lynx", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("halazzi determining dps target", {
|
||||||
|
NextAction("halazzi assign dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
triggers.push_back(new TriggerNode("hex lord malacrass pulling boss", {
|
||||||
|
NextAction("hex lord malacrass misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hex lord malacrass determining kill order", {
|
||||||
|
NextAction("hex lord malacrass assign dps priority", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hex lord malacrass boss is channeling whirlwind", {
|
||||||
|
NextAction("hex lord malacrass run away from whirlwind", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hex lord malacrass boss has spell reflection", {
|
||||||
|
NextAction("hex lord malacrass casters stop attacking", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("hex lord malacrass boss placed freezing trap", {
|
||||||
|
NextAction("hex lord malacrass move away from freezing trap", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
triggers.push_back(new TriggerNode("zul'jin main tank needs aggro upon pull or phase change", {
|
||||||
|
NextAction("zul'jin misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("zul'jin boss engaged by main tank", {
|
||||||
|
NextAction("zul'jin main tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("zul'jin boss is channeling whirlwind in troll form", {
|
||||||
|
NextAction("zul'jin run away from whirlwind", ACTION_EMERGENCY + 6) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("zul'jin boss is summoning cyclones in eagle form", {
|
||||||
|
NextAction("zul'jin avoid cyclones", ACTION_EMERGENCY + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("zul'jin boss casts aoe abilities in dragonhawk form", {
|
||||||
|
NextAction("zul'jin spread ranged", ACTION_RAID + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaidZulAmanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
multipliers.push_back(new AkilzonDisableCombatFormationMoveMultiplier(botAI));
|
||||||
|
multipliers.push_back(new AkilzonStayInEyeOfTheStormMultiplier(botAI));
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
multipliers.push_back(new NalorakkDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NalorakkControlMisdirectionMultiplier(botAI));
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
multipliers.push_back(new JanalaiDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new JanalaiDisableCombatFormationMoveMultiplier(botAI));
|
||||||
|
multipliers.push_back(new JanalaiStayAwayFromFireBombsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new JanalaiDoNotCrowdControlHatchersMultiplier(botAI));
|
||||||
|
multipliers.push_back(new JanalaiDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
multipliers.push_back(new HalazziDisableTankActionsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HalazziControlMisdirectionMultiplier(botAI));
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
multipliers.push_back(new HexLordMalacrassAvoidWhirlwindMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HexLordMalacrassStopAttackingDuringSpellReflectionMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HexLordMalacrassDoNotDispelUnstableAfflictionMultiplier(botAI));
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
multipliers.push_back(new ZuljinDisableTankFaceMultiplier(botAI));
|
||||||
|
multipliers.push_back(new ZuljinAvoidWhirlwindMultiplier(botAI));
|
||||||
|
multipliers.push_back(new ZuljinDisableAvoidAoeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new ZuljinDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
}
|
||||||
23
src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h
Normal file
23
src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANSTRATEGY_H_
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANSTRATEGY_H_
|
||||||
|
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class RaidZulAmanStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidZulAmanStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "zulaman"; }
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
271
src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp
Normal file
271
src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RaidZulAmanTriggers.h"
|
||||||
|
#include "RaidZulAmanHelpers.h"
|
||||||
|
#include "RaidZulAmanActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
|
using namespace ZulAmanHelpers;
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
bool AmanishiMedicineManSummonedWardTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "amani'shi medicine man");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
bool AkilzonPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* akilzon = AI_VALUE2(Unit*, "find target", "akil'zon");
|
||||||
|
return akilzon && akilzon->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonBossEngagedByTanksTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "akil'zon"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !GetElectricalStormTarget(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonBossCastsStaticDisruptionTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "akil'zon"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId());
|
||||||
|
if (it == akilzonStormTimer.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return !IsInStormWindow(it->second, std::time(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonElectricalStormIncomingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "akil'zon"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it = akilzonStormTimer.find(bot->GetMap()->GetInstanceId());
|
||||||
|
if (it == akilzonStormTimer.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsInStormWindow(it->second, std::time(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkilzonBotsNeedToPrepareForElectricalStormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return IsMechanicTrackerBot(botAI, bot, ZULAMAN_MAP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
bool NalorakkPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* nalorakk = AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
return nalorakk && nalorakk->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkBossSwitchesFormsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true)) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NalorakkBossCastsSurgeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsRanged(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "nalorakk");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
bool JanalaiPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* janalai = AI_VALUE2(Unit*, "find target", "jan'alai");
|
||||||
|
return janalai && janalai->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiBossEngagedByTanksTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !HasFireBombNearby(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiBossCastsFlameBreathTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "jan'alai") ||
|
||||||
|
AI_VALUE2(Unit*, "find target", "amani dragonhawk hatchling"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !HasFireBombNearby(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiBossSummoningFireBombsTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "jan'alai") &&
|
||||||
|
HasFireBombNearby(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JanalaiAmanishiHatchersSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRangedDps(bot) ||
|
||||||
|
!AI_VALUE2(Unit*, "find target", "jan'alai"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->FindNearestCreature(
|
||||||
|
static_cast<uint32>(ZulAmanNPCs::NPC_AMANISHI_HATCHER), 40.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
bool HalazziPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* halazzi = AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
return halazzi && halazzi->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsMainTank(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziBossSummonsSpiritLynxTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HalazziDeterminingDpsTargetTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsDps(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "halazzi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
bool HexLordMalacrassPullingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
return malacrass && malacrass->GetHealthPct() > 95.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassDeterminingKillOrderTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsDps(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassBossIsChannelingWhirlwindTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
if (!malacrass ||
|
||||||
|
!malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_WHIRLWIND)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !(botAI->IsTank(bot) && malacrass->GetVictim() == bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassBossHasSpellReflectionTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsCaster(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* malacrass = AI_VALUE2(Unit*, "find target", "hex lord malacrass");
|
||||||
|
return malacrass &&
|
||||||
|
malacrass->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_HEX_LORD_SPELL_REFLECTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HexLordMalacrassBossPlacedFreezingTrapTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "hex lord malacrass") &&
|
||||||
|
bot->FindNearestGameObject(
|
||||||
|
static_cast<uint32>(ZulAmanObjects::GO_FREEZING_TRAP), 20.0f, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
bool ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float hp = zuljin->GetHealthPct();
|
||||||
|
|
||||||
|
return (hp <= 100.0f && hp > 95.0f) ||
|
||||||
|
(hp <= 80.0f && hp > 75.0f &&
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_BEAR))) ||
|
||||||
|
(hp <= 40.0f && hp > 35.0f &&
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_LYNX))) ||
|
||||||
|
(hp <= 20.0f && hp > 15.0f &&
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinBossEngagedByTanksTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
return zuljin &&
|
||||||
|
!zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE)) &&
|
||||||
|
!zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinBossIsChannelingWhirlwindInTrollFormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
if (!zuljin ||
|
||||||
|
!zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_ZULJIN_WHIRLWIND)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !(botAI->IsTank(bot) && zuljin->GetVictim() == bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinBossIsSummoningCyclonesInEagleFormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
return zuljin &&
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_EAGLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* zuljin = AI_VALUE2(Unit*, "find target", "zul'jin");
|
||||||
|
return zuljin &&
|
||||||
|
zuljin->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_SHAPE_OF_THE_DRAGONHAWK));
|
||||||
|
}
|
||||||
249
src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.h
Normal file
249
src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.h
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
|
||||||
|
class AmanishiMedicineManSummonedWardTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AmanishiMedicineManSummonedWardTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "amani'shi medicine man summoned ward") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
|
||||||
|
class AkilzonPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "akil'zon pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonBossEngagedByTanksTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonBossEngagedByTanksTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "akil'zon boss engaged by tanks") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonBossCastsStaticDisruptionTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonBossCastsStaticDisruptionTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "akil'zon boss casts static disruption") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonElectricalStormIncomingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonElectricalStormIncomingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "akil'zon electrical storm incoming") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AkilzonBotsNeedToPrepareForElectricalStormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AkilzonBotsNeedToPrepareForElectricalStormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "akil'zon bots need to prepare for electrical storm") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
|
||||||
|
class NalorakkPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nalorakk pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalorakkBossSwitchesFormsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkBossSwitchesFormsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nalorakk boss switches forms") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NalorakkBossCastsSurgeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NalorakkBossCastsSurgeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nalorakk boss casts surge") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
|
||||||
|
class JanalaiPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "jan'alai pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiBossEngagedByTanksTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiBossEngagedByTanksTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss engaged by tanks") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiBossCastsFlameBreathTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiBossCastsFlameBreathTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss casts flame breath") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiBossSummoningFireBombsTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiBossSummoningFireBombsTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "jan'alai boss summoning fire bombs") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanalaiAmanishiHatchersSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JanalaiAmanishiHatchersSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "jan'alai amani'shi hatchers spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
|
||||||
|
class HalazziPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "halazzi pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "halazzi boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziBossSummonsSpiritLynxTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziBossSummonsSpiritLynxTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "halazzi boss summons spirit lynx") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HalazziDeterminingDpsTargetTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HalazziDeterminingDpsTargetTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "halazzi determining dps target") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
|
||||||
|
class HexLordMalacrassPullingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassPullingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass pulling boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassDeterminingKillOrderTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassDeterminingKillOrderTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass determining kill order") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassBossIsChannelingWhirlwindTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassBossIsChannelingWhirlwindTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss is channeling whirlwind") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassBossHasSpellReflectionTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassBossHasSpellReflectionTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss has spell reflection") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HexLordMalacrassBossPlacedFreezingTrapTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HexLordMalacrassBossPlacedFreezingTrapTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "hex lord malacrass boss placed freezing trap") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
|
||||||
|
class ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinMainTankNeedsAggroUponPullOrPhaseChangeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "zul'jin main tank needs aggro upon pull or phase change") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinBossEngagedByTanksTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinBossEngagedByTanksTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss engaged by tanks") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinBossIsChannelingWhirlwindInTrollFormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinBossIsChannelingWhirlwindInTrollFormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss is channeling whirlwind in troll form") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinBossIsSummoningCyclonesInEagleFormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinBossIsSummoningCyclonesInEagleFormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss is summoning cyclones in eagle form") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZuljinBossCastsAoeAbilitiesInDragonhawkFormTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "zul'jin boss casts aoe abilities in dragonhawk form") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
190
src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp
Normal file
190
src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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 "RaidZulAmanHelpers.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
namespace ZulAmanHelpers
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
Position FindSafestNearbyPosition(Player* bot,
|
||||||
|
const std::vector<Unit*>& hazards, const Position& safeZoneCenter,
|
||||||
|
float safeZoneRadius, float hazardRadius, bool requireSafePath)
|
||||||
|
{
|
||||||
|
constexpr float searchStep = M_PI / 8.0f;
|
||||||
|
constexpr float distanceStep = 1.0f;
|
||||||
|
|
||||||
|
Position bestPos;
|
||||||
|
float minMoveDistance = std::numeric_limits<float>::max();
|
||||||
|
bool foundSafe = false;
|
||||||
|
|
||||||
|
for (float distance = 0.0f;
|
||||||
|
distance <= safeZoneRadius; distance += distanceStep)
|
||||||
|
{
|
||||||
|
for (float angle = 0.0f; angle < 2 * M_PI; angle += searchStep)
|
||||||
|
{
|
||||||
|
float x = bot->GetPositionX() + distance * std::cos(angle);
|
||||||
|
float y = bot->GetPositionY() + distance * std::sin(angle);
|
||||||
|
|
||||||
|
if (safeZoneCenter.GetExactDist2d(x, y) > safeZoneRadius)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!IsPositionSafeFromHazards(x, y, hazards, hazardRadius))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Position testPos(x, y, bot->GetPositionZ());
|
||||||
|
|
||||||
|
bool pathSafe = true;
|
||||||
|
if (requireSafePath)
|
||||||
|
{
|
||||||
|
pathSafe =
|
||||||
|
IsPathSafeFromHazards(bot->GetPosition(), testPos, hazards, hazardRadius);
|
||||||
|
if (!pathSafe)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float moveDistance = bot->GetExactDist2d(x, y);
|
||||||
|
if (!foundSafe || moveDistance < minMoveDistance)
|
||||||
|
{
|
||||||
|
bestPos = testPos;
|
||||||
|
minMoveDistance = moveDistance;
|
||||||
|
foundSafe = pathSafe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSafe)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPathSafeFromHazards(const Position& start, const Position& end,
|
||||||
|
const std::vector<Unit*>& hazards, float hazardRadius)
|
||||||
|
{
|
||||||
|
constexpr uint8 numChecks = 10;
|
||||||
|
float dx = end.GetPositionX() - start.GetPositionX();
|
||||||
|
float dy = end.GetPositionY() - start.GetPositionY();
|
||||||
|
|
||||||
|
for (uint8 i = 1; i <= numChecks; ++i)
|
||||||
|
{
|
||||||
|
float ratio = static_cast<float>(i) / numChecks;
|
||||||
|
float checkX = start.GetPositionX() + dx * ratio;
|
||||||
|
float checkY = start.GetPositionY() + dy * ratio;
|
||||||
|
|
||||||
|
if (!IsPositionSafeFromHazards(checkX, checkY, hazards, hazardRadius))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPositionSafeFromHazards(
|
||||||
|
float x, float y, const std::vector<Unit*>& hazards, float hazardRadius)
|
||||||
|
{
|
||||||
|
for (Unit* hazard : hazards)
|
||||||
|
{
|
||||||
|
if (hazard->GetDistance2d(x, y) < hazardRadius)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Unit*> GetAllHazardTriggers(Player* bot, uint32 entry, float searchRadius)
|
||||||
|
{
|
||||||
|
std::vector<Unit*> triggers;
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(creatureList, entry, searchRadius);
|
||||||
|
|
||||||
|
for (Creature* creature : creatureList)
|
||||||
|
{
|
||||||
|
if (creature && creature->IsAlive())
|
||||||
|
triggers.push_back(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
const Position AKILZON_TANK_POSITION = { 378.369f, 1407.718f, 74.797f };
|
||||||
|
std::unordered_map<uint32, time_t> akilzonStormTimer;
|
||||||
|
|
||||||
|
bool IsInStormWindow(time_t start, time_t now)
|
||||||
|
{
|
||||||
|
time_t elapsed = now - start;
|
||||||
|
uint32 seconds = elapsed % 60;
|
||||||
|
return elapsed >= 55 && (seconds >= 55 || seconds < 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* GetElectricalStormTarget(Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member &&
|
||||||
|
member->HasAura(static_cast<uint32>(ZulAmanSpells::SPELL_ELECTRICAL_STORM)))
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
const Position NALORAKK_TANK_POSITION = { -80.208f, 1324.530f, 40.942f };
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
const Position JANALAI_TANK_POSITION = { -33.873f, 1149.571f, 19.146f };
|
||||||
|
|
||||||
|
bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot)
|
||||||
|
{
|
||||||
|
constexpr float searchRadius = 30.0f;
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(
|
||||||
|
creatureList, static_cast<uint32>(ZulAmanNPCs::NPC_FIRE_BOMB), searchRadius);
|
||||||
|
|
||||||
|
for (Creature* creature : creatureList)
|
||||||
|
{
|
||||||
|
if (creature && creature->IsAlive())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Unit*, Unit*> GetAmanishiHatcherPair(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Unit* lowest = nullptr;
|
||||||
|
Unit* highest = nullptr;
|
||||||
|
|
||||||
|
for (auto const& guid :
|
||||||
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get())
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (unit &&
|
||||||
|
unit->GetEntry() == static_cast<uint32>(ZulAmanNPCs::NPC_AMANISHI_HATCHER))
|
||||||
|
{
|
||||||
|
if (!lowest || unit->GetGUID().GetCounter() < lowest->GetGUID().GetCounter())
|
||||||
|
lowest = unit;
|
||||||
|
|
||||||
|
if (!highest || unit->GetGUID().GetCounter() > highest->GetGUID().GetCounter())
|
||||||
|
highest = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {lowest, highest};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
const Position HALAZZI_TANK_POSITION = { 370.733f, 1131.202f, 6.516f };
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
const Position ZULJIN_TANK_POSITION = { 120.210f, 705.564f, 45.111f };
|
||||||
|
}
|
||||||
110
src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h
Normal file
110
src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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_RAIDZULAMANHELPERS_H_
|
||||||
|
#define _PLAYERBOT_RAIDZULAMANHELPERS_H_
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
namespace ZulAmanHelpers
|
||||||
|
{
|
||||||
|
enum class ZulAmanSpells : uint32
|
||||||
|
{
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
SPELL_ELECTRICAL_STORM = 43648,
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
SPELL_BEARFORM = 42377,
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
SPELL_HEX_LORD_WHIRLWIND = 43442,
|
||||||
|
SPELL_HEX_LORD_SPELL_REFLECTION = 43443,
|
||||||
|
SPELL_UNSTABLE_AFFLICTION = 43522,
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
SPELL_ZULJIN_WHIRLWIND = 17207,
|
||||||
|
SPELL_SHAPE_OF_THE_BEAR = 42594,
|
||||||
|
SPELL_SHAPE_OF_THE_EAGLE = 42606,
|
||||||
|
SPELL_SHAPE_OF_THE_LYNX = 42607,
|
||||||
|
SPELL_SHAPE_OF_THE_DRAGONHAWK = 42608,
|
||||||
|
// SPELL_CLAW_RAGE = 43149, // Would require getting Zul'jin's bossai
|
||||||
|
|
||||||
|
// Hunter
|
||||||
|
SPELL_MISDIRECTION = 35079,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ZulAmanNPCs : uint32
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
NPC_AMANI_HEALING_WARD = 23757,
|
||||||
|
NPC_AMANI_PROTECTIVE_WARD = 23822,
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
NPC_AMANI_DRAGONHAWK_HATCHLING = 23598,
|
||||||
|
NPC_AMANISHI_HATCHER = 23818,
|
||||||
|
NPC_FIRE_BOMB = 23920,
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
NPC_CORRUPTED_LIGHTNING_TOTEM = 24224,
|
||||||
|
|
||||||
|
// Hex Lord Malacrass
|
||||||
|
NPC_HEX_LORD_MALACRASS = 24239,
|
||||||
|
NPC_ALYSON_ANTILLE = 24240,
|
||||||
|
NPC_THURG = 24241,
|
||||||
|
NPC_SLITHER = 24242,
|
||||||
|
NPC_LORD_RAADAN = 24243,
|
||||||
|
NPC_GAZAKROTH = 24244,
|
||||||
|
NPC_FENSTALKER = 24245,
|
||||||
|
NPC_DARKHEART = 24246,
|
||||||
|
NPC_KORAGG = 24247,
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
NPC_FEATHER_VORTEX = 24136,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ZulAmanObjects : uint32
|
||||||
|
{
|
||||||
|
GO_FREEZING_TRAP = 186669,
|
||||||
|
};
|
||||||
|
|
||||||
|
// General
|
||||||
|
constexpr uint32 ZULAMAN_MAP_ID = 568;
|
||||||
|
Position FindSafestNearbyPosition(
|
||||||
|
Player* bot, const std::vector<Unit*>& hazards, const Position& center,
|
||||||
|
float safeZoneRadius, float hazardRadius, bool requireSafePath);
|
||||||
|
bool IsPathSafeFromHazards(
|
||||||
|
const Position& start, const Position& end,
|
||||||
|
const std::vector<Unit*>& hazards, float hazardRadius);
|
||||||
|
bool IsPositionSafeFromHazards(
|
||||||
|
float x, float y, const std::vector<Unit*>& hazards, float hazardRadius);
|
||||||
|
std::vector<Unit*> GetAllHazardTriggers(
|
||||||
|
Player* bot, uint32 entry, float searchRadius);
|
||||||
|
|
||||||
|
// Akil'zon <Eagle Avatar>
|
||||||
|
extern const Position AKILZON_TANK_POSITION;
|
||||||
|
extern std::unordered_map<uint32, time_t> akilzonStormTimer;
|
||||||
|
bool IsInStormWindow(time_t start, time_t now);
|
||||||
|
Player* GetElectricalStormTarget(Player* bot);
|
||||||
|
|
||||||
|
// Nalorakk <Bear Avatar>
|
||||||
|
extern const Position NALORAKK_TANK_POSITION;
|
||||||
|
|
||||||
|
// Jan'alai <Dragonhawk Avatar>
|
||||||
|
extern const Position JANALAI_TANK_POSITION;
|
||||||
|
bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot);
|
||||||
|
std::pair<Unit*, Unit*> GetAmanishiHatcherPair(PlayerbotAI* botAI);
|
||||||
|
|
||||||
|
// Halazzi <Lynx Avatar>
|
||||||
|
extern const Position HALAZZI_TANK_POSITION;
|
||||||
|
|
||||||
|
// Zul'jin
|
||||||
|
extern const Position ZULJIN_TANK_POSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -11,6 +11,7 @@
|
|||||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||||
|
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||||
@ -32,6 +33,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
|||||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||||
actionContexts.Add(new RaidSSCActionContext());
|
actionContexts.Add(new RaidSSCActionContext());
|
||||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||||
|
actionContexts.Add(new RaidZulAmanActionContext());
|
||||||
actionContexts.Add(new RaidNaxxActionContext());
|
actionContexts.Add(new RaidNaxxActionContext());
|
||||||
actionContexts.Add(new RaidOsActionContext());
|
actionContexts.Add(new RaidOsActionContext());
|
||||||
actionContexts.Add(new RaidEoEActionContext());
|
actionContexts.Add(new RaidEoEActionContext());
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
||||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||||
|
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||||
@ -33,6 +34,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
|||||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidZulAmanTriggerContext());
|
||||||
triggerContexts.Add(new RaidOsTriggerContext());
|
triggerContexts.Add(new RaidOsTriggerContext());
|
||||||
triggerContexts.Add(new RaidEoETriggerContext());
|
triggerContexts.Add(new RaidEoETriggerContext());
|
||||||
triggerContexts.Add(new RaidVoATriggerContext());
|
triggerContexts.Add(new RaidVoATriggerContext());
|
||||||
|
|||||||
@ -1015,29 +1015,59 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
|
|||||||
}
|
}
|
||||||
else if (filtered == "logout")
|
else if (filtered == "logout")
|
||||||
{
|
{
|
||||||
if (!bot->GetSession()->isLogingOut())
|
if (bot->GetSession()->isLogingOut())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Verify the command came from this bot's master. Also handles nullptr
|
||||||
|
if (fromPlayer != master)
|
||||||
{
|
{
|
||||||
if (type == CHAT_MSG_WHISPER)
|
if (type == CHAT_MSG_WHISPER)
|
||||||
TellMaster("I'm logging out!");
|
{
|
||||||
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"bot_not_your_master", "You are not my master!", {});
|
||||||
|
bot->Whisper(message, LANG_UNIVERSAL, fromPlayer);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerbotMgr* masterBotMgr = GET_PLAYERBOT_MGR(master);
|
||||||
|
if (!masterBotMgr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only respond if this bot is in master's collection (alt/addclass)
|
||||||
|
if (masterBotMgr->GetPlayerBot(bot->GetGUID()))
|
||||||
|
{
|
||||||
|
if (type == CHAT_MSG_WHISPER)
|
||||||
|
{
|
||||||
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"logout_start", "I'm logging out!", {});
|
||||||
|
TellMaster(message);
|
||||||
|
}
|
||||||
|
|
||||||
PlayerbotMgr* masterBotMgr = nullptr;
|
|
||||||
if (master)
|
|
||||||
masterBotMgr = GET_PLAYERBOT_MGR(master);
|
|
||||||
if (masterBotMgr)
|
|
||||||
masterBotMgr->LogoutPlayerBot(bot->GetGUID());
|
masterBotMgr->LogoutPlayerBot(bot->GetGUID());
|
||||||
}
|
}
|
||||||
|
else if (type == CHAT_MSG_WHISPER)
|
||||||
|
{
|
||||||
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"bot_rndbot_no_logout", "You can't command me to logout!", {});
|
||||||
|
TellMaster(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (filtered == "logout cancel")
|
else if (filtered == "logout cancel")
|
||||||
{
|
{
|
||||||
if (bot->GetSession()->isLogingOut())
|
if (!bot->GetSession()->isLogingOut())
|
||||||
{
|
return;
|
||||||
|
|
||||||
if (type == CHAT_MSG_WHISPER)
|
if (type == CHAT_MSG_WHISPER)
|
||||||
TellMaster("Logout cancelled!");
|
{
|
||||||
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"logout_cancel", "Logout cancelled!", {});
|
||||||
|
TellMaster(message);
|
||||||
|
}
|
||||||
|
|
||||||
WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL);
|
WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL);
|
||||||
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
chatCommands.push_back(ChatCommandHolder(filtered, fromPlayer, type));
|
chatCommands.push_back(ChatCommandHolder(filtered, fromPlayer, type));
|
||||||
@ -1532,7 +1562,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
"naxx", "onyxia", "ssc", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
"naxx", "onyxia", "ssc", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
||||||
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
||||||
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
||||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh"
|
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const std::string& strat : allInstanceStrategies)
|
for (const std::string& strat : allInstanceStrategies)
|
||||||
@ -1574,6 +1604,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 565:
|
case 565:
|
||||||
strategyName = "gruulslair"; // Gruul's Lair
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
break;
|
break;
|
||||||
|
case 568:
|
||||||
|
strategyName = "zulaman"; // Zul'Aman
|
||||||
|
break;
|
||||||
case 574:
|
case 574:
|
||||||
strategyName = "wotlk-uk"; // Utgarde Keep
|
strategyName = "wotlk-uk"; // Utgarde Keep
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -362,6 +362,9 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
if (master)
|
if (master)
|
||||||
masterWorldSessionPtr = master->GetSession();
|
masterWorldSessionPtr = master->GetSession();
|
||||||
|
|
||||||
|
// TODO: Review whether or not to implement timed logout.
|
||||||
|
// Unused block. Useful only for timed logout.
|
||||||
|
/*
|
||||||
// check for instant logout
|
// check for instant logout
|
||||||
bool logout = botWorldSessionPtr->ShouldLogOut(time(nullptr));
|
bool logout = botWorldSessionPtr->ShouldLogOut(time(nullptr));
|
||||||
|
|
||||||
@ -373,58 +376,19 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
|
|
||||||
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||||
botWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))
|
botWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))
|
||||||
{
|
|
||||||
logout = true;
|
logout = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (master &&
|
if (master &&
|
||||||
(master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || master->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
(master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || master->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||||
(masterWorldSessionPtr &&
|
(masterWorldSessionPtr &&
|
||||||
masterWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))))
|
masterWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))))
|
||||||
{
|
|
||||||
logout = true;
|
logout = true;
|
||||||
}
|
*/
|
||||||
|
// Instant logout (the only option right now)
|
||||||
TravelTarget* target = nullptr;
|
|
||||||
if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
|
||||||
{
|
{
|
||||||
target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
}
|
"goodbye", "Goodbye!", {});
|
||||||
|
botAI->TellMaster(message);
|
||||||
// Peiru: Allow bots to always instant logout to see if this resolves logout crashes
|
|
||||||
logout = true;
|
|
||||||
|
|
||||||
// if no instant logout, request normal logout
|
|
||||||
if (!logout)
|
|
||||||
{
|
|
||||||
if (bot->GetSession()->isLogingOut())
|
|
||||||
return;
|
|
||||||
else if (bot)
|
|
||||||
{
|
|
||||||
botAI->TellMaster("I'm logging out!");
|
|
||||||
WorldPackets::Character::LogoutRequest data = WorldPacket(CMSG_LOGOUT_REQUEST);
|
|
||||||
botWorldSessionPtr->HandleLogoutRequestOpcode(data);
|
|
||||||
if (!bot)
|
|
||||||
{
|
|
||||||
RemoveFromPlayerbotsMap(guid);
|
|
||||||
delete botWorldSessionPtr;
|
|
||||||
if (target)
|
|
||||||
delete target;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
|
||||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
|
||||||
if (target)
|
|
||||||
delete target;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} // if instant logout possible, do it
|
|
||||||
else if (bot && (logout || !botWorldSessionPtr->isLogingOut()))
|
|
||||||
{
|
|
||||||
botAI->TellMaster("Goodbye!");
|
|
||||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||||
botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object
|
botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object
|
||||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user