mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 00:58:33 +00:00
Implement Gruul's Lair strategy (#1647)
* Implement Gruul's Lair strategy * minor non-gameplay tweaks to code * Use multiplier for tank assist * HKM warlock & Gruul tank tweaks * Fixed declarations * rewrote HKM + minor Gruul tweaks * Update PlayerbotAI.cpp * modernize code and address comments * clean ups to tank logic * Oops. * Remove post-move delay For actions like positioning bosses, the standard post-movement delay should be overridden IMO since a player would be sequencing their actions in this type of situation * Update RaidGruulsLairActions.cpp * Replace break statements with return true * enum class to enum * moved all isuseful checks to triggers * Split multipliers and improved banish logic * Update for comments * changes to int * use helpers for marking icons * address compile errors * correct MoveTo and use RTI helper * address comments and rename actions/triggers * adjust alignment of location constants * fix some crappy returns * allow return true when changing targets * change indents and move enums inside namespace * style changes, trim trailing whitespaces, address comments
This commit is contained in:
@@ -1534,6 +1534,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 533:
|
case 533:
|
||||||
strategyName = "naxx"; // Naxxramas
|
strategyName = "naxx"; // Naxxramas
|
||||||
break;
|
break;
|
||||||
|
case 565:
|
||||||
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
|
break;
|
||||||
case 574:
|
case 574:
|
||||||
strategyName = "wotlk-uk"; // Utgarde Keep
|
strategyName = "wotlk-uk"; // Utgarde Keep
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
|
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
|
||||||
#include "raids/karazhan/RaidKarazhanActionContext.h"
|
#include "raids/karazhan/RaidKarazhanActionContext.h"
|
||||||
#include "raids/karazhan/RaidKarazhanTriggerContext.h"
|
#include "raids/karazhan/RaidKarazhanTriggerContext.h"
|
||||||
|
#include "raids/gruulslair/RaidGruulsLairActionContext.h"
|
||||||
|
#include "raids/gruulslair/RaidGruulsLairTriggerContext.h"
|
||||||
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
||||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||||
#include "raids/eyeofeternity/RaidEoEActionContext.h"
|
#include "raids/eyeofeternity/RaidEoEActionContext.h"
|
||||||
@@ -111,6 +113,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
|||||||
actionContexts.Add(new RaidMcActionContext());
|
actionContexts.Add(new RaidMcActionContext());
|
||||||
actionContexts.Add(new RaidBwlActionContext());
|
actionContexts.Add(new RaidBwlActionContext());
|
||||||
actionContexts.Add(new RaidKarazhanActionContext());
|
actionContexts.Add(new RaidKarazhanActionContext());
|
||||||
|
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||||
actionContexts.Add(new RaidNaxxActionContext());
|
actionContexts.Add(new RaidNaxxActionContext());
|
||||||
actionContexts.Add(new RaidOsActionContext());
|
actionContexts.Add(new RaidOsActionContext());
|
||||||
actionContexts.Add(new RaidEoEActionContext());
|
actionContexts.Add(new RaidEoEActionContext());
|
||||||
@@ -144,6 +147,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
|||||||
triggerContexts.Add(new RaidMcTriggerContext());
|
triggerContexts.Add(new RaidMcTriggerContext());
|
||||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||||
triggerContexts.Add(new RaidOsTriggerContext());
|
triggerContexts.Add(new RaidOsTriggerContext());
|
||||||
triggerContexts.Add(new RaidEoETriggerContext());
|
triggerContexts.Add(new RaidEoETriggerContext());
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "RaidMcStrategy.h"
|
#include "RaidMcStrategy.h"
|
||||||
#include "RaidBwlStrategy.h"
|
#include "RaidBwlStrategy.h"
|
||||||
#include "RaidKarazhanStrategy.h"
|
#include "RaidKarazhanStrategy.h"
|
||||||
|
#include "RaidGruulsLairStrategy.h"
|
||||||
#include "RaidNaxxStrategy.h"
|
#include "RaidNaxxStrategy.h"
|
||||||
#include "RaidOsStrategy.h"
|
#include "RaidOsStrategy.h"
|
||||||
#include "RaidEoEStrategy.h"
|
#include "RaidEoEStrategy.h"
|
||||||
@@ -23,6 +24,7 @@ public:
|
|||||||
creators["mc"] = &RaidStrategyContext::mc;
|
creators["mc"] = &RaidStrategyContext::mc;
|
||||||
creators["bwl"] = &RaidStrategyContext::bwl;
|
creators["bwl"] = &RaidStrategyContext::bwl;
|
||||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||||
|
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||||
@@ -37,6 +39,7 @@ private:
|
|||||||
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
||||||
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
||||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||||
|
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||||
|
|||||||
49
src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h
Normal file
49
src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidGruulsLairActions.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
|
class RaidGruulsLairActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidGruulsLairActionContext()
|
||||||
|
{
|
||||||
|
// High King Maulgar
|
||||||
|
creators["high king maulgar main tank attack maulgar"] = &RaidGruulsLairActionContext::high_king_maulgar_main_tank_attack_maulgar;
|
||||||
|
creators["high king maulgar first assist tank attack olm"] = &RaidGruulsLairActionContext::high_king_maulgar_first_assist_tank_attack_olm;
|
||||||
|
creators["high king maulgar second assist tank attack blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_second_assist_tank_attack_blindeye;
|
||||||
|
creators["high king maulgar mage tank attack krosh"] = &RaidGruulsLairActionContext::high_king_maulgar_mage_tank_attack_krosh;
|
||||||
|
creators["high king maulgar moonkin tank attack kiggler"] = &RaidGruulsLairActionContext::high_king_maulgar_moonkin_tank_attack_kiggler;
|
||||||
|
creators["high king maulgar assign dps priority"] = &RaidGruulsLairActionContext::high_king_maulgar_assign_dps_priority;
|
||||||
|
creators["high king maulgar healer find safe position"] = &RaidGruulsLairActionContext::high_king_maulgar_healer_find_safe_position;
|
||||||
|
creators["high king maulgar run away from whirlwind"] = &RaidGruulsLairActionContext::high_king_maulgar_run_away_from_whirlwind;
|
||||||
|
creators["high king maulgar banish felstalker"] = &RaidGruulsLairActionContext::high_king_maulgar_banish_felstalker;
|
||||||
|
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss;
|
||||||
|
creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
||||||
|
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// High King Maulgar
|
||||||
|
static Action* high_king_maulgar_main_tank_attack_maulgar(PlayerbotAI* botAI) { return new HighKingMaulgarMainTankAttackMaulgarAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_first_assist_tank_attack_olm(PlayerbotAI* botAI) { return new HighKingMaulgarFirstAssistTankAttackOlmAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_second_assist_tank_attack_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarSecondAssistTankAttackBlindeyeAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_mage_tank_attack_krosh(PlayerbotAI* botAI) { return new HighKingMaulgarMageTankAttackKroshAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_moonkin_tank_attack_kiggler(PlayerbotAI* botAI) { return new HighKingMaulgarMoonkinTankAttackKigglerAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_assign_dps_priority(PlayerbotAI* botAI) { return new HighKingMaulgarAssignDPSPriorityAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_healer_find_safe_position(PlayerbotAI* botAI) { return new HighKingMaulgarHealerFindSafePositionAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_run_away_from_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarRunAwayFromWhirlwindAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_banish_felstalker(PlayerbotAI* botAI) { return new HighKingMaulgarBanishFelstalkerAction(botAI); }
|
||||||
|
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); }
|
||||||
|
static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
||||||
|
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
696
src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp
Normal file
696
src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp
Normal file
@@ -0,0 +1,696 @@
|
|||||||
|
#include "RaidGruulsLairActions.h"
|
||||||
|
#include "RaidGruulsLairHelpers.h"
|
||||||
|
#include "CreatureAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
using namespace GruulsLairHelpers;
|
||||||
|
|
||||||
|
// High King Maulgar Actions
|
||||||
|
|
||||||
|
// Main tank on Maulgar
|
||||||
|
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
|
MarkTargetWithSquare(bot, maulgar);
|
||||||
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != maulgar)
|
||||||
|
return Attack(maulgar);
|
||||||
|
|
||||||
|
if (maulgar->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
||||||
|
const float maxDistance = 3.0f;
|
||||||
|
|
||||||
|
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||||
|
|
||||||
|
if (distanceToTankPosition > maxDistance)
|
||||||
|
{
|
||||||
|
float dX = tankPosition.x - bot->GetPositionX();
|
||||||
|
float dY = tankPosition.y - bot->GetPositionY();
|
||||||
|
float dist = sqrt(dX * dX + dY * dY);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(),
|
||||||
|
maulgar->GetPositionX() - bot->GetPositionX());
|
||||||
|
bot->SetFacingTo(orientation);
|
||||||
|
}
|
||||||
|
else if (!bot->IsWithinMeleeRange(maulgar))
|
||||||
|
{
|
||||||
|
return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(),
|
||||||
|
maulgar->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First offtank on Olm
|
||||||
|
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
|
||||||
|
MarkTargetWithCircle(bot, olm);
|
||||||
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != olm)
|
||||||
|
return Attack(olm);
|
||||||
|
|
||||||
|
if (olm->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
||||||
|
const float maxDistance = 3.0f;
|
||||||
|
const float olmTankLeeway = 30.0f;
|
||||||
|
|
||||||
|
float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||||
|
|
||||||
|
if (distanceOlmToTankPosition > olmTankLeeway)
|
||||||
|
{
|
||||||
|
float dX = tankPosition.x - bot->GetPositionX();
|
||||||
|
float dY = tankPosition.y - bot->GetPositionY();
|
||||||
|
float dist = sqrt(dX * dX + dY * dY);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!bot->IsWithinMeleeRange(olm))
|
||||||
|
{
|
||||||
|
return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(),
|
||||||
|
olm->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second offtank on Blindeye
|
||||||
|
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
|
MarkTargetWithStar(bot, blindeye);
|
||||||
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
|
|
||||||
|
if (bot->GetVictim() != blindeye)
|
||||||
|
return Attack(blindeye);
|
||||||
|
|
||||||
|
if (blindeye->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
||||||
|
const float maxDistance = 3.0f;
|
||||||
|
|
||||||
|
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||||
|
|
||||||
|
if (distanceToTankPosition > maxDistance)
|
||||||
|
{
|
||||||
|
float dX = tankPosition.x - bot->GetPositionX();
|
||||||
|
float dY = tankPosition.y - bot->GetPositionY();
|
||||||
|
float dist = sqrt(dX * dX + dY * dY);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(),
|
||||||
|
blindeye->GetPositionX() - bot->GetPositionX());
|
||||||
|
bot->SetFacingTo(orientation);
|
||||||
|
}
|
||||||
|
else if (!bot->IsWithinMeleeRange(blindeye))
|
||||||
|
{
|
||||||
|
return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(),
|
||||||
|
blindeye->GetPositionZ(), false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mage with highest max HP on Krosh
|
||||||
|
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
|
MarkTargetWithTriangle(bot, krosh);
|
||||||
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
|
|
||||||
|
if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("spellsteal", krosh))
|
||||||
|
return botAI->CastSpell("spellsteal", krosh);
|
||||||
|
|
||||||
|
if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot))
|
||||||
|
return botAI->CastSpell("fire ward", bot);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(krosh->GetGUID());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (krosh->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
||||||
|
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||||
|
const float minDistance = 16.0f;
|
||||||
|
const float maxDistance = 29.0f;
|
||||||
|
const float tankPositionLeeway = 1.0f;
|
||||||
|
|
||||||
|
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
||||||
|
{
|
||||||
|
if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway))
|
||||||
|
{
|
||||||
|
return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false,
|
||||||
|
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
||||||
|
krosh->GetPositionX() - bot->GetPositionX());
|
||||||
|
bot->SetFacingTo(orientation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moonkin with highest max HP on Kiggler
|
||||||
|
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
|
||||||
|
MarkTargetWithDiamond(bot, kiggler);
|
||||||
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(kiggler->GetGUID());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// Target priority 1: Blindeye
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
if (blindeye && blindeye->IsAlive())
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != blindeye->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(blindeye->GetGUID());
|
||||||
|
return Attack(blindeye);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target priority 2: Olm
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
if (olm && olm->IsAlive())
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != olm->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(olm->GetGUID());
|
||||||
|
return Attack(olm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target priority 3a: Krosh (ranged only)
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(krosh->GetGUID());
|
||||||
|
return Attack(krosh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target priority 3b: Kiggler
|
||||||
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
if (kiggler && kiggler->IsAlive())
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(kiggler->GetGUID());
|
||||||
|
return Attack(kiggler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target priority 4: Maulgar
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
if (maulgar && maulgar->IsAlive())
|
||||||
|
{
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
|
|
||||||
|
if (bot->GetTarget() != maulgar->GetGUID())
|
||||||
|
{
|
||||||
|
bot->SetSelection(maulgar->GetGUID());
|
||||||
|
return Attack(maulgar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||||
|
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
||||||
|
const float maxDistanceFromFight = 50.0f;
|
||||||
|
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
||||||
|
|
||||||
|
if (distToFight > maxDistanceFromFight)
|
||||||
|
{
|
||||||
|
float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x);
|
||||||
|
float destX = fightCenter.x + 40.0f * cos(angle);
|
||||||
|
float destY = fightCenter.y + 40.0f * sin(angle);
|
||||||
|
float destZ = fightCenter.z;
|
||||||
|
|
||||||
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), destX, destY, destZ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position safePos;
|
||||||
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run away from Maulgar during Whirlwind (logic for after all other ogres are dead)
|
||||||
|
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
|
const float safeDistance = 10.0f;
|
||||||
|
float distance = bot->GetExactDist2d(maulgar);
|
||||||
|
|
||||||
|
if (distance < safeDistance)
|
||||||
|
{
|
||||||
|
float angle = atan2(bot->GetPositionY() - maulgar->GetPositionY(),
|
||||||
|
bot->GetPositionX() - maulgar->GetPositionX());
|
||||||
|
float destX = maulgar->GetPositionX() + safeDistance * cos(angle);
|
||||||
|
float destY = maulgar->GetPositionY() + safeDistance * sin(angle);
|
||||||
|
float destZ = bot->GetPositionZ();
|
||||||
|
|
||||||
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), destX, destY, destZ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float destDist = maulgar->GetExactDist2d(destX, destY);
|
||||||
|
|
||||||
|
if (destDist >= safeDistance - 0.1f)
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(true);
|
||||||
|
return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||||
|
std::vector<Unit*> felStalkers;
|
||||||
|
for (auto const& npc : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(npc);
|
||||||
|
if (unit && unit->GetEntry() == NPC_WILD_FEL_STALKER && unit->IsAlive())
|
||||||
|
felStalkers.push_back(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Player*> warlocks;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member))
|
||||||
|
warlocks.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
int warlockIndex = -1;
|
||||||
|
for (size_t i = 0; i < warlocks.size(); ++i)
|
||||||
|
{
|
||||||
|
if (warlocks[i] == bot)
|
||||||
|
{
|
||||||
|
warlockIndex = static_cast<int>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||||
|
{
|
||||||
|
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
||||||
|
if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true))
|
||||||
|
return botAI->CastSpell("banish", assignedFelStalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye
|
||||||
|
// Hunter 2: Misdirect Blindeye to second offtank
|
||||||
|
bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<Player*> hunters;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
|
||||||
|
hunters.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hunterIndex = -1;
|
||||||
|
for (size_t i = 0; i < hunters.size(); ++i)
|
||||||
|
{
|
||||||
|
if (hunters[i] == bot)
|
||||||
|
{
|
||||||
|
hunterIndex = static_cast<int>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
Player* olmTank = nullptr;
|
||||||
|
Player* blindeyeTank = nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member;
|
||||||
|
else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (hunterIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
botAI->CastSpell("misdirection", olmTank);
|
||||||
|
if (bot->HasAura(SPELL_MISDIRECTION))
|
||||||
|
{
|
||||||
|
Pet* pet = bot->GetPet();
|
||||||
|
if (pet && pet->IsAlive() && pet->GetVictim() != blindeye)
|
||||||
|
{
|
||||||
|
pet->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||||
|
pet->AttackStop();
|
||||||
|
pet->SetTarget(blindeye->GetGUID());
|
||||||
|
if (pet->GetCharmInfo())
|
||||||
|
{
|
||||||
|
pet->GetCharmInfo()->SetIsCommandAttack(true);
|
||||||
|
pet->GetCharmInfo()->SetIsAtStay(false);
|
||||||
|
pet->GetCharmInfo()->SetIsFollowing(false);
|
||||||
|
pet->GetCharmInfo()->SetIsCommandFollow(false);
|
||||||
|
pet->GetCharmInfo()->SetIsReturning(false);
|
||||||
|
}
|
||||||
|
pet->ToCreature()->AI()->AttackStart(blindeye);
|
||||||
|
}
|
||||||
|
return botAI->CastSpell("steady shot", olm);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
botAI->CastSpell("misdirection", blindeyeTank);
|
||||||
|
if (bot->HasAura(SPELL_MISDIRECTION))
|
||||||
|
return botAI->CastSpell("steady shot", blindeye);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller Actions
|
||||||
|
|
||||||
|
// Position in center of the room
|
||||||
|
bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
|
if (bot->GetVictim() != gruul)
|
||||||
|
return Attack(gruul);
|
||||||
|
|
||||||
|
if (gruul->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||||
|
const float maxDistance = 3.0f;
|
||||||
|
|
||||||
|
float dX = tankPosition.x - bot->GetPositionX();
|
||||||
|
float dY = tankPosition.y - bot->GetPositionY();
|
||||||
|
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||||
|
|
||||||
|
if (distanceToTankPosition > maxDistance)
|
||||||
|
{
|
||||||
|
float step = std::min(maxDistance, distanceToTankPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||||
|
const float moveZ = tankPosition.z;
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(),
|
||||||
|
gruul->GetPositionX() - bot->GetPositionX());
|
||||||
|
bot->SetFacingTo(orientation);
|
||||||
|
}
|
||||||
|
else if (!bot->IsWithinMeleeRange(gruul))
|
||||||
|
{
|
||||||
|
return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(),
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged will take initial positions around the middle of the room, 25-40 yards from center
|
||||||
|
// Ranged should spread out 10 yards from each other
|
||||||
|
bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
static std::unordered_map<ObjectGuid, Position> initialPositions;
|
||||||
|
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||||
|
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||||
|
{
|
||||||
|
initialPositions.clear();
|
||||||
|
hasReachedInitialPosition.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||||
|
const float centerX = tankPosition.x;
|
||||||
|
const float centerY = tankPosition.y;
|
||||||
|
float centerZ = bot->GetPositionZ();
|
||||||
|
const float minRadius = 25.0f;
|
||||||
|
const float maxRadius = 40.0f;
|
||||||
|
|
||||||
|
std::vector<Player*> members;
|
||||||
|
Player* closestMember = nullptr;
|
||||||
|
float closestDist = std::numeric_limits<float>::max();
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
members.push_back(member);
|
||||||
|
if (member != bot)
|
||||||
|
{
|
||||||
|
float dist = bot->GetExactDist2d(member);
|
||||||
|
if (dist < closestDist)
|
||||||
|
{
|
||||||
|
closestDist = dist;
|
||||||
|
closestMember = member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialPositions.count(bot->GetGUID()))
|
||||||
|
{
|
||||||
|
auto it = std::find(members.begin(), members.end(), bot);
|
||||||
|
uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0;
|
||||||
|
uint8 count = members.size();
|
||||||
|
|
||||||
|
float angle = 2 * M_PI * botIndex / count;
|
||||||
|
float radius = minRadius + static_cast<float>(rand()) /
|
||||||
|
static_cast<float>(RAND_MAX) * (maxRadius - minRadius);
|
||||||
|
float targetX = centerX + radius * cos(angle);
|
||||||
|
float targetY = centerY + radius * sin(angle);
|
||||||
|
|
||||||
|
initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ);
|
||||||
|
hasReachedInitialPosition[bot->GetGUID()] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position targetPosition = initialPositions[bot->GetGUID()];
|
||||||
|
if (!hasReachedInitialPosition[bot->GetGUID()])
|
||||||
|
{
|
||||||
|
if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f))
|
||||||
|
{
|
||||||
|
float destX = targetPosition.GetPositionX();
|
||||||
|
float destY = targetPosition.GetPositionY();
|
||||||
|
float destZ = targetPosition.GetPositionZ();
|
||||||
|
|
||||||
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(),
|
||||||
|
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||||
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float minSpreadDistance = 10.0f;
|
||||||
|
const float movementThreshold = 2.0f;
|
||||||
|
|
||||||
|
if (closestMember && closestDist < minSpreadDistance - movementThreshold)
|
||||||
|
{
|
||||||
|
return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(),
|
||||||
|
closestMember->GetPositionZ()), minSpreadDistance, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get away from other group members when Ground Slam is cast
|
||||||
|
bool GruulTheDragonkillerShatterSpreadAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||||
|
Unit* closestMember = nullptr;
|
||||||
|
float closestDist = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
for (auto& member : members)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(member);
|
||||||
|
if (!unit || bot->GetGUID() == member)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const float dist = bot->GetExactDist2d(unit);
|
||||||
|
if (dist < closestDist)
|
||||||
|
{
|
||||||
|
closestDist = dist;
|
||||||
|
closestMember = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closestMember)
|
||||||
|
{
|
||||||
|
return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(),
|
||||||
|
closestMember->GetPositionZ()), 6.0f, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
112
src/strategy/raids/gruulslair/RaidGruulsLairActions.h
Normal file
112
src/strategy/raids/gruulslair/RaidGruulsLairActions.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
class HighKingMaulgarMainTankAttackMaulgarAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarMainTankAttackMaulgarAction(PlayerbotAI* botAI, std::string const name = "high king maulgar main tank attack maulgar") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarFirstAssistTankAttackOlmAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarFirstAssistTankAttackOlmAction(PlayerbotAI* botAI, std::string const name = "high king maulgar first assist tank attack olm") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarSecondAssistTankAttackBlindeyeAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarSecondAssistTankAttackBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar second assist tank attack blindeye") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarMageTankAttackKroshAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarMageTankAttackKroshAction(PlayerbotAI* botAI, std::string const name = "high king maulgar mage tank attack krosh") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarMoonkinTankAttackKigglerAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarMoonkinTankAttackKigglerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar moonkin tank attack kiggler") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarAssignDPSPriorityAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "high king maulgar assign dps priority") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarHealerFindSafePositionAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarHealerFindSafePositionAction(PlayerbotAI* botAI, std::string const name = "high king maulgar healer find safe position") : MovementAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarRunAwayFromWhirlwindAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarRunAwayFromWhirlwindAction(PlayerbotAI* botAI, std::string const name = "high king maulgar run away from whirlwind") : MovementAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarBanishFelstalkerAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarBanishFelstalkerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar banish felstalker") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarMisdirectOlmAndBlindeyeAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarMisdirectOlmAndBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar misdirect olm and blindeye") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller spread ranged") : MovementAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerShatterSpreadAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerShatterSpreadAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller shatter spread") : MovementAction(botAI, name) {};
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
241
src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp
Normal file
241
src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
#include "RaidGruulsLairHelpers.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "GroupReference.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
namespace GruulsLairHelpers
|
||||||
|
{
|
||||||
|
namespace GruulsLairLocations
|
||||||
|
{
|
||||||
|
// Olm does not chase properly due to the Core's caster movement issues
|
||||||
|
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
||||||
|
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
||||||
|
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||||
|
const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
||||||
|
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
||||||
|
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
||||||
|
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
||||||
|
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
||||||
|
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
const char* ogreBossNames[] =
|
||||||
|
{
|
||||||
|
"high king maulgar",
|
||||||
|
"kiggler the crazed",
|
||||||
|
"krosh firehand",
|
||||||
|
"olm the summoner",
|
||||||
|
"blindeye the seer"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* name : ogreBossNames)
|
||||||
|
{
|
||||||
|
Unit* boss = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", name)->Get();
|
||||||
|
if (!boss || !boss->IsAlive())
|
||||||
|
continue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!target || !group)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||||
|
if (currentGuid != target->GetGUID())
|
||||||
|
{
|
||||||
|
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||||
|
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||||
|
|
||||||
|
if (currentRti != rtiName || currentTarget != target)
|
||||||
|
{
|
||||||
|
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||||
|
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* highestHpMage = nullptr;
|
||||||
|
uint32 highestHp = 0;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->getClass() == CLASS_MAGE)
|
||||||
|
{
|
||||||
|
uint32 hp = member->GetMaxHealth();
|
||||||
|
if (!highestHpMage || hp > highestHp)
|
||||||
|
{
|
||||||
|
highestHpMage = member;
|
||||||
|
highestHp = hp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return highestHpMage == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot)
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* highestHpMoonkin = nullptr;
|
||||||
|
uint32 highestHp = 0;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->getClass() == CLASS_DRUID)
|
||||||
|
{
|
||||||
|
int tab = AiFactory::GetPlayerSpecTab(member);
|
||||||
|
if (tab == DRUID_TAB_BALANCE)
|
||||||
|
{
|
||||||
|
uint32 hp = member->GetMaxHealth();
|
||||||
|
if (!highestHpMoonkin || hp > highestHp)
|
||||||
|
{
|
||||||
|
highestHpMoonkin = member;
|
||||||
|
highestHp = hp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return highestHpMoonkin == bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos)
|
||||||
|
{
|
||||||
|
const float KROSH_SAFE_DISTANCE = 20.0f;
|
||||||
|
const float MAULGAR_SAFE_DISTANCE = 10.0f;
|
||||||
|
bool isSafe = true;
|
||||||
|
|
||||||
|
Unit* krosh = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "krosh firehand")->Get();
|
||||||
|
if (krosh && krosh->IsAlive())
|
||||||
|
{
|
||||||
|
float dist = sqrt(pow(pos.GetPositionX() - krosh->GetPositionX(), 2) + pow(pos.GetPositionY() - krosh->GetPositionY(), 2));
|
||||||
|
if (dist < KROSH_SAFE_DISTANCE)
|
||||||
|
isSafe = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* maulgar = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "high king maulgar")->Get();
|
||||||
|
if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive())
|
||||||
|
{
|
||||||
|
float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2));
|
||||||
|
if (dist < MAULGAR_SAFE_DISTANCE)
|
||||||
|
isSafe = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos)
|
||||||
|
{
|
||||||
|
const float SEARCH_RADIUS = 30.0f;
|
||||||
|
const uint8 NUM_POSITIONS = 32;
|
||||||
|
|
||||||
|
outPos = { bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() };
|
||||||
|
if (IsPositionSafe(botAI, bot, outPos))
|
||||||
|
{
|
||||||
|
outPos = Position();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float bestScore = std::numeric_limits<float>::max();
|
||||||
|
bool foundSafeSpot = false;
|
||||||
|
Position bestPos;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_POSITIONS; ++i)
|
||||||
|
{
|
||||||
|
float angle = 2 * M_PI * i / NUM_POSITIONS;
|
||||||
|
Position candidatePos;
|
||||||
|
candidatePos.m_positionX = bot->GetPositionX() + SEARCH_RADIUS * cos(angle);
|
||||||
|
candidatePos.m_positionY = bot->GetPositionY() + SEARCH_RADIUS * sin(angle);
|
||||||
|
candidatePos.m_positionZ = bot->GetPositionZ();
|
||||||
|
|
||||||
|
float destX = candidatePos.m_positionX, destY = candidatePos.m_positionY, destZ = candidatePos.m_positionZ;
|
||||||
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), destX, destY, destZ, true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (destX != candidatePos.m_positionX || destY != candidatePos.m_positionY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidatePos.m_positionX = destX;
|
||||||
|
candidatePos.m_positionY = destY;
|
||||||
|
candidatePos.m_positionZ = destZ;
|
||||||
|
|
||||||
|
if (IsPositionSafe(botAI, bot, candidatePos))
|
||||||
|
{
|
||||||
|
float movementDistance = sqrt(pow(destX - bot->GetPositionX(), 2) + pow(destY - bot->GetPositionY(), 2));
|
||||||
|
if (movementDistance < bestScore)
|
||||||
|
{
|
||||||
|
bestScore = movementDistance;
|
||||||
|
bestPos = candidatePos;
|
||||||
|
foundSafeSpot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSafeSpot)
|
||||||
|
{
|
||||||
|
outPos = bestPos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPos = Position();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h
Normal file
62
src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#ifndef RAID_GRUULSLAIRHELPERS_H
|
||||||
|
#define RAID_GRUULSLAIRHELPERS_H
|
||||||
|
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
|
|
||||||
|
namespace GruulsLairHelpers
|
||||||
|
{
|
||||||
|
enum GruulsLairSpells
|
||||||
|
{
|
||||||
|
// High King Maulgar
|
||||||
|
SPELL_WHIRLWIND = 33238,
|
||||||
|
|
||||||
|
// Krosh Firehand
|
||||||
|
SPELL_SPELL_SHIELD = 33054,
|
||||||
|
|
||||||
|
// Hunter
|
||||||
|
SPELL_MISDIRECTION = 34477,
|
||||||
|
|
||||||
|
// Warlock
|
||||||
|
SPELL_BANISH = 18647, // Rank 2
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
SPELL_GROUND_SLAM_1 = 33525,
|
||||||
|
SPELL_GROUND_SLAM_2 = 39187,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GruulsLairNPCs
|
||||||
|
{
|
||||||
|
NPC_WILD_FEL_STALKER = 18847,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
||||||
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||||
|
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||||
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||||
|
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||||
|
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||||
|
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||||
|
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||||
|
|
||||||
|
struct Location
|
||||||
|
{
|
||||||
|
float x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace GruulsLairLocations
|
||||||
|
{
|
||||||
|
extern const Location MaulgarTankPosition;
|
||||||
|
extern const Location OlmTankPosition;
|
||||||
|
extern const Location BlindeyeTankPosition;
|
||||||
|
extern const Location KroshTankPosition;
|
||||||
|
extern const Location MaulgarRoomCenter;
|
||||||
|
extern const Location GruulTankPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
110
src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp
Normal file
110
src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include "RaidGruulsLairMultipliers.h"
|
||||||
|
#include "RaidGruulsLairActions.h"
|
||||||
|
#include "RaidGruulsLairHelpers.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "DruidBearActions.h"
|
||||||
|
#include "DruidCatActions.h"
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "MageActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
|
using namespace GruulsLairHelpers;
|
||||||
|
|
||||||
|
static bool IsChargeAction(Action* action)
|
||||||
|
{
|
||||||
|
return dynamic_cast<CastChargeAction*>(action) ||
|
||||||
|
dynamic_cast<CastInterceptAction*>(action) ||
|
||||||
|
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
||||||
|
dynamic_cast<CastFeralChargeCatAction*>(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't run back in during Whirlwind
|
||||||
|
float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
|
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||||
|
(!kiggler || !kiggler->IsAlive()) &&
|
||||||
|
(!krosh || !krosh->IsAlive()) &&
|
||||||
|
(!olm || !olm->IsAlive()) &&
|
||||||
|
(!blindeye || !blindeye->IsAlive()))
|
||||||
|
{
|
||||||
|
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arcane Shot will remove Spell Shield, which the mage tank needs to survive
|
||||||
|
float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
|
||||||
|
if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast<CastArcaneShotAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HighKingMaulgarDisableMageTankAOEMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (IsKroshMageTank(botAI, bot) &&
|
||||||
|
(dynamic_cast<CastFrostNovaAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||||
|
dynamic_cast<CastConeOfColdAction*>(action) || dynamic_cast<CastFlamestrikeAction*>(action) ||
|
||||||
|
dynamic_cast<CastDragonsBreathAction*>(action) || dynamic_cast<CastBlastWaveAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GruulTheDragonkillerMainTankMovementMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
if (!gruul)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
{
|
||||||
|
if (gruul->GetVictim() == bot && dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
if (!gruul)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
|
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||||
|
{
|
||||||
|
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||||
|
IsChargeAction(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
48
src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h
Normal file
48
src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class HighKingMaulgarDisableTankAssistMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarDisableTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable tank assist multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarAvoidWhirlwindMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar avoid whirlwind multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarDisableArcaneShotOnKroshMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarDisableArcaneShotOnKroshMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable arcane shot on krosh multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarDisableMageTankAOEMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarDisableMageTankAOEMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable mage tank aoe multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerMainTankMovementMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerMainTankMovementMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller main tank movement multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerGroundSlamMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerGroundSlamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller ground slam multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
56
src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp
Normal file
56
src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include "RaidGruulsLairStrategy.h"
|
||||||
|
#include "RaidGruulsLairMultipliers.h"
|
||||||
|
|
||||||
|
void RaidGruulsLairStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
// High King Maulgar
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar is main tank", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar main tank attack maulgar", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar is first assist tank", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar first assist tank attack olm", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar is second assist tank", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar second assist tank attack blindeye", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar is mage tank", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar mage tank attack krosh", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar is moonkin tank", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar moonkin tank attack kiggler", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar determining kill order", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar assign dps priority", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar healer in danger", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar healer find safe position", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar boss channeling whirlwind", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar run away from whirlwind", ACTION_EMERGENCY + 6), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar wild felstalker spawned", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar banish felstalker", ACTION_RAID + 2), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("high king maulgar pulling olm and blindeye", NextAction::array(0,
|
||||||
|
new NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2), nullptr)));
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", NextAction::array(0,
|
||||||
|
new NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", NextAction::array(0,
|
||||||
|
new NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", NextAction::array(0,
|
||||||
|
new NextAction("gruul the dragonkiller shatter spread", ACTION_EMERGENCY + 6), nullptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaidGruulsLairStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new HighKingMaulgarDisableTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HighKingMaulgarAvoidWhirlwindMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HighKingMaulgarDisableArcaneShotOnKroshMultiplier(botAI));
|
||||||
|
multipliers.push_back(new HighKingMaulgarDisableMageTankAOEMultiplier(botAI));
|
||||||
|
multipliers.push_back(new GruulTheDragonkillerMainTankMovementMultiplier(botAI));
|
||||||
|
multipliers.push_back(new GruulTheDragonkillerGroundSlamMultiplier(botAI));
|
||||||
|
}
|
||||||
18
src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h
Normal file
18
src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H
|
||||||
|
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class RaidGruulsLairStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidGruulsLairStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "gruulslair"; }
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
49
src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h
Normal file
49
src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "RaidGruulsLairTriggers.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
class RaidGruulsLairTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidGruulsLairTriggerContext() : NamedObjectContext<Trigger>()
|
||||||
|
{
|
||||||
|
// High King Maulgar
|
||||||
|
creators["high king maulgar is main tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_main_tank;
|
||||||
|
creators["high king maulgar is first assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_first_assist_tank;
|
||||||
|
creators["high king maulgar is second assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_second_assist_tank;
|
||||||
|
creators["high king maulgar is mage tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_mage_tank;
|
||||||
|
creators["high king maulgar is moonkin tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_moonkin_tank;
|
||||||
|
creators["high king maulgar determining kill order"] = &RaidGruulsLairTriggerContext::high_king_maulgar_determining_kill_order;
|
||||||
|
creators["high king maulgar healer in danger"] = &RaidGruulsLairTriggerContext::high_king_maulgar_healer_in_danger;
|
||||||
|
creators["high king maulgar boss channeling whirlwind"] = &RaidGruulsLairTriggerContext::high_king_maulgar_boss_channeling_whirlwind;
|
||||||
|
creators["high king maulgar wild felstalker spawned"] = &RaidGruulsLairTriggerContext::high_king_maulgar_wild_felstalker_spawned;
|
||||||
|
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank;
|
||||||
|
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
||||||
|
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// High King Maulgar
|
||||||
|
static Trigger* high_king_maulgar_is_main_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMainTankTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_is_first_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsFirstAssistTankTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_is_second_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsSecondAssistTankTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_is_mage_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMageTankTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_is_moonkin_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMoonkinTankTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_determining_kill_order(PlayerbotAI* botAI) { return new HighKingMaulgarDeterminingKillOrderTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_healer_in_danger(PlayerbotAI* botAI) { return new HighKingMaulgarHealerInDangerTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_boss_channeling_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarBossChannelingWhirlwindTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_wild_felstalker_spawned(PlayerbotAI* botAI) { return new HighKingMaulgarWildFelstalkerSpawnedTrigger(botAI); }
|
||||||
|
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller
|
||||||
|
static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); }
|
||||||
|
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
160
src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp
Normal file
160
src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#include "RaidGruulsLairTriggers.h"
|
||||||
|
#include "RaidGruulsLairHelpers.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
using namespace GruulsLairHelpers;
|
||||||
|
|
||||||
|
// High King Maulgar Triggers
|
||||||
|
|
||||||
|
bool HighKingMaulgarIsMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
|
return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
|
||||||
|
return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
|
return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
|
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
|
||||||
|
return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
|
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||||
|
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
||||||
|
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
||||||
|
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
||||||
|
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
||||||
|
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsHeal(bot) && IsAnyOgreBossAlive(botAI);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
|
||||||
|
return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||||
|
!botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
||||||
|
|
||||||
|
return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group || bot->getClass() != CLASS_HUNTER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<Player*> hunters;
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
|
||||||
|
hunters.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hunterIndex = -1;
|
||||||
|
for (size_t i = 0; i < hunters.size(); ++i)
|
||||||
|
{
|
||||||
|
if (hunters[i] == bot)
|
||||||
|
{
|
||||||
|
hunterIndex = static_cast<int>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hunterIndex == -1 || hunterIndex > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
Player* olmTank = nullptr;
|
||||||
|
Player* blindeyeTank = nullptr;
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member;
|
||||||
|
else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (hunterIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
||||||
|
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
||||||
|
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gruul the Dragonkiller Triggers
|
||||||
|
|
||||||
|
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
|
return gruul && gruul->IsAlive() && botAI->IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
|
return gruul && gruul->IsAlive() && botAI->IsRanged(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
|
return gruul && gruul->IsAlive() &&
|
||||||
|
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
|
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||||
|
}
|
||||||
97
src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h
Normal file
97
src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
class HighKingMaulgarIsMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarIsFirstAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarIsFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is first assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarIsSecondAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarIsSecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is second assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarIsMageTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarIsMageTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is mage tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarIsMoonkinTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarIsMoonkinTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is moonkin tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarDeterminingKillOrderTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar determining kill order") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarHealerInDangerTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarHealerInDangerTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar healers in danger") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarBossChannelingWhirlwindTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarBossChannelingWhirlwindTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar boss channeling whirlwind") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarWildFelstalkerSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarWildFelstalkerSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar wild felstalker spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighKingMaulgarPullingOlmAndBlindeyeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighKingMaulgarPullingOlmAndBlindeyeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar pulling olm and blindeye") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GruulTheDragonkillerIncomingShatterTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GruulTheDragonkillerIncomingShatterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller incoming shatter") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user