// /*
// * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU
GPL v2 license, you may redistribute it
// and/or modify it under version 2 of the License, or (at your option), any
later version.
// */
#ifndef _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H
#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "RaidNaxxActions.h"
class RaidNaxxActionContext : public NamedObjectContext<Action>
{
public:
RaidNaxxActionContext()
{
creators["grobbulus go behind the boss"] =
&RaidNaxxActionContext::go_behind_the_boss;
creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus;
creators["grobbulus move center"] =
&RaidNaxxActionContext::grobbulus_move_center;
creators["heigan dance melee"] =
&RaidNaxxActionContext::heigan_dance_melee;
creators["heigan dance ranged"] =
&RaidNaxxActionContext::heigan_dance_ranged;
creators["thaddius attack nearest pet"] =
&RaidNaxxActionContext::thaddius_attack_nearest_pet;
// creators["thaddius melee to place"] =
&RaidNaxxActionContext::thaddius_tank_to_place;
// creators["thaddius ranged to place"] =
&RaidNaxxActionContext::thaddius_ranged_to_place;
creators["thaddius move to platform"] =
&RaidNaxxActionContext::thaddius_move_to_platform;
creators["thaddius move polarity"] =
&RaidNaxxActionContext::thaddius_move_polarity;
creators["razuvious use obedience crystal"] =
&RaidNaxxActionContext::razuvious_use_obedience_crystal;
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
creators["horseman attract alternatively"] =
&RaidNaxxActionContext::horseman_attract_alternatively;
creators["horseman attack in order"] =
&RaidNaxxActionContext::horseman_attack_in_order;
creators["sapphiron ground position"] =
&RaidNaxxActionContext::sapphiron_ground_position;
creators["sapphiron flight position"] =
&RaidNaxxActionContext::sapphiron_flight_position;
creators["kel'thuzad choose target"] =
&RaidNaxxActionContext::kelthuzad_choose_target;
creators["kel'thuzad position"] =
&RaidNaxxActionContext::kelthuzad_position;
creators["anub'rekhan choose target"] =
&RaidNaxxActionContext::anubrekhan_choose_target;
creators["anub'rekhan position"] =
&RaidNaxxActionContext::anubrekhan_position;
creators["gluth choose target"] =
&RaidNaxxActionContext::gluth_choose_target;
creators["gluth position"] = &RaidNaxxActionContext::gluth_position;
creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown;
creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position;
creators["loatheb choose target"] =
&RaidNaxxActionContext::loatheb_choose_target;
}
private:
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new
GrobbulusGoBehindAction(ai); }
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new
GrobbulusRotateAction(ai); }
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new
GrobblulusMoveCenterAction(ai); }
static Action* heigan_dance_melee(PlayerbotAI* ai) { return new
HeiganDanceMeleeAction(ai); }
static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new
HeiganDanceRangedAction(ai); }
static Action* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new
ThaddiusAttackNearestPetAction(ai); }
// static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new
ThaddiusMeleeToPlaceAction(ai); }
// static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new
ThaddiusRangedToPlaceAction(ai); }
static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new
ThaddiusMoveToPlatformAction(ai); }
static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new
ThaddiusMovePolarityAction(ai); }
static Action* razuvious_target(PlayerbotAI* ai) { return new
RazuviousTargetAction(ai); }
static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai)
{
return new RazuviousUseObedienceCrystalAction(ai);
}
static Action* horseman_attract_alternatively(PlayerbotAI* ai)
{
return new HorsemanAttractAlternativelyAction(ai);
}
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new
HorsemanAttactInOrderAction(ai); }
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai)
{ return new
// SapphironGroundMainTankPositionAction(ai); }
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new
SapphironGroundPositionAction(ai); }
static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new
SapphironFlightPositionAction(ai); }
// static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new
SapphironAvoidChillAction(ai); }
static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new
KelthuzadChooseTargetAction(ai); }
static Action* kelthuzad_position(PlayerbotAI* ai) { return new
KelthuzadPositionAction(ai); }
static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new
AnubrekhanChooseTargetAction(ai); }
static Action* anubrekhan_position(PlayerbotAI* ai) { return new
AnubrekhanPositionAction(ai); }
static Action* gluth_choose_target(PlayerbotAI* ai) { return new
GluthChooseTargetAction(ai); }
static Action* gluth_position(PlayerbotAI* ai) { return new
GluthPositionAction(ai); }
static Action* gluth_slowdown(PlayerbotAI* ai) { return new
GluthSlowdownAction(ai); }
static Action* loatheb_position(PlayerbotAI* ai) { return new
LoathebPositionAction(ai); }
static Action* loatheb_choose_target(PlayerbotAI* ai) { return new
LoathebChooseTargetAction(ai); }
};
#endif
#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H
#define _PLAYERBOT_RAIDNAXXACTIONS_H
#include "Action.h"
#include "AttackAction.h"
#include "GenericActions.h"
#include "MovementActions.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "RaidNaxxBossHelper.h"
#include "RaidNaxxScripts.h"
// just for test
// class TryToGetBossAIAction : public Action
// {
// public:
// TryToGetBossAIAction(PlayerbotAI* ai) : Action(ai, "try to get boss ai")
{}
// public:
// virtual bool Execute(Event event);
// };
class GrobbulusGoBehindAction : public MovementAction
{
public:
GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float
delta_angle = M_PI / 8)
: MovementAction(ai, "grobbulus go behind")
{
this->distance = distance;
this->delta_angle = delta_angle;
}
virtual bool Execute(Event event);
protected:
float distance, delta_angle;
};
class GrobbulusRotateAction : public RotateAroundTheCenterPointAction
{
public:
GrobbulusRotateAction(PlayerbotAI* botAI)
: RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f,
-3310.38f, 35.0f, 8, true, M_PI)
{
}
virtual bool isUseful() override
{
return RotateAroundTheCenterPointAction::isUseful() && botAI-
>IsMainTank(bot) &&
AI_VALUE2(bool, "has aggro", "boss target");
}
uint32 GetCurrWaypoint() override;
};
class GrobblulusMoveCenterAction : public MoveInsideAction
{
public:
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f,
-3310.38f, 5.0f) {}
};
class HeiganDanceAction : public MovementAction
{
public:
HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance")
{
this->prev_phase = 0;
this->prev_erupt = 0;
this->prev_timer = 0;
ResetSafe();
waypoints.push_back(std::make_pair(2793.58f, -3665.93f));
waypoints.push_back(std::make_pair(2775.49f, -3674.43f));
waypoints.push_back(std::make_pair(2762.30f, -3684.59f));
waypoints.push_back(std::make_pair(2755.99f, -3703.96f));
}
protected:
bool CalculateSafe();
void ResetSafe()
{
curr_safe = 0;
curr_dir = 1;
}
void NextSafe()
{
curr_safe += curr_dir;
if (curr_safe == 3 || curr_safe == 0)
{
curr_dir = -curr_dir;
}
}
uint32 prev_phase, prev_erupt, prev_timer;
uint32 curr_safe, curr_dir;
std::vector<std::pair<float, float>> waypoints;
};
class HeiganDanceMeleeAction : public HeiganDanceAction
{
public:
HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {}
virtual bool Execute(Event event);
};
class HeiganDanceRangedAction : public HeiganDanceAction
{
public:
HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) { platform
= std::make_pair(2794.26f, -3706.67f); }
virtual bool Execute(Event event);
protected:
std::pair<float, float> platform;
};
class ThaddiusAttackNearestPetAction : public AttackAction
{
public:
ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius
attack nearest pet"), helper(ai) {}
virtual bool Execute(Event event);
virtual bool isUseful();
private:
ThaddiusBossHelper helper;
};
// class ThaddiusMeleeToPlaceAction : public MovementAction
// {
// public:
// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai,
"thaddius melee to place") {}
// virtual bool Execute(Event event);
// virtual bool isUseful();
// };
// class ThaddiusRangedToPlaceAction : public MovementAction
// {
// public:
// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai,
"thaddius ranged to place") {}
// virtual bool Execute(Event event);
// virtual bool isUseful();
// };
class ThaddiusMoveToPlatformAction : public MovementAction
{
public:
ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius
move to platform") {}
virtual bool Execute(Event event);
virtual bool isUseful();
};
class ThaddiusMovePolarityAction : public MovementAction
{
public:
ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius
move polarity") {}
virtual bool Execute(Event event);
virtual bool isUseful();
};
class RazuviousUseObedienceCrystalAction : public MovementAction
{
public:
RazuviousUseObedienceCrystalAction(PlayerbotAI* ai)
: MovementAction(ai, "razuvious use obedience crystal"), helper(ai)
{
}
bool Execute(Event event) override;
private:
RazuviousBossHelper helper;
};
class RazuviousTargetAction : public AttackAction
{
public:
RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious
target"), helper(ai) {}
bool Execute(Event event) override;
private:
RazuviousBossHelper helper;
};
class HorsemanAttractAlternativelyAction : public AttackAction
{
public:
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai,
"horseman attract alternatively"), helper(ai)
{
}
bool Execute(Event event) override;
protected:
FourhorsemanBossHelper helper;
};
class HorsemanAttactInOrderAction : public AttackAction
{
public:
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman
attact in order"), helper(ai) {}
bool Execute(Event event) override;
protected:
FourhorsemanBossHelper helper;
};
// class SapphironGroundMainTankPositionAction : public MovementAction
// {
// public:
// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) :
MovementAction(ai, "sapphiron ground main tank
// position") {} virtual bool Execute(Event event);
// };
class SapphironGroundPositionAction : public MovementAction
{
public:
SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai,
"sapphiron ground position"), helper(ai) {}
bool Execute(Event event) override;
protected:
SapphironBossHelper helper;
};
class SapphironFlightPositionAction : public MovementAction
{
public:
SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai,
"sapphiron flight position"), helper(ai) {}
bool Execute(Event event) override;
protected:
SapphironBossHelper helper;
bool MoveToNearestIcebolt();
};
// class SapphironAvoidChillAction : public MovementAction
// {
// public:
// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai,
"sapphiron avoid chill") {}
// virtual bool Execute(Event event);
// };
class KelthuzadChooseTargetAction : public AttackAction
{
public:
KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad
choose target"), helper(ai) {}
virtual bool Execute(Event event);
private:
KelthuzadBossHelper helper;
};
class KelthuzadPositionAction : public MovementAction
{
public:
KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad
position"), helper(ai) {}
virtual bool Execute(Event event);
private:
KelthuzadBossHelper helper;
};
class AnubrekhanChooseTargetAction : public AttackAction
{
public:
AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai,
"anub'rekhan choose target") {}
bool Execute(Event event) override;
};
class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction
{
public:
AnubrekhanPositionAction(PlayerbotAI* ai)
: RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f,
-3476.27f, 45.0f, 16)
{
}
bool Execute(Event event) override;
};
class GluthChooseTargetAction : public AttackAction
{
public:
GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose
target"), helper(ai) {}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class GluthPositionAction : public RotateAroundTheCenterPointAction
{
public:
GluthPositionAction(PlayerbotAI* ai)
: RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f,
-3149.01f, 12.0f, 12), helper(ai)
{
}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class GluthSlowdownAction : public Action
{
public:
GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"),
helper(ai) {}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class LoathebPositionAction : public MovementAction
{
public:
LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb
position"), helper(ai) {}
virtual bool Execute(Event event);
private:
LoathebBossHelper helper;
};
class LoathebChooseTargetAction : public AttackAction
{
public:
LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb
choose target"), helper(ai) {}
virtual bool Execute(Event event);
private:
LoathebBossHelper helper;
};
#endif
#include "RaidNaxxStrategy.h"
#include "RaidNaxxMultipliers.h"
void RaidNaxxStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// Grobbulus
triggers.push_back(new TriggerNode(
"mutating injection",
NextAction::array(0, new NextAction("grobbulus go behind the boss",
ACTION_RAID + 2), nullptr)));
triggers.push_back(
new TriggerNode("mutating injection removed",
NextAction::array(0, new NextAction("grobbulus move
center", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode(
"grobbulus cloud", NextAction::array(0, new NextAction("rotate
grobbulus", ACTION_RAID + 1), nullptr)));
// Heigan the Unclean
triggers.push_back(new TriggerNode(
"heigan melee", NextAction::array(0, new NextAction("heigan dance
melee", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode(
"heigan ranged", NextAction::array(0, new NextAction("heigan dance
ranged", ACTION_RAID + 1), nullptr)));
// Kel'Thuzad
triggers.push_back(new TriggerNode(
"kel'thuzad", NextAction::array(0, new NextAction("kel'thuzad position",
ACTION_RAID + 2),
new NextAction("kel'thuzad choose
target", ACTION_RAID + 1), nullptr)));
// Anub'Rekhan
triggers.push_back(new TriggerNode(
"anub'rekhan", NextAction::array(0, new NextAction("anub'rekhan
position", ACTION_RAID + 1), nullptr)));
// Thaddius
triggers.push_back(
new TriggerNode("thaddius phase pet",
NextAction::array(0, new NextAction("thaddius attack
nearest pet", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode("thaddius phase pet lose aggro",
NextAction::array(0, new
NextAction("taunt spell", ACTION_RAID + 2), nullptr)));
triggers.push_back(
new TriggerNode("thaddius phase transition",
NextAction::array(0, new NextAction("thaddius move to
platform", ACTION_RAID + 1), nullptr)));
triggers.push_back(
new TriggerNode("thaddius phase thaddius",
NextAction::array(0, new NextAction("thaddius move
polarity", ACTION_RAID + 1), nullptr)));
// Instructor Razuvious
triggers.push_back(new TriggerNode(
"razuvious tank",
NextAction::array(0, new NextAction("razuvious use obedience crystal",
ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode(
"razuvious nontank", NextAction::array(0, new NextAction("razuvious
target", ACTION_RAID + 1), nullptr)));
// four horseman
triggers.push_back(new TriggerNode(
"horseman attractors",
NextAction::array(0, new NextAction("horseman attract alternatively",
ACTION_RAID + 1), nullptr)));
triggers.push_back(
new TriggerNode("horseman except attractors",
NextAction::array(0, new NextAction("horseman attack in
order", ACTION_RAID + 1), nullptr)));
// sapphiron
triggers.push_back(
new TriggerNode("sapphiron ground",
NextAction::array(0, new NextAction("sapphiron ground
position", ACTION_RAID + 1), nullptr)));
triggers.push_back(
new TriggerNode("sapphiron flight",
NextAction::array(0, new NextAction("sapphiron flight
position", ACTION_RAID + 1), nullptr)));
// Gluth
triggers.push_back(
new TriggerNode("gluth", NextAction::array(0, new NextAction("gluth
choose target", ACTION_RAID + 1),
new NextAction("gluth
position", ACTION_RAID + 1),
new NextAction("gluth
slowdown", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode("gluth main tank mortal wound",
NextAction::array(0, new
NextAction("taunt spell", ACTION_RAID + 1), nullptr)));
// Loatheb
triggers.push_back(new TriggerNode(
"loatheb", NextAction::array(0, new NextAction("loatheb position",
ACTION_RAID + 1),
new NextAction("loatheb choose target",
ACTION_RAID + 1), nullptr)));
}
void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new GrobbulusMultiplier(botAI));
multipliers.push_back(new HeiganDanceMultiplier(botAI));
multipliers.push_back(new LoathebGenericMultiplier(botAI));
multipliers.push_back(new ThaddiusGenericMultiplier(botAI));
multipliers.push_back(new SapphironGenericMultiplier(botAI));
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI));
// multipliers.push_back(new GothikGenericMultiplier(botAI));
multipliers.push_back(new GluthGenericMultiplier(botAI));
}
#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H
#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H
#include <string>
#include "AiObject.h"
#include "AiObjectContext.h"
#include "EventMap.h"
#include "Log.h"
#include "NamedObjectContext.h"
#include "ObjectGuid.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "RaidNaxxScripts.h"
#include "ScriptedCreature.h"
#include "SharedDefines.h"
const uint32 NAXX_MAP_ID = 533;
template <class BossAiType>
class GenericBossHelper : public AiObject
{
public:
GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI),
_name(name) {}
virtual bool UpdateBossAI()
{
if (!bot->IsInCombat())
{
_unit = nullptr;
}
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
{
_unit = nullptr;
}
if (!_unit)
{
_unit = AI_VALUE2(Unit*, "find target", _name);
if (!_unit)
{
return false;
}
_target = _unit->ToCreature();
if (!_target)
{
return false;
}
_ai = dynamic_cast<BossAiType*>(_target->GetAI());
if (!_ai)
{
return false;
}
_event_map = &_ai->events;
if (!_event_map)
{
return false;
}
}
if (!_event_map)
{
return false;
}
_timer = _event_map->GetTimer();
return true;
}
virtual void Reset()
{
_unit = nullptr;
_target = nullptr;
_ai = nullptr;
_event_map = nullptr;
_timer = 0;
}
protected:
std::string _name;
Unit* _unit = nullptr;
Creature* _target = nullptr;
BossAiType* _ai = nullptr;
EventMap* _event_map = nullptr;
uint32 _timer = 0;
};
class KelthuzadBossHelper : public
GenericBossHelper<Kelthuzad::boss_kelthuzad::boss_kelthuzadAI>
{
public:
KelthuzadBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI,
"kel'thuzad") {}
const std::pair<float, float> center = {3716.19f, -5106.58f};
const std::pair<float, float> tank_pos = {3709.19f, -5104.86f};
const std::pair<float, float> assist_tank_pos = {3746.05f, -5112.74f};
bool IsPhaseOne() { return _event_map-
>GetNextEventTime(Kelthuzad::EVENT_PHASE_2) != 0; }
bool IsPhaseTwo() { return !IsPhaseOne(); }
Unit* GetAnyShadowFissure()
{
Unit* shadow_fissure = nullptr;
GuidVector units = *context->GetValue<GuidVector>("nearest triggers");
for (auto i = units.begin(); i != units.end(); i++)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure"))
{
shadow_fissure = unit;
}
}
return shadow_fissure;
}
};
class RazuviousBossHelper : public
GenericBossHelper<Razuvious::boss_razuvious::boss_razuviousAI>
{
public:
RazuviousBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI,
"instructor razuvious") {}
};
class SapphironBossHelper : public
GenericBossHelper<Sapphiron::boss_sapphiron::boss_sapphironAI>
{
public:
const std::pair<float, float> mainTankPos = {3512.07f, -5274.06f};
const std::pair<float, float> center = {3517.31f, -5253.74f};
const float GENERIC_HEIGHT = 137.29f;
SapphironBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI,
"sapphiron") {}
bool UpdateBossAI() override
{
if (!GenericBossHelper::UpdateBossAI())
{
return false;
}
uint32 nextEventGround = _event_map-
>GetNextEventTime(Sapphiron::EVENT_GROUND);
if (nextEventGround && nextEventGround != lastEventGround)
lastEventGround = nextEventGround;
return true;
}
bool IsPhaseGround() { return _target->GetReactState() ==
REACT_AGGRESSIVE; }
bool IsPhaseFlight() { return !IsPhaseGround(); }
bool JustLanded()
{
return (_event_map->GetNextEventTime(Sapphiron::EVENT_FLIGHT_START) -
_timer) >=
EVENT_FLIGHT_INTERVAL - POSITION_TIME_AFTER_LANDED;
}
bool WaitForExplosion() { return _event_map-
>GetNextEventTime(Sapphiron::EVENT_FLIGHT_SPELL_EXPLOSION); }
bool FindPosToAvoidChill(std::vector<float>& dest)
{
Aura* aura = botAI->GetAura("chill", bot);
if (!aura)
{
return false;
}
DynamicObject* dyn_obj = aura->GetDynobjOwner();
if (!dyn_obj)
{
return false;
}
Unit* currentTarget = AI_VALUE(Unit*, "current target");
float angle = 0;
uint32 index = botAI->GetGroupSlotIndex(bot);
if (currentTarget)
{
if (botAI->IsRanged(bot))
{
if (bot->GetExactDist2d(currentTarget) <= 45.0f)
{
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5)
* M_PI / 2;
}
else
{
if (index % 2 == 0)
{
angle = bot->GetAngle(currentTarget) + M_PI / 2;
}
else
{
angle = bot->GetAngle(currentTarget) - M_PI / 2;
}
}
}
else
{
if (index % 3 == 0)
{
angle = bot->GetAngle(currentTarget);
}
else if (index % 3 == 1)
{
angle = bot->GetAngle(currentTarget) + M_PI / 2;
}
else
{
angle = bot->GetAngle(currentTarget) - M_PI / 2;
}
}
}
else
{
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI /
2;
}
dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() +
sin(angle) * 5.0f, bot->GetPositionZ()};
return true;
}
private:
const uint32 POSITION_TIME_AFTER_LANDED = 5000;
const uint32 EVENT_FLIGHT_INTERVAL = 45000;
uint32 lastEventGround = 0;
};
class GluthBossHelper : public
GenericBossHelper<Gluth::boss_gluth::boss_gluthAI>
{
public:
const std::pair<float, float> mainTankPos25 = {3331.48f, -3109.06f};
const std::pair<float, float> mainTankPos10 = {3278.29f, -3162.06f};
const std::pair<float, float> beforeDecimatePos = {3267.34f, -3175.68f};
const std::pair<float, float> leftSlowDownPos = {3290.68f, -3141.65f};
const std::pair<float, float> rightSlowDownPos = {3300.78f, -3151.98f};
const std::pair<float, float> rangedPos = {3301.45f, -3139.29f};
const std::pair<float, float> healPos = {3303.09f, -3135.24f};
const float decimatedZombiePct = 10.0f;
GluthBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI, "gluth") {}
bool BeforeDecimate()
{
uint32 decimate = _event_map->GetNextEventTime(Gluth::EVENT_DECIMATE);
return decimate && decimate - _timer <= 3000;
}
bool JustStartCombat() { return _timer < 10000; }
};
class LoathebBossHelper : public
GenericBossHelper<Loatheb::boss_loatheb::boss_loathebAI>
{
public:
const std::pair<float, float> mainTankPos = {2877.57f, -3967.00f};
const std::pair<float, float> rangePos = {2896.96f, -3980.61f};
LoathebBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI, "loatheb")
{}
};
class FourhorsemanBossHelper : public
GenericBossHelper<FourHorsemen::boss_four_horsemen::boss_four_horsemenAI>
{
public:
const float posZ = 241.27f;
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
{2484.61f, -2947.07f}}; //
left (sir zeliek), right (lady blaumeux)
FourhorsemanBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI, "sir
zeliek") {}
bool UpdateBossAI() override
{
if (!GenericBossHelper::UpdateBossAI())
{
return false;
}
if (!bot->IsInCombat())
{
Reset();
}
sir = _unit;
lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
if (!lady)
{
return true;
}
ladyAI =
dynamic_cast<FourHorsemen::boss_four_horsemen::boss_four_horsemenAI*>(lady-
>GetAI());
if (!ladyAI)
{
return true;
}
ladyEvent = &ladyAI->events;
const uint32 voidZone = ladyEvent-
>GetNextEventTime(FourHorsemen::EVENT_SECONDARY_SPELL);
if (voidZone && lastEventVoidZone != voidZone)
{
voidZoneCounter++;
voidZoneCounter %= 8;
lastEventVoidZone = voidZone;
}
return true;
}
void Reset() override
{
GenericBossHelper::Reset();
sir = nullptr;
lady = nullptr;
ladyAI = nullptr;
ladyEvent = nullptr;
lastEventVoidZone = 0;
voidZoneCounter = 0;
posToGo = 0;
}
bool IsAttracter(Player* bot)
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_25MAN_NORMAL)
{
return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI-
>IsHealAssistantOfIndex(bot, 0) ||
botAI->IsHealAssistantOfIndex(bot, 1) || botAI-
>IsHealAssistantOfIndex(bot, 2);
}
return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI-
>IsHealAssistantOfIndex(bot, 0);
}
void CalculatePosToGo(Player* bot)
{
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
if (!lady)
{
posToGo = 0;
}
else
{
// Interval: 24s - 15s - 15s - ...
posToGo = !(_timer <= 9000 || ((_timer - 9000) / 67500) % 2 == 0);
if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI-
>IsHealAssistantOfIndex(bot, 1)))
{
posToGo = 1 - posToGo;
}
}
}
std::pair<float, float> CurrentAttractPos()
{
float posX = attractPos[posToGo].first, posY =
attractPos[posToGo].second;
if (posToGo == 1)
{
float offset_x;
float offset_y;
if (voidZoneCounter < 4)
{
offset_x = voidZoneCounter * (-4.5f);
offset_y = voidZoneCounter * (4.5f);
}
if (voidZoneCounter >= 4)
{
offset_x = (7 - voidZoneCounter) * (-4.5f);
offset_y = (7 - voidZoneCounter) * (4.5f);
offset_x += 4.5f;
offset_y += 4.5f;
}
posX += offset_x;
posY += offset_y;
}
return {posX, posY};
}
Unit* CurrentAttackTarget()
{
if (posToGo == 0)
{
return sir;
}
return lady;
}
protected:
Unit* sir = nullptr;
Unit* lady = nullptr;
FourHorsemen::boss_four_horsemen::boss_four_horsemenAI* ladyAI = nullptr;
EventMap* ladyEvent = nullptr;
uint32 lastEventVoidZone = 0;
uint32 voidZoneCounter = 0;
int posToGo = 0;
};
class ThaddiusBossHelper : public
GenericBossHelper<Thaddius::boss_thaddius::boss_thaddiusAI>
{
public:
const std::pair<float, float> tankPosFeugen = {3522.94f, -3002.60f};
const std::pair<float, float> tankPosStalagg = {3436.14f, -2919.98f};
const std::pair<float, float> rangedPosFeugen = {3500.45f, -2997.92f};
const std::pair<float, float> rangedPosStalagg = {3441.01f, -2942.04f};
const float tankPosZ = 312.61f;
ThaddiusBossHelper(PlayerbotAI* botAI) : GenericBossHelper(botAI,
"thaddius") {}
bool UpdateBossAI() override
{
if (!GenericBossHelper::UpdateBossAI())
{
return false;
}
feugen = AI_VALUE2(Unit*, "find target", "feugen");
stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
return true;
}
bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg &&
stalagg->IsAlive()); }
bool IsPhaseTransition()
{
if (IsPhasePet())
{
return false;
}
return _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); }
Unit* GetNearestPet()
{
Unit* unit = nullptr;
if (feugen && feugen->IsAlive())
{
unit = feugen;
}
if (stalagg && stalagg->IsAlive() && (!feugen || bot-
>GetDistance(stalagg) < bot->GetDistance(feugen)))
{
unit = stalagg;
}
return unit;
}
std::pair<float, float> PetPhaseGetPosForTank()
{
if (GetNearestPet() == feugen)
{
return tankPosFeugen;
}
return tankPosStalagg;
}
std::pair<float, float> PetPhaseGetPosForRanged()
{
if (GetNearestPet() == feugen)
{
return rangedPosFeugen;
}
return rangedPosStalagg;
}
protected:
Unit* feugen = nullptr;
Unit* stalagg = nullptr;
};
#endif
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_
#include "Multiplier.h"
#include "raids/naxxramas/RaidNaxxBossHelper.h"
class GrobbulusMultiplier : public Multiplier
{
public:
GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {}
public:
virtual float GetValue(Action* action);
};
class HeiganDanceMultiplier : public Multiplier
{
public:
HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {}
public:
virtual float GetValue(Action* action);
};
class LoathebGenericMultiplier : public Multiplier
{
public:
LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb
generic") {}
public:
virtual float GetValue(Action* action);
};
class ThaddiusGenericMultiplier : public Multiplier
{
public:
ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius
generic"), helper(ai) {}
public:
virtual float GetValue(Action* action);
private:
ThaddiusBossHelper helper;
};
class SapphironGenericMultiplier : public Multiplier
{
public:
SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron
generic"), helper(ai) {}
virtual float GetValue(Action* action);
private:
SapphironBossHelper helper;
};
class InstructorRazuviousGenericMultiplier : public Multiplier
{
public:
InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai,
"instructor razuvious generic"), helper(ai)
{
}
virtual float GetValue(Action* action);
private:
RazuviousBossHelper helper;
};
class KelthuzadGenericMultiplier : public Multiplier
{
public:
KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad
generic"), helper(ai) {}
virtual float GetValue(Action* action);
private:
KelthuzadBossHelper helper;
};
class AnubrekhanGenericMultiplier : public Multiplier
{
public:
AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan
generic") {}
public:
virtual float GetValue(Action* action);
};
class FourhorsemanGenericMultiplier : public Multiplier
{
public:
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai,
"fourhorseman generic") {}
public:
virtual float GetValue(Action* action);
};
// class GothikGenericMultiplier : public Multiplier
// {
// public:
// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik
generic") {}
// public:
// virtual float GetValue(Action* action);
// };
class GluthGenericMultiplier : public Multiplier
{
public:
GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"),
helper(ai) {}
float GetValue(Action* action) override;
private:
GluthBossHelper helper;
};
#endif
#include "RaidNaxxActions.h"
#include "LastMovementValue.h"
#include "ObjectGuid.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "RaidNaxxBossHelper.h"
#include "RaidNaxxStrategy.h"
#include "ScriptedCreature.h"
#include "SharedDefines.h"
bool GrobbulusGoBehindAction::Execute(Event event)
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss)
{
return false;
}
// Position* pos = boss->GetPosition();
float orientation = boss->GetOrientation() + M_PI + delta_angle;
float x = boss->GetPositionX();
float y = boss->GetPositionY();
float z = boss->GetPositionZ();
float rx = x + cos(orientation) * distance;
float ry = y + sin(orientation) * distance;
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint()
{
float minDistance = 0;
int ret = -1;
for (int i = 0; i < intervals; i++)
{
float w_x = waypoints[i].first, w_y = waypoints[i].second;
float dis = bot->GetDistance2d(w_x, w_y);
if (ret == -1 || dis < minDistance)
{
ret = i;
minDistance = dis;
}
}
return ret;
}
uint32 GrobbulusRotateAction::GetCurrWaypoint()
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss)
{
return false;
}
auto* boss_ai =
dynamic_cast<Grobbulus::boss_grobbulus::boss_grobbulusAI*>(boss->GetAI());
EventMap* eventMap = &boss_ai->events;
const uint32 event_time = eventMap->GetNextEventTime(2);
return (event_time / 15000) % intervals;
}
bool HeiganDanceAction::CalculateSafe()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
if (!boss)
{
return false;
}
auto* boss_ai = dynamic_cast<Heigan::boss_heigan::boss_heiganAI*>(boss-
>GetAI());
EventMap* eventMap = &boss_ai->events;
uint32 curr_phase = boss_ai->currentPhase;
uint32 curr_erupt = eventMap->GetNextEventTime(3);
uint32 curr_dance = eventMap->GetNextEventTime(4);
uint32 curr_timer = eventMap->GetTimer();
if ((curr_phase == 0 && curr_dance - curr_timer >= 85000) || (curr_phase ==
1 && curr_dance - curr_timer >= 40000))
{
ResetSafe();
}
else if (curr_erupt != prev_erupt)
{
NextSafe();
}
prev_phase = curr_phase;
prev_erupt = curr_erupt;
return true;
}
bool HeiganDanceMeleeAction::Execute(Event event)
{
CalculateSafe();
if (prev_phase == 0 && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has
aggro", "boss target"))
{
return false;
}
assert(curr_safe >= 0 && curr_safe <= 3);
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first,
waypoints[curr_safe].second, bot->GetPositionZ(),
botAI->IsMainTank(bot) ? 0 : 0,
MovementPriority::MOVEMENT_COMBAT);
}
bool HeiganDanceRangedAction::Execute(Event event)
{
CalculateSafe();
if (prev_phase != 1)
{
return MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f,
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
botAI->InterruptSpell();
return MoveInside(bot->GetMapId(), waypoints[curr_safe].first,
waypoints[curr_safe].second, bot->GetPositionZ(), 0,
MovementPriority::MOVEMENT_COMBAT);
}
bool ThaddiusAttackNearestPetAction::isUseful()
{
if (!helper.UpdateBossAI())
{
return false;
}
if (!helper.IsPhasePet())
{
return false;
}
Unit* target = helper.GetNearestPet();
if (bot->GetDistance(target) > 50.0f)
{
return false;
}
return true;
}
bool ThaddiusAttackNearestPetAction::Execute(Event event)
{
Unit* target = helper.GetNearestPet();
if (!bot->IsWithinLOSInMap(target))
{
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
}
if (AI_VALUE(Unit*, "current target") != target)
{
return Attack(target);
}
if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target"))
{
std::pair<float, float> posForTank = helper.PetPhaseGetPosForTank();
return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ,
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
if (botAI->IsRanged(bot))
{
std::pair<float, float> posForRanged = helper.PetPhaseGetPosForRanged();
return MoveTo(533, posForRanged.first, posForRanged.second,
helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool ThaddiusMoveToPlatformAction::isUseful() { return true; }
bool ThaddiusMoveToPlatformAction::Execute(Event event)
{
std::vector<std::pair<float, float>> position = {
// high left
{3462.99f, -2918.90f},
// high right
{3520.65f, -2976.51f},
// low left
{3471.36f, -2910.65f},
// low right
{3528.80f, -2967.04f},
// center
{3512.19f, -2928.58f},
};
float high_z = 312.00f, low_z = 304.02f;
bool is_left = bot->GetDistance2d(position[0].first, position[0].second) <
bot->GetDistance2d(position[1].first, position[1].second);
if (bot->GetPositionZ() >= (high_z - 3.0f))
{
if (is_left)
{
if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second,
high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
float distance = bot->GetExactDist2d(position[0].first,
position[0].second);
if (distance < sPlayerbotAIConfig->contactDistance)
JumpTo(bot->GetMapId(), position[2].first,
position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[2].first,
position[2].second, low_z, bot->GetOrientation());
}
}
else
{
if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second,
high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
float distance = bot->GetExactDist2d(position[1].first,
position[1].second);
if (distance < sPlayerbotAIConfig->contactDistance)
JumpTo(bot->GetMapId(), position[3].first,
position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[3].first,
position[3].second, low_z, bot->GetOrientation());
}
}
}
else
{
return MoveTo(bot->GetMapId(), position[4].first, position[4].second,
low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return true;
}
bool ThaddiusMovePolarityAction::isUseful()
{
return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current
target");
}
bool ThaddiusMovePolarityAction::Execute(Event event)
{
std::vector<std::pair<float, float>> position = {
// left melee
{3508.29f, -2920.12f},
// left ranged
{3501.72f, -2913.36f},
// right melee
{3519.74f, -2931.69f},
// right ranged
{3524.32f, -2936.26f},
// center melee
{3512.19f, -2928.58f},
// center ranged
{3504.68f, -2936.68f},
};
uint32 idx;
if (botAI->HasAura("negative charge", bot, false, false, -1, true))
{
idx = 0;
}
else if (botAI->HasAura("positive charge", bot, false, false, -1, true))
{
idx = 1;
}
else
{
idx = 2;
}
idx = idx * 2 + botAI->IsRanged(bot);
return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
bool RazuviousUseObedienceCrystalAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
// bot->GetCharm
if (Unit* charm = bot->GetCharm())
{
Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious");
if (!target)
{
return false;
}
if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) ==
NULL_MOTION_TYPE)
{
charm->GetMotionMaster()->Clear();
charm->GetMotionMaster()->MoveChase(target);
charm->GetAI()->AttackStart(target);
}
Aura* forceObedience = botAI->GetAura("force obedience", charm);
uint32 duration_time;
if (!forceObedience)
{
forceObedience = botAI->GetAura("mind control", charm);
duration_time = 60000;
}
else
{
duration_time = 90000;
}
if (!forceObedience)
{
return false;
}
if (charm->GetDistance(target) <= 0.51f)
{
// taunt
bool tauntUseful = true;
if (forceObedience->GetDuration() <= (duration_time - 5000))
{
if (target->GetVictim() && botAI->HasAura(29061, target-
>GetVictim()))
{
tauntUseful = false;
}
if (forceObedience->GetDuration() <= 3000)
{
tauntUseful = false;
}
}
if (forceObedience->GetDuration() >= (duration_time - 500))
{
tauntUseful = false;
}
if (tauntUseful && !charm->HasSpellCooldown(29060))
{
// shield
if (!charm->HasSpellCooldown(29061))
{
charm->CastSpell(charm, 29061, true);
charm->AddSpellCooldown(29061, 0, 30 * 1000);
}
charm->CastSpell(target, 29060, true);
charm->AddSpellCooldown(29060, 0, 20 * 1000);
}
// strike
if (!charm->HasSpellCooldown(61696))
{
charm->CastSpell(target, 61696, true);
charm->AddSpellCooldown(61696, 0, 4 * 1000);
}
}
}
else
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
{
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (auto i = npcs.begin(); i != npcs.end(); i++)
{
Creature* unit = botAI->GetCreature(*i);
if (!unit)
{
continue;
}
if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352)
{
continue;
}
if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353)
{
continue;
}
if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
Creature* creature = bot->GetNPCIfCanInteractWith(*i,
UNIT_NPC_FLAG_SPELLCLICK);
if (!creature)
continue;
creature->HandleSpellClick(bot);
return true;
}
}
else
{
GuidVector attackers = context->GetValue<GuidVector>("attackers")-
>Get();
Unit* target = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "death knight
understudy"))
{
target = unit;
break;
}
}
if (target)
{
if (bot->GetDistance2d(target) > sPlayerbotAIConfig-
>spellDistance)
{
return MoveNear(target, sPlayerbotAIConfig->spellDistance,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
return botAI->CastSpell("mind control", target);
}
}
}
}
return false;
}
bool RazuviousTargetAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious");
Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight
understudy");
Unit* target = nullptr;
if (botAI->IsTank(bot))
{
target = understudy;
}
else
{
target = razuvious;
}
if (AI_VALUE(Unit*, "current target") == target)
{
return false;
}
return Attack(target);
}
bool HorsemanAttractAlternativelyAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
helper.CalculatePosToGo(bot);
auto [posX, posY] = helper.CurrentAttractPos();
if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false,
false, MovementPriority::MOVEMENT_COMBAT))
{
return true;
}
Unit* attackTarget = helper.CurrentAttackTarget();
if (context->GetValue<Unit*>("current target")->Get() != attackTarget)
{
return Attack(attackTarget);
}
return false;
}
bool HorsemanAttactInOrderAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
Unit* target = nullptr;
Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz");
Unit* baron = AI_VALUE2(Unit*, "find target", "baron rivendare");
Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek");
std::vector<Unit*> attack_order;
if (botAI->IsAssistTank(bot))
{
attack_order = {baron, thane, lady, sir};
}
else
{
attack_order = {thane, baron, lady, sir};
}
for (Unit* t : attack_order)
{
if (t && t->IsAlive())
{
target = t;
break;
}
}
if (target)
{
if (context->GetValue<Unit*>("current target")->Get() == target &&
botAI->GetState() == BOT_STATE_COMBAT)
{
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT);
}
return Attack(target);
}
return false;
}
bool SapphironGroundPositionAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
if (botAI->IsMainTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first,
helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
if (helper.JustLanded())
{
uint32 index = botAI->GetGroupSlotIndex(bot);
float start_angle = 0.85 * M_PI;
float offset_angle = M_PI * 0.02 * index;
float angle = start_angle + offset_angle;
float distance;
if (botAI->IsRanged(bot))
{
distance = 35.0f;
}
else if (botAI->IsHeal(bot))
{
distance = 30.0f;
}
else
{
distance = 5.0f;
}
return MoveTo(NAXX_MAP_ID, helper.center.first + cos(angle) * distance,
helper.center.second + sin(angle) * distance,
helper.GENERIC_HEIGHT, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
{
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false,
false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool SapphironFlightPositionAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
if (helper.WaitForExplosion())
{
return MoveToNearestIcebolt();
}
else
{
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
{
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false,
false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool SapphironFlightPositionAction::MoveToNearestIcebolt()
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
Group::MemberSlotList const& slots = group->GetMemberSlots();
Player* playerWithIcebolt = nullptr;
float minDistance;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (botAI->HasAura("icebolt", member, false, false, -1, true))
{
if (!playerWithIcebolt || minDistance > bot->GetDistance(member))
{
playerWithIcebolt = member;
minDistance = bot->GetDistance(member);
}
}
}
if (playerWithIcebolt)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron");
if (boss)
{
float angle = boss->GetAngle(playerWithIcebolt);
return MoveTo(NAXX_MAP_ID, playerWithIcebolt->GetPositionX() +
cos(angle) * 3.0f,
playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f,
helper.GENERIC_HEIGHT, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool KelthuzadChooseTargetAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit *target_soldier = nullptr, *target_weaver = nullptr,
*target_abomination = nullptr,
*target_kelthuzad = nullptr, *target_guardian = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown"))
{
if (!target_guardian)
{
target_guardian = unit;
}
else if (unit->GetVictim() && target_guardian->GetVictim() && unit-
>GetVictim()->ToPlayer() &&
target_guardian->GetVictim()->ToPlayer() && !botAI-
>IsAssistTank(unit->GetVictim()->ToPlayer()) &&
botAI->IsAssistTank(target_guardian->GetVictim()-
>ToPlayer()))
{
target_guardian = unit;
}
else if (unit->GetVictim() && target_guardian->GetVictim() && unit-
>GetVictim()->ToPlayer() &&
target_guardian->GetVictim()->ToPlayer() && !botAI-
>IsAssistTank(unit->GetVictim()->ToPlayer()) &&
!botAI->IsAssistTank(target_guardian->GetVictim()-
>ToPlayer()) &&
target_guardian->GetDistance2d(helper.center.first,
helper.center.second) >
bot->GetDistance2d(unit))
{
target_guardian = unit;
}
}
if (unit->GetDistance2d(helper.center.first, helper.center.second) >
30.0f)
{
continue;
}
if (bot->GetDistance2d(unit) > sPlayerbotAIConfig->spellDistance)
{
continue;
}
if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable
abomination"))
{
if (target_abomination == nullptr ||
target_abomination->GetDistance2d(helper.center.first,
helper.center.second) >
unit->GetDistance2d(helper.center.first,
helper.center.second))
{
target_abomination = unit;
}
}
if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen
wastes"))
{
if (target_soldier == nullptr || target_soldier-
>GetDistance2d(helper.center.first, helper.center.second) >
unit-
>GetDistance2d(helper.center.first, helper.center.second))
{
target_soldier = unit;
}
}
if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver"))
{
if (target_weaver == nullptr || target_weaver-
>GetDistance2d(helper.center.first, helper.center.second) >
unit-
>GetDistance2d(helper.center.first, helper.center.second))
{
target_weaver = unit;
}
}
if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad"))
{
target_kelthuzad = unit;
}
}
std::vector<Unit*> targets;
if (botAI->IsRanged(bot))
{
if (botAI->GetRangedDpsIndex(bot) <= 1)
{
targets = {target_soldier, target_weaver, target_abomination,
target_kelthuzad};
}
else
{
targets = {target_weaver, target_soldier, target_abomination,
target_kelthuzad};
}
}
else if (botAI->IsAssistTank(bot))
{
targets = {target_abomination, target_guardian, target_kelthuzad};
}
else
{
targets = {target_abomination, target_kelthuzad};
}
for (Unit* t : targets)
{
if (t)
{
target = t;
break;
}
}
if (context->GetValue<Unit*>("current target")->Get() == target)
{
return false;
}
if (target_kelthuzad && target == target_kelthuzad)
{
return Attack(target, true);
}
return Attack(target, false);
}
bool KelthuzadPositionAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
if (helper.IsPhaseOne())
{
if (AI_VALUE(Unit*, "current target") == nullptr)
{
return MoveInside(NAXX_MAP_ID, helper.center.first,
helper.center.second, bot->GetPositionZ(), 3.0f,
MovementPriority::MOVEMENT_COMBAT);
}
}
else if (helper.IsPhaseTwo())
{
Unit* shadow_fissure = helper.GetAnyShadowFissure();
if (!shadow_fissure || bot->GetDistance2d(shadow_fissure) > 10.0f)
{
float distance, angle;
if (botAI->IsMainTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
return MoveTo(NAXX_MAP_ID, helper.tank_pos.first,
helper.tank_pos.second, bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
return false;
}
}
else if (botAI->IsRanged(bot))
{
uint32 index = botAI->GetRangedIndex(bot);
if (index < 8)
{
distance = 20.0f;
angle = index * M_PI / 4;
}
else
{
distance = 32.0f;
angle = (index - 8) * M_PI / 4;
}
float dx, dy;
dx = helper.center.first + cos(angle) * distance;
dy = helper.center.second + sin(angle) * distance;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false,
false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsTank(bot))
{
Unit* cur_tar = AI_VALUE(Unit*, "current target");
if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()-
>ToPlayer() &&
botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of
icecrown") &&
botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer()))
{
return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first,
helper.assist_tank_pos.second,
bot->GetPositionZ(), false, false, false,
false, MovementPriority::MOVEMENT_COMBAT);
}
else
{
return false;
}
}
}
else
{
float dx, dy;
float angle;
if (!botAI->IsRanged(bot))
{
angle = shadow_fissure->GetAngle(helper.center.first,
helper.center.second);
}
else
{
angle = bot->GetAngle(shadow_fissure) + M_PI;
}
dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f;
dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false,
false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool AnubrekhanChooseTargetAction::Execute(Event event)
{
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
std::vector<Unit*> target_guards;
for (ObjectGuid const guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard"))
{
target_guards.push_back(unit);
}
if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan"))
{
target_boss = unit;
}
}
if (botAI->IsMainTank(bot))
{
target = target_boss;
}
else
{
if (target_guards.size() == 0)
{
target = target_boss;
}
else
{
if (botAI->IsAssistTank(bot))
{
for (Unit* t : target_guards)
{
if (target == nullptr || (target->GetVictim() && target-
>GetVictim()->ToPlayer() &&
botAI->IsTank(target->GetVictim()-
>ToPlayer())))
{
target = t;
}
}
}
else
{
for (Unit* t : target_guards)
{
if (target == nullptr || target->GetHealthPct() > t-
>GetHealthPct())
{
target = t;
}
}
}
}
}
if (context->GetValue<Unit*>("current target")->Get() == target)
{
return false;
}
return Attack(target);
}
bool AnubrekhanPositionAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
{
return false;
}
auto* boss_ai =
dynamic_cast<Anubrekhan::boss_anubrekhan::boss_anubrekhanAI*>(boss->GetAI());
if (!boss_ai)
{
return false;
}
EventMap* eventMap = &boss_ai->events;
uint32 locust = eventMap->GetNextEventTime(2);
uint32 timer = eventMap->GetTimer();
bool inPhase = botAI->HasAura("locust swarm", boss) || boss-
>GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (inPhase || (locust && locust - timer <= 8000))
{
if (botAI->IsMainTank(bot))
{
uint32 nearest = FindNearestWaypoint();
uint32 next_point;
if (inPhase || (locust && locust - timer <= 3000))
{
next_point = (nearest + 1) % intervals;
}
else
{
next_point = nearest;
}
return MoveTo(bot->GetMapId(), waypoints[next_point].first,
waypoints[next_point].second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(),
3.0f, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}
bool GluthChooseTargetAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
GuidVector attackers = context->GetValue<GuidVector>("possible targets")-
>Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
std::vector<Unit*> target_zombies;
for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (!unit->IsAlive())
{
continue;
}
if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow"))
{
target_zombies.push_back(unit);
}
if (botAI->EqualLowercaseName(unit->GetName(), "gluth"))
{
target_boss = unit;
}
}
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
{
target = target_boss;
}
else if (botAI->IsAssistTankOfIndex(bot, 1))
{
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() > helper.decimatedZombiePct && t-
>GetVictim() != bot &&
t->GetDistance2d(bot) <= 10.0f)
{
if (!target || t->GetDistance2d(bot) < target-
>GetDistance2d(bot))
{
target = t;
}
}
}
}
else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI-
>GetClassIndex(bot, CLASS_HUNTER) == 1)
{
// prevent zombie go straight to gluth
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim()
== target_boss &&
t->GetDistance2d(bot) <= sPlayerbotAIConfig->spellDistance)
{
if (!target || t->GetDistance2d(bot) < target-
>GetDistance2d(bot))
{
target = t;
}
}
}
if (!target)
{
target = target_boss;
}
}
else
{
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() <= helper.decimatedZombiePct)
{
if (target == nullptr ||
target->GetDistance2d(helper.mainTankPos25.first,
helper.mainTankPos25.second) >
t->GetDistance2d(helper.mainTankPos25.first,
helper.mainTankPos25.second))
{
target = t;
}
}
}
if (target == nullptr)
{
target = target_boss;
}
}
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
{
return false;
}
if (target_boss && target == target_boss)
return Attack(target, true);
return Attack(target, false);
// return Attack(target);
}
bool GluthPositionAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
{
if (AI_VALUE2(bool, "has aggro", "boss target"))
{
if (raid25)
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first,
helper.mainTankPos25.second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
return MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first,
helper.mainTankPos10.second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
}
}
else if (botAI->IsAssistTankOfIndex(bot, 1))
{
if (helper.BeforeDecimate())
{
return MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first,
helper.beforeDecimatePos.second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
uint32 nearest = FindNearestWaypoint();
uint32 next_point = (nearest + 1) % intervals;
return MoveTo(bot->GetMapId(), waypoints[next_point].first,
waypoints[next_point].second,
bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
}
}
else if (botAI->IsRangedDps(bot))
{
if (raid25)
{
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0)
{
return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first,
helper.leftSlowDownPos.second,
bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
}
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
{
return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first,
helper.rightSlowDownPos.second,
bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
}
}
return MoveInside(NAXX_MAP_ID, helper.rangedPos.first,
helper.rangedPos.second, bot->GetPositionZ(), 3.0f,
MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsHeal(bot))
{
return MoveInside(NAXX_MAP_ID, helper.healPos.first,
helper.healPos.second, bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool GluthSlowdownAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
if (!raid25)
{
return false;
}
if (helper.JustStartCombat())
{
return false;
}
switch (bot->getClass())
{
case CLASS_HUNTER:
return botAI->CastSpell("frost trap", bot);
break;
default:
break;
}
return false;
}
bool LoathebPositionAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
if (botAI->IsTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "boss target"))
{
return MoveTo(533, helper.mainTankPos.first,
helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
}
else if (botAI->IsRanged(bot))
{
return MoveInside(533, helper.rangePos.first, helper.rangePos.second,
bot->GetPositionZ(), 1.0f, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool LoathebChooseTargetAction::Execute(Event event)
{
if (!helper.UpdateBossAI())
{
return false;
}
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
Unit* target_spore = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (!unit->IsAlive())
{
continue;
}
if (botAI->EqualLowercaseName(unit->GetName(), "spore"))
{
target_spore = unit;
}
if (botAI->EqualLowercaseName(unit->GetName(), "loatheb"))
{
target_boss = unit;
}
}
if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f)
{
target = target_spore;
}
else
{
target = target_boss;
}
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
{
return false;
}
return Attack(target);
}
#include "RaidNaxxMultipliers.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "FollowActions.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "HunterActions.h"
#include "MageActions.h"
#include "MovementActions.h"
#include "PaladinActions.h"
#include "PriestActions.h"
#include "RaidNaxxActions.h"
#include "ReachTargetActions.h"
#include "RogueActions.h"
#include "ScriptedCreature.h"
#include "ShamanActions.h"
#include "UseMeetingStoneAction.h"
#include "WarriorActions.h"
float GrobbulusMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
{
return 1.0f;
}
if (dynamic_cast<AvoidAoeAction*>(action) ||
dynamic_cast<CombatFormationMoveAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float HeiganDanceMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
if (!boss)
{
return 1.0f;
}
auto* boss_ai = dynamic_cast<Heigan::boss_heigan::boss_heiganAI*>(boss-
>GetAI());
EventMap* eventMap = &boss_ai->events;
uint32 curr_phase = boss_ai->currentPhase;
uint32 curr_dance = eventMap->GetNextEventTime(4);
uint32 curr_timer = eventMap->GetTimer();
uint32 curr_erupt = eventMap->GetNextEventTime(3);
if (dynamic_cast<CombatFormationMoveAction*>(action))
{
return 0.0f;
}
if (curr_phase != 1 && (int32)curr_dance - curr_timer >= 3000)
{
return 1.0f;
}
if (dynamic_cast<HeiganDanceAction*>(action) ||
dynamic_cast<CurePartyMemberAction*>(action))
{
return 1.0f;
}
if (dynamic_cast<CastSpellAction*>(action) && !
dynamic_cast<CastMeleeSpellAction*>(action))
{
uint32 spellId = AI_VALUE2(uint32, "spell id", action->getName());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
return 0.0f;
}
uint32 castTime = spellInfo->CalcCastTime();
if (castTime == 0 && !spellInfo->IsChanneled())
{
return 1.0f;
}
}
return 0.0f;
}
float LoathebGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb");
if (!boss)
{
return 1.0f;
}
context->GetValue<bool>("neglect threat")->Set(true);
if (botAI->GetState() == BOT_STATE_COMBAT &&
(dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CombatFormationMoveAction*>(action)))
{
return 0.0f;
}
if (!dynamic_cast<CastHealingSpellAction*>(action))
{
return 1.0f;
}
Aura* aura = botAI->GetAura("necrotic aura", bot);
if (!aura || aura->GetDuration() <= 1500)
{
return 1.0f;
}
return 0.0f;
}
float ThaddiusGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
{
return 1.0f;
}
// pet phase
if (helper.IsPhasePet() &&
(dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<ReachPartyMemberToHealAction*>(action) ||
dynamic_cast<BuffOnMainTankAction*>(action) ||
dynamic_cast<CombatFormationMoveAction*>(action)))
{
return 0.0f;
}
// die at the same time
Unit* target = AI_VALUE(Unit*, "current target");
Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen");
Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
if (helper.IsPhasePet() && target && feugen && stalagg && target-
>GetHealthPct() <= 40 &&
(feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg-
>GetHealthPct() >= target->GetHealthPct() + 3))
{
if (dynamic_cast<CastSpellAction*>(action) && !
dynamic_cast<CastHealingSpellAction*>(action))
{
return 0.0f;
}
}
// magnetic pull
// uint32 curr_timer = eventMap->GetTimer();
// // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f &&
dynamic_cast<MovementAction*>(action)) {
// if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000
<= 2000) &&
// dynamic_cast<MovementAction*>(action)) {
// // MotionMaster *mm = bot->GetMotionMaster();
// // mm->Clear();
// return 0.0f;
// }
// thaddius phase
// if (curr_phase == 8 && dynamic_cast<FleeAction*>(action)) {
// return 0.0f;
// }
return 1.0f;
}
float SapphironGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
{
return 1.0f;
}
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<CastDeathGripAction*>(action) ||
dynamic_cast<CombatFormationMoveAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
float InstructorRazuviousGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
{
return 1.0f;
}
context->GetValue<bool>("neglect threat")->Set(true);
if (botAI->GetState() == BOT_STATE_COMBAT &&
(dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastTauntAction*>(action) ||
dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
dynamic_cast<CastGrowlAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float KelthuzadGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
{
return 1.0f;
}
if ((dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action)))
{
return 0.0f;
}
if (helper.IsPhaseOne())
{
if (dynamic_cast<CastTotemAction*>(action) ||
dynamic_cast<CastShadowfiendAction*>(action) ||
dynamic_cast<CastRaiseDeadAction*>(action) ||
dynamic_cast<CastFeignDeathAction*>(action) ||
dynamic_cast<CastInvisibilityAction*>(action) ||
dynamic_cast<CastVanishAction*>(action) ||
dynamic_cast<PetAttackAction*>(action))
{
return 0.0f;
}
}
if (helper.IsPhaseTwo())
{
if (dynamic_cast<CastBlizzardAction*>(action) ||
dynamic_cast<CastFrostNovaAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}
float AnubrekhanGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
{
return 1.0f;
}
if (
// (dynamic_cast<DpsAssistAction*>(action) ||
// dynamic_cast<DpsAssistAction*>(action) ||
// dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<FollowAction*>(action))
{
return 0.0f;
}
// BossAI* boss_ai = dynamic_cast<BossAI*>(boss->GetAI());
// EventMap* eventMap = boss_ai->GetEvents();
// uint32 curr_phase = eventMap->GetPhaseMask();
if (botAI->HasAura("locust swarm", boss))
{
if (dynamic_cast<FleeAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}
float FourhorsemanGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
if (!boss)
{
return 1.0f;
}
context->GetValue<bool>("neglect threat")->Set(true);
if ((dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
// float GothikGenericMultiplier::GetValue(Action* action)
// {
// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester");
// if (!boss) {
// return 1.0f;
// }
// BossAI* boss_ai = dynamic_cast<BossAI*>(boss->GetAI());
// EventMap* eventMap = boss_botAI->GetEvents();
// uint32 curr_phase = eventMap->GetPhaseMask();
// if (curr_phase == 1 && (dynamic_cast<FollowAction*>(action))) {
// return 0.0f;
// }
// if (curr_phase == 1 && (dynamic_cast<AttackAction*>(action))) {
// Unit* target = action->GetTarget();
// if (target == boss) {
// return 0.0f;
// }
// }
// return 1.0f;
// }
float GluthGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
{
return 1.0f;
}
if ((dynamic_cast<DpsAssistAction*>(action) ||
dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<CastStarfallAction*>(action)))
{
return 0.0f;
}
if (botAI->IsMainTank(bot))
{
Aura* aura = botAI->GetAura("mortal wound", bot, false, true);
if (aura && aura->GetStackAmount() >= 5)
{
if (dynamic_cast<CastTauntAction*>(action) ||
dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
dynamic_cast<CastGrowlAction*>(action))
{
return 0.0f;
}
}
}
if (dynamic_cast<PetAttackAction*>(action))
{
Unit* target = AI_VALUE(Unit*, "current target");
if (target && target->GetEntry() == Gluth::NPC_ZOMBIE_CHOW)
{
return 0.0f;
}
}
return 1.0f;
}
#ifndef _PLAYERBOT_RAIDNAXXSCRIPTS_H
#define _PLAYERBOT_RAIDNAXXSCRIPTS_H
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_faerlina.h"
#include
"../../../../src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_gluth.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_gothik.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_grobbulus.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_heigan.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_loatheb.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_maexxna.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_noth.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_patchwerk.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_razuvious.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_sapphiron.h"
#include "../../../../src/server/scripts/Northrend/Naxxramas/boss_thaddius.h"
#endif
#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H
#define _PLAYERBOT_RAIDNAXXSTRATEGY_H
#include "AiObjectContext.h"
#include "Multiplier.h"
#include "RaidNaxxScripts.h"
#include "Strategy.h"
class RaidNaxxStrategy : public Strategy
{
public:
RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {}
virtual std::string const getName() override { return "naxx"; }
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers)
override;
};
#endif
// /*
// * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU
GPL v2 license, you may redistribute it
// and/or modify it under version 2 of the License, or (at your option), any
later version.
// */
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
#include "AiObjectContext.h"
#include "NamedObjectContext.h"
#include "RaidNaxxTriggers.h"
class RaidNaxxTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidNaxxTriggerContext()
{
creators["mutating injection"] =
&RaidNaxxTriggerContext::mutating_injection;
creators["mutating injection removed"] =
&RaidNaxxTriggerContext::mutating_injection_removed;
creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud;
creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee;
creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged;
creators["thaddius phase pet"] =
&RaidNaxxTriggerContext::thaddius_phase_pet;
creators["thaddius phase pet lose aggro"] =
&RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro;
creators["thaddius phase transition"] =
&RaidNaxxTriggerContext::thaddius_phase_transition;
creators["thaddius phase thaddius"] =
&RaidNaxxTriggerContext::thaddius_phase_thaddius;
creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank;
creators["razuvious nontank"] =
&RaidNaxxTriggerContext::razuvious_nontank;
creators["horseman attractors"] =
&RaidNaxxTriggerContext::horseman_attractors;
creators["horseman except attractors"] =
&RaidNaxxTriggerContext::horseman_except_attractors;
creators["sapphiron ground"] =
&RaidNaxxTriggerContext::sapphiron_ground;
creators["sapphiron flight"] =
&RaidNaxxTriggerContext::sapphiron_flight;
creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad;
creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan;
creators["gluth"] = &RaidNaxxTriggerContext::gluth;
creators["gluth main tank mortal wound"] =
&RaidNaxxTriggerContext::gluth_main_tank_mortal_wound;
creators["loatheb"] = &RaidNaxxTriggerContext::loatheb;
}
private:
static Trigger* mutating_injection(PlayerbotAI* ai) { return new
MutatingInjectionTrigger(ai); }
static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new
MutatingInjectionRemovedTrigger(ai); }
static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new
GrobbulusCloudTrigger(ai); }
static Trigger* heigan_melee(PlayerbotAI* ai) { return new
HeiganMeleeTrigger(ai); }
static Trigger* heigan_ranged(PlayerbotAI* ai) { return new
HeiganRangedTrigger(ai); }
static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new
ThaddiusPhasePetTrigger(ai); }
static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new
ThaddiusPhasePetLoseAggroTrigger(ai); }
static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new
ThaddiusPhaseTransitionTrigger(ai); }
static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new
ThaddiusPhaseThaddiusTrigger(ai); }
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new
RazuviousTankTrigger(ai); }
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new
RazuviousNontankTrigger(ai); }
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new
HorsemanAttractorsTrigger(ai); }
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new
HorsemanExceptAttractorsTrigger(ai); }
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new
SapphironGroundTrigger(ai); }
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new
SapphironFlightTrigger(ai); }
static Trigger* kelthuzad(PlayerbotAI* ai) { return new
KelthuzadTrigger(ai); }
static Trigger* anubrekhan(PlayerbotAI* ai) { return new
AnubrekhanTrigger(ai); }
static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); }
static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new
GluthMainTankMortalWoundTrigger(ai); }
static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); }
};
#endif
#include "RaidNaxxTriggers.h"
#include "EventMap.h"
#include "Playerbots.h"
#include "ScriptedCreature.h"
#include "Trigger.h"
bool AuraRemovedTrigger::IsActive()
{
bool check = botAI->HasAura(name, bot, false, false, -1, true);
bool ret = false;
if (prev_check && !check)
{
ret = true;
}
prev_check = check;
return ret;
}
bool MutatingInjectionRemovedTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
{
return false;
}
return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT
&& botAI->IsRanged(bot);
}
template <class T>
bool BossEventTrigger<T>::IsActive()
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss || boss->GetEntry() != boss_entry)
{
return false;
}
T* ai = dynamic_cast<T*>(boss->GetAI());
EventMap* eventMap = &ai->events;
if (!eventMap)
{
return false;
}
const uint32 event_time = eventMap->GetNextEventTime(event_id);
if (event_time != last_event_time)
{
last_event_time = event_time;
return true;
}
return false;
}
template <class T>
bool BossPhaseTrigger<T>::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", boss_name);
if (!boss)
{
return false;
}
if (this->phase_mask == 0)
{
return true;
}
T* boss_ai = dynamic_cast<T*>(boss->GetAI());
EventMap* eventMap = &boss_ai->events;
uint8 phase_mask = eventMap->GetPhaseMask();
// bot->Yell("phase mask detected: " + to_string(phase_mask) + " compare
with " + to_string(this->phase_mask),
// LANG_UNIVERSAL);
return phase_mask == this->phase_mask;
}
bool GrobbulusCloudTrigger::IsActive()
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss || boss->GetEntry() != boss_entry)
{
return false;
}
if (!botAI->IsMainTank(bot))
{
return false;
}
// bot->Yell("has aggro on " + boss->GetName() + " : " +
to_string(AI_VALUE2(bool, "has aggro", "boss target")),
// LANG_UNIVERSAL);
return AI_VALUE2(bool, "has aggro", "boss target");
}
bool HeiganMeleeTrigger::IsActive()
{
Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
if (!heigan)
{
return false;
}
return !botAI->IsRanged(bot);
}
bool HeiganRangedTrigger::IsActive()
{
Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
if (!heigan)
{
return false;
}
return botAI->IsRanged(bot);
}
bool RazuviousTankTrigger::IsActive()
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
{
return helper.UpdateBossAI() && botAI->IsTank(bot);
}
return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST;
}
bool RazuviousNontankTrigger::IsActive()
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
{
return helper.UpdateBossAI() && !(botAI->IsTank(bot));
}
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
}
bool HorsemanAttractorsTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsAttracter(bot);
}
bool HorsemanExceptAttractorsTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return !helper.IsAttracter(bot);
}
bool SapphironGroundTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsPhaseGround();
}
bool SapphironFlightTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsPhaseFlight();
}
// bool SapphironGroundExceptMainTankTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive() && !botAI->IsMainTank(bot);
// }
// bool SapphironFlightTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive();
// }
// bool SapphironGroundChillTrigger::IsActive()
// {
// return BossPhaseTrigger::IsActive() && !botAI->IsMainTank(bot) && botAI-
>HasAura("chill", bot);
// }
bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); }
bool GluthMainTankMortalWoundTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
if (!botAI->IsAssistTankOfIndex(bot, 0))
{
return false;
}
Unit* mt = AI_VALUE(Unit*, "main tank");
if (!mt)
{
return false;
}
Aura* aura = botAI->GetAura("mortal wound", mt, false, true);
if (!aura || aura->GetStackAmount() < 5)
{
return false;
}
return true;
}
bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); }
bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); }
bool ThaddiusPhasePetTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsPhasePet();
}
bool ThaddiusPhaseTransitionTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsPhaseTransition();
}
bool ThaddiusPhaseThaddiusTrigger::IsActive()
{
if (!helper.UpdateBossAI())
{
return false;
}
return helper.IsPhaseThaddius();
}
template bool
BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>::IsActive();
template bool
BossPhaseTrigger<Anubrekhan::boss_anubrekhan::boss_anubrekhanAI>::IsActive();
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H
#define _PLAYERBOT_RAIDNAXXTRIGGERS_H
#include "EventMap.h"
#include "GenericTriggers.h"
#include "PlayerbotAIConfig.h"
#include "RaidNaxxBossHelper.h"
#include "RaidNaxxScripts.h"
#include "Trigger.h"
class MutatingInjectionTrigger : public HasAuraTrigger
{
public:
MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating
injection", 1) {}
};
class AuraRemovedTrigger : public Trigger
{
public:
AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI,
name, 1) { this->prev_check = false; }
virtual bool IsActive() override;
protected:
bool prev_check;
};
class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger
{
public:
MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai,
"mutating injection") {}
virtual bool IsActive();
};
template <class T>
class BossEventTrigger : public Trigger
{
public:
BossEventTrigger(PlayerbotAI* ai, uint32 boss_entry, uint32 event_id,
std::string name = "boss event")
: Trigger(ai, name, 1)
{
this->boss_entry = boss_entry;
this->event_id = event_id;
this->last_event_time = -1;
}
virtual bool IsActive();
protected:
uint32 boss_entry, event_id, last_event_time;
};
template <class T>
class BossPhaseTrigger : public Trigger
{
public:
BossPhaseTrigger(PlayerbotAI* ai, std::string boss_name, uint32 phase_mask,
std::string name = "boss event")
: Trigger(ai, name, 1)
{
this->boss_name = boss_name;
this->phase_mask = phase_mask;
}
virtual bool IsActive();
protected:
std::string boss_name;
uint32 phase_mask;
};
class GrobbulusCloudTrigger : public
BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>
{
public:
GrobbulusCloudTrigger(PlayerbotAI* ai) : BossEventTrigger(ai, 15931, 2,
"grobbulus cloud event") {}
virtual bool IsActive();
};
class HeiganMeleeTrigger : public Trigger
{
public:
HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {}
virtual bool IsActive();
};
class HeiganRangedTrigger : public Trigger
{
public:
HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {}
bool IsActive() override;
};
class RazuviousTankTrigger : public Trigger
{
public:
RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious
tank"), helper(ai) {}
bool IsActive() override;
private:
RazuviousBossHelper helper;
};
class RazuviousNontankTrigger : public Trigger
{
public:
RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious
non-tank"), helper(ai) {}
bool IsActive() override;
private:
RazuviousBossHelper helper;
};
class KelthuzadTrigger : public Trigger
{
public:
KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"),
helper(ai) {}
bool IsActive() override;
private:
KelthuzadBossHelper helper;
};
class AnubrekhanTrigger : public
BossPhaseTrigger<Anubrekhan::boss_anubrekhan::boss_anubrekhanAI>
{
public:
AnubrekhanTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai, "anub'rekhan", 0,
"anub'rekhan trigger") {}
};
class ThaddiusPhasePetTrigger : public Trigger
{
public:
ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase
pet"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger
{
public:
ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) :
ThaddiusPhasePetTrigger(ai) {}
virtual bool IsActive()
{
Unit* target = AI_VALUE(Unit*, "current target");
return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) &&
target && target->GetVictim() != bot;
}
};
class ThaddiusPhaseTransitionTrigger : public Trigger
{
public:
ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius
phase transition"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class ThaddiusPhaseThaddiusTrigger : public Trigger
{
public:
ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase
thaddius"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class HorsemanAttractorsTrigger : public Trigger
{
public:
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen
attractors"), helper(ai) {}
bool IsActive() override;
private:
FourhorsemanBossHelper helper;
};
class HorsemanExceptAttractorsTrigger : public Trigger
{
public:
HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen
except attractors"), helper(ai) {}
bool IsActive() override;
private:
FourhorsemanBossHelper helper;
};
class SapphironGroundTrigger : public Trigger
{
public:
SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"),
helper(ai) {}
bool IsActive() override;
private:
SapphironBossHelper helper;
};
// class SapphironGroundExceptMainTankTrigger : public BossPhaseTrigger
// {
// public:
// SapphironGroundExceptMainTankTrigger(PlayerbotAI* ai) :
BossPhaseTrigger(ai, "sapphiron", (1 << (2 - 1)),
// "sapphiron ground except main tank") {} virtual bool IsActive();
// };
class SapphironFlightTrigger : public Trigger
{
public:
SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"),
helper(ai) {}
bool IsActive() override;
private:
SapphironBossHelper helper;
};
// class SapphironGroundChillTrigger : public BossPhaseTrigger
// {
// public:
// SapphironGroundChillTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai,
"sapphiron", 0, "sapphiron chill") {}
// virtual bool IsActive();
// };
// class KelthuzadPhaseTwoTrigger : public BossPhaseTrigger
// {
// public:
// KelthuzadPhaseTwoTrigger(PlayerbotAI* ai) : BossPhaseTrigger(ai,
"kel'thuzad", 1 << (2 - 1), "kel'thuzad
// trigger") {}
// };
class GluthTrigger : public Trigger
{
public:
GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {}
bool IsActive() override;
private:
GluthBossHelper helper;
};
class GluthMainTankMortalWoundTrigger : public Trigger
{
public:
GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main
tank mortal wound trigger"), helper(ai)
{
}
bool IsActive() override;
private:
GluthBossHelper helper;
};
class LoathebTrigger : public Trigger
{
public:
LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {}
bool IsActive() override;
private:
LoathebBossHelper helper;
};
// template BossEventTrigger<class boss_grobbulus::boss_grobbulusAI>;
#endif