mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
commit
fdd228c7f0
2
.github/workflows/core_build.yml
vendored
2
.github/workflows/core_build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
ref: ${{ (github.base_ref || github.ref_name) == 'test-staging' && 'test-staging' || 'Playerbot' }}
|
||||
|
||||
- name: Set reusable strings
|
||||
id: strings
|
||||
|
||||
38
.github/workflows/label_translation-pr.yml
vendored
Normal file
38
.github/workflows/label_translation-pr.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Label translation PRs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label-translation:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Fetch PR diff
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh api \
|
||||
repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} \
|
||||
--header "Accept: application/vnd.github.v3.diff" > pr.diff
|
||||
|
||||
- name: Detect ai_playerbot_texts inserts
|
||||
id: detect
|
||||
run: |
|
||||
if grep -E '^\+.*INSERT[[:space:]]+INTO[[:space:]]+`?ai_playerbot_texts`?' pr.diff; then
|
||||
echo "has_translation=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_translation=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Add label
|
||||
if: steps.detect.outputs.has_translation == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh pr edit ${{ github.event.pull_request.number }} \
|
||||
--add-label "Added translation"
|
||||
2
.github/workflows/macos_build.yml
vendored
2
.github/workflows/macos_build.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
ref: ${{ (github.base_ref || github.ref_name) == 'test-staging' && 'test-staging' || 'Playerbot' }}
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/windows_build.yml
vendored
2
.github/workflows/windows_build.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
ref: ${{ (github.base_ref || github.ref_name) == 'test-staging' && 'test-staging' || 'Playerbot' }}
|
||||
path: 'ac'
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@ -61,29 +61,14 @@ 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 is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor.
|
||||
We expect contributors to be honest about what they do and do not understand.
|
||||
-->
|
||||
- Was AI assistance used while working on this change?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
Was AI assistance used while working on this change?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
<!--
|
||||
If yes, please specify:
|
||||
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation).
|
||||
@ -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
|
||||
|
||||
- - [ ] Stability is not compromised.
|
||||
- - [ ] Performance impact is understood, tested, and acceptable.
|
||||
- - [ ] Added logic complexity is justified and explained.
|
||||
- - [ ] Any new bot dialogue lines are translated.
|
||||
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
||||
|
||||
## Notes for Reviewers
|
||||
<!-- 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 (
|
||||
1743,
|
||||
'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 (
|
||||
1744,
|
||||
'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 "FishingAction.h"
|
||||
#include "CancelChannelAction.h"
|
||||
#include "WaitForAttackAction.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
@ -164,6 +165,7 @@ public:
|
||||
creators["blood fury"] = &ActionContext::blood_fury;
|
||||
creators["berserking"] = &ActionContext::berserking;
|
||||
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["auto talents"] = &ActionContext::auto_talents;
|
||||
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 do quest"] = &ActionContext::new_rpg_do_quest;
|
||||
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:
|
||||
@ -359,6 +362,7 @@ private:
|
||||
static Action* blood_fury(PlayerbotAI* botAI) { return new CastBloodFuryAction(botAI); }
|
||||
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(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* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
|
||||
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_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(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
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "ServerFacade.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Unit.h"
|
||||
#include "WaitForAttackStrategy.h"
|
||||
|
||||
bool AttackAction::Execute(Event /*event*/)
|
||||
{
|
||||
@ -164,6 +165,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||
|
||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||
|
||||
if (!WaitForAttackStrategy::ShouldWait(botAI))
|
||||
bot->Attack(target, shouldMelee);
|
||||
/* prevent pet dead immediately in group */
|
||||
// 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 "Group.h"
|
||||
#include "Chat.h"
|
||||
#include "GenericBuffUtils.h"
|
||||
#include "Ai/Base/Util/GenericBuffUtils.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
using ai::buff::MakeAuraQualifierForBuff;
|
||||
using ai::spell::HasSpellOrCategoryCooldown;
|
||||
|
||||
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
||||
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
||||
@ -320,7 +321,7 @@ bool CastEveryManForHimselfAction::isPossible()
|
||||
if (!bot->HasSpell(spellId))
|
||||
return false;
|
||||
|
||||
if (bot->HasSpellCooldown(spellId))
|
||||
if (HasSpellOrCategoryCooldown(bot, spellId))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -328,11 +329,36 @@ bool CastEveryManForHimselfAction::isPossible()
|
||||
|
||||
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_ROOT) ||
|
||||
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*/)
|
||||
|
||||
@ -294,6 +294,16 @@ public:
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
||||
@ -948,14 +948,15 @@ void MovementAction::UpdateMovementState()
|
||||
const auto liquidState = bot->GetLiquidData().Status;
|
||||
const float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
||||
const bool onGroundZ = bot->GetPositionZ() < gZ + 1.f;
|
||||
const bool canSwim = liquidState == LIQUID_MAP_IN_WATER || liquidState == LIQUID_MAP_UNDER_WATER;
|
||||
const bool canFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
|
||||
const bool wantsSwim = liquidState == LIQUID_MAP_IN_WATER || liquidState == LIQUID_MAP_UNDER_WATER;
|
||||
const bool wantsFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
|
||||
const bool canWaterWalk = bot->HasWaterWalkAura();
|
||||
const bool isMasterFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true;
|
||||
const bool isMasterSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true;
|
||||
const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
|
||||
const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
|
||||
const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
|
||||
const bool hasGravityDisabled = bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
bool movementFlagsUpdated = false;
|
||||
|
||||
// handle water (fragile logic do not alter without testing every detail, animation and transition)
|
||||
@ -970,11 +971,11 @@ void MovementAction::UpdateMovementState()
|
||||
else if ((!canWaterWalk || isMasterSwimming) && isWaterWalking)
|
||||
{
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
|
||||
if (canSwim)
|
||||
if (wantsSwim)
|
||||
bot->SetSwim(true);
|
||||
movementFlagsUpdated = true;
|
||||
}
|
||||
else if (!canSwim && isSwimming)
|
||||
else if (!wantsSwim && isSwimming)
|
||||
{
|
||||
bot->SetSwim(false);
|
||||
movementFlagsUpdated = true;
|
||||
@ -990,17 +991,21 @@ void MovementAction::UpdateMovementState()
|
||||
}
|
||||
|
||||
// handle flying
|
||||
if ((canFly && !isFlying) && isMasterFlying)
|
||||
if (wantsFly && !isFlying && isMasterFlying)
|
||||
{
|
||||
bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
|
||||
bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
|
||||
|
||||
// required for transition and state monitoring.
|
||||
if (MotionMaster* mm = bot->GetMotionMaster())
|
||||
mm->MoveTakeoff(0, {bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 1.F}, 0.F, true);
|
||||
movementFlagsUpdated = true;
|
||||
}
|
||||
else if ((!canFly && !isWaterWalking && isFlying) || (!isMasterFlying && isFlying && onGroundZ))
|
||||
else if (!wantsFly && !isWaterWalking && (isFlying || hasGravityDisabled))
|
||||
{
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
|
||||
movementFlagsUpdated = true;
|
||||
}
|
||||
else if (!isMasterFlying && isFlying && onGroundZ)
|
||||
{
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
|
||||
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 "EquipGlyphsAction.h"
|
||||
#include "PetsAction.h"
|
||||
#include "WaitForAttackAction.h"
|
||||
|
||||
class ChatActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
@ -199,6 +200,7 @@ public:
|
||||
creators["pet"] = &ChatActionContext::pet;
|
||||
creators["pet attack"] = &ChatActionContext::pet_attack;
|
||||
creators["roll"] = &ChatActionContext::roll_action;
|
||||
creators["wait for attack time"] = &ChatActionContext::wait_for_attack_time;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -311,6 +313,7 @@ private:
|
||||
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
|
||||
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
|
||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||
static Action* wait_for_attack_time(PlayerbotAI* botAI) { return new SetWaitForAttackTimeAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -145,6 +145,7 @@ public:
|
||||
creators["pet"] = &ChatTriggerContext::pet;
|
||||
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
||||
creators["roll"] = &ChatTriggerContext::roll_action;
|
||||
creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -269,6 +270,7 @@ private:
|
||||
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
||||
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* wait_for_attack_time(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wait for attack time"); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -199,4 +199,5 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||
supported.push_back("pet");
|
||||
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(
|
||||
"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)
|
||||
|
||||
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 "UseFoodStrategy.h"
|
||||
#include "UsePotionsStrategy.h"
|
||||
#include "WaitForAttackStrategy.h"
|
||||
#include "WorldPacketHandlerStrategy.h"
|
||||
|
||||
class StrategyContext : public NamedObjectContext<Strategy>
|
||||
@ -124,6 +125,7 @@ public:
|
||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
||||
creators["master fishing"] = &StrategyContext::master_fishing;
|
||||
creators["wait for attack"] = &StrategyContext::wait_for_attack;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -195,6 +197,7 @@ private:
|
||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(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>
|
||||
|
||||
@ -473,6 +473,14 @@ bool LossOfControlTrigger::IsActive()
|
||||
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()
|
||||
{
|
||||
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
||||
|
||||
@ -754,6 +754,14 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FearCharmSleepTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FearCharmSleepTrigger(PlayerbotAI* botAI) : Trigger(botAI, "fear charm sleep", 1) {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IsSwimmingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include "CellImpl.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Playerbots.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "MapCollisionData.h"
|
||||
|
||||
bool MoveStuckTrigger::IsActive()
|
||||
{
|
||||
@ -89,8 +89,7 @@ bool MoveLongStuckTrigger::IsActive()
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell.GridX() > 0 && cell.GridY() > 0 &&
|
||||
!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(botPos.GetMapId(), cell.GridX(), cell.GridY()))
|
||||
if (bot->GetMap()->IsGridCreated(GridCoord(cell.GridX(), cell.GridY())))
|
||||
{
|
||||
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> was in unloaded grid {},{} on map {}",
|
||||
// bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
|
||||
|
||||
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 "StuckTriggers.h"
|
||||
#include "TravelTriggers.h"
|
||||
#include "WaitForAttackTriggers.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
@ -60,6 +61,7 @@ public:
|
||||
|
||||
creators["generic boost"] = &TriggerContext::generic_boost;
|
||||
creators["loss of control"] = &TriggerContext::loss_of_control;
|
||||
creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep;
|
||||
|
||||
creators["protect party member"] = &TriggerContext::protect_party_member;
|
||||
|
||||
@ -231,6 +233,7 @@ public:
|
||||
creators["can fish"] = &TriggerContext::can_fish;
|
||||
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
||||
creators["new pet"] = &TriggerContext::new_pet;
|
||||
creators["wait for attack safe distance"] = &TriggerContext::wait_for_attack_safe_distance;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -365,6 +368,7 @@ private:
|
||||
}
|
||||
static Trigger* generic_boost(PlayerbotAI* botAI) { return new GenericBoostTrigger(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)
|
||||
{
|
||||
return new PartyMemberCriticalHealthTrigger(botAI);
|
||||
@ -434,6 +438,7 @@ private:
|
||||
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(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* wait_for_attack_safe_distance(PlayerbotAI* ai) { return new WaitForAttackSafeDistanceTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -143,3 +143,28 @@ namespace ai::buff
|
||||
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 "TradeValues.h"
|
||||
#include "Value.h"
|
||||
#include "WaitForAttackTimeValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
@ -322,6 +323,8 @@ public:
|
||||
creators["can fish"] = &ValueContext::can_fish;
|
||||
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
||||
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:
|
||||
@ -578,6 +581,8 @@ private:
|
||||
{
|
||||
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
|
||||
|
||||
@ -112,6 +112,7 @@ public:
|
||||
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
|
||||
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
|
||||
creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check;
|
||||
creators["no healer dps strategy"] = &DruidTriggerFactoryInternal::no_healer_dps_strategy;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -149,6 +150,7 @@ private:
|
||||
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(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* no_healer_dps_strategy(PlayerbotAI* ai) { return new NoHealerDpsStrategyTrigger(ai); }
|
||||
};
|
||||
|
||||
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
|
||||
@ -149,10 +149,10 @@ void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(
|
||||
new TriggerNode("healer should attack",
|
||||
{
|
||||
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("starfire", ACTION_DEFAULT),
|
||||
NextAction("cancel tree form", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("moonfire", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("starfire", ACTION_DEFAULT + 0.1f),
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
@ -33,6 +33,10 @@ void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
// no healer dps strategy
|
||||
triggers.push_back(new TriggerNode("no healer dps strategy",
|
||||
{ NextAction("tree form", ACTION_DEFAULT) }));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
|
||||
|
||||
@ -280,4 +280,15 @@ protected:
|
||||
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
|
||||
|
||||
@ -107,6 +107,8 @@ bool SwitchToMeleeTrigger::IsActive()
|
||||
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()
|
||||
{
|
||||
std::vector<std::string> track_list = {
|
||||
@ -115,8 +117,13 @@ bool NoTrackTrigger::IsActive()
|
||||
"track dragonkin",
|
||||
"track elementals",
|
||||
"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)
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
||||
#include "GenericBuffUtils.h"
|
||||
#include "Ai/Base/Util/GenericBuffUtils.h"
|
||||
#include "Group.h"
|
||||
#include "ObjectAccessor.h"
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidTempestKeepStrategy.h"
|
||||
#include "RaidZulAmanStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidEoEStrategy.h"
|
||||
#include "RaidVoAStrategy.h"
|
||||
@ -32,6 +33,7 @@ public:
|
||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||
creators["zulaman"] = &RaidStrategyContext::zulaman;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
creators["voa"] = &RaidStrategyContext::voa;
|
||||
@ -50,6 +52,7 @@ private:
|
||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(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_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||
|
||||
@ -116,8 +116,8 @@ public:
|
||||
creators["kael'thas sunstrider assign legendary weapon dps priority"] =
|
||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_legendary_weapon_dps_priority;
|
||||
|
||||
creators["kael'thas sunstrider main tank move devastation away"] =
|
||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_move_devastation_away;
|
||||
creators["kael'thas sunstrider move devastation away"] =
|
||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_move_devastation_away;
|
||||
|
||||
creators["kael'thas sunstrider loot legendary weapons"] =
|
||||
&RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons;
|
||||
@ -255,7 +255,7 @@ private:
|
||||
static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority(
|
||||
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); }
|
||||
|
||||
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) }));
|
||||
|
||||
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", {
|
||||
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/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||
@ -32,6 +33,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||
actionContexts.Add(new RaidZulAmanActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||
@ -33,6 +34,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||
triggerContexts.Add(new RaidZulAmanTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
|
||||
@ -1015,29 +1015,59 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
|
||||
}
|
||||
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)
|
||||
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());
|
||||
}
|
||||
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")
|
||||
{
|
||||
if (bot->GetSession()->isLogingOut())
|
||||
{
|
||||
if (!bot->GetSession()->isLogingOut())
|
||||
return;
|
||||
|
||||
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);
|
||||
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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",
|
||||
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
||||
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh"
|
||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
||||
};
|
||||
|
||||
for (const std::string& strat : allInstanceStrategies)
|
||||
@ -1574,6 +1604,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 565:
|
||||
strategyName = "gruulslair"; // Gruul's Lair
|
||||
break;
|
||||
case 568:
|
||||
strategyName = "zulaman"; // Zul'Aman
|
||||
break;
|
||||
case 574:
|
||||
strategyName = "wotlk-uk"; // Utgarde Keep
|
||||
break;
|
||||
|
||||
@ -362,6 +362,9 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
||||
if (master)
|
||||
masterWorldSessionPtr = master->GetSession();
|
||||
|
||||
// TODO: Review whether or not to implement timed logout.
|
||||
// Unused block. Useful only for timed logout.
|
||||
/*
|
||||
// check for instant logout
|
||||
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) ||
|
||||
botWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))
|
||||
{
|
||||
logout = true;
|
||||
}
|
||||
|
||||
if (master &&
|
||||
(master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || master->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||
(masterWorldSessionPtr &&
|
||||
masterWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))))
|
||||
{
|
||||
logout = true;
|
||||
}
|
||||
|
||||
TravelTarget* target = nullptr;
|
||||
if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
||||
*/
|
||||
// Instant logout (the only option right now)
|
||||
{
|
||||
target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
||||
}
|
||||
|
||||
// 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!");
|
||||
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||
"goodbye", "Goodbye!", {});
|
||||
botAI->TellMaster(message);
|
||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||
botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object
|
||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
#include "TravelNode.h"
|
||||
#include "Talentspec.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "MapCollisionData.h"
|
||||
#include "MapMgr.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Playerbots.h"
|
||||
@ -687,10 +687,11 @@ std::vector<WorldPosition> WorldPosition::frommGridCoord(mGridCoord GridCoord)
|
||||
return retVec;
|
||||
}
|
||||
|
||||
// TODO: Cleanup — make this actually work.
|
||||
void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y)
|
||||
{
|
||||
std::string const fileName = "load_map_grid.csv";
|
||||
|
||||
/*
|
||||
if (isOverworld() && false || false)
|
||||
{
|
||||
if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y))
|
||||
@ -745,11 +746,12 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y)
|
||||
sPlayerbotAIConfig.log(fileName, out.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
if (!TravelMgr::instance().isBadMmap(mapId, x, y))
|
||||
{
|
||||
// load navmesh
|
||||
if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y))
|
||||
Map* map = getMap();
|
||||
if (map && map->GetMapCollisionData().LoadMMapTile(x, y) == MMAP::MMAP_LOAD_RESULT_ERROR)
|
||||
TravelMgr::instance().addBadMmap(mapId, x, y);
|
||||
|
||||
if (sPlayerbotAIConfig.hasLog(fileName))
|
||||
@ -762,7 +764,6 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y)
|
||||
sPlayerbotAIConfig.log(fileName, out.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldPosition::loadMapAndVMaps(WorldPosition secondPos)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user