0% found this document useful (0 votes)
22 views

Code Base

Preview of code

Uploaded by

xidare5057
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views

Code Base

Preview of code

Uploaded by

xidare5057
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 54

// /*

// * 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

You might also like