mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-03 19:03:49 +00:00
[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)
This commit is contained in:
48
src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp
Normal file
48
src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "RaidAq20Actions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
bool Aq20UseCrystalAction::Execute(Event event)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||
{
|
||||
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||
{
|
||||
float botDist = bot->GetDistance(crystal);
|
||||
if (botDist > INTERACTION_DISTANCE)
|
||||
return MoveTo(bot->GetMapId(),
|
||||
crystal->GetPositionX() + frand(-3.5f, 3.5f),
|
||||
crystal->GetPositionY() + frand(-3.5f, 3.5f),
|
||||
crystal->GetPositionZ());
|
||||
|
||||
// if we're already in range just wait here until it's time to activate crystal
|
||||
SetNextMovementDelay(500);
|
||||
|
||||
// don't activate crystal if boss too far or its already been activated
|
||||
if (boss->GetDistance(crystal) > 25.0f ||
|
||||
crystal->HasGameObjectFlag(GO_FLAG_IN_USE))
|
||||
return false;
|
||||
|
||||
// don't activate crystal if boss doesn't have buff yet AND isn't going to have it soon
|
||||
// (though ideally bot should activate it ~5 seconds early due to time it takes for
|
||||
// crystal to activate and remove buff)
|
||||
if (!RaidAq20Utils::IsOssirianBuffActive(boss) &&
|
||||
RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss) > 5000)
|
||||
return false;
|
||||
|
||||
// this makes crystal do its animation (then disappear after)
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << crystal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
// this makes crystal actually remove the buff and put on debuff (took a while to figure that out)
|
||||
WorldPacket data2(CMSG_GAMEOBJ_USE);
|
||||
data2 << crystal->GetGUID();
|
||||
bot->GetSession()->HandleGameobjectReportUse(data2);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
14
src/Ai/Raid/Aq20/Action/RaidAq20Actions.h
Normal file
14
src/Ai/Raid/Aq20/Action/RaidAq20Actions.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||
#define _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class Aq20UseCrystalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
Aq20UseCrystalAction(PlayerbotAI* botAI, std::string const name = "aq20 use crystal") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
#endif
|
||||
20
src/Ai/Raid/Aq20/RaidAq20ActionContext.h
Normal file
20
src/Ai/Raid/Aq20/RaidAq20ActionContext.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidAq20Actions.h"
|
||||
|
||||
class RaidAq20ActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidAq20ActionContext()
|
||||
{
|
||||
creators["aq20 use crystal"] = &RaidAq20ActionContext::use_crystal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* use_crystal(PlayerbotAI* ai) { return new Aq20UseCrystalAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
20
src/Ai/Raid/Aq20/RaidAq20TriggerContext.h
Normal file
20
src/Ai/Raid/Aq20/RaidAq20TriggerContext.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidAq20Triggers.h"
|
||||
|
||||
class RaidAq20TriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidAq20TriggerContext()
|
||||
{
|
||||
creators["aq20 move to crystal"] = &RaidAq20TriggerContext::move_to_crystal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* move_to_crystal(PlayerbotAI* ai) { return new Aq20MoveToCrystalTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
11
src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.cpp
Normal file
11
src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "RaidAq20Strategy.h"
|
||||
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidAq20Strategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("aq20 move to crystal",
|
||||
{ NextAction("aq20 use crystal", ACTION_RAID) }));
|
||||
|
||||
}
|
||||
17
src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h
Normal file
17
src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
#define _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidAq20Strategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidAq20Strategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "aq20"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
// virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
36
src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp
Normal file
36
src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "RaidAq20Triggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
bool Aq20MoveToCrystalTrigger::IsActive()
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||
{
|
||||
if (boss->IsInCombat())
|
||||
{
|
||||
// if buff is active move to crystal
|
||||
if (RaidAq20Utils::IsOssirianBuffActive(boss))
|
||||
return true;
|
||||
|
||||
// if buff is not active a debuff will be, buff becomes active once debuff expires
|
||||
// so move to crystal when debuff almost done, or based debuff time left and
|
||||
// distance bot is from crystal (ie: start moving early enough to make it)
|
||||
int32 debuffTimeRemaining = RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss);
|
||||
if (debuffTimeRemaining < 5000)
|
||||
return true;
|
||||
if (debuffTimeRemaining < 30000)
|
||||
{
|
||||
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||
{
|
||||
float botDist = bot->GetDistance(crystal);
|
||||
float timeToReach = botDist / bot->GetSpeed(MOVE_RUN);
|
||||
// bot should ideally activate crystal a ~5 seconds early (due to time it takes for crystal
|
||||
// to activate) so aim to get there in time to do so
|
||||
return debuffTimeRemaining - 5000 < timeToReach * 1000.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
14
src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.h
Normal file
14
src/Ai/Raid/Aq20/Trigger/RaidAq20Triggers.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class Aq20MoveToCrystalTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
Aq20MoveToCrystalTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq20 move to crystal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
#endif
|
||||
38
src/Ai/Raid/Aq20/Util/RaidAq20Utils.cpp
Normal file
38
src/Ai/Raid/Aq20/Util/RaidAq20Utils.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
#include "SpellAuras.h"
|
||||
|
||||
uint32 const OSSIRIAN_BUFF = 25176;
|
||||
uint32 const OSSIRIAN_DEBUFFS[] = {25177, 25178, 25180, 25181, 25183};
|
||||
uint32 const OSSIRIAN_CRYSTAL_GO_ENTRY = 180619;
|
||||
|
||||
bool RaidAq20Utils::IsOssirianBuffActive(Unit* ossirian)
|
||||
{
|
||||
return ossirian && ossirian->HasAura(OSSIRIAN_BUFF);
|
||||
}
|
||||
|
||||
int32 RaidAq20Utils::GetOssirianDebuffTimeRemaining(Unit* ossirian)
|
||||
{
|
||||
int32 retVal = 0xffffff;
|
||||
if (ossirian)
|
||||
{
|
||||
for (uint32 debuff : OSSIRIAN_DEBUFFS)
|
||||
{
|
||||
if (AuraApplication* auraApplication = ossirian->GetAuraApplication(debuff))
|
||||
{
|
||||
if (Aura* aura = auraApplication->GetBase())
|
||||
{
|
||||
int32 duration = aura->GetDuration();
|
||||
if (retVal > duration)
|
||||
retVal = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
GameObject* RaidAq20Utils::GetNearestCrystal(Unit* ossirian)
|
||||
{
|
||||
return ossirian ? ossirian->FindNearestGameObject(OSSIRIAN_CRYSTAL_GO_ENTRY, 200.0f) : nullptr;
|
||||
}
|
||||
15
src/Ai/Raid/Aq20/Util/RaidAq20Utils.h
Normal file
15
src/Ai/Raid/Aq20/Util/RaidAq20Utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20UTILS_H
|
||||
#define _PLAYERBOT_RAIDAQ20UTILS_H
|
||||
|
||||
#include "GameObject.h"
|
||||
#include "Unit.h"
|
||||
|
||||
class RaidAq20Utils
|
||||
{
|
||||
public:
|
||||
static bool IsOssirianBuffActive(Unit* ossirian);
|
||||
static int32 GetOssirianDebuffTimeRemaining(Unit* ossirian);
|
||||
static GameObject* GetNearestCrystal(Unit* ossirian);
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp
Normal file
32
src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "RaidBwlActions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event event)
|
||||
{
|
||||
bot->AddAura(22683, bot);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BwlOnyxiaScaleCloakAuraCheckAction::isUseful() { return !bot->HasAura(22683); }
|
||||
|
||||
bool BwlTurnOffSuppressionDeviceAction::Execute(Event event)
|
||||
{
|
||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects");
|
||||
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(*i);
|
||||
if (!go)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (go->GetEntry() != 179784 || go->GetDistance(bot) >= 15.0f || go->GetGoState() != GO_STATE_READY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BwlUseHourglassSandAction::Execute(Event event) { return botAI->CastSpell(23645, bot); }
|
||||
33
src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h
Normal file
33
src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLACTIONS_H
|
||||
#define _PLAYERBOT_RAIDBWLACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class BwlOnyxiaScaleCloakAuraCheckAction : public Action
|
||||
{
|
||||
public:
|
||||
BwlOnyxiaScaleCloakAuraCheckAction(PlayerbotAI* botAI) : Action(botAI, "bwl onyxia scale cloak aura check") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class BwlTurnOffSuppressionDeviceAction : public Action
|
||||
{
|
||||
public:
|
||||
BwlTurnOffSuppressionDeviceAction(PlayerbotAI* botAI) : Action(botAI, "bwl turn off suppression device") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class BwlUseHourglassSandAction : public Action
|
||||
{
|
||||
public:
|
||||
BwlUseHourglassSandAction(PlayerbotAI* botAI) : Action(botAI, "bwl use hourglass sand") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Raid/BlackwingLair/RaidBwlActionContext.h
Normal file
24
src/Ai/Raid/BlackwingLair/RaidBwlActionContext.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDBWLACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidBwlActions.h"
|
||||
|
||||
class RaidBwlActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidBwlActionContext()
|
||||
{
|
||||
creators["bwl check onyxia scale cloak"] = &RaidBwlActionContext::bwl_check_onyxia_scale_cloak;
|
||||
creators["bwl turn off suppression device"] = &RaidBwlActionContext::bwl_turn_off_suppression_device;
|
||||
creators["bwl use hourglass sand"] = &RaidBwlActionContext::bwl_use_hourglass_sand;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* bwl_check_onyxia_scale_cloak(PlayerbotAI* botAI) { return new BwlOnyxiaScaleCloakAuraCheckAction(botAI); }
|
||||
static Action* bwl_turn_off_suppression_device(PlayerbotAI* botAI) { return new BwlTurnOffSuppressionDeviceAction(botAI); }
|
||||
static Action* bwl_use_hourglass_sand(PlayerbotAI* botAI) { return new BwlUseHourglassSandAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h
Normal file
22
src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidBwlTriggers.h"
|
||||
|
||||
class RaidBwlTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidBwlTriggerContext()
|
||||
{
|
||||
creators["bwl suppression device"] = &RaidBwlTriggerContext::bwl_suppression_device;
|
||||
creators["bwl affliction bronze"] = &RaidBwlTriggerContext::bwl_affliction_bronze;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* bwl_suppression_device(PlayerbotAI* ai) { return new BwlSuppressionDeviceTrigger(ai); }
|
||||
static Trigger* bwl_affliction_bronze(PlayerbotAI* ai) { return new BwlAfflictionBronzeTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
15
src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp
Normal file
15
src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "RaidBwlStrategy.h"
|
||||
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidBwlStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("often",
|
||||
{ NextAction("bwl check onyxia scale cloak", ACTION_RAID) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("bwl suppression device",
|
||||
{ NextAction("bwl turn off suppression device", ACTION_RAID) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("bwl affliction bronze",
|
||||
{ NextAction("bwl use hourglass sand", ACTION_RAID) }));
|
||||
}
|
||||
18
src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h
Normal file
18
src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDBWLSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDBWLSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidBwlStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidBwlStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "bwl"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
// virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp
Normal file
24
src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "RaidBwlTriggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool BwlSuppressionDeviceTrigger::IsActive()
|
||||
{
|
||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects");
|
||||
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(*i);
|
||||
if (!go)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (go->GetEntry() != 179784 || go->GetDistance(bot) >= 15.0f || go->GetGoState() != GO_STATE_READY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BwlAfflictionBronzeTrigger::IsActive() { return bot->HasAura(23170); }
|
||||
22
src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h
Normal file
22
src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDBWLTRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class BwlSuppressionDeviceTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BwlSuppressionDeviceTrigger(PlayerbotAI* botAI) : Trigger(botAI, "bwl suppression device") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class BwlAfflictionBronzeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BwlAfflictionBronzeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "bwl affliction bronze") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
404
src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp
Normal file
404
src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
#include "Playerbots.h"
|
||||
#include "RaidEoEActions.h"
|
||||
#include "RaidEoETriggers.h"
|
||||
|
||||
bool MalygosPositionAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
if (!boss) { return false; }
|
||||
|
||||
uint8 phase = MalygosTrigger::getPhase(bot, boss);
|
||||
|
||||
float distance = 5.0f;
|
||||
|
||||
if (phase == 1)
|
||||
{
|
||||
Unit* spark = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
{
|
||||
spark = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Position tank
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (bot->GetDistance2d(MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second) > distance)
|
||||
{
|
||||
return MoveTo(EOE_MAP_ID, MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second, bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Position DK for spark pull
|
||||
// else if (spark && bot->IsClass(CLASS_DEATH_KNIGHT))
|
||||
// {
|
||||
// if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > distance)
|
||||
// {
|
||||
// bot->Yell("SPARK SPAWNED, MOVING TO STACK", LANG_UNIVERSAL);
|
||||
// return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(),
|
||||
// false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
else if (spark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!bot->IsClass(CLASS_HUNTER))
|
||||
{
|
||||
if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > (distance * 3.0f))
|
||||
{
|
||||
return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MalygosTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
if (!boss) { return false; }
|
||||
|
||||
uint8 phase = MalygosTrigger::getPhase(bot, boss);
|
||||
|
||||
if (phase == 1)
|
||||
{
|
||||
if (botAI->IsHeal(bot)) { return false; }
|
||||
|
||||
// Init this as boss by default, if no better target is found just fall back to Malygos
|
||||
Unit* newTarget = boss;
|
||||
// Unit* spark = nullptr;
|
||||
|
||||
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
// for (auto& target : targets)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(target);
|
||||
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
// {
|
||||
// spark = unit;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (spark && botAI->IsRangedDps(bot))
|
||||
// {
|
||||
// newTarget = spark;
|
||||
// }
|
||||
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
|
||||
if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry())
|
||||
{
|
||||
return Attack(newTarget);
|
||||
}
|
||||
}
|
||||
else if (phase == 2)
|
||||
{
|
||||
if (botAI->IsHeal(bot)) { return false; }
|
||||
|
||||
Unit* newTarget = nullptr;
|
||||
Unit* nexusLord = nullptr;
|
||||
Unit* scionOfEternity = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (!unit) { continue; }
|
||||
|
||||
if (unit->GetEntry() == NPC_NEXUS_LORD)
|
||||
{
|
||||
nexusLord = unit;
|
||||
}
|
||||
else if (unit->GetEntry() == NPC_SCION_OF_ETERNITY)
|
||||
{
|
||||
scionOfEternity = unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (botAI->IsRangedDps(bot) && scionOfEternity)
|
||||
{
|
||||
newTarget = scionOfEternity;
|
||||
}
|
||||
else
|
||||
{
|
||||
newTarget = nexusLord;
|
||||
}
|
||||
|
||||
if (!newTarget) { return false; }
|
||||
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry())
|
||||
{
|
||||
return Attack(newTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// else if (phase == 3)
|
||||
// {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// bool PullPowerSparkAction::Execute(Event event)
|
||||
// {
|
||||
// Unit* spark = nullptr;
|
||||
|
||||
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
// for (auto& target : targets)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(target);
|
||||
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
// {
|
||||
// spark = unit;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!spark) { return false; }
|
||||
|
||||
// if (spark->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > 3.0f)
|
||||
// {
|
||||
// bot->Yell("GRIPPING SPARK", LANG_UNIVERSAL);
|
||||
// return botAI->CastSpell("death grip", spark);
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// bool PullPowerSparkAction::isPossible()
|
||||
// {
|
||||
// Unit* spark = nullptr;
|
||||
|
||||
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
// for (auto& target : targets)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(target);
|
||||
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
// {
|
||||
// spark = unit;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return botAI->CanCastSpell(spell, spark);
|
||||
// }
|
||||
|
||||
// bool PullPowerSparkAction::isUseful()
|
||||
// {
|
||||
// Unit* spark = nullptr;
|
||||
|
||||
// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
// for (auto& target : targets)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(target);
|
||||
// if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
// {
|
||||
// spark = unit;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!spark)
|
||||
// return false;
|
||||
|
||||
// if (!spark->IsInWorld() || spark->GetMapId() != bot->GetMapId())
|
||||
// return false;
|
||||
|
||||
// return bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) < 3.0f;
|
||||
// }
|
||||
|
||||
// bool KillPowerSparkAction::Execute(Event event)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
bool EoEFlyDrakeAction::isPossible()
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON);
|
||||
}
|
||||
bool EoEFlyDrakeAction::Execute(Event event)
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
Unit* masterVehicle = master->GetVehicleBase();
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase || !masterVehicle) { return false; }
|
||||
|
||||
MotionMaster* mm = vehicleBase->GetMotionMaster();
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
if (boss && false)
|
||||
{
|
||||
// Handle as boss encounter instead of formation flight
|
||||
mm->Clear(false);
|
||||
float distance = vehicleBase->GetExactDist(boss);
|
||||
float range = 55.0f; // Drake range is 60yd
|
||||
if (distance > range)
|
||||
{
|
||||
mm->MoveForwards(boss, range - distance);
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
vehicleBase->SetFacingToObject(boss);
|
||||
mm->MoveIdle();
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vehicleBase->GetExactDist(masterVehicle) > 5.0f)
|
||||
{
|
||||
uint8 numPlayers;
|
||||
bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numPlayers = 25 : numPlayers = 10;
|
||||
// 3/4 of a circle, with frontal cone 90 deg unobstructed
|
||||
float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2;
|
||||
// float angle = M_PI;
|
||||
vehicleBase->SetCanFly(true);
|
||||
mm->MoveFollow(masterVehicle, 3.0f, angle);
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::isPossible()
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON);
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::Execute(Event event)
|
||||
{
|
||||
vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unit* target = AI_VALUE(Unit*, "current target");
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
// if (!boss) { return false; }
|
||||
|
||||
if (!boss)
|
||||
{
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "possible targets");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (!unit || unit->GetEntry() != NPC_MALYGOS)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boss = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check this again to see if a target was assigned
|
||||
if (!boss)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 numHealers;
|
||||
bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numHealers = 10 : numHealers = 4;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
std::vector<std::pair<ObjectGuid, Player*>> sortedMembers;
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
sortedMembers.push_back(std::make_pair(member->GetGUID(), member));
|
||||
}
|
||||
std::sort(sortedMembers.begin(), sortedMembers.end());
|
||||
|
||||
int botIndex = -1;
|
||||
for (size_t i = 0; i < sortedMembers.size(); ++i)
|
||||
{
|
||||
if (sortedMembers[i].first == bot->GetGUID())
|
||||
{
|
||||
botIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (botIndex == -1)
|
||||
return false;
|
||||
|
||||
if (botIndex > numHealers)
|
||||
{
|
||||
return DrakeDpsAction(boss);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DrakeHealAction();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown)
|
||||
{
|
||||
if (botAI->CanCastVehicleSpell(spellId, target))
|
||||
if (botAI->CastVehicleSpell(spellId, target))
|
||||
{
|
||||
vehicleBase->AddSpellCooldown(spellId, 0, cooldown);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target)
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase) { return false; }
|
||||
|
||||
Vehicle* veh = bot->GetVehicle();
|
||||
|
||||
uint8 comboPoints = vehicleBase->GetComboPoints(target);
|
||||
if (comboPoints >= 2)
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_ENGULF_IN_FLAMES, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_FLAME_SPIKE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::DrakeHealAction()
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 comboPoints = vehicleBase->GetComboPoints(vehicleBase);
|
||||
if (comboPoints >= 5)
|
||||
{
|
||||
return CastDrakeSpellAction(vehicleBase, SPELL_LIFE_BURST, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// "Revivify" may be bugged server-side:
|
||||
// "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes.
|
||||
// Forcing the cast attempt seems to succeed, not sure what's going on here.
|
||||
// return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0);
|
||||
return botAI->CastVehicleSpell(SPELL_REVIVIFY, vehicleBase);
|
||||
}
|
||||
}
|
||||
69
src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h
Normal file
69
src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOEACTIONS_H
|
||||
#define _PLAYERBOT_RAIDEOEACTIONS_H
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
const std::pair<float, float> MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f};
|
||||
const std::pair<float, float> MALYGOS_STACK_POSITION = {755.0f, 1301.0f};
|
||||
|
||||
class MalygosPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MalygosTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class PullPowerSparkAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark")
|
||||
: CastSpellAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class KillPowerSparkAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class EoEFlyDrakeAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
};
|
||||
|
||||
class EoEDrakeAttackAction : public Action
|
||||
{
|
||||
public:
|
||||
EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
|
||||
protected:
|
||||
Unit* vehicleBase;
|
||||
bool CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown);
|
||||
bool DrakeDpsAction(Unit* target);
|
||||
bool DrakeHealAction();
|
||||
};
|
||||
|
||||
#endif
|
||||
81
src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp
Normal file
81
src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "RaidEoEMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "RaidEoEActions.h"
|
||||
#include "RaidEoETriggers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float MalygosMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
|
||||
uint8 phase = MalygosTrigger::getPhase(bot, boss);
|
||||
if (phase == 0) { return 1.0f; }
|
||||
|
||||
if (phase == 1)
|
||||
{
|
||||
if (dynamic_cast<FollowAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsRangedDps(bot) && dynamic_cast<DropTargetAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (!botAI->IsMainTank(bot) && dynamic_cast<TankAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<MalygosPositionAction*>(action))
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
}
|
||||
else if (phase == 2)
|
||||
{
|
||||
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
{
|
||||
Unit* target = action->GetTarget();
|
||||
if (target && target->GetEntry() == NPC_SCION_OF_ETERNITY)
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
else if (phase == 3)
|
||||
{
|
||||
// Suppresses FollowAction as well as some attack-based movements
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<EoEFlyDrakeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
16
src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.h
Normal file
16
src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDEOEMULTIPLIERS_H
|
||||
#define _PLAYERRBOT_RAIDEOEMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class MalygosMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MalygosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "malygos") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
30
src/Ai/Raid/EyeOfEternity/RaidEoEActionContext.h
Normal file
30
src/Ai/Raid/EyeOfEternity/RaidEoEActionContext.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOEACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDEOEACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidEoEActions.h"
|
||||
|
||||
class RaidEoEActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidEoEActionContext()
|
||||
{
|
||||
creators["malygos position"] = &RaidEoEActionContext::position;
|
||||
creators["malygos target"] = &RaidEoEActionContext::target;
|
||||
// creators["pull power spark"] = &RaidEoEActionContext::pull_power_spark;
|
||||
// creators["kill power spark"] = &RaidEoEActionContext::kill_power_spark;
|
||||
creators["eoe fly drake"] = &RaidEoEActionContext::eoe_fly_drake;
|
||||
creators["eoe drake attack"] = &RaidEoEActionContext::eoe_drake_attack;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* position(PlayerbotAI* ai) { return new MalygosPositionAction(ai); }
|
||||
static Action* target(PlayerbotAI* ai) { return new MalygosTargetAction(ai); }
|
||||
// static Action* pull_power_spark(PlayerbotAI* ai) { return new PullPowerSparkAction(ai); }
|
||||
// static Action* kill_power_spark(PlayerbotAI* ai) { return new KillPowerSparkAction(ai); }
|
||||
static Action* eoe_fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); }
|
||||
static Action* eoe_drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h
Normal file
22
src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidEoETriggers.h"
|
||||
|
||||
class RaidEoETriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidEoETriggerContext()
|
||||
{
|
||||
creators["malygos"] = &RaidEoETriggerContext::malygos;
|
||||
creators["power spark"] = &RaidEoETriggerContext::power_spark;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* power_spark(PlayerbotAI* ai) { return new PowerSparkTrigger(ai); }
|
||||
static Trigger* malygos(PlayerbotAI* ai) { return new MalygosTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
21
src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp
Normal file
21
src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "RaidEoEStrategy.h"
|
||||
#include "RaidEoEMultipliers.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidEoEStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("malygos",
|
||||
{ NextAction("malygos position", ACTION_MOVE) }));
|
||||
triggers.push_back(new TriggerNode("malygos",
|
||||
{ NextAction("malygos target", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("group flying",
|
||||
{ NextAction("eoe fly drake", ACTION_NORMAL + 1) }));
|
||||
triggers.push_back(new TriggerNode("drake combat",
|
||||
{ NextAction("eoe drake attack", ACTION_NORMAL + 5) }));
|
||||
}
|
||||
|
||||
void RaidEoEStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new MalygosMultiplier(botAI));
|
||||
}
|
||||
17
src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h
Normal file
17
src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOESTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDEOESTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidEoEStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidEoEStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "wotlk-eoe"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
53
src/Ai/Raid/EyeOfEternity/Trigger/RaidEoETriggers.cpp
Normal file
53
src/Ai/Raid/EyeOfEternity/Trigger/RaidEoETriggers.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "RaidEoETriggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
|
||||
uint8 MalygosTrigger::getPhase(Player* bot, Unit* boss)
|
||||
{
|
||||
uint8 phase = 0;
|
||||
Unit* vehicle = bot->GetVehicleBase();
|
||||
if (bot->GetMapId() != EOE_MAP_ID) { return phase; }
|
||||
|
||||
if (vehicle && vehicle->GetEntry() == NPC_WYRMREST_SKYTALON)
|
||||
{
|
||||
phase = 3;
|
||||
}
|
||||
else if (boss && boss->HealthAbovePct(50))
|
||||
{
|
||||
phase = 1;
|
||||
}
|
||||
else if (boss)
|
||||
{
|
||||
phase = 2;
|
||||
}
|
||||
|
||||
return phase;
|
||||
}
|
||||
|
||||
bool MalygosTrigger::IsActive()
|
||||
{
|
||||
return bool(AI_VALUE2(Unit*, "find target", "malygos"));
|
||||
}
|
||||
|
||||
bool PowerSparkTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
if (!boss) { return false; }
|
||||
|
||||
if (bot->getClass() != CLASS_DEATH_KNIGHT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->GetEntry() == NPC_POWER_SPARK)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
58
src/Ai/Raid/EyeOfEternity/Trigger/RaidEoETriggers.h
Normal file
58
src/Ai/Raid/EyeOfEternity/Trigger/RaidEoETriggers.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOETRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDEOETRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum EyeOfEternityIDs
|
||||
{
|
||||
NPC_MALYGOS = 28859,
|
||||
NPC_POWER_SPARK = 30084,
|
||||
NPC_NEXUS_LORD = 30245,
|
||||
NPC_SCION_OF_ETERNITY = 30249,
|
||||
NPC_WYRMREST_SKYTALON = 30161,
|
||||
|
||||
SPELL_POWER_SPARK_VISUAL = 55845,
|
||||
SPELL_POWER_SPARK_GROUND_BUFF = 55852,
|
||||
SPELL_POWER_SPARK_MALYGOS_BUFF = 56152,
|
||||
|
||||
SPELL_TELEPORT_VISUAL = 52096,
|
||||
|
||||
SPELL_SCION_ARCANE_BARRAGE = 56397,
|
||||
SPELL_ARCANE_SHOCK_N = 57058,
|
||||
SPELL_ARCANE_SHOCK_H = 60073,
|
||||
SPELL_HASTE = 57060,
|
||||
|
||||
SPELL_ALEXSTRASZA_GIFT = 61028,
|
||||
|
||||
// Drake Abilities:
|
||||
// DPS
|
||||
SPELL_FLAME_SPIKE = 56091,
|
||||
SPELL_ENGULF_IN_FLAMES = 56092,
|
||||
// Healing
|
||||
SPELL_REVIVIFY = 57090,
|
||||
SPELL_LIFE_BURST = 57143,
|
||||
// Utility
|
||||
SPELL_FLAME_SHIELD = 57108,
|
||||
SPELL_BLAZING_SPEED = 57092,
|
||||
};
|
||||
|
||||
const uint32 EOE_MAP_ID = 616;
|
||||
|
||||
class MalygosTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MalygosTrigger(PlayerbotAI* botAI) : Trigger(botAI, "malygos") {}
|
||||
bool IsActive() override;
|
||||
uint8 static getPhase(Player* bot, Unit* boss);
|
||||
};
|
||||
|
||||
class PowerSparkTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PowerSparkTrigger(PlayerbotAI* botAI) : Trigger(botAI, "power spark") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
696
src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp
Normal file
696
src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp
Normal file
@@ -0,0 +1,696 @@
|
||||
#include "RaidGruulsLairActions.h"
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Unit.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
|
||||
// High King Maulgar Actions
|
||||
|
||||
// Main tank on Maulgar
|
||||
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
MarkTargetWithSquare(bot, maulgar);
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
|
||||
if (bot->GetVictim() != maulgar)
|
||||
return Attack(maulgar);
|
||||
|
||||
if (maulgar->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(),
|
||||
maulgar->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(maulgar))
|
||||
{
|
||||
return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(),
|
||||
maulgar->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// First offtank on Olm
|
||||
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
|
||||
MarkTargetWithCircle(bot, olm);
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
|
||||
if (bot->GetVictim() != olm)
|
||||
return Attack(olm);
|
||||
|
||||
if (olm->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
const float olmTankLeeway = 30.0f;
|
||||
|
||||
float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
|
||||
if (distanceOlmToTankPosition > olmTankLeeway)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(olm))
|
||||
{
|
||||
return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(),
|
||||
olm->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Second offtank on Blindeye
|
||||
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
MarkTargetWithStar(bot, blindeye);
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
|
||||
if (bot->GetVictim() != blindeye)
|
||||
return Attack(blindeye);
|
||||
|
||||
if (blindeye->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(),
|
||||
blindeye->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(blindeye))
|
||||
{
|
||||
return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(),
|
||||
blindeye->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mage with highest max HP on Krosh
|
||||
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
MarkTargetWithTriangle(bot, krosh);
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
|
||||
if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("spellsteal", krosh))
|
||||
return botAI->CastSpell("spellsteal", krosh);
|
||||
|
||||
if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot))
|
||||
return botAI->CastSpell("fire ward", bot);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (krosh->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
||||
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
const float minDistance = 16.0f;
|
||||
const float maxDistance = 29.0f;
|
||||
const float tankPositionLeeway = 1.0f;
|
||||
|
||||
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
||||
{
|
||||
if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false,
|
||||
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
||||
krosh->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Moonkin with highest max HP on Kiggler
|
||||
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
|
||||
MarkTargetWithDiamond(bot, kiggler);
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Target priority 1: Blindeye
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
if (blindeye && blindeye->IsAlive())
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
|
||||
if (bot->GetTarget() != blindeye->GetGUID())
|
||||
{
|
||||
bot->SetSelection(blindeye->GetGUID());
|
||||
return Attack(blindeye);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 2: Olm
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
if (olm && olm->IsAlive())
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
|
||||
if (bot->GetTarget() != olm->GetGUID())
|
||||
{
|
||||
bot->SetSelection(olm->GetGUID());
|
||||
return Attack(olm);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3a: Krosh (ranged only)
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return Attack(krosh);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3b: Kiggler
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
if (kiggler && kiggler->IsAlive())
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return Attack(kiggler);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 4: Maulgar
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (maulgar && maulgar->IsAlive())
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
|
||||
if (bot->GetTarget() != maulgar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(maulgar->GetGUID());
|
||||
return Attack(maulgar);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
{
|
||||
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
||||
const float maxDistanceFromFight = 50.0f;
|
||||
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
||||
|
||||
if (distToFight > maxDistanceFromFight)
|
||||
{
|
||||
float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x);
|
||||
float destX = fightCenter.x + 40.0f * cos(angle);
|
||||
float destY = fightCenter.y + 40.0f * sin(angle);
|
||||
float destZ = fightCenter.z;
|
||||
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run away from Maulgar during Whirlwind (logic for after all other ogres are dead)
|
||||
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
const float safeDistance = 10.0f;
|
||||
float distance = bot->GetExactDist2d(maulgar);
|
||||
|
||||
if (distance < safeDistance)
|
||||
{
|
||||
float angle = atan2(bot->GetPositionY() - maulgar->GetPositionY(),
|
||||
bot->GetPositionX() - maulgar->GetPositionX());
|
||||
float destX = maulgar->GetPositionX() + safeDistance * cos(angle);
|
||||
float destY = maulgar->GetPositionY() + safeDistance * sin(angle);
|
||||
float destZ = bot->GetPositionZ();
|
||||
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
float destDist = maulgar->GetExactDist2d(destX, destY);
|
||||
|
||||
if (destDist >= safeDistance - 0.1f)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
std::vector<Unit*> felStalkers;
|
||||
for (auto const& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_WILD_FEL_STALKER && unit->IsAlive())
|
||||
felStalkers.push_back(unit);
|
||||
}
|
||||
|
||||
std::vector<Player*> warlocks;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member))
|
||||
warlocks.push_back(member);
|
||||
}
|
||||
|
||||
int warlockIndex = -1;
|
||||
for (size_t i = 0; i < warlocks.size(); ++i)
|
||||
{
|
||||
if (warlocks[i] == bot)
|
||||
{
|
||||
warlockIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||
{
|
||||
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
||||
if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true))
|
||||
return botAI->CastSpell("banish", assignedFelStalker);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye
|
||||
// Hunter 2: Misdirect Blindeye to second offtank
|
||||
bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
std::vector<Player*> hunters;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
|
||||
hunters.push_back(member);
|
||||
}
|
||||
|
||||
int hunterIndex = -1;
|
||||
for (size_t i = 0; i < hunters.size(); ++i)
|
||||
{
|
||||
if (hunters[i] == bot)
|
||||
{
|
||||
hunterIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
Player* olmTank = nullptr;
|
||||
Player* blindeyeTank = nullptr;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member;
|
||||
else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member;
|
||||
}
|
||||
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
botAI->CastSpell("misdirection", olmTank);
|
||||
if (bot->HasAura(SPELL_MISDIRECTION))
|
||||
{
|
||||
Pet* pet = bot->GetPet();
|
||||
if (pet && pet->IsAlive() && pet->GetVictim() != blindeye)
|
||||
{
|
||||
pet->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||
pet->AttackStop();
|
||||
pet->SetTarget(blindeye->GetGUID());
|
||||
if (pet->GetCharmInfo())
|
||||
{
|
||||
pet->GetCharmInfo()->SetIsCommandAttack(true);
|
||||
pet->GetCharmInfo()->SetIsAtStay(false);
|
||||
pet->GetCharmInfo()->SetIsFollowing(false);
|
||||
pet->GetCharmInfo()->SetIsCommandFollow(false);
|
||||
pet->GetCharmInfo()->SetIsReturning(false);
|
||||
}
|
||||
pet->ToCreature()->AI()->AttackStart(blindeye);
|
||||
}
|
||||
return botAI->CastSpell("steady shot", olm);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
botAI->CastSpell("misdirection", blindeyeTank);
|
||||
if (bot->HasAura(SPELL_MISDIRECTION))
|
||||
return botAI->CastSpell("steady shot", blindeye);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gruul the Dragonkiller Actions
|
||||
|
||||
// Position in center of the room
|
||||
bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event)
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
if (bot->GetVictim() != gruul)
|
||||
return Attack(gruul);
|
||||
|
||||
if (gruul->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float step = std::min(maxDistance, distanceToTankPosition);
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||
const float moveZ = tankPosition.z;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(),
|
||||
gruul->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(gruul))
|
||||
{
|
||||
return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ranged will take initial positions around the middle of the room, 25-40 yards from center
|
||||
// Ranged should spread out 10 yards from each other
|
||||
bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
static std::unordered_map<ObjectGuid, Position> initialPositions;
|
||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||
{
|
||||
initialPositions.clear();
|
||||
hasReachedInitialPosition.clear();
|
||||
}
|
||||
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float centerX = tankPosition.x;
|
||||
const float centerY = tankPosition.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
const float minRadius = 25.0f;
|
||||
const float maxRadius = 40.0f;
|
||||
|
||||
std::vector<Player*> members;
|
||||
Player* closestMember = nullptr;
|
||||
float closestDist = std::numeric_limits<float>::max();
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
members.push_back(member);
|
||||
if (member != bot)
|
||||
{
|
||||
float dist = bot->GetExactDist2d(member);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
closestMember = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialPositions.count(bot->GetGUID()))
|
||||
{
|
||||
auto it = std::find(members.begin(), members.end(), bot);
|
||||
uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0;
|
||||
uint8 count = members.size();
|
||||
|
||||
float angle = 2 * M_PI * botIndex / count;
|
||||
float radius = minRadius + static_cast<float>(rand()) /
|
||||
static_cast<float>(RAND_MAX) * (maxRadius - minRadius);
|
||||
float targetX = centerX + radius * cos(angle);
|
||||
float targetY = centerY + radius * sin(angle);
|
||||
|
||||
initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ);
|
||||
hasReachedInitialPosition[bot->GetGUID()] = false;
|
||||
}
|
||||
|
||||
Position targetPosition = initialPositions[bot->GetGUID()];
|
||||
if (!hasReachedInitialPosition[bot->GetGUID()])
|
||||
{
|
||||
if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f))
|
||||
{
|
||||
float destX = targetPosition.GetPositionX();
|
||||
float destY = targetPosition.GetPositionY();
|
||||
float destZ = targetPosition.GetPositionZ();
|
||||
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(),
|
||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||
}
|
||||
|
||||
const float minSpreadDistance = 10.0f;
|
||||
const float movementThreshold = 2.0f;
|
||||
|
||||
if (closestMember && closestDist < minSpreadDistance - movementThreshold)
|
||||
{
|
||||
return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(),
|
||||
closestMember->GetPositionZ()), minSpreadDistance, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to get away from other group members when Ground Slam is cast
|
||||
bool GruulTheDragonkillerShatterSpreadAction::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
Unit* closestMember = nullptr;
|
||||
float closestDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || bot->GetGUID() == member)
|
||||
continue;
|
||||
|
||||
const float dist = bot->GetExactDist2d(unit);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
closestMember = unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (closestMember)
|
||||
{
|
||||
return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(),
|
||||
closestMember->GetPositionZ()), 6.0f, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
112
src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h
Normal file
112
src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
class HighKingMaulgarMainTankAttackMaulgarAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarMainTankAttackMaulgarAction(PlayerbotAI* botAI, std::string const name = "high king maulgar main tank attack maulgar") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarFirstAssistTankAttackOlmAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarFirstAssistTankAttackOlmAction(PlayerbotAI* botAI, std::string const name = "high king maulgar first assist tank attack olm") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarSecondAssistTankAttackBlindeyeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarSecondAssistTankAttackBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar second assist tank attack blindeye") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarMageTankAttackKroshAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarMageTankAttackKroshAction(PlayerbotAI* botAI, std::string const name = "high king maulgar mage tank attack krosh") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarMoonkinTankAttackKigglerAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarMoonkinTankAttackKigglerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar moonkin tank attack kiggler") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarAssignDPSPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "high king maulgar assign dps priority") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarHealerFindSafePositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarHealerFindSafePositionAction(PlayerbotAI* botAI, std::string const name = "high king maulgar healer find safe position") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarRunAwayFromWhirlwindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarRunAwayFromWhirlwindAction(PlayerbotAI* botAI, std::string const name = "high king maulgar run away from whirlwind") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarBanishFelstalkerAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarBanishFelstalkerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar banish felstalker") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarMisdirectOlmAndBlindeyeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarMisdirectOlmAndBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar misdirect olm and blindeye") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerSpreadRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller spread ranged") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerShatterSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerShatterSpreadAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller shatter spread") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
110
src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp
Normal file
110
src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "RaidGruulsLairMultipliers.h"
|
||||
#include "RaidGruulsLairActions.h"
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "DruidCatActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
|
||||
static bool IsChargeAction(Action* action)
|
||||
{
|
||||
return dynamic_cast<CastChargeAction*>(action) ||
|
||||
dynamic_cast<CastInterceptAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeCatAction*>(action);
|
||||
}
|
||||
|
||||
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Don't run back in during Whirlwind
|
||||
float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
(!kiggler || !kiggler->IsAlive()) &&
|
||||
(!krosh || !krosh->IsAlive()) &&
|
||||
(!olm || !olm->IsAlive()) &&
|
||||
(!blindeye || !blindeye->IsAlive()))
|
||||
{
|
||||
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Arcane Shot will remove Spell Shield, which the mage tank needs to survive
|
||||
float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
|
||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast<CastArcaneShotAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float HighKingMaulgarDisableMageTankAOEMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (IsKroshMageTank(botAI, bot) &&
|
||||
(dynamic_cast<CastFrostNovaAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||
dynamic_cast<CastConeOfColdAction*>(action) || dynamic_cast<CastFlamestrikeAction*>(action) ||
|
||||
dynamic_cast<CastDragonsBreathAction*>(action) || dynamic_cast<CastBlastWaveAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float GruulTheDragonkillerMainTankMovementMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (!gruul)
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (gruul->GetVictim() == bot && dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (!gruul)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||
{
|
||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||
IsChargeAction(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class HighKingMaulgarDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarDisableTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable tank assist multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarAvoidWhirlwindMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar avoid whirlwind multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarDisableArcaneShotOnKroshMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarDisableArcaneShotOnKroshMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable arcane shot on krosh multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarDisableMageTankAOEMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarDisableMageTankAOEMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable mage tank aoe multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerMainTankMovementMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerMainTankMovementMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller main tank movement multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerGroundSlamMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerGroundSlamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller ground slam multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
49
src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h
Normal file
49
src/Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H
|
||||
|
||||
#include "RaidGruulsLairActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidGruulsLairActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidGruulsLairActionContext()
|
||||
{
|
||||
// High King Maulgar
|
||||
creators["high king maulgar main tank attack maulgar"] = &RaidGruulsLairActionContext::high_king_maulgar_main_tank_attack_maulgar;
|
||||
creators["high king maulgar first assist tank attack olm"] = &RaidGruulsLairActionContext::high_king_maulgar_first_assist_tank_attack_olm;
|
||||
creators["high king maulgar second assist tank attack blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_second_assist_tank_attack_blindeye;
|
||||
creators["high king maulgar mage tank attack krosh"] = &RaidGruulsLairActionContext::high_king_maulgar_mage_tank_attack_krosh;
|
||||
creators["high king maulgar moonkin tank attack kiggler"] = &RaidGruulsLairActionContext::high_king_maulgar_moonkin_tank_attack_kiggler;
|
||||
creators["high king maulgar assign dps priority"] = &RaidGruulsLairActionContext::high_king_maulgar_assign_dps_priority;
|
||||
creators["high king maulgar healer find safe position"] = &RaidGruulsLairActionContext::high_king_maulgar_healer_find_safe_position;
|
||||
creators["high king maulgar run away from whirlwind"] = &RaidGruulsLairActionContext::high_king_maulgar_run_away_from_whirlwind;
|
||||
creators["high king maulgar banish felstalker"] = &RaidGruulsLairActionContext::high_king_maulgar_banish_felstalker;
|
||||
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss;
|
||||
creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
||||
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
||||
}
|
||||
|
||||
private:
|
||||
// High King Maulgar
|
||||
static Action* high_king_maulgar_main_tank_attack_maulgar(PlayerbotAI* botAI) { return new HighKingMaulgarMainTankAttackMaulgarAction(botAI); }
|
||||
static Action* high_king_maulgar_first_assist_tank_attack_olm(PlayerbotAI* botAI) { return new HighKingMaulgarFirstAssistTankAttackOlmAction(botAI); }
|
||||
static Action* high_king_maulgar_second_assist_tank_attack_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarSecondAssistTankAttackBlindeyeAction(botAI); }
|
||||
static Action* high_king_maulgar_mage_tank_attack_krosh(PlayerbotAI* botAI) { return new HighKingMaulgarMageTankAttackKroshAction(botAI); }
|
||||
static Action* high_king_maulgar_moonkin_tank_attack_kiggler(PlayerbotAI* botAI) { return new HighKingMaulgarMoonkinTankAttackKigglerAction(botAI); }
|
||||
static Action* high_king_maulgar_assign_dps_priority(PlayerbotAI* botAI) { return new HighKingMaulgarAssignDPSPriorityAction(botAI); }
|
||||
static Action* high_king_maulgar_healer_find_safe_position(PlayerbotAI* botAI) { return new HighKingMaulgarHealerFindSafePositionAction(botAI); }
|
||||
static Action* high_king_maulgar_run_away_from_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarRunAwayFromWhirlwindAction(botAI); }
|
||||
static Action* high_king_maulgar_banish_felstalker(PlayerbotAI* botAI) { return new HighKingMaulgarBanishFelstalkerAction(botAI); }
|
||||
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); }
|
||||
static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
||||
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
49
src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h
Normal file
49
src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidGruulsLairTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class RaidGruulsLairTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidGruulsLairTriggerContext() : NamedObjectContext<Trigger>()
|
||||
{
|
||||
// High King Maulgar
|
||||
creators["high king maulgar is main tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_main_tank;
|
||||
creators["high king maulgar is first assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_first_assist_tank;
|
||||
creators["high king maulgar is second assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_second_assist_tank;
|
||||
creators["high king maulgar is mage tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_mage_tank;
|
||||
creators["high king maulgar is moonkin tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_moonkin_tank;
|
||||
creators["high king maulgar determining kill order"] = &RaidGruulsLairTriggerContext::high_king_maulgar_determining_kill_order;
|
||||
creators["high king maulgar healer in danger"] = &RaidGruulsLairTriggerContext::high_king_maulgar_healer_in_danger;
|
||||
creators["high king maulgar boss channeling whirlwind"] = &RaidGruulsLairTriggerContext::high_king_maulgar_boss_channeling_whirlwind;
|
||||
creators["high king maulgar wild felstalker spawned"] = &RaidGruulsLairTriggerContext::high_king_maulgar_wild_felstalker_spawned;
|
||||
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank;
|
||||
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
||||
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
||||
}
|
||||
|
||||
private:
|
||||
// High King Maulgar
|
||||
static Trigger* high_king_maulgar_is_main_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMainTankTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_is_first_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsFirstAssistTankTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_is_second_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsSecondAssistTankTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_is_mage_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMageTankTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_is_moonkin_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMoonkinTankTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_determining_kill_order(PlayerbotAI* botAI) { return new HighKingMaulgarDeterminingKillOrderTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_healer_in_danger(PlayerbotAI* botAI) { return new HighKingMaulgarHealerInDangerTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_boss_channeling_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarBossChannelingWhirlwindTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_wild_felstalker_spawned(PlayerbotAI* botAI) { return new HighKingMaulgarWildFelstalkerSpawnedTrigger(botAI); }
|
||||
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
56
src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp
Normal file
56
src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "RaidGruulsLairStrategy.h"
|
||||
#include "RaidGruulsLairMultipliers.h"
|
||||
|
||||
void RaidGruulsLairStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// High King Maulgar
|
||||
triggers.push_back(new TriggerNode("high king maulgar is main tank", {
|
||||
NextAction("high king maulgar main tank attack maulgar", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar is first assist tank", {
|
||||
NextAction("high king maulgar first assist tank attack olm", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar is second assist tank", {
|
||||
NextAction("high king maulgar second assist tank attack blindeye", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar is mage tank", {
|
||||
NextAction("high king maulgar mage tank attack krosh", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar is moonkin tank", {
|
||||
NextAction("high king maulgar moonkin tank attack kiggler", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar determining kill order", {
|
||||
NextAction("high king maulgar assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar healer in danger", {
|
||||
NextAction("high king maulgar healer find safe position", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar boss channeling whirlwind", {
|
||||
NextAction("high king maulgar run away from whirlwind", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar wild felstalker spawned", {
|
||||
NextAction("high king maulgar banish felstalker", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("high king maulgar pulling olm and blindeye", {
|
||||
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", {
|
||||
NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", {
|
||||
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
||||
NextAction("gruul the dragonkiller shatter spread", ACTION_EMERGENCY + 6) }));
|
||||
}
|
||||
|
||||
void RaidGruulsLairStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new HighKingMaulgarDisableTankAssistMultiplier(botAI));
|
||||
multipliers.push_back(new HighKingMaulgarAvoidWhirlwindMultiplier(botAI));
|
||||
multipliers.push_back(new HighKingMaulgarDisableArcaneShotOnKroshMultiplier(botAI));
|
||||
multipliers.push_back(new HighKingMaulgarDisableMageTankAOEMultiplier(botAI));
|
||||
multipliers.push_back(new GruulTheDragonkillerMainTankMovementMultiplier(botAI));
|
||||
multipliers.push_back(new GruulTheDragonkillerGroundSlamMultiplier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h
Normal file
18
src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidGruulsLairStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidGruulsLairStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "gruulslair"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
160
src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp
Normal file
160
src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "RaidGruulsLairTriggers.h"
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
|
||||
// High King Maulgar Triggers
|
||||
|
||||
bool HighKingMaulgarIsMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive();
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive();
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive();
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
|
||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive();
|
||||
}
|
||||
|
||||
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
||||
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
||||
}
|
||||
|
||||
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsHeal(bot) && IsAnyOgreBossAlive(botAI);
|
||||
}
|
||||
|
||||
bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
!botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
||||
|
||||
return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group || bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
std::vector<Player*> hunters;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
|
||||
hunters.push_back(member);
|
||||
}
|
||||
|
||||
int hunterIndex = -1;
|
||||
for (size_t i = 0; i < hunters.size(); ++i)
|
||||
{
|
||||
if (hunters[i] == bot)
|
||||
{
|
||||
hunterIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hunterIndex == -1 || hunterIndex > 1)
|
||||
return false;
|
||||
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
Player* olmTank = nullptr;
|
||||
Player* blindeyeTank = nullptr;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member;
|
||||
else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member;
|
||||
}
|
||||
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
||||
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
||||
|
||||
case 1:
|
||||
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
||||
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gruul the Dragonkiller Triggers
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() &&
|
||||
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||
}
|
||||
97
src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h
Normal file
97
src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
class HighKingMaulgarIsMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarIsFirstAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarIsFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is first assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarIsSecondAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarIsSecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is second assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarIsMageTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarIsMageTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is mage tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarIsMoonkinTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarIsMoonkinTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is moonkin tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarDeterminingKillOrderTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar determining kill order") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarHealerInDangerTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarHealerInDangerTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar healers in danger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarBossChannelingWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarBossChannelingWhirlwindTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar boss channeling whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarWildFelstalkerSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarWildFelstalkerSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar wild felstalker spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HighKingMaulgarPullingOlmAndBlindeyeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HighKingMaulgarPullingOlmAndBlindeyeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar pulling olm and blindeye") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerIncomingShatterTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerIncomingShatterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller incoming shatter") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
241
src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp
Normal file
241
src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "AiFactory.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
// Olm does not chase properly due to the Core's caster movement issues
|
||||
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
||||
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
||||
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||
const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
||||
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
||||
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
||||
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
||||
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
||||
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
||||
}
|
||||
|
||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
||||
{
|
||||
const char* ogreBossNames[] =
|
||||
{
|
||||
"high king maulgar",
|
||||
"kiggler the crazed",
|
||||
"krosh firehand",
|
||||
"olm the summoner",
|
||||
"blindeye the seer"
|
||||
};
|
||||
|
||||
for (const char* name : ogreBossNames)
|
||||
{
|
||||
Unit* boss = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", name)->Get();
|
||||
if (!boss || !boss->IsAlive())
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
{
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMage = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_MAGE)
|
||||
{
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMage || hp > highestHp)
|
||||
{
|
||||
highestHpMage = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return highestHpMage == bot;
|
||||
}
|
||||
|
||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMoonkin = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_DRUID)
|
||||
{
|
||||
int tab = AiFactory::GetPlayerSpecTab(member);
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
{
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMoonkin || hp > highestHp)
|
||||
{
|
||||
highestHpMoonkin = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return highestHpMoonkin == bot;
|
||||
}
|
||||
|
||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos)
|
||||
{
|
||||
const float KROSH_SAFE_DISTANCE = 20.0f;
|
||||
const float MAULGAR_SAFE_DISTANCE = 10.0f;
|
||||
bool isSafe = true;
|
||||
|
||||
Unit* krosh = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "krosh firehand")->Get();
|
||||
if (krosh && krosh->IsAlive())
|
||||
{
|
||||
float dist = sqrt(pow(pos.GetPositionX() - krosh->GetPositionX(), 2) + pow(pos.GetPositionY() - krosh->GetPositionY(), 2));
|
||||
if (dist < KROSH_SAFE_DISTANCE)
|
||||
isSafe = false;
|
||||
}
|
||||
|
||||
Unit* maulgar = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "high king maulgar")->Get();
|
||||
if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive())
|
||||
{
|
||||
float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2));
|
||||
if (dist < MAULGAR_SAFE_DISTANCE)
|
||||
isSafe = false;
|
||||
}
|
||||
|
||||
return isSafe;
|
||||
}
|
||||
|
||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos)
|
||||
{
|
||||
const float SEARCH_RADIUS = 30.0f;
|
||||
const uint8 NUM_POSITIONS = 32;
|
||||
|
||||
outPos = { bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() };
|
||||
if (IsPositionSafe(botAI, bot, outPos))
|
||||
{
|
||||
outPos = Position();
|
||||
return false;
|
||||
}
|
||||
|
||||
float bestScore = std::numeric_limits<float>::max();
|
||||
bool foundSafeSpot = false;
|
||||
Position bestPos;
|
||||
|
||||
for (int i = 0; i < NUM_POSITIONS; ++i)
|
||||
{
|
||||
float angle = 2 * M_PI * i / NUM_POSITIONS;
|
||||
Position candidatePos;
|
||||
candidatePos.m_positionX = bot->GetPositionX() + SEARCH_RADIUS * cos(angle);
|
||||
candidatePos.m_positionY = bot->GetPositionY() + SEARCH_RADIUS * sin(angle);
|
||||
candidatePos.m_positionZ = bot->GetPositionZ();
|
||||
|
||||
float destX = candidatePos.m_positionX, destY = candidatePos.m_positionY, destZ = candidatePos.m_positionZ;
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), destX, destY, destZ, true))
|
||||
continue;
|
||||
|
||||
if (destX != candidatePos.m_positionX || destY != candidatePos.m_positionY)
|
||||
continue;
|
||||
|
||||
candidatePos.m_positionX = destX;
|
||||
candidatePos.m_positionY = destY;
|
||||
candidatePos.m_positionZ = destZ;
|
||||
|
||||
if (IsPositionSafe(botAI, bot, candidatePos))
|
||||
{
|
||||
float movementDistance = sqrt(pow(destX - bot->GetPositionX(), 2) + pow(destY - bot->GetPositionY(), 2));
|
||||
if (movementDistance < bestScore)
|
||||
{
|
||||
bestScore = movementDistance;
|
||||
bestPos = candidatePos;
|
||||
foundSafeSpot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSafeSpot)
|
||||
{
|
||||
outPos = bestPos;
|
||||
return true;
|
||||
}
|
||||
|
||||
outPos = Position();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
62
src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h
Normal file
62
src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef RAID_GRUULSLAIRHELPERS_H
|
||||
#define RAID_GRUULSLAIRHELPERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
enum GruulsLairSpells
|
||||
{
|
||||
// High King Maulgar
|
||||
SPELL_WHIRLWIND = 33238,
|
||||
|
||||
// Krosh Firehand
|
||||
SPELL_SPELL_SHIELD = 33054,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647, // Rank 2
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
SPELL_GROUND_SLAM_1 = 33525,
|
||||
SPELL_GROUND_SLAM_2 = 39187,
|
||||
};
|
||||
|
||||
enum GruulsLairNPCs
|
||||
{
|
||||
NPC_WILD_FEL_STALKER = 18847,
|
||||
};
|
||||
|
||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
extern const Location MaulgarTankPosition;
|
||||
extern const Location OlmTankPosition;
|
||||
extern const Location BlindeyeTankPosition;
|
||||
extern const Location KroshTankPosition;
|
||||
extern const Location MaulgarRoomCenter;
|
||||
extern const Location GruulTankPosition;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
9338
src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp
Normal file
9338
src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
669
src/Ai/Raid/Icecrown/Action/RaidIccActions.h
Normal file
669
src/Ai/Raid/Icecrown/Action/RaidIccActions.h
Normal file
@@ -0,0 +1,669 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCACTIONS_H
|
||||
#define _PLAYERBOT_RAIDICCACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "AttackAction.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "RaidIccStrategy.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Trigger.h"
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Vehicle.h"
|
||||
#include "RaidIccTriggers.h"
|
||||
|
||||
const Position ICC_LM_TANK_POSITION = Position(-391.0f, 2259.0f, 42.0f);
|
||||
const Position ICC_DARK_RECKONING_SAFE_POSITION = Position(-523.33386f, 2211.2031f, 62.823116f);
|
||||
const Position ICC_LDW_TANK_POSTION = Position(-570.1f, 2211.2456f, 49.476616f); //-590.0f
|
||||
const Position ICC_ROTTING_FROST_GIANT_TANK_POSITION = Position(-328.5085f, 2225.5142f, 199.97298f);
|
||||
const Position ICC_GUNSHIP_TELEPORT_ALLY = Position (-370.04645f, 1993.3536f, 466.65656f);
|
||||
const Position ICC_GUNSHIP_TELEPORT_ALLY2 = Position (-392.66208f, 2064.893f, 466.5672f, 5.058196f);
|
||||
const Position ICC_GUNSHIP_TELEPORT_HORDE = Position (-449.5343f, 2477.2024f, 470.17648f);
|
||||
const Position ICC_GUNSHIP_TELEPORT_HORDE2 = Position (-429.81586f, 2400.6804f, 471.56537f);
|
||||
const Position ICC_DBS_TANK_POSITION = Position(-494.26517f, 2211.549f, 541.11414f);
|
||||
const Position ICC_FESTERGUT_TANK_POSITION = Position(4269.1772f, 3144.7673f, 360.38577f);
|
||||
const Position ICC_FESTERGUT_RANGED_SPORE = Position(4261.143f, 3109.4146f, 360.38605f);
|
||||
const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360.38577f);
|
||||
const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f);
|
||||
const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38623f);
|
||||
const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.51843f);
|
||||
const Position ICC_ROTFACE_CENTER_POSITION = Position(4446.0547f, 3144.8677f, 360.38593f); //actual center 4.74089 4445.6616f, 3137.1526f, 360.38608
|
||||
const Position ICC_PUTRICIDE_TANK_POSITION = Position(4373.227f, 3222.058f, 389.4029f);
|
||||
const Position ICC_PUTRICIDE_GREEN_POSITION = Position(4423.4126f, 3194.2715f, 389.37683f);
|
||||
const Position ICC_PUTRICIDE_BAD_POSITION = Position(4356.1724f, 3261.5232f, 389.3985f);
|
||||
//const Position ICC_PUTRICIDE_GAS3_POSITION = Position(4367.753f, 3177.5894f, 389.39575f);
|
||||
//const Position ICC_PUTRICIDE_GAS4_POSITION = Position(4321.8486f, 3206.464f, 389.3982f);
|
||||
const Position ICC_BPC_OT_POSITION = Position(4649.2236f, 2796.0972f, 361.1815f);
|
||||
const Position ICC_BPC_MT_POSITION = Position(4648.5674f, 2744.847f, 361.18222f);
|
||||
const Position ICC_BQL_CENTER_POSITION = Position(4595.0f, 2769.0f, 400.0f);
|
||||
const Position ICC_BQL_LWALL1_POSITION = Position(4624.685f, 2789.4895f, 400.13834f);
|
||||
const Position ICC_BQL_LWALL2_POSITION = Position(4600.749f, 2805.7568f, 400.1374f);
|
||||
const Position ICC_BQL_LWALL3_POSITION = Position(4572.857f, 2797.3872f, 400.1374f);
|
||||
const Position ICC_BQL_RWALL1_POSITION = Position(4625.724f, 2748.9917f, 400.13693f);
|
||||
const Position ICC_BQL_RWALL2_POSITION = Position(4608.3774f, 2735.7466f, 400.13693f);
|
||||
const Position ICC_BQL_RWALL3_POSITION = Position(4576.813f, 2739.6067f, 400.13693f);
|
||||
const Position ICC_BQL_LRWALL4_POSITION = Position(4539.345f, 2769.3853f, 403.7267f);
|
||||
const Position ICC_BQL_TANK_POSITION = Position(4629.746f, 2769.6396f, 401.7479f); //old just in front of stairs 4616.102f, 2768.9167f, 400.13797f
|
||||
const Position ICC_VDW_HEAL_POSITION = Position(4203.752f, 2483.4343f, 364.87274f);
|
||||
const Position ICC_VDW_GROUP1_POSITION = Position(4203.585f, 2464.422f, 364.86887f);
|
||||
const Position ICC_VDW_GROUP2_POSITION = Position(4203.5806f, 2505.2383f, 364.87677f);
|
||||
const Position ICC_VDW_PORTALSTART_POSITION = Position(4202.637f, 2488.171f, 375.00256f);
|
||||
const Position ICC_SINDRAGOSA_TANK_POSITION = Position(4408.016f, 2508.0647f, 203.37955f);
|
||||
const Position ICC_SINDRAGOSA_FLYING_POSITION = Position(4525.6f, 2485.15f, 245.082f);
|
||||
const Position ICC_SINDRAGOSA_RANGED_POSITION = Position(4441.572f, 2484.482f, 203.37836f);
|
||||
const Position ICC_SINDRAGOSA_MELEE_POSITION = Position(4423.4546f, 2491.7175f, 203.37686f);
|
||||
const Position ICC_SINDRAGOSA_BLISTERING_COLD_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f);
|
||||
const Position ICC_SINDRAGOSA_THOMB1_POSITION = Position(4433.6484f, 2469.4133f, 203.3806f);
|
||||
const Position ICC_SINDRAGOSA_THOMB2_POSITION = Position(4434.143f, 2486.201f, 203.37473f);
|
||||
const Position ICC_SINDRAGOSA_THOMB3_POSITION = Position(4436.1147f, 2501.464f, 203.38266f);
|
||||
const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC1_POSITION = Position(4444.9707f, 2455.7322f, 203.38701f);
|
||||
const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC2_POSITION = Position(4461.3945f, 2463.5513f, 203.38727f);
|
||||
const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC3_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f);
|
||||
const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC4_POSITION = Position(4459.9336f, 2507.409f, 203.38606f);
|
||||
const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC5_POSITION = Position(4442.3096f, 2512.4688f, 203.38647f);
|
||||
const Position ICC_SINDRAGOSA_CENTER_POSITION = Position(4408.0464f, 2484.478f, 203.37529f);
|
||||
const Position ICC_SINDRAGOSA_THOMBMB2_POSITION = Position(4436.895f, 2498.1401f, 203.38133f);
|
||||
const Position ICC_SINDRAGOSA_FBOMB_POSITION = Position(4449.3647f, 2486.4524f, 203.379f);
|
||||
const Position ICC_SINDRAGOSA_FBOMB10_POSITION = Position(4449.3647f, 2486.4524f, 203.379f);
|
||||
const Position ICC_SINDRAGOSA_LOS2_POSITION = Position(4441.8286f, 2501.946f, 203.38435f);
|
||||
const Position ICC_LICH_KING_ADDS_POSITION = Position(476.7332f, -2095.3894f, 840.857f); // old 486.63647f, -2095.7915f, 840.857f
|
||||
const Position ICC_LICH_KING_MELEE_POSITION = Position(503.5546f, -2106.8213f, 840.857f);
|
||||
const Position ICC_LICH_KING_RANGED_POSITION = Position(501.3563f, -2085.1816f, 840.857f);
|
||||
const Position ICC_LICH_KING_ASSISTHC_POSITION = Position(517.2145f, -2125.0674f, 840.857f);
|
||||
const Position ICC_LK_FROST1_POSITION = Position(503.96548f, -2183.216f, 840.857f);
|
||||
const Position ICC_LK_FROST2_POSITION = Position(563.07166f, -2125.7578f, 840.857f);
|
||||
const Position ICC_LK_FROST3_POSITION = Position(503.40182f, -2067.3435f, 840.857f);
|
||||
const Position ICC_LK_FROSTR1_POSITION = Position(481.168f, -2177.8723f, 840.857f);
|
||||
const Position ICC_LK_FROSTR2_POSITION = Position(562.20807f, -2100.2393f, 840.857f);
|
||||
const Position ICC_LK_FROSTR3_POSITION = Position(526.35297f, -2071.0317f, 840.857f);
|
||||
|
||||
//Lord Marrogwar
|
||||
class IccLmTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccLmTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc lm tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool MoveTowardPosition(const Position& position, float incrementSize);
|
||||
};
|
||||
|
||||
class IccSpikeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSpikeAction(PlayerbotAI* botAI) : AttackAction(botAI, "icc spike") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HandleSpikeTargeting(Unit* boss);
|
||||
bool MoveTowardPosition(const Position& position, float incrementSize);
|
||||
void UpdateRaidTargetIcon(Unit* target);
|
||||
};
|
||||
|
||||
//Lady Deathwhisper
|
||||
class IccDarkReckoningAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccDarkReckoningAction(PlayerbotAI* botAI, std::string const name = "icc dark reckoning")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccRangedPositionLadyDeathwhisperAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccRangedPositionLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc ranged position lady deathwhisper")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool MaintainRangedSpacing();
|
||||
};
|
||||
|
||||
class IccAddsLadyDeathwhisperAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccAddsLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc adds lady deathwhisper")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool IsTargetedByShade(uint32 shadeEntry);
|
||||
bool MoveTowardPosition(const Position& position, float incrementSize);
|
||||
bool HandleAddTargeting(Unit* boss);
|
||||
void UpdateRaidTargetIcon(Unit* target);
|
||||
|
||||
};
|
||||
|
||||
class IccShadeLadyDeathwhisperAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccShadeLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc shade lady deathwhisper")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
//Gunship Battle
|
||||
class IccRottingFrostGiantTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccRottingFrostGiantTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotting frost giant tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccCannonFireAction : public Action
|
||||
{
|
||||
public:
|
||||
IccCannonFireAction(PlayerbotAI* botAI, std::string const name = "icc cannon fire")
|
||||
: Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Unit* FindValidCannonTarget();
|
||||
bool TryCastCannonSpell(uint32 spellId, Unit* target, Unit* vehicleBase);
|
||||
};
|
||||
|
||||
class IccGunshipEnterCannonAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccGunshipEnterCannonAction(PlayerbotAI* botAI, std::string const name = "icc gunship enter cannon")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool EnterVehicle(Unit* vehicleBase, bool moveIfFar);
|
||||
Unit* FindBestAvailableCannon();
|
||||
bool IsValidCannon(Unit* vehicle, const uint32 validEntries[]);
|
||||
};
|
||||
|
||||
class IccGunshipTeleportAllyAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccGunshipTeleportAllyAction(PlayerbotAI* botAI, std::string const name = "icc gunship teleport ally")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool TeleportTo(const Position& position);
|
||||
void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX);
|
||||
void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX);
|
||||
};
|
||||
|
||||
class IccGunshipTeleportHordeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccGunshipTeleportHordeAction(PlayerbotAI* botAI, std::string const name = "icc gunship teleport horde")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool TeleportTo(const Position& position);
|
||||
void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX);
|
||||
void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX);
|
||||
};
|
||||
|
||||
//DBS
|
||||
class IccDbsTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccDbsTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc dbs tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool CrowdControlBloodBeasts();
|
||||
bool EvadeBloodBeasts();
|
||||
bool PositionInRangedFormation();
|
||||
};
|
||||
|
||||
class IccAddsDbsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccAddsDbsAction(PlayerbotAI* botAI, std::string const name = "icc adds dbs")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Unit* FindPriorityTarget(Unit* boss);
|
||||
void UpdateSkullMarker(Unit* priorityTarget);
|
||||
};
|
||||
|
||||
//FESTERGUT
|
||||
class IccFestergutGroupPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccFestergutGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc festergut group position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HasSporesInGroup();
|
||||
bool PositionNonTankMembers();
|
||||
int CalculatePositionIndex(Group* group);
|
||||
};
|
||||
|
||||
class IccFestergutSporeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccFestergutSporeAction(PlayerbotAI* botAI, std::string const name = "icc festergut spore")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Position CalculateSpreadPosition();
|
||||
struct SporeInfo
|
||||
{
|
||||
std::vector<Unit*> sporedPlayers;
|
||||
ObjectGuid lowestGuid;
|
||||
bool hasLowestGuid = false;
|
||||
};
|
||||
SporeInfo FindSporedPlayers();
|
||||
Position DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos);
|
||||
bool CheckMainTankSpore();
|
||||
};
|
||||
|
||||
//Rotface
|
||||
class IccRotfaceTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccRotfaceTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void MarkBossWithSkull(Unit* boss);
|
||||
bool PositionMainTankAndMelee(Unit *boss);
|
||||
bool HandleAssistTankPositioning(Unit* boss);
|
||||
bool HandleBigOozePositioning(Unit* boss);
|
||||
};
|
||||
|
||||
class IccRotfaceGroupPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccRotfaceGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface group position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
//bool MoveAwayFromBigOoze(Unit* bigOoze);
|
||||
bool HandlePuddleAvoidance(Unit* boss);
|
||||
bool MoveAwayFromPuddle(Unit* boss, Unit* puddle, float puddleDistance);
|
||||
bool HandleOozeTargeting();
|
||||
bool HandleOozeMemberPositioning();
|
||||
bool PositionRangedAndHealers(Unit* boss,Unit* smallOoze);
|
||||
bool FindAndMoveFromClosestMember(Unit* boss, Unit* smallOoze);
|
||||
};
|
||||
|
||||
class IccRotfaceMoveAwayFromExplosionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccRotfaceMoveAwayFromExplosionAction(PlayerbotAI* botAI, std::string const name = "icc rotface move away from explosion")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool MoveToRandomSafeLocation();
|
||||
|
||||
};
|
||||
|
||||
//PP
|
||||
class IccPutricideGrowingOozePuddleAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccPutricideGrowingOozePuddleAction(PlayerbotAI* botAI, std::string const name = "icc putricide growing ooze puddle")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Unit* FindClosestThreateningPuddle();
|
||||
Position CalculateSafeMovePosition(Unit* closestPuddle);
|
||||
bool IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle);
|
||||
};
|
||||
|
||||
class IccPutricideVolatileOozeAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccPutricideVolatileOozeAction(PlayerbotAI* botAI, std::string const name = "icc putricide volatile ooze")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void MarkOozeWithSkull(Unit* ooze);
|
||||
Unit* FindAuraTarget();
|
||||
};
|
||||
|
||||
class IccPutricideGasCloudAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccPutricideGasCloudAction(PlayerbotAI* botAI, std::string const name = "icc putricide gas cloud")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HandleGaseousBloatMovement(Unit* gasCloud);
|
||||
Position CalculateEmergencyPosition(const Position& botPos, float dx, float dy);
|
||||
bool FindSafeMovementPosition(const Position& botPos, const Position& cloudPos, float dx, float dy, int numAngles,
|
||||
Position& resultPos);
|
||||
bool HandleGroupAuraSituation(Unit* gasCloud);
|
||||
bool GroupHasGaseousBloat(Group* group);
|
||||
};
|
||||
|
||||
class IccPutricideAvoidMalleableGooAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccPutricideAvoidMalleableGooAction(PlayerbotAI* botAI, std::string const name = "icc putricide avoid malleable goo")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HandleTankPositioning(Unit* boss);
|
||||
bool HandleUnboundPlague(Unit* boss);
|
||||
bool HandleBossPositioning(Unit* boss);
|
||||
Position CalculateBossPosition(Unit* boss, float distance);
|
||||
bool HasObstacleBetween(const Position& from, const Position& to);
|
||||
bool IsOnPath(const Position& from, const Position& to, const Position& point, float threshold);
|
||||
Position CalculateArcPoint(const Position& current, const Position& target, const Position& center);
|
||||
Position CalculateIncrementalMove(const Position& current, const Position& target, float maxDistance);
|
||||
};
|
||||
|
||||
//BPC
|
||||
class IccBpcKelesethTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccBpcKelesethTankAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc bpc keleseth tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccBpcMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccBpcMainTankAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc bpc main tank") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void MarkEmpoweredPrince();
|
||||
};
|
||||
|
||||
class IccBpcEmpoweredVortexAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccBpcEmpoweredVortexAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc bpc empowered vortex") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool MaintainRangedSpacing();
|
||||
bool HandleEmpoweredVortexSpread();
|
||||
};
|
||||
|
||||
class IccBpcKineticBombAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccBpcKineticBombAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc bpc kinetic bomb") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Unit* FindOptimalKineticBomb();
|
||||
bool IsBombAlreadyHandled(Unit* bomb, Group* group);
|
||||
};
|
||||
|
||||
class IccBpcBallOfFlameAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccBpcBallOfFlameAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc bpc ball of flame") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
//Blood Queen Lana'thel
|
||||
class IccBqlGroupPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccBqlGroupPositionAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc group tank position") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura);
|
||||
bool HandleShadowsMovement();
|
||||
Position AdjustControlPoint(const Position& wall, const Position& center, float factor);
|
||||
Position CalculateBezierPoint(float t, const Position path[4]);
|
||||
bool HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura);
|
||||
|
||||
private:
|
||||
// Evaluate curves
|
||||
struct CurveInfo
|
||||
{
|
||||
Position moveTarget;
|
||||
int curveIdx = 0;
|
||||
bool foundSafe = false;
|
||||
float minDist = 0.0f;
|
||||
float score = 0.0f;
|
||||
Position closestPoint;
|
||||
float t_closest = 0.0f;
|
||||
};
|
||||
};
|
||||
|
||||
class IccBqlPactOfDarkfallenAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccBqlPactOfDarkfallenAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc bql pact of darkfallen") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void CalculateCenterPosition(Position& targetPos, const std::vector<Player*>& playersWithAura);
|
||||
bool MoveToTargetPosition(const Position& targetPos, int auraCount);
|
||||
};
|
||||
|
||||
class IccBqlVampiricBiteAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccBqlVampiricBiteAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc bql vampiric bite") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
Player* FindBestBiteTarget(Group* group);
|
||||
bool IsInvalidTarget(Player* player);
|
||||
bool MoveTowardsTarget(Player* target);
|
||||
bool CastVampiricBite(Player* target);
|
||||
};
|
||||
|
||||
// Sister Svalna
|
||||
class IccValkyreSpearAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccValkyreSpearAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc valkyre spear") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSisterSvalnaAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSisterSvalnaAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc sister svalna") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Valithria Dreamwalker
|
||||
|
||||
class IccValithriaGroupAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccValithriaGroupAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc valithria group") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool MoveTowardsPosition(const Position& pos, float increment);
|
||||
bool Handle25ManGroupLogic();
|
||||
bool HandleMarkingLogic(bool inGroup1, bool inGroup2, const Position& group1Pos, const Position& group2Pos);
|
||||
bool Handle10ManGroupLogic();
|
||||
};
|
||||
|
||||
class IccValithriaPortalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccValithriaPortalAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc valithria portal") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccValithriaHealAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccValithriaHealAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc valithria heal") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccValithriaDreamCloudAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccValithriaDreamCloudAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc valithria dream cloud") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
//Sindragosa
|
||||
class IccSindragosaGroupPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaGroupPositionAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc sindragosa group position") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool HandleTankPositioning(Unit* boss);
|
||||
bool HandleNonTankPositioning();
|
||||
bool MoveIncrementallyToPosition(const Position& targetPos, float maxStep);
|
||||
};
|
||||
|
||||
class IccSindragosaFrostBeaconAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaFrostBeaconAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc sindragosa frost beacon") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void HandleSupportActions();
|
||||
bool HandleBeaconedPlayer(const Unit* boss);
|
||||
bool HandleNonBeaconedPlayer(const Unit* boss);
|
||||
bool MoveToPositionIfNeeded(const Position& position, float tolerance);
|
||||
bool MoveToPosition(const Position& position);
|
||||
bool IsBossFlying(const Unit* boss);
|
||||
|
||||
private:
|
||||
static constexpr uint32 FROST_BEACON_AURA_ID = SPELL_FROST_BEACON;
|
||||
static constexpr uint32 HAND_OF_FREEDOM_SPELL_ID = 1044;
|
||||
static constexpr float POSITION_TOLERANCE = 1.0f;
|
||||
static constexpr float TOMB_POSITION_TOLERANCE = 0.5f;
|
||||
static constexpr float MIN_SAFE_DISTANCE = 13.0f;
|
||||
static constexpr float MOVE_TOLERANCE = 2.0f;
|
||||
};
|
||||
|
||||
class IccSindragosaBlisteringColdAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaBlisteringColdAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc sindragosa blistering cold") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSindragosaUnchainedMagicAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaUnchainedMagicAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc sindragosa unchained magic") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSindragosaChilledToTheBoneAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaChilledToTheBoneAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc sindragosa chilled to the bone") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSindragosaMysticBuffetAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaMysticBuffetAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc sindragosa mystic buffet") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSindragosaFrostBombAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaFrostBombAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc sindragosa frost bomb") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccSindragosaTankSwapPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccSindragosaTankSwapPositionAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "sindragosa tank swap position") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
//LK
|
||||
class IccLichKingShadowTrapAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccLichKingShadowTrapAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc lich king shadow trap") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccLichKingNecroticPlagueAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IccLichKingNecroticPlagueAction(PlayerbotAI* botAI)
|
||||
: MovementAction(botAI, "icc lich king necrotic plague") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class IccLichKingWinterAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccLichKingWinterAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc lich king winter") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void HandlePositionCorrection();
|
||||
bool IsValidCollectibleAdd(Unit* unit);
|
||||
bool IsPositionSafeFromDefile(float x, float y, float z, float minSafeDistance);
|
||||
void HandleTankPositioning();
|
||||
void HandleMeleePositioning();
|
||||
void HandleRangedPositioning();
|
||||
void HandleMainTankAddManagement(Unit* boss, const Position* tankPos);
|
||||
void HandleAssistTankAddManagement(Unit* boss, const Position* tankPos);
|
||||
|
||||
private:
|
||||
const Position* GetMainTankPosition();
|
||||
const Position* GetMainTankRangedPosition();
|
||||
bool TryMoveToPosition(float targetX, float targetY, float targetZ, bool isForced = true);
|
||||
};
|
||||
|
||||
class IccLichKingAddsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IccLichKingAddsAction(PlayerbotAI* botAI)
|
||||
: AttackAction(botAI, "icc lich king adds") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
void HandleTeleportationFixes(Difficulty diff, Unit* terenasMenethilHC);
|
||||
bool HandleSpiritBombAvoidance(Difficulty diff, Unit* terenasMenethilHC);
|
||||
void HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenasMenethilHC);
|
||||
void HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenasMenethilHC);
|
||||
bool HandleQuakeMechanics(Unit* boss);
|
||||
void HandleShamblingHorrors(Unit* boss, bool hasPlague);
|
||||
bool HandleAssistTankAddManagement(Unit* boss, Difficulty diff);
|
||||
void HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff);
|
||||
void HandleMainTankTargeting(Unit* boss, Difficulty diff);
|
||||
void HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague);
|
||||
void HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff);
|
||||
void HandleDefileMechanics(Unit* boss, Difficulty diff);
|
||||
void HandleValkyrMechanics(Difficulty diff);
|
||||
std::vector<size_t> CalculateBalancedGroupSizes(size_t totalAssist, size_t numValkyrs);
|
||||
size_t GetAssignedValkyrIndex(size_t assistIndex, const std::vector<size_t>& groupSizes);
|
||||
std::string GetRTIValueForValkyr(size_t valkyrIndex);
|
||||
void HandleValkyrMarking(const std::vector<Unit*>& grabbingValkyrs, Difficulty diff);
|
||||
void HandleValkyrAssignment(const std::vector<Unit*>& grabbingValkyrs);
|
||||
void ApplyCCToValkyr(Unit* valkyr);
|
||||
bool IsValkyr(Unit* unit);
|
||||
void HandleVileSpiritMechanics();
|
||||
};
|
||||
|
||||
#endif
|
||||
875
src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp
Normal file
875
src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp
Normal file
@@ -0,0 +1,875 @@
|
||||
#include "RaidIccMultipliers.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 "RaidIccActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RaidIccTriggers.h"
|
||||
|
||||
// LK global variables
|
||||
namespace
|
||||
{
|
||||
uint32 g_lastPlagueTime = 0;
|
||||
bool g_plagueAllowedToCure = false;
|
||||
std::map<ObjectGuid, uint32> g_plagueTimes;
|
||||
std::map<ObjectGuid, bool> g_allowCure;
|
||||
std::mutex g_plagueMutex; // Lock before accessing shared variables
|
||||
}
|
||||
|
||||
// Lady Deathwhisper
|
||||
float IccLadyDeathwhisperMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action) || dynamic_cast<FollowAction*>(action) || dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE;
|
||||
|
||||
// Get the nearest hostile NPCs
|
||||
const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
|
||||
// Allow the IccShadeLadyDeathwhisperAction to run
|
||||
if (dynamic_cast<IccShadeLadyDeathwhisperAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* shade = botAI->GetUnit(npcGuid);
|
||||
|
||||
if (!shade || shade->GetEntry() != VENGEFUL_SHADE_ID)
|
||||
continue;
|
||||
|
||||
if (!shade->GetVictim() || shade->GetVictim()->GetGUID() != bot->GetGUID())
|
||||
continue;
|
||||
|
||||
return 0.0f; // Cancel all other actions when we need to handle Vengeful Shade
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// dbs
|
||||
float IccAddsDbsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastHurricaneAction*>(action) ||
|
||||
dynamic_cast<CastVolleyAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<FanOfKnivesAction*>(action) ||
|
||||
dynamic_cast<CastWhirlwindAction*>(action) || dynamic_cast<CastMindSearAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsRanged(bot))
|
||||
if (dynamic_cast<ReachSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = botAI->GetAura("rune of blood", bot);
|
||||
if (aura)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// dogs
|
||||
float IccDogsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
bool bossPresent = false;
|
||||
if (AI_VALUE2(Unit*, "find target", "stinky") || AI_VALUE2(Unit*, "find target", "precious"))
|
||||
bossPresent = true;
|
||||
|
||||
if (!bossPresent)
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = botAI->GetAura("mortal wound", bot, false, true);
|
||||
if (aura && aura->GetStackAmount() >= 8)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Festergut
|
||||
float IccFestergutMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "festergut");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = botAI->GetAura("gastric bloat", bot, false, true);
|
||||
if (aura && aura->GetStackAmount() >= 6)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamic_cast<IccFestergutSporeAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_GAS_SPORE))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Rotface
|
||||
float IccRotfaceMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss1 = AI_VALUE2(Unit*, "find target", "rotface");
|
||||
if (!boss1)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action) && !(bot->getClass() == CLASS_HUNTER))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsAssistTank(bot) && (dynamic_cast<AttackRtiTargetAction*>(action) || dynamic_cast<TankAssistAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "big ooze");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
static std::map<ObjectGuid, uint32> lastExplosionTimes;
|
||||
static std::map<ObjectGuid, bool> hasMoved;
|
||||
|
||||
ObjectGuid botGuid = bot->GetGUID();
|
||||
|
||||
// When cast starts, record the time
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION))
|
||||
{
|
||||
if (lastExplosionTimes[botGuid] == 0) // Only set if not already set
|
||||
{
|
||||
lastExplosionTimes[botGuid] = time(nullptr);
|
||||
hasMoved[botGuid] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If explosion cast is no longer active, reset the timers
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION))
|
||||
{
|
||||
if (lastExplosionTimes[botGuid] > 0 && time(nullptr) - lastExplosionTimes[botGuid] >= 16)
|
||||
{
|
||||
lastExplosionTimes[botGuid] = 0;
|
||||
hasMoved[botGuid] = false;
|
||||
return 1.0f; // Allow normal actions to resume
|
||||
}
|
||||
}
|
||||
|
||||
// If 9 seconds have passed since cast start and we haven't moved yet
|
||||
if (lastExplosionTimes[botGuid] > 0 && !hasMoved[botGuid] && time(nullptr) - lastExplosionTimes[botGuid] >= 9)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action)
|
||||
&& !dynamic_cast<IccRotfaceMoveAwayFromExplosionAction*>(action))
|
||||
{
|
||||
return 0.0f; // Block other movement actions
|
||||
}
|
||||
hasMoved[botGuid] = true; // Mark that we've initiated movement
|
||||
}
|
||||
|
||||
// Continue blocking other movements for 7 seconds after moving
|
||||
if (hasMoved[botGuid] && time(nullptr) - lastExplosionTimes[botGuid] < 16 // 9 seconds wait + 7 seconds stay
|
||||
&& dynamic_cast<MovementAction*>(action)
|
||||
&& !dynamic_cast<IccRotfaceMoveAwayFromExplosionAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// pp
|
||||
float IccAddsPutricideMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot);
|
||||
bool hasUnboundPlague = botAI->HasAura("Unbound Plague", bot);
|
||||
|
||||
if (!(bot->getClass() == CLASS_HUNTER) && dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastDisengageAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = botAI->GetAura("mutated plague", bot, false, true);
|
||||
if (aura && aura->GetStackAmount() >= 4)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasGaseousBloat)
|
||||
{
|
||||
if (dynamic_cast<IccPutricideGasCloudAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<IccPutricideGrowingOozePuddleAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Gaseous Bloat
|
||||
}
|
||||
|
||||
if (hasUnboundPlague && boss && !boss->HealthBelowPct(35))
|
||||
{
|
||||
if (dynamic_cast<IccPutricideAvoidMalleableGooAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Unbound Plague
|
||||
}
|
||||
|
||||
if (dynamic_cast<IccPutricideVolatileOozeAction*>(action))
|
||||
{
|
||||
if (dynamic_cast<IccPutricideAvoidMalleableGooAction*>(action))
|
||||
return 0.0f;
|
||||
if (dynamic_cast<IccPutricideGrowingOozePuddleAction*>(action) && !botAI->IsMainTank(bot))
|
||||
return 0.0f;
|
||||
//if (dynamic_cast<IccPutricideGasCloudAction*>(action) && !hasGaseousBloat)
|
||||
//return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// bpc
|
||||
float IccBpcAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth");
|
||||
if (!keleseth)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastHurricaneAction*>(action) ||
|
||||
dynamic_cast<CastVolleyAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<FanOfKnivesAction*>(action) ||
|
||||
dynamic_cast<CastWhirlwindAction*>(action) || dynamic_cast<CastMindSearAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true);
|
||||
if (aura)
|
||||
{
|
||||
if (aura->GetStackAmount() > 18 && botAI->IsTank(bot))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (aura->GetStackAmount() > 12 && !botAI->IsTank(bot))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar");
|
||||
if (!valanar)
|
||||
return 1.0f;
|
||||
|
||||
if (valanar && valanar->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
(valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX1) ||
|
||||
valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX2) ||
|
||||
valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX3) ||
|
||||
valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX4)))
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action) || dynamic_cast<IccBpcEmpoweredVortexAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Empowered Vortex
|
||||
}
|
||||
|
||||
Unit* flame1 = bot->FindNearestCreature(NPC_BALL_OF_FLAME, 100.0f);
|
||||
Unit* flame2 = bot->FindNearestCreature(NPC_BALL_OF_INFERNO_FLAME, 100.0f);
|
||||
bool ballOfFlame = flame1 && flame1->GetVictim() == bot;
|
||||
bool infernoFlame = flame2 && flame2->GetVictim() == bot;
|
||||
|
||||
if (flame2)
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action) || dynamic_cast<IccBpcKineticBombAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<IccBpcBallOfFlameAction*>(action))
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (ballOfFlame || infernoFlame)
|
||||
{
|
||||
// If bot is tank, do nothing special
|
||||
if (dynamic_cast<IccBpcBallOfFlameAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Ball of Flame
|
||||
}
|
||||
|
||||
static const std::array<uint32, 4> bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3,
|
||||
NPC_KINETIC_BOMB4};
|
||||
const GuidVector bombs = AI_VALUE(GuidVector, "possible targets no los");
|
||||
|
||||
bool bombFound = false;
|
||||
|
||||
for (const auto entry : bombEntries)
|
||||
{
|
||||
for (auto const& guid : bombs)
|
||||
{
|
||||
if (Unit* unit = botAI->GetUnit(guid))
|
||||
{
|
||||
if (unit->GetEntry() == entry)
|
||||
{
|
||||
// Check if bomb is within valid Z-axis range
|
||||
if (unit->GetPositionZ() - bot->GetPositionZ() < 25.0f)
|
||||
{
|
||||
bombFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bombFound)
|
||||
break;
|
||||
}
|
||||
|
||||
if (bombFound && !(aura && aura->GetStackAmount() > 12) && !botAI->IsTank(bot))
|
||||
{
|
||||
// If kinetic bomb action is active, disable these actions
|
||||
if (dynamic_cast<IccBpcKineticBombAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<AttackRtiTargetAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// For assist tank during BPC fight
|
||||
if (botAI->IsAssistTank(bot) && !(aura && aura->GetStackAmount() > 18))
|
||||
{
|
||||
// Allow BPC-specific actions
|
||||
if (dynamic_cast<IccBpcKelesethTankAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
// Disable normal assist behavior
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<AttackRtiTargetAction*>(action) ||
|
||||
dynamic_cast<CastConsecrationAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//BQL
|
||||
float IccBqlMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
Aura* aura2 = botAI->GetAura("Swarming Shadows", bot);
|
||||
Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot);
|
||||
|
||||
if (botAI->IsRanged(bot))
|
||||
if (dynamic_cast<AvoidAoeAction*>(action) || dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<CastDisengageAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
// If bot has Pact of Darkfallen aura, return 0 for all other actions
|
||||
if (bot->HasAura(SPELL_PACT_OF_THE_DARKFALLEN))
|
||||
{
|
||||
if (dynamic_cast<IccBqlPactOfDarkfallenAction*>(action))
|
||||
return 1.0f; // Allow Pact of Darkfallen action
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Pact of Darkfallen
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot) && ((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && !aura)
|
||||
{
|
||||
if (dynamic_cast<IccBqlGroupPositionAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// If bot has frenzied bloodthirst, allow highest priority for bite action
|
||||
if (aura) // If bot has frenzied bloodthirst
|
||||
{
|
||||
if (dynamic_cast<IccBqlVampiricBiteAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (aura2 && !aura)
|
||||
{
|
||||
if (dynamic_cast<IccBqlGroupPositionAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Swarming Shadows
|
||||
}
|
||||
|
||||
if ((boss->GetExactDist2d(ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY()) > 10.0f) &&
|
||||
botAI->IsRanged(bot) && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f))
|
||||
{
|
||||
if (dynamic_cast<FleeAction*>(action) || dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//VDW
|
||||
float IccValithriaDreamCloudMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f);
|
||||
|
||||
Aura* twistedNightmares = botAI->GetAura("Twisted Nightmares", bot);
|
||||
Aura* emeraldVigor = botAI->GetAura("Emerald Vigor", bot);
|
||||
|
||||
if (!boss && !bot->HasAura(SPELL_DREAM_STATE))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FollowAction*>(action) || dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsTank(bot))
|
||||
{
|
||||
if (dynamic_cast<AttackRtiTargetAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsHeal(bot) && (twistedNightmares || emeraldVigor))
|
||||
if (dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<AttackRtiTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_DREAM_STATE) && !bot->HealthBelowPct(50))
|
||||
{
|
||||
if (dynamic_cast<IccValithriaDreamCloudAction*>(action))
|
||||
return 1.0f; // Allow Dream Cloud action
|
||||
else
|
||||
return 0.0f; // Cancel all other actions when we need to handle Dream Cloud
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
|
||||
}
|
||||
|
||||
//SINDRAGOSA
|
||||
|
||||
float IccSindragosaMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f);
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true);
|
||||
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
|
||||
if (boss->HealthBelowPct(95))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) || dynamic_cast<CastStarfallAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (aura && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) &&
|
||||
!dynamic_cast<IccSindragosaFrostBombAction*>(action))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) || dynamic_cast<IccSindragosaUnchainedMagicAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Check if boss is casting blistering cold (using both normal and heroic spell IDs)
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
(boss->FindCurrentSpellBySpellId(70123) || boss->FindCurrentSpellBySpellId(71047) ||
|
||||
boss->FindCurrentSpellBySpellId(71048) || boss->FindCurrentSpellBySpellId(71049)))
|
||||
{
|
||||
// If this is the blistering cold action, give it highest priority
|
||||
if (dynamic_cast<IccSindragosaBlisteringColdAction*>(action) ||
|
||||
dynamic_cast<HealPartyMemberAction*>(action) ||
|
||||
dynamic_cast<ReachPartyMemberToHealAction*>(action) ||
|
||||
dynamic_cast<IccSindragosaTankSwapPositionAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
// Disable all other actions while blistering cold is casting
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Highest priority if we have beacon
|
||||
if (bot->HasAura(SPELL_FROST_BEACON))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaFrostBeaconAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
// Check if anyone in group has Frost Beacon (SPELL_FROST_BEACON)
|
||||
bool anyoneHasFrostBeacon = false;
|
||||
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON))
|
||||
{
|
||||
anyoneHasFrostBeacon = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyoneHasFrostBeacon && boss &&
|
||||
boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(),
|
||||
ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f &&
|
||||
!boss->HealthBelowPct(25) && !boss->HealthAbovePct(99))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaFrostBeaconAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (anyoneHasFrostBeacon && !botAI->IsMainTank(bot))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaGroupPositionAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = botAI->GetAura("mystic buffet", bot, false, true);
|
||||
if (aura && aura->GetStackAmount() >= 6)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (!botAI->IsTank(bot) && boss && boss->HealthBelowPct(35))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaGroupPositionAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (boss && botAI->IsTank(bot))
|
||||
{
|
||||
if (boss->HealthBelowPct(35))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaTankSwapPositionAction*>(action) || dynamic_cast<TankFaceAction*>(action) ||
|
||||
dynamic_cast<AttackAction*>(action) || dynamic_cast<MovementAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (boss && boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f && !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99))
|
||||
{
|
||||
if (dynamic_cast<IccSindragosaFrostBombAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FollowAction*>(action) || dynamic_cast<IccSindragosaBlisteringColdAction*>(action) ||
|
||||
dynamic_cast<IccSindragosaChilledToTheBoneAction*>(action) || dynamic_cast<IccSindragosaMysticBuffetAction*>(action) ||
|
||||
dynamic_cast<IccSindragosaFrostBeaconAction*>(action) || dynamic_cast<IccSindragosaUnchainedMagicAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) || dynamic_cast<CastDisengageAction*>(action) || dynamic_cast<PetAttackAction*>(action) ||
|
||||
dynamic_cast<IccSindragosaGroupPositionAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastHurricaneAction*>(action) ||
|
||||
dynamic_cast<CastVolleyAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<FanOfKnivesAction*>(action) ||
|
||||
dynamic_cast<CastWhirlwindAction*>(action) || dynamic_cast<CastMindSearAction*>(action) ||
|
||||
dynamic_cast<CastMagmaTotemAction*>(action) || dynamic_cast<CastConsecrationAction*>(action) ||
|
||||
dynamic_cast<CastFlamestrikeAction*>(action) || dynamic_cast<CastExplosiveTrapAction*>(action) ||
|
||||
dynamic_cast<CastExplosiveShotAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float IccLichKingAddsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f);
|
||||
|
||||
if (!terenasMenethilHC)
|
||||
if (dynamic_cast<CastStarfallAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (terenasMenethilHC)
|
||||
{
|
||||
Unit* mainTank = AI_VALUE(Unit*, "main tank");
|
||||
|
||||
if (!botAI->IsMainTank(bot) && mainTank && bot->GetExactDist2d(mainTank->GetPositionX(), mainTank->GetPositionY()) < 2.0f)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot) || (bot->getClass() == CLASS_WARLOCK))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) || dynamic_cast<IccLichKingAddsAction*>(action))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) || dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) || dynamic_cast<CastChargeAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeBearAction*>(action) || dynamic_cast<CastIceBlockAction*>(action) ||
|
||||
dynamic_cast<CastRevivePetAction*>(action) || dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
// Handle cure actions
|
||||
if (dynamic_cast<CurePartyMemberAction*>(action) || dynamic_cast<CastCleanseDiseaseAction*>(action) ||
|
||||
dynamic_cast<CastCleanseDiseaseOnPartyAction*>(action) ||
|
||||
dynamic_cast<CastCleanseSpiritCurseOnPartyAction*>(action) || dynamic_cast<CastCleanseSpiritAction*>(action))
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
|
||||
// Check if any bot in the group has plague
|
||||
bool anyBotHasPlague = false;
|
||||
ObjectGuid plaguedPlayerGuid; // Track who has plague
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
if (Player* member = ref->GetSource())
|
||||
{
|
||||
if (botAI->HasAura("Necrotic Plague", member))
|
||||
{
|
||||
anyBotHasPlague = true;
|
||||
plaguedPlayerGuid = member->GetGUID(); // Changed from GetObjectGuid()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 currentTime = getMSTime();
|
||||
|
||||
// Reset state if no one has plague
|
||||
if (!anyBotHasPlague)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_plagueMutex); // Properly initialized
|
||||
g_plagueTimes.clear();
|
||||
g_allowCure.clear();
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
{ // New scope for lock_guard
|
||||
std::lock_guard<std::mutex> lock(g_plagueMutex); // Properly initialized
|
||||
|
||||
// Start timer if this is a new plague
|
||||
if (g_plagueTimes.find(plaguedPlayerGuid) == g_plagueTimes.end())
|
||||
{
|
||||
g_plagueTimes[plaguedPlayerGuid] = currentTime;
|
||||
g_allowCure[plaguedPlayerGuid] = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Once we allow cure, keep allowing it until plague is gone
|
||||
if (g_allowCure[plaguedPlayerGuid])
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Check if enough time has passed (2,5 seconds)
|
||||
if (currentTime - g_plagueTimes[plaguedPlayerGuid] >= 2500)
|
||||
{
|
||||
g_allowCure[plaguedPlayerGuid] = true;
|
||||
return 1.0f;
|
||||
}
|
||||
} // lock_guard is automatically released here
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action) && (bot->getClass() != CLASS_HUNTER))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) || dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) || dynamic_cast<CastDisengageAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (boss && !boss->HealthBelowPct(71))
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
if (dynamic_cast<CastConsecrationAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastHurricaneAction*>(action) ||
|
||||
dynamic_cast<CastVolleyAction*>(action) || dynamic_cast<CastBlizzardAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<FanOfKnivesAction*>(action) ||
|
||||
dynamic_cast<CastWhirlwindAction*>(action) || dynamic_cast<CastMindSearAction*>(action) ||
|
||||
dynamic_cast<CastMagmaTotemAction*>(action) || dynamic_cast<CastFlamestrikeAction*>(action) ||
|
||||
dynamic_cast<CastExplosiveTrapAction*>(action) || dynamic_cast<CastExplosiveShotAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
|
||||
bool hasWinterAura = false;
|
||||
if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) ||
|
||||
boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4)))
|
||||
hasWinterAura = true;
|
||||
|
||||
bool hasWinter2Aura = false;
|
||||
if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) ||
|
||||
boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8)))
|
||||
hasWinter2Aura = true;
|
||||
|
||||
bool isCasting = false;
|
||||
if (boss && boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
isCasting = true;
|
||||
|
||||
bool isWinter = false;
|
||||
if (boss && (boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) ||
|
||||
boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8)))
|
||||
isWinter = true;
|
||||
|
||||
if (hasWinterAura || hasWinter2Aura || (isCasting && isWinter))
|
||||
{
|
||||
if (dynamic_cast<IccLichKingWinterAction*>(action) || dynamic_cast<SetFacingTargetAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsAssistTank(bot) && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<IccLichKingAddsAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (currentTarget && boss && bot->GetDistance2d(boss->GetPositionX(), boss->GetPositionY()) > 50.0f && currentTarget == boss)
|
||||
{
|
||||
if (dynamic_cast<AttackRtiTargetAction*>(action) || dynamic_cast<ReachSpellAction*>(action) ||
|
||||
dynamic_cast<ReachMeleeAction*>(action) || dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action) || dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<MovementAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (currentTarget && (currentTarget->GetEntry() == NPC_ICE_SPHERE1 || currentTarget->GetEntry() == NPC_ICE_SPHERE2 ||
|
||||
currentTarget->GetEntry() == NPC_ICE_SPHERE3 || currentTarget->GetEntry() == NPC_ICE_SPHERE4))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) || dynamic_cast<ReachMeleeAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (botAI->IsRanged(bot) && !botAI->GetAura("Harvest Soul", bot, false, false))
|
||||
{
|
||||
// Check for defile presence
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
bool defilePresent = false;
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) // Defile entry
|
||||
{
|
||||
defilePresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only disable movement if defile is present
|
||||
if (defilePresent && (
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<MoveRandomAction*>(action) ||
|
||||
dynamic_cast<MoveFromGroupAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (botAI->IsAssistTank(bot) && boss && !boss->HealthBelowPct(71) && currentTarget == boss)
|
||||
{
|
||||
if (dynamic_cast<AttackRtiTargetAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
102
src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.h
Normal file
102
src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDICCMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
//Lady Deathwhisper
|
||||
class IccLadyDeathwhisperMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccLadyDeathwhisperMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc lady deathwhisper") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//DBS
|
||||
class IccAddsDbsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccAddsDbsMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc adds dbs") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//DOGS
|
||||
|
||||
class IccDogsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccDogsMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc dogs") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//FESTERGUT
|
||||
class IccFestergutMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccFestergutMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc festergut") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//ROTFACE
|
||||
class IccRotfaceMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccRotfaceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc rotface") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
/*class IccRotfaceGroupPositionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccRotfaceGroupPositionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc rotface group position") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};*/
|
||||
|
||||
//PP
|
||||
class IccAddsPutricideMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccAddsPutricideMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc adds putricide") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//BPC
|
||||
class IccBpcAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccBpcAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "icc bpc assist") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//BQL
|
||||
class IccBqlMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccBqlMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "icc bql multiplier") {}
|
||||
virtual float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
//VDW
|
||||
class IccValithriaDreamCloudMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccValithriaDreamCloudMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc valithria dream cloud") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//SINDRAGOSA
|
||||
class IccSindragosaMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccSindragosaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc sindragosa") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//LK
|
||||
class IccLichKingAddsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IccLichKingAddsMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc lich king adds") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
139
src/Ai/Raid/Icecrown/RaidIccActionContext.h
Normal file
139
src/Ai/Raid/Icecrown/RaidIccActionContext.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDICCACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidIccActions.h"
|
||||
|
||||
class RaidIccActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidIccActionContext()
|
||||
{
|
||||
creators["icc lm tank position"] = &RaidIccActionContext::icc_lm_tank_position;
|
||||
creators["icc spike"] = &RaidIccActionContext::icc_spike;
|
||||
|
||||
creators["icc dark reckoning"] = &RaidIccActionContext::icc_dark_reckoning;
|
||||
creators["icc ranged position lady deathwhisper"] = &RaidIccActionContext::icc_ranged_position_lady_deathwhisper;
|
||||
creators["icc adds lady deathwhisper"] = &RaidIccActionContext::icc_adds_lady_deathwhisper;
|
||||
creators["icc shade lady deathwhisper"] = &RaidIccActionContext::icc_shade_lady_deathwhisper;
|
||||
|
||||
creators["icc rotting frost giant tank position"] = &RaidIccActionContext::icc_rotting_frost_giant_tank_position;
|
||||
creators["icc cannon fire"] = &RaidIccActionContext::icc_cannon_fire;
|
||||
creators["icc gunship enter cannon"] = &RaidIccActionContext::icc_gunship_enter_cannon;
|
||||
creators["icc gunship teleport ally"] = &RaidIccActionContext::icc_gunship_teleport_ally;
|
||||
creators["icc gunship teleport horde"] = &RaidIccActionContext::icc_gunship_teleport_horde;
|
||||
|
||||
creators["icc dbs tank position"] = &RaidIccActionContext::icc_dbs_tank_position;
|
||||
creators["icc adds dbs"] = &RaidIccActionContext::icc_adds_dbs;
|
||||
|
||||
creators["icc festergut group position"] = &RaidIccActionContext::icc_festergut_group_position;
|
||||
creators["icc festergut spore"] = &RaidIccActionContext::icc_festergut_spore;
|
||||
|
||||
creators["icc rotface tank position"] = &RaidIccActionContext::icc_rotface_tank_position;
|
||||
creators["icc rotface group position"] = &RaidIccActionContext::icc_rotface_group_position;
|
||||
creators["icc rotface move away from explosion"] = &RaidIccActionContext::icc_rotface_move_away_from_explosion;
|
||||
|
||||
creators["icc putricide volatile ooze"] = &RaidIccActionContext::icc_putricide_volatile_ooze;
|
||||
creators["icc putricide gas cloud"] = &RaidIccActionContext::icc_putricide_gas_cloud;
|
||||
creators["icc putricide growing ooze puddle"] = &RaidIccActionContext::icc_putricide_growing_ooze_puddle;
|
||||
creators["icc putricide avoid malleable goo"] = &RaidIccActionContext::icc_putricide_avoid_malleable_goo;
|
||||
|
||||
creators["icc bpc keleseth tank"] = &RaidIccActionContext::icc_bpc_keleseth_tank;
|
||||
creators["icc bpc main tank"] = &RaidIccActionContext::icc_bpc_main_tank;
|
||||
creators["icc bpc empowered vortex"] = &RaidIccActionContext::icc_bpc_empowered_vortex;
|
||||
creators["icc bpc kinetic bomb"] = &RaidIccActionContext::icc_bpc_kinetic_bomb;
|
||||
creators["icc bpc ball of flame"] = &RaidIccActionContext::icc_bpc_ball_of_flame;
|
||||
|
||||
creators["icc bql group position"] = &RaidIccActionContext::icc_bql_group_position;
|
||||
creators["icc bql pact of darkfallen"] = &RaidIccActionContext::icc_bql_pact_of_darkfallen;
|
||||
creators["icc bql vampiric bite"] = &RaidIccActionContext::icc_bql_vampiric_bite;
|
||||
|
||||
creators["icc valkyre spear"] = &RaidIccActionContext::icc_valkyre_spear;
|
||||
creators["icc sister svalna"] = &RaidIccActionContext::icc_sister_svalna;
|
||||
|
||||
creators["icc valithria group"] = &RaidIccActionContext::icc_valithria_group;
|
||||
creators["icc valithria portal"] = &RaidIccActionContext::icc_valithria_portal;
|
||||
creators["icc valithria heal"] = &RaidIccActionContext::icc_valithria_heal;
|
||||
creators["icc valithria dream cloud"] = &RaidIccActionContext::icc_valithria_dream_cloud;
|
||||
|
||||
creators["icc sindragosa group position"] = &RaidIccActionContext::icc_sindragosa_group_position;
|
||||
creators["icc sindragosa frost beacon"] = &RaidIccActionContext::icc_sindragosa_frost_beacon;
|
||||
creators["icc sindragosa blistering cold"] = &RaidIccActionContext::icc_sindragosa_blistering_cold;
|
||||
creators["icc sindragosa unchained magic"] = &RaidIccActionContext::icc_sindragosa_unchained_magic;
|
||||
creators["icc sindragosa chilled to the bone"] = &RaidIccActionContext::icc_sindragosa_chilled_to_the_bone;
|
||||
creators["icc sindragosa mystic buffet"] = &RaidIccActionContext::icc_sindragosa_mystic_buffet;
|
||||
creators["icc sindragosa frost bomb"] = &RaidIccActionContext::icc_sindragosa_frost_bomb;
|
||||
creators["icc sindragosa tank swap position"] = &RaidIccActionContext::icc_sindragosa_tank_swap_position;
|
||||
|
||||
creators["icc lich king shadow trap"] = &RaidIccActionContext::icc_lich_king_shadow_trap;
|
||||
creators["icc lich king necrotic plague"] = &RaidIccActionContext::icc_lich_king_necrotic_plague;
|
||||
creators["icc lich king winter"] = &RaidIccActionContext::icc_lich_king_winter;
|
||||
creators["icc lich king adds"] = &RaidIccActionContext::icc_lich_king_adds;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* icc_lm_tank_position(PlayerbotAI* ai) { return new IccLmTankPositionAction(ai); }
|
||||
static Action* icc_spike(PlayerbotAI* ai) { return new IccSpikeAction(ai); }
|
||||
|
||||
static Action* icc_dark_reckoning(PlayerbotAI* ai) { return new IccDarkReckoningAction(ai); }
|
||||
static Action* icc_ranged_position_lady_deathwhisper(PlayerbotAI* ai) { return new IccRangedPositionLadyDeathwhisperAction(ai); }
|
||||
static Action* icc_adds_lady_deathwhisper(PlayerbotAI* ai) { return new IccAddsLadyDeathwhisperAction(ai); }
|
||||
static Action* icc_shade_lady_deathwhisper(PlayerbotAI* ai) { return new IccShadeLadyDeathwhisperAction(ai); }
|
||||
|
||||
static Action* icc_rotting_frost_giant_tank_position(PlayerbotAI* ai) { return new IccRottingFrostGiantTankPositionAction(ai); }
|
||||
static Action* icc_cannon_fire(PlayerbotAI* ai) { return new IccCannonFireAction(ai); }
|
||||
static Action* icc_gunship_enter_cannon(PlayerbotAI* ai) { return new IccGunshipEnterCannonAction(ai); }
|
||||
static Action* icc_gunship_teleport_ally(PlayerbotAI* ai) { return new IccGunshipTeleportAllyAction(ai); }
|
||||
static Action* icc_gunship_teleport_horde(PlayerbotAI* ai) { return new IccGunshipTeleportHordeAction(ai); }
|
||||
|
||||
static Action* icc_dbs_tank_position(PlayerbotAI* ai) { return new IccDbsTankPositionAction(ai); }
|
||||
static Action* icc_adds_dbs(PlayerbotAI* ai) { return new IccAddsDbsAction(ai); }
|
||||
|
||||
static Action* icc_festergut_group_position(PlayerbotAI* ai) { return new IccFestergutGroupPositionAction(ai); }
|
||||
static Action* icc_festergut_spore(PlayerbotAI* ai) { return new IccFestergutSporeAction(ai); }
|
||||
|
||||
static Action* icc_rotface_tank_position(PlayerbotAI* ai) { return new IccRotfaceTankPositionAction(ai); }
|
||||
static Action* icc_rotface_group_position(PlayerbotAI* ai) { return new IccRotfaceGroupPositionAction(ai); }
|
||||
static Action* icc_rotface_move_away_from_explosion(PlayerbotAI* ai) { return new IccRotfaceMoveAwayFromExplosionAction(ai); }
|
||||
|
||||
static Action* icc_putricide_volatile_ooze(PlayerbotAI* ai) { return new IccPutricideVolatileOozeAction(ai); }
|
||||
static Action* icc_putricide_gas_cloud(PlayerbotAI* ai) { return new IccPutricideGasCloudAction(ai); }
|
||||
static Action* icc_putricide_growing_ooze_puddle(PlayerbotAI* ai) { return new IccPutricideGrowingOozePuddleAction(ai); }
|
||||
static Action* icc_putricide_avoid_malleable_goo(PlayerbotAI* ai) { return new IccPutricideAvoidMalleableGooAction(ai); }
|
||||
|
||||
static Action* icc_bpc_keleseth_tank(PlayerbotAI* ai) { return new IccBpcKelesethTankAction(ai); }
|
||||
static Action* icc_bpc_main_tank(PlayerbotAI* ai) { return new IccBpcMainTankAction(ai); }
|
||||
static Action* icc_bpc_empowered_vortex(PlayerbotAI* ai) { return new IccBpcEmpoweredVortexAction(ai); }
|
||||
static Action* icc_bpc_kinetic_bomb(PlayerbotAI* ai) { return new IccBpcKineticBombAction(ai); }
|
||||
static Action* icc_bpc_ball_of_flame(PlayerbotAI* ai) { return new IccBpcBallOfFlameAction(ai); }
|
||||
|
||||
static Action* icc_bql_group_position(PlayerbotAI* ai) { return new IccBqlGroupPositionAction(ai); }
|
||||
static Action* icc_bql_pact_of_darkfallen(PlayerbotAI* ai) { return new IccBqlPactOfDarkfallenAction(ai); }
|
||||
static Action* icc_bql_vampiric_bite(PlayerbotAI* ai) { return new IccBqlVampiricBiteAction(ai); }
|
||||
|
||||
static Action* icc_valkyre_spear(PlayerbotAI* ai) { return new IccValkyreSpearAction(ai); }
|
||||
static Action* icc_sister_svalna(PlayerbotAI* ai) { return new IccSisterSvalnaAction(ai); }
|
||||
|
||||
static Action* icc_valithria_group(PlayerbotAI* ai) { return new IccValithriaGroupAction(ai); }
|
||||
static Action* icc_valithria_portal(PlayerbotAI* ai) { return new IccValithriaPortalAction(ai); }
|
||||
static Action* icc_valithria_heal(PlayerbotAI* ai) { return new IccValithriaHealAction(ai); }
|
||||
static Action* icc_valithria_dream_cloud(PlayerbotAI* ai) { return new IccValithriaDreamCloudAction(ai); }
|
||||
|
||||
static Action* icc_sindragosa_group_position(PlayerbotAI* ai) { return new IccSindragosaGroupPositionAction(ai); }
|
||||
static Action* icc_sindragosa_frost_beacon(PlayerbotAI* ai) { return new IccSindragosaFrostBeaconAction(ai); }
|
||||
static Action* icc_sindragosa_blistering_cold(PlayerbotAI* ai) { return new IccSindragosaBlisteringColdAction(ai); }
|
||||
static Action* icc_sindragosa_unchained_magic(PlayerbotAI* ai) { return new IccSindragosaUnchainedMagicAction(ai); }
|
||||
static Action* icc_sindragosa_chilled_to_the_bone(PlayerbotAI* ai) { return new IccSindragosaChilledToTheBoneAction(ai); }
|
||||
static Action* icc_sindragosa_mystic_buffet(PlayerbotAI* ai) { return new IccSindragosaMysticBuffetAction(ai); }
|
||||
static Action* icc_sindragosa_frost_bomb(PlayerbotAI* ai) { return new IccSindragosaFrostBombAction(ai); }
|
||||
static Action* icc_sindragosa_tank_swap_position(PlayerbotAI* ai) { return new IccSindragosaTankSwapPositionAction(ai); }
|
||||
|
||||
static Action* icc_lich_king_shadow_trap(PlayerbotAI* ai) { return new IccLichKingShadowTrapAction(ai); }
|
||||
static Action* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueAction(ai); }
|
||||
static Action* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterAction(ai); }
|
||||
static Action* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsAction(ai); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
6
src/Ai/Raid/Icecrown/RaidIccScripts.h
Normal file
6
src/Ai/Raid/Icecrown/RaidIccScripts.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCSCRIPTS_H
|
||||
#define _PLAYERBOT_RAIDICCSCRIPTS_H
|
||||
|
||||
#include "../../../../src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h"
|
||||
|
||||
#endif
|
||||
143
src/Ai/Raid/Icecrown/RaidIccTriggerContext.h
Normal file
143
src/Ai/Raid/Icecrown/RaidIccTriggerContext.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidIccTriggers.h"
|
||||
|
||||
class RaidIccTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidIccTriggerContext()
|
||||
{
|
||||
creators["icc lm"] = &RaidIccTriggerContext::icc_lm;
|
||||
|
||||
creators["icc dark reckoning"] = &RaidIccTriggerContext::icc_dark_reckoning;
|
||||
creators["icc lady deathwhisper"] = &RaidIccTriggerContext::icc_lady_deathwhisper;
|
||||
|
||||
creators["icc rotting frost giant tank position"] = &RaidIccTriggerContext::icc_rotting_frost_giant_tank_position;
|
||||
creators["icc in cannon"] = &RaidIccTriggerContext::icc_in_cannon;
|
||||
creators["icc gunship cannon near"] = &RaidIccTriggerContext::icc_gunship_cannon_near;
|
||||
creators["icc gunship teleport ally"] = &RaidIccTriggerContext::icc_gunship_teleport_ally;
|
||||
creators["icc gunship teleport horde"] = &RaidIccTriggerContext::icc_gunship_teleport_horde;
|
||||
|
||||
creators["icc dbs"] = &RaidIccTriggerContext::icc_dbs;
|
||||
creators["icc dbs main tank rune of blood"] = &RaidIccTriggerContext::icc_dbs_main_tank_rune_of_blood;
|
||||
|
||||
creators["icc stinky precious main tank mortal wound"] = &RaidIccTriggerContext::icc_stinky_precious_main_tank_mortal_wound;
|
||||
|
||||
creators["icc festergut group position"] = &RaidIccTriggerContext::icc_festergut_group_position;
|
||||
creators["icc festergut main tank gastric bloat"] = &RaidIccTriggerContext::icc_festergut_main_tank_gastric_bloat;
|
||||
creators["icc festergut spore"] = &RaidIccTriggerContext::icc_festergut_spore;
|
||||
|
||||
creators["icc rotface tank position"] = &RaidIccTriggerContext::icc_rotface_tank_position;
|
||||
creators["icc rotface group position"] = &RaidIccTriggerContext::icc_rotface_group_position;
|
||||
creators["icc rotface move away from explosion"] = &RaidIccTriggerContext::icc_rotface_move_away_from_explosion;
|
||||
|
||||
creators["icc putricide volatile ooze"] = &RaidIccTriggerContext::icc_putricide_volatile_ooze;
|
||||
creators["icc putricide gas cloud"] = &RaidIccTriggerContext::icc_putricide_gas_cloud;
|
||||
creators["icc putricide growing ooze puddle"] = &RaidIccTriggerContext::icc_putricide_growing_ooze_puddle;
|
||||
creators["icc putricide main tank mutated plague"] = &RaidIccTriggerContext::icc_putricide_main_tank_mutated_plague;
|
||||
creators["icc putricide malleable goo"] = &RaidIccTriggerContext::icc_putricide_malleable_goo;
|
||||
|
||||
creators["icc bpc keleseth tank"] = &RaidIccTriggerContext::icc_bpc_keleseth_tank;
|
||||
creators["icc bpc main tank"] = &RaidIccTriggerContext::icc_bpc_main_tank;
|
||||
creators["icc bpc empowered vortex"] = &RaidIccTriggerContext::icc_bpc_empowered_vortex;
|
||||
creators["icc bpc kinetic bomb"] = &RaidIccTriggerContext::icc_bpc_kinetic_bomb;
|
||||
creators["icc bpc ball of flame"] = &RaidIccTriggerContext::icc_bpc_ball_of_flame;
|
||||
|
||||
creators["icc bql group position"] = &RaidIccTriggerContext::icc_bql_group_position;
|
||||
creators["icc bql pact of darkfallen"] = &RaidIccTriggerContext::icc_bql_pact_of_darkfallen;
|
||||
creators["icc bql vampiric bite"] = &RaidIccTriggerContext::icc_bql_vampiric_bite;
|
||||
|
||||
creators["icc valkyre spear"] = &RaidIccTriggerContext::icc_valkyre_spear;
|
||||
creators["icc sister svalna"] = &RaidIccTriggerContext::icc_sister_svalna;
|
||||
|
||||
creators["icc valithria group"] = &RaidIccTriggerContext::icc_valithria_group;
|
||||
creators["icc valithria portal"] = &RaidIccTriggerContext::icc_valithria_portal;
|
||||
creators["icc valithria heal"] = &RaidIccTriggerContext::icc_valithria_heal;
|
||||
creators["icc valithria dream cloud"] = &RaidIccTriggerContext::icc_valithria_dream_cloud;
|
||||
|
||||
creators["icc sindragosa group position"] = &RaidIccTriggerContext::icc_sindragosa_group_position;
|
||||
creators["icc sindragosa frost beacon"] = &RaidIccTriggerContext::icc_sindragosa_frost_beacon;
|
||||
creators["icc sindragosa blistering cold"] = &RaidIccTriggerContext::icc_sindragosa_blistering_cold;
|
||||
creators["icc sindragosa unchained magic"] = &RaidIccTriggerContext::icc_sindragosa_unchained_magic;
|
||||
creators["icc sindragosa chilled to the bone"] = &RaidIccTriggerContext::icc_sindragosa_chilled_to_the_bone;
|
||||
creators["icc sindragosa mystic buffet"] = &RaidIccTriggerContext::icc_sindragosa_mystic_buffet;
|
||||
creators["icc sindragosa main tank mystic buffet"] = &RaidIccTriggerContext::icc_sindragosa_main_tank_mystic_buffet;
|
||||
creators["icc sindragosa frost bomb"] = &RaidIccTriggerContext::icc_sindragosa_frost_bomb;
|
||||
creators["icc sindragosa tank swap position"] = &RaidIccTriggerContext::icc_sindragosa_tank_swap_position;
|
||||
|
||||
creators["icc lich king shadow trap"] = &RaidIccTriggerContext::icc_lich_king_shadow_trap;
|
||||
creators["icc lich king necrotic plague"] = &RaidIccTriggerContext::icc_lich_king_necrotic_plague;
|
||||
creators["icc lich king winter"] = &RaidIccTriggerContext::icc_lich_king_winter;
|
||||
creators["icc lich king adds"] = &RaidIccTriggerContext::icc_lich_king_adds;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* icc_lm(PlayerbotAI* ai) { return new IccLmTrigger(ai); }
|
||||
|
||||
static Trigger* icc_dark_reckoning(PlayerbotAI* ai) { return new IccDarkReckoningTrigger(ai); }
|
||||
static Trigger* icc_lady_deathwhisper(PlayerbotAI* ai) { return new IccLadyDeathwhisperTrigger(ai); }
|
||||
|
||||
static Trigger* icc_rotting_frost_giant_tank_position(PlayerbotAI* ai) { return new IccRottingFrostGiantTankPositionTrigger(ai); }
|
||||
static Trigger* icc_in_cannon(PlayerbotAI* ai) { return new IccInCannonTrigger(ai); }
|
||||
static Trigger* icc_gunship_cannon_near(PlayerbotAI* ai) { return new IccGunshipCannonNearTrigger(ai); }
|
||||
static Trigger* icc_gunship_teleport_ally(PlayerbotAI* ai) { return new IccGunshipTeleportAllyTrigger(ai); }
|
||||
static Trigger* icc_gunship_teleport_horde(PlayerbotAI* ai) { return new IccGunshipTeleportHordeTrigger(ai); }
|
||||
|
||||
static Trigger* icc_dbs(PlayerbotAI* ai) { return new IccDbsTrigger(ai); }
|
||||
static Trigger* icc_dbs_main_tank_rune_of_blood(PlayerbotAI* ai) { return new IccDbsMainTankRuneOfBloodTrigger(ai); }
|
||||
|
||||
static Trigger* icc_stinky_precious_main_tank_mortal_wound(PlayerbotAI* ai) { return new IccStinkyPreciousMainTankMortalWoundTrigger(ai); }
|
||||
|
||||
static Trigger* icc_festergut_group_position(PlayerbotAI* ai) { return new IccFestergutGroupPositionTrigger(ai); }
|
||||
static Trigger* icc_festergut_main_tank_gastric_bloat(PlayerbotAI* ai) { return new IccFestergutMainTankGastricBloatTrigger(ai); }
|
||||
static Trigger* icc_festergut_spore(PlayerbotAI* ai) { return new IccFestergutSporeTrigger(ai); }
|
||||
|
||||
static Trigger* icc_rotface_tank_position(PlayerbotAI* ai) { return new IccRotfaceTankPositionTrigger(ai); }
|
||||
static Trigger* icc_rotface_group_position(PlayerbotAI* ai) { return new IccRotfaceGroupPositionTrigger(ai); }
|
||||
static Trigger* icc_rotface_move_away_from_explosion(PlayerbotAI* ai) { return new IccRotfaceMoveAwayFromExplosionTrigger(ai); }
|
||||
|
||||
static Trigger* icc_putricide_volatile_ooze(PlayerbotAI* ai) { return new IccPutricideVolatileOozeTrigger(ai); }
|
||||
static Trigger* icc_putricide_gas_cloud(PlayerbotAI* ai) { return new IccPutricideGasCloudTrigger(ai); }
|
||||
static Trigger* icc_putricide_growing_ooze_puddle(PlayerbotAI* ai) { return new IccPutricideGrowingOozePuddleTrigger(ai); }
|
||||
static Trigger* icc_putricide_main_tank_mutated_plague(PlayerbotAI* ai) { return new IccPutricideMainTankMutatedPlagueTrigger(ai); }
|
||||
static Trigger* icc_putricide_malleable_goo(PlayerbotAI* ai) { return new IccPutricideMalleableGooTrigger(ai); }
|
||||
|
||||
static Trigger* icc_bpc_keleseth_tank(PlayerbotAI* ai) { return new IccBpcKelesethTankTrigger(ai); }
|
||||
static Trigger* icc_bpc_main_tank(PlayerbotAI* ai) { return new IccBpcMainTankTrigger(ai); }
|
||||
static Trigger* icc_bpc_empowered_vortex(PlayerbotAI* ai) { return new IccBpcEmpoweredVortexTrigger(ai); }
|
||||
static Trigger* icc_bpc_kinetic_bomb(PlayerbotAI* ai) { return new IccBpcKineticBombTrigger(ai); }
|
||||
static Trigger* icc_bpc_ball_of_flame(PlayerbotAI* ai) { return new IccBpcBallOfFlameTrigger(ai); }
|
||||
|
||||
static Trigger* icc_bql_group_position(PlayerbotAI* ai) { return new IccBqlGroupPositionTrigger(ai); }
|
||||
static Trigger* icc_bql_pact_of_darkfallen(PlayerbotAI* ai) { return new IccBqlPactOfDarkfallenTrigger(ai); }
|
||||
static Trigger* icc_bql_vampiric_bite(PlayerbotAI* ai) { return new IccBqlVampiricBiteTrigger(ai); }
|
||||
|
||||
static Trigger* icc_valkyre_spear(PlayerbotAI* ai) { return new IccValkyreSpearTrigger(ai); }
|
||||
static Trigger* icc_sister_svalna(PlayerbotAI* ai) { return new IccSisterSvalnaTrigger(ai); }
|
||||
|
||||
static Trigger* icc_valithria_group(PlayerbotAI* ai) { return new IccValithriaGroupTrigger(ai); }
|
||||
static Trigger* icc_valithria_portal(PlayerbotAI* ai) { return new IccValithriaPortalTrigger(ai); }
|
||||
static Trigger* icc_valithria_heal(PlayerbotAI* ai) { return new IccValithriaHealTrigger(ai); }
|
||||
static Trigger* icc_valithria_dream_cloud(PlayerbotAI* ai) { return new IccValithriaDreamCloudTrigger(ai); }
|
||||
|
||||
static Trigger* icc_sindragosa_group_position(PlayerbotAI* ai) { return new IccSindragosaGroupPositionTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_frost_beacon(PlayerbotAI* ai) { return new IccSindragosaFrostBeaconTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_blistering_cold(PlayerbotAI* ai) { return new IccSindragosaBlisteringColdTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_unchained_magic(PlayerbotAI* ai) { return new IccSindragosaUnchainedMagicTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_chilled_to_the_bone(PlayerbotAI* ai) { return new IccSindragosaChilledToTheBoneTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_mystic_buffet(PlayerbotAI* ai) { return new IccSindragosaMysticBuffetTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_main_tank_mystic_buffet(PlayerbotAI* ai) { return new IccSindragosaMainTankMysticBuffetTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_frost_bomb(PlayerbotAI* ai) { return new IccSindragosaFrostBombTrigger(ai); }
|
||||
static Trigger* icc_sindragosa_tank_swap_position(PlayerbotAI* ai) { return new IccSindragosaTankSwapPositionTrigger(ai); }
|
||||
|
||||
static Trigger* icc_lich_king_shadow_trap(PlayerbotAI* ai) { return new IccLichKingShadowTrapTrigger(ai); }
|
||||
static Trigger* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueTrigger(ai); }
|
||||
static Trigger* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterTrigger(ai); }
|
||||
static Trigger* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsTrigger(ai); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
186
src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.cpp
Normal file
186
src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "RaidIccStrategy.h"
|
||||
|
||||
#include "RaidIccMultipliers.h"
|
||||
|
||||
void RaidIccStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
//Lord Marrogwar
|
||||
triggers.push_back(new TriggerNode("icc lm",
|
||||
{ NextAction("icc lm tank position", ACTION_RAID + 5),
|
||||
NextAction("icc spike", ACTION_RAID + 3) }));
|
||||
|
||||
//Lady Deathwhisper
|
||||
triggers.push_back(new TriggerNode("icc dark reckoning",
|
||||
{ NextAction("icc dark reckoning", ACTION_MOVE + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc lady deathwhisper",
|
||||
{ NextAction("icc ranged position lady deathwhisper", ACTION_MOVE + 2),
|
||||
NextAction("icc adds lady deathwhisper", ACTION_RAID + 3),
|
||||
NextAction("icc shade lady deathwhisper", ACTION_RAID + 4) }));
|
||||
|
||||
//Gunship Battle
|
||||
triggers.push_back(new TriggerNode("icc rotting frost giant tank position",
|
||||
{ NextAction("icc rotting frost giant tank position", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc gunship cannon near",
|
||||
{ NextAction("icc gunship enter cannon", ACTION_RAID + 6) }));
|
||||
|
||||
triggers.push_back( new TriggerNode("icc in cannon",
|
||||
{ NextAction("icc cannon fire", ACTION_RAID+5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc gunship teleport ally",
|
||||
{ NextAction("icc gunship teleport ally", ACTION_RAID + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc gunship teleport horde",
|
||||
{ NextAction("icc gunship teleport horde", ACTION_RAID + 4) }));
|
||||
|
||||
//DBS
|
||||
triggers.push_back(new TriggerNode("icc dbs",
|
||||
{ NextAction("icc dbs tank position", ACTION_RAID + 3),
|
||||
NextAction("icc adds dbs", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc dbs main tank rune of blood",
|
||||
{ NextAction("taunt spell", ACTION_EMERGENCY + 4) }));
|
||||
|
||||
//DOGS
|
||||
triggers.push_back(new TriggerNode("icc stinky precious main tank mortal wound",
|
||||
{ NextAction("taunt spell", ACTION_EMERGENCY + 4) }));
|
||||
|
||||
//FESTERGUT
|
||||
triggers.push_back(new TriggerNode("icc festergut group position",
|
||||
{ NextAction("icc festergut group position", ACTION_MOVE + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc festergut main tank gastric bloat",
|
||||
{ NextAction("taunt spell", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc festergut spore",
|
||||
{ NextAction("icc festergut spore", ACTION_MOVE + 5) }));
|
||||
|
||||
//ROTFACE
|
||||
triggers.push_back(new TriggerNode("icc rotface tank position",
|
||||
{ NextAction("icc rotface tank position", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc rotface group position",
|
||||
{ NextAction("icc rotface group position", ACTION_RAID + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc rotface move away from explosion",
|
||||
{ NextAction("icc rotface move away from explosion", ACTION_RAID +7) }));
|
||||
|
||||
//PP
|
||||
triggers.push_back(new TriggerNode("icc putricide volatile ooze",
|
||||
{ NextAction("icc putricide volatile ooze", ACTION_RAID + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc putricide gas cloud",
|
||||
{ NextAction("icc putricide gas cloud", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc putricide growing ooze puddle",
|
||||
{ NextAction("icc putricide growing ooze puddle", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc putricide main tank mutated plague",
|
||||
{ NextAction("taunt spell", ACTION_RAID + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc putricide malleable goo",
|
||||
{ NextAction("icc putricide avoid malleable goo", ACTION_RAID + 2) }));
|
||||
|
||||
//BPC
|
||||
triggers.push_back(new TriggerNode("icc bpc keleseth tank",
|
||||
{ NextAction("icc bpc keleseth tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bpc main tank",
|
||||
{ NextAction("icc bpc main tank", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bpc empowered vortex",
|
||||
{ NextAction("icc bpc empowered vortex", ACTION_RAID + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bpc kinetic bomb",
|
||||
{ NextAction("icc bpc kinetic bomb", ACTION_RAID + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bpc ball of flame",
|
||||
{ NextAction("icc bpc ball of flame", ACTION_RAID + 7) }));
|
||||
|
||||
//BQL
|
||||
triggers.push_back(new TriggerNode("icc bql group position",
|
||||
{ NextAction("icc bql group position", ACTION_RAID) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bql pact of darkfallen",
|
||||
{ NextAction("icc bql pact of darkfallen", ACTION_RAID +1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc bql vampiric bite",
|
||||
{ NextAction("icc bql vampiric bite", ACTION_EMERGENCY + 5) }));
|
||||
|
||||
//Sister Svalna
|
||||
triggers.push_back(new TriggerNode("icc valkyre spear",
|
||||
{ NextAction("icc valkyre spear", ACTION_EMERGENCY + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sister svalna",
|
||||
{ NextAction("icc sister svalna", ACTION_RAID + 5) }));
|
||||
|
||||
//VDW
|
||||
triggers.push_back(new TriggerNode("icc valithria group",
|
||||
{ NextAction("icc valithria group", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc valithria portal",
|
||||
{ NextAction("icc valithria portal", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc valithria heal",
|
||||
{ NextAction("icc valithria heal", ACTION_RAID+2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc valithria dream cloud",
|
||||
{ NextAction("icc valithria dream cloud", ACTION_RAID + 4) }));
|
||||
|
||||
//SINDRAGOSA
|
||||
triggers.push_back(new TriggerNode("icc sindragosa group position",
|
||||
{ NextAction("icc sindragosa group position", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa frost beacon",
|
||||
{ NextAction("icc sindragosa frost beacon", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa blistering cold",
|
||||
{ NextAction("icc sindragosa blistering cold", ACTION_EMERGENCY + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa unchained magic",
|
||||
{ NextAction("icc sindragosa unchained magic", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa chilled to the bone",
|
||||
{ NextAction("icc sindragosa chilled to the bone", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa mystic buffet",
|
||||
{ NextAction("icc sindragosa mystic buffet", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa main tank mystic buffet",
|
||||
{ NextAction("taunt spell", ACTION_EMERGENCY + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa frost bomb",
|
||||
{ NextAction("icc sindragosa frost bomb", ACTION_RAID + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc sindragosa tank swap position",
|
||||
{ NextAction("icc sindragosa tank swap position", ACTION_EMERGENCY + 2) }));
|
||||
|
||||
//LICH KING
|
||||
triggers.push_back(new TriggerNode("icc lich king shadow trap",
|
||||
{ NextAction("icc lich king shadow trap", ACTION_RAID + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc lich king necrotic plague",
|
||||
{ NextAction("icc lich king necrotic plague", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc lich king winter",
|
||||
{ NextAction("icc lich king winter", ACTION_RAID +5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("icc lich king adds",
|
||||
{ NextAction("icc lich king adds", ACTION_RAID +2) }));
|
||||
}
|
||||
|
||||
void RaidIccStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new IccLadyDeathwhisperMultiplier(botAI));
|
||||
multipliers.push_back(new IccAddsDbsMultiplier(botAI));
|
||||
multipliers.push_back(new IccDogsMultiplier(botAI));
|
||||
multipliers.push_back(new IccFestergutMultiplier(botAI));
|
||||
multipliers.push_back(new IccRotfaceMultiplier(botAI));
|
||||
multipliers.push_back(new IccAddsPutricideMultiplier(botAI));
|
||||
multipliers.push_back(new IccBpcAssistMultiplier(botAI));
|
||||
multipliers.push_back(new IccBqlMultiplier(botAI));
|
||||
multipliers.push_back(new IccValithriaDreamCloudMultiplier(botAI));
|
||||
multipliers.push_back(new IccSindragosaMultiplier(botAI));
|
||||
multipliers.push_back(new IccLichKingAddsMultiplier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h
Normal file
18
src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDICCSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
#include "RaidIccMultipliers.h"
|
||||
|
||||
class RaidIccStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidIccStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "icc"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
1188
src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp
Normal file
1188
src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
578
src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h
Normal file
578
src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h
Normal file
@@ -0,0 +1,578 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDICCTRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum CreatureIdsICC
|
||||
{
|
||||
|
||||
// Lord Marrowgar
|
||||
NPC_SPIKE1 = 36619,
|
||||
NPC_SPIKE2 = 38711,
|
||||
NPC_SPIKE3 = 38712,
|
||||
|
||||
// Lady Deathwhisper
|
||||
NPC_SHADE = 38222,
|
||||
|
||||
// Gunship Battle
|
||||
NPC_KOR_KRON_BATTLE_MAGE = 37117,
|
||||
NPC_KOR_KRON_AXETHROWER = 36968,
|
||||
NPC_KOR_KRON_ROCKETEER = 36982,
|
||||
NPC_SKYBREAKER_SORCERER = 37116,
|
||||
NPC_SKYBREAKER_RIFLEMAN = 36969,
|
||||
NPC_SKYBREAKER_MORTAR_SOLDIER = 36978,
|
||||
NPC_IGB_HIGH_OVERLORD_SAURFANG = 36939,
|
||||
NPC_IGB_MURADIN_BRONZEBEARD = 36948,
|
||||
NPC_CANNONA = 36838,
|
||||
NPC_CANNONH = 36839,
|
||||
NPC_MURADIN_BRONZEBEARD = 36948,
|
||||
NPC_HIGH_OVERLORD_SAURFANG = 36939,
|
||||
|
||||
// Deathbringer Saurfang
|
||||
NPC_BLOOD_BEAST1 = 38508,
|
||||
NPC_BLOOD_BEAST2 = 38596,
|
||||
NPC_BLOOD_BEAST3 = 38597,
|
||||
NPC_BLOOD_BEAST4 = 38598,
|
||||
|
||||
// Rotface
|
||||
NPC_PUDDLE = 37013,
|
||||
NPC_BIG_OOZE = 36899,
|
||||
|
||||
// Putricide
|
||||
NPC_MALLEABLE_OOZE_STALKER = 38556,
|
||||
NPC_GROWING_OOZE_PUDDLE = 37690,
|
||||
NPC_CHOKING_GAS_BOMB = 38159,
|
||||
|
||||
// Blood Prince Council
|
||||
NPC_DARK_NUCLEUS = 38369,
|
||||
NPC_PRINCE_KELESETH = 37972,
|
||||
NPC_PRINCE_TALDARAM = 37973,
|
||||
NPC_PRINCE_VALANAR = 37970,
|
||||
NPC_KINETIC_BOMB1 = 38454,
|
||||
NPC_KINETIC_BOMB2 = 38775,
|
||||
NPC_KINETIC_BOMB3 = 38776,
|
||||
NPC_KINETIC_BOMB4 = 38777,
|
||||
NPC_BALL_OF_FLAME = 38332,
|
||||
NPC_BALL_OF_INFERNO_FLAME = 38451,
|
||||
|
||||
// Blood Queen Lana'thel
|
||||
NPC_SWARMING_SHADOWS = 38163,
|
||||
|
||||
// Sister Svalna
|
||||
NPC_SPEAR = 38248,
|
||||
ITEM_SPEAR = 50307,
|
||||
|
||||
// Valithria Dreamwalker
|
||||
NPC_VALITHRIA_DREAMWALKER = 36789,
|
||||
NPC_DREAM_PORTAL = 37945,
|
||||
NPC_NIGHTMARE_PORTAL = 38430,
|
||||
NPC_DREAM_CLOUD = 37985,
|
||||
NPC_NIGHTMARE_CLOUD = 38421,
|
||||
NPC_RISEN_ARCHMAGE = 37868,
|
||||
NPC_BLAZING_SKELETON = 36791,
|
||||
NPC_SUPPRESSER = 37863,
|
||||
NPC_BLISTERING_ZOMBIE = 37934,
|
||||
NPC_GLUTTONOUS_ABOMINATION = 37886,
|
||||
NPC_ROT_WORM = 37907,
|
||||
NPC_COLUMN_OF_FROST = 37918,
|
||||
NPC_MANA_VOID = 38068,
|
||||
NPC_DREAM_PORTAL_PRE_EFFECT = 38186,
|
||||
NPC_NIGHTMARE_PORTAL_PRE_EFFECT = 38429,
|
||||
|
||||
// Sindragosa
|
||||
NPC_SINDRAGOSA = 36853,
|
||||
NPC_TOMB1 = 36980,
|
||||
NPC_TOMB2 = 38320,
|
||||
NPC_TOMB3 = 38321,
|
||||
NPC_TOMB4 = 38322,
|
||||
|
||||
// Lich King
|
||||
NPC_THE_LICH_KING = 36597,
|
||||
NPC_TERENAS_MENETHIL = 36823,
|
||||
NPC_TERENAS_MENETHIL_HC = 39217,
|
||||
NPC_SPIRIT_BOMB = 39189,
|
||||
NPC_WICKED_SPIRIT1 = 39190,
|
||||
NPC_WICKED_SPIRIT2 = 39287,
|
||||
NPC_WICKED_SPIRIT3 = 39288,
|
||||
NPC_WICKED_SPIRIT4 = 39289,
|
||||
NPC_SHADOW_TRAP = 39137,
|
||||
NPC_SHAMBLING_HORROR1 = 37698,
|
||||
NPC_SHAMBLING_HORROR2 = 39299,
|
||||
NPC_SHAMBLING_HORROR3 = 39300,
|
||||
NPC_SHAMBLING_HORROR4 = 39301,
|
||||
NPC_ICE_SPHERE1 = 36633,
|
||||
NPC_ICE_SPHERE2 = 39305,
|
||||
NPC_ICE_SPHERE3 = 39306,
|
||||
NPC_ICE_SPHERE4 = 39307,
|
||||
NPC_RAGING_SPIRIT1 = 36701,
|
||||
NPC_RAGING_SPIRIT2 = 39302,
|
||||
NPC_RAGING_SPIRIT3 = 39303,
|
||||
NPC_RAGING_SPIRIT4 = 39304,
|
||||
NPC_DRUDGE_GHOUL1 = 37695,
|
||||
NPC_DRUDGE_GHOUL2 = 39309,
|
||||
NPC_DRUDGE_GHOUL3 = 39310,
|
||||
NPC_DRUDGE_GHOUL4 = 39311,
|
||||
NPC_VALKYR_SHADOWGUARD1 = 36609,
|
||||
NPC_VALKYR_SHADOWGUARD2 = 39120,
|
||||
NPC_VALKYR_SHADOWGUARD3 = 39121,
|
||||
NPC_VALKYR_SHADOWGUARD4 = 39122,
|
||||
NPC_VILE_SPIRIT1 = 37799,
|
||||
NPC_VILE_SPIRIT2 = 39284,
|
||||
NPC_VILE_SPIRIT3 = 39285,
|
||||
NPC_VILE_SPIRIT4 = 39286,
|
||||
|
||||
};
|
||||
|
||||
enum SpellIdsICC
|
||||
{
|
||||
// ICC cheat spells
|
||||
SPELL_EMPOWERED_BLOOD = 70227, //70304 -->50%, 70227 /*100% more dmg, 100% more att speed*/
|
||||
SPELL_EXPERIENCED = 71188, //dmg 30% 20% att speed
|
||||
SPELL_NO_THREAT = 70115, //reduce threat
|
||||
SPELL_SPITEFULL_FURY = 36886, //500% more threat
|
||||
SPELL_NITRO_BOOSTS = 54861, //Speed
|
||||
SPELL_PAIN_SUPPRESION = 69910, //40% dmg reduction
|
||||
SPELL_AGEIS_OF_DALARAN = 71638, //268 all ress
|
||||
SPELL_CYCLONE = 33786,
|
||||
SPELL_HAMMER_OF_JUSTICE = 10308, //stun
|
||||
|
||||
// Lady Deathwhisper
|
||||
SPELL_DARK_RECKONING = 69483,
|
||||
|
||||
// Gunship Battle
|
||||
SPELL_DEATH_PLAGUE = 72865,
|
||||
SPELL_BELOW_ZERO = 69705,
|
||||
|
||||
// Festergut
|
||||
SPELL_GAS_SPORE = 69279,
|
||||
|
||||
// Rotface
|
||||
SPELL_SLIME_SPRAY = 69508,
|
||||
SPELL_OOZE_FLOOD = 71215,
|
||||
SPELL_UNSTABLE_OOZE_EXPLOSION = 69839,
|
||||
SPELL_OOZE_FLOOD_VISUAL = 69785,
|
||||
|
||||
// Putricide
|
||||
SPELL_MALLEABLE_GOO = 70852,
|
||||
SPELL_GROW_AURA = 70347,
|
||||
|
||||
// Blood Prince Council
|
||||
SPELL_EMPOWERED_SHOCK_VORTEX1 = 72039,
|
||||
SPELL_EMPOWERED_SHOCK_VORTEX2 = 73037,
|
||||
SPELL_EMPOWERED_SHOCK_VORTEX3 = 73038,
|
||||
SPELL_EMPOWERED_SHOCK_VORTEX4 = 73039,
|
||||
|
||||
// Blood Queen Lana'thel
|
||||
SPELL_PACT_OF_THE_DARKFALLEN = 71340,
|
||||
|
||||
// Sister Svalna
|
||||
SPELL_AETHER_SHIELD = 71463,
|
||||
|
||||
// Valithria Dreamwalker
|
||||
SPELL_DREAM_STATE = 70766,
|
||||
SPELL_EMERALD_VIGOR = 70873,
|
||||
|
||||
// Sindragosa
|
||||
SPELL_FROST_BEACON = 70126,
|
||||
SPELL_ICE_TOMB = 70157,
|
||||
SPELL_FROST_BOMB_VISUAL = 70022,
|
||||
SPELL_BLISTERING_COLD1 = 70123,
|
||||
SPELL_BLISTERING_COLD2 = 71047,
|
||||
SPELL_BLISTERING_COLD3 = 71048,
|
||||
SPELL_BLISTERING_COLD4 = 71049,
|
||||
|
||||
// The Lich King
|
||||
SPELL_HARVEST_SOUL_VALKYR = 68985,
|
||||
SPELL_QUAKE = 72262,
|
||||
SPELL_REMORSELESS_WINTER1 = 72259,
|
||||
SPELL_REMORSELESS_WINTER2 = 74273,
|
||||
SPELL_REMORSELESS_WINTER3 = 74274,
|
||||
SPELL_REMORSELESS_WINTER4 = 74275,
|
||||
SPELL_REMORSELESS_WINTER5 = 68981,
|
||||
SPELL_REMORSELESS_WINTER6 = 74270,
|
||||
SPELL_REMORSELESS_WINTER7 = 74271,
|
||||
SPELL_REMORSELESS_WINTER8 = 74272,
|
||||
};
|
||||
|
||||
const uint32 DEFILE_AURAS[] = {72756, 74162, 74163, 74164};
|
||||
const uint32 DEFILE_CAST_ID = 72762;
|
||||
const uint32 DEFILE_NPC_ID = 38757;
|
||||
const size_t DEFILE_AURA_COUNT = 4;
|
||||
|
||||
// All fanatics and adherents entry ids Lady Deathwhisper
|
||||
static const std::array<uint32, 23> addEntriesLady = {
|
||||
37949, 38394, 38625, 38626, 38010, 38397, 39000, 39001,
|
||||
38136, 38396, 38632, 38633, 37890, 38393, 38628, 38629,
|
||||
38135, 38395, 38634, 38009, 38398, 38630, 38631};
|
||||
|
||||
const std::vector<uint32> spellEntriesFlood = {
|
||||
69782, 69783, 69796, 69797, 69798,
|
||||
69799, 69801, 69802, 69795};
|
||||
|
||||
const std::vector<uint32> availableTargetsGS = {
|
||||
NPC_KOR_KRON_AXETHROWER, NPC_KOR_KRON_ROCKETEER, NPC_KOR_KRON_BATTLE_MAGE, NPC_IGB_HIGH_OVERLORD_SAURFANG,
|
||||
NPC_SKYBREAKER_RIFLEMAN, NPC_SKYBREAKER_MORTAR_SOLDIER, NPC_SKYBREAKER_SORCERER, NPC_IGB_MURADIN_BRONZEBEARD};
|
||||
|
||||
static std::vector<ObjectGuid> sporeOrder;
|
||||
|
||||
//Lord Marrowgar
|
||||
class IccLmTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLmTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lm") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//Lady Deathwhisper
|
||||
class IccDarkReckoningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccDarkReckoningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc dark reckoning") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccLadyDeathwhisperTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLadyDeathwhisperTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lady deathwhisper") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//Gunship Battle
|
||||
class IccRottingFrostGiantTankPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccRottingFrostGiantTankPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc rotting frost giant tank position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccInCannonTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccInCannonTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc in cannon") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccGunshipCannonNearTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccGunshipCannonNearTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc in cannon") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccGunshipTeleportAllyTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccGunshipTeleportAllyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship teleport ally") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccGunshipTeleportHordeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccGunshipTeleportHordeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship teleport horde") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//DBS
|
||||
class IccDbsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccDbsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc dbs") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccDbsMainTankRuneOfBloodTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccDbsMainTankRuneOfBloodTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc dbs main tank rune of blood") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//DOGS
|
||||
class IccStinkyPreciousMainTankMortalWoundTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccStinkyPreciousMainTankMortalWoundTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc stinky precious main tank mortal wound") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//FESTERGUT
|
||||
class IccFestergutGroupPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccFestergutGroupPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc festergut group position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccFestergutMainTankGastricBloatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccFestergutMainTankGastricBloatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc festergut main tank gastric bloat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccFestergutSporeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccFestergutSporeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc festergut spore") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//ROTFACE
|
||||
class IccRotfaceTankPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccRotfaceTankPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc rotface tank position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccRotfaceGroupPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccRotfaceGroupPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc rotface group position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccRotfaceMoveAwayFromExplosionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccRotfaceMoveAwayFromExplosionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc rotface move away from explosion") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//PP
|
||||
class IccPutricideVolatileOozeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccPutricideVolatileOozeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide volatile ooze") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccPutricideGasCloudTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccPutricideGasCloudTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide gas cloud") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccPutricideGrowingOozePuddleTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccPutricideGrowingOozePuddleTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide growing ooze puddle") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccPutricideMainTankMutatedPlagueTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccPutricideMainTankMutatedPlagueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide main tank mutated plague") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccPutricideMalleableGooTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccPutricideMalleableGooTrigger(PlayerbotAI* ai) : Trigger(ai, "icc putricide malleable goo") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//BPC
|
||||
class IccBpcKelesethTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBpcKelesethTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bpc keleseth tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBpcMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBpcMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bpc main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBpcEmpoweredVortexTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBpcEmpoweredVortexTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bpc empowered vortex") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBpcKineticBombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBpcKineticBombTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bpc kinetic bomb") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBpcBallOfFlameTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBpcBallOfFlameTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bpc ball of flame") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//Bql
|
||||
class IccBqlGroupPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBqlGroupPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bql tank position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBqlPactOfDarkfallenTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBqlPactOfDarkfallenTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bql pact of darkfallen") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccBqlVampiricBiteTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccBqlVampiricBiteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc bql vampiric bite") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Sister Svalna
|
||||
class IccValkyreSpearTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccValkyreSpearTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valkyre spear") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSisterSvalnaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSisterSvalnaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sister svalna") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Valithria Dreamwalker
|
||||
|
||||
class IccValithriaGroupTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccValithriaGroupTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valithria group") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccValithriaPortalTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccValithriaPortalTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valithria portal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccValithriaHealTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccValithriaHealTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valithria heal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccValithriaDreamCloudTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccValithriaDreamCloudTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valithria dream cloud") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//SINDRAGOSA
|
||||
class IccSindragosaGroupPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaGroupPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa group position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaFrostBeaconTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaFrostBeaconTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa frost beacon") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaBlisteringColdTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaBlisteringColdTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa blistering cold") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaUnchainedMagicTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaUnchainedMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa unchained magic") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaChilledToTheBoneTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaChilledToTheBoneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa chilled to the bone") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaMysticBuffetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaMysticBuffetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa mystic buffet") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaMainTankMysticBuffetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaMainTankMysticBuffetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa main tank mystic buffet") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaTankSwapPositionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaTankSwapPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa tank swap position") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccSindragosaFrostBombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccSindragosaFrostBombTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa frost bomb") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//LICH KING
|
||||
class IccLichKingShadowTrapTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLichKingShadowTrapTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king shadow trap") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccLichKingNecroticPlagueTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLichKingNecroticPlagueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king necrotic plague") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccLichKingWinterTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLichKingWinterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king winter") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IccLichKingAddsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IccLichKingAddsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king adds") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
1493
src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp
Normal file
1493
src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
322
src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h
Normal file
322
src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h
Normal file
@@ -0,0 +1,322 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONS_H
|
||||
#define _PLAYERBOT_RAIDKARAZHANACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
class ManaWarpStunCreatureBeforeWarpBreachAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
ManaWarpStunCreatureBeforeWarpBreachAction(
|
||||
PlayerbotAI* botAI, std::string const name = "mana warp stun creature before warp breach") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanMarkTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanMarkTargetAction(
|
||||
PlayerbotAI* botAI, std::string const name = "attumen the huntsman mark target") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanSplitBossesAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanSplitBossesAction(
|
||||
PlayerbotAI* botAI, std::string const name = "attumen the huntsman split bosses") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanStackBehindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanStackBehindAction(
|
||||
PlayerbotAI* botAI, std::string const name = "attumen the huntsman stack behind") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanManageDpsTimerAction : public Action
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanManageDpsTimerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "attumen the huntsman manage dps timer") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MoroesMainTankAttackBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MoroesMainTankAttackBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "moroes main tank attack boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MoroesMarkTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
MoroesMarkTargetAction(
|
||||
PlayerbotAI* botAI, std::string const name = "moroes mark target") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MaidenOfVirtueMoveBossToHealerAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MaidenOfVirtueMoveBossToHealerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "maiden of virtue move boss to healer") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MaidenOfVirtuePositionRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MaidenOfVirtuePositionRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "maiden of virtue position ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class BigBadWolfPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
BigBadWolfPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "big bad wolf position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class BigBadWolfRunAwayFromBossAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
BigBadWolfRunAwayFromBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "big bad wolf run away from boss") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class RomuloAndJulianneMarkTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
RomuloAndJulianneMarkTargetAction(
|
||||
PlayerbotAI* botAI, std::string const name = "romulo and julianne mark target") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class WizardOfOzMarkTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
WizardOfOzMarkTargetAction(
|
||||
PlayerbotAI* botAI, std::string const name = "wizard of oz mark target") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class WizardOfOzScorchStrawmanAction : public Action
|
||||
{
|
||||
public:
|
||||
WizardOfOzScorchStrawmanAction(
|
||||
PlayerbotAI* botAI, std::string const name = "wizard of oz scorch strawman") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheCuratorMarkAstralFlareAction : public Action
|
||||
{
|
||||
public:
|
||||
TheCuratorMarkAstralFlareAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the curator mark astral flare") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheCuratorPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TheCuratorPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the curator position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheCuratorSpreadRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TheCuratorSpreadRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the curator spread ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TerestianIllhoofMarkTargetAction : public Action
|
||||
{
|
||||
public:
|
||||
TerestianIllhoofMarkTargetAction(
|
||||
PlayerbotAI* botAI, std::string const name = "terestian illhoof mark target") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ShadeOfAranRunAwayFromArcaneExplosionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ShadeOfAranRunAwayFromArcaneExplosionAction(
|
||||
PlayerbotAI* botAI, std::string const name = "shade of aran run away from arcane explosion") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ShadeOfAranStopMovingDuringFlameWreathAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ShadeOfAranStopMovingDuringFlameWreathAction(
|
||||
PlayerbotAI* botAI, std::string const name = "shade of aran stop moving during flame wreath") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ShadeOfAranMarkConjuredElementalAction : public Action
|
||||
{
|
||||
public:
|
||||
ShadeOfAranMarkConjuredElementalAction(
|
||||
PlayerbotAI* botAI, std::string const name = "shade of aran mark conjured elemental") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ShadeOfAranRangedMaintainDistanceAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ShadeOfAranRangedMaintainDistanceAction(
|
||||
PlayerbotAI* botAI, std::string const name = "shade of aran ranged maintain distance") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NetherspiteBlockRedBeamAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NetherspiteBlockRedBeamAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite block red beam") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
Position GetPositionOnBeam(Unit* netherspite, Unit* portal, float distanceFromBoss);
|
||||
std::unordered_map<ObjectGuid, bool> _wasBlockingRedBeam;
|
||||
};
|
||||
|
||||
class NetherspiteBlockBlueBeamAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NetherspiteBlockBlueBeamAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite block blue beam") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
std::unordered_map<ObjectGuid, bool> _wasBlockingBlueBeam;
|
||||
};
|
||||
|
||||
class NetherspiteBlockGreenBeamAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NetherspiteBlockGreenBeamAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite block green beam") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
std::unordered_map<ObjectGuid, bool> _wasBlockingGreenBeam;
|
||||
};
|
||||
|
||||
class NetherspiteAvoidBeamAndVoidZoneAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NetherspiteAvoidBeamAndVoidZoneAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite avoid beam and void zone") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
struct BeamAvoid
|
||||
{
|
||||
Unit* portal;
|
||||
float minDist, maxDist;
|
||||
};
|
||||
bool IsAwayFromBeams(float x, float y, const std::vector<BeamAvoid>& beams, Unit* netherspite);
|
||||
};
|
||||
|
||||
class NetherspiteBanishPhaseAvoidVoidZoneAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NetherspiteBanishPhaseAvoidVoidZoneAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite banish phase avoid void zone") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NetherspiteManageTimersAndTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
NetherspiteManageTimersAndTrackersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "netherspite manage timers and trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarEnfeebledAvoidHazardAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarEnfeebledAvoidHazardAction(
|
||||
PlayerbotAI* botAI, std::string const name = "prince malchezaar enfeebled avoid hazard") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarNonTankAvoidInfernalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarNonTankAvoidInfernalAction(
|
||||
PlayerbotAI* botAI, std::string const name = "prince malchezaar non tank avoid infernal") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarMainTankMovementAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarMainTankMovementAction(
|
||||
PlayerbotAI* botAI, std::string const name = "prince malchezaar main tank movement") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneGroundPhasePositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
NightbaneGroundPhasePositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane ground phase position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneGroundPhaseRotateRangedPositionsAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NightbaneGroundPhaseRotateRangedPositionsAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane ground phase rotate ranged positions") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneCastFearWardOnMainTankAction : public Action
|
||||
{
|
||||
public:
|
||||
NightbaneCastFearWardOnMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane cast fear ward on main tank") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneControlPetAggressionAction : public Action
|
||||
{
|
||||
public:
|
||||
NightbaneControlPetAggressionAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane control pet aggression") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneFlightPhaseMovementAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NightbaneFlightPhaseMovementAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane flight phase movement") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NightbaneManageTimersAndTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
NightbaneManageTimersAndTrackersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "nightbane manage timers and trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
363
src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp
Normal file
363
src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "RaidKarazhanMultipliers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "AttackAction.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ShamanActions.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
// Keep tanks from jumping back and forth between Attumen and Midnight
|
||||
float AttumenTheHuntsmanDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||
if (!midnight)
|
||||
return 1.0f;
|
||||
|
||||
Unit* attumen = AI_VALUE2(Unit*, "find target", "attumen the huntsman");
|
||||
if (!attumen)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Try to get rid of jittering when bots are stacked behind Attumen
|
||||
float AttumenTheHuntsmanStayStackedMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||
if (!attumenMounted)
|
||||
return 1.0f;
|
||||
|
||||
if (!botAI->IsMainTank(bot) && attumenMounted->GetVictim() != bot)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Give the main tank 8 seconds to grab aggro when Attumen mounts Midnight
|
||||
// In reality it's shorter because it takes Attumen a few seconds to aggro after mounting
|
||||
float AttumenTheHuntsmanWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||
if (!attumenMounted)
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = attumenMounted->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
const uint8 dpsWaitSeconds = 8;
|
||||
|
||||
auto it = attumenDpsWaitTimer.find(instanceId);
|
||||
if (it == attumenDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts
|
||||
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||
if (!curator)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Save Bloodlust/Heroism for Evocation (100% increased damage)
|
||||
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||
if (!curator)
|
||||
return 1.0f;
|
||||
|
||||
if (!curator->HasAura(SPELL_CURATOR_EVOCATION))
|
||||
{
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Don't charge back in when running from Arcane Explosion
|
||||
float ShadeOfAranArcaneExplosionDisableChargeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||
if (!aran)
|
||||
return 1.0f;
|
||||
|
||||
if (aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (bot->GetDistance2d(aran) >= 20.0f)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// I will not move when Flame Wreath is cast or the raid blows up
|
||||
float ShadeOfAranFlameWreathDisableMovementMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||
if (!aran)
|
||||
return 1.0f;
|
||||
|
||||
if (IsFlameWreathActive(botAI, bot))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<AvoidAoeAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Try to rid of the jittering when blocking beams
|
||||
float NetherspiteKeepBlockingBeamMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return 1.0f;
|
||||
|
||||
auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot);
|
||||
|
||||
if (bot == redBlocker)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (bot == blueBlocker)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (bot == greenBlocker)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Give tanks 5 seconds to get aggro during phase transitions
|
||||
float NetherspiteWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = netherspite->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
const uint8 dpsWaitSeconds = 5;
|
||||
|
||||
auto it = netherspiteDpsWaitTimer.find(instanceId);
|
||||
if (it == netherspiteDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disable standard "avoid aoe" strategy, which may interfere with scripted avoidance
|
||||
float PrinceMalchezaarDisableAvoidAoeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||
if (!malchezaar)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Don't run back into Shadow Nova when Enfeebled
|
||||
float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||
if (!malchezaar)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Wait until Phase 3 to use Bloodlust/Heroism
|
||||
float PrinceMalchezaarDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||
if (!malchezaar)
|
||||
return 1.0f;
|
||||
|
||||
if (malchezaar->GetHealthPct() > 30.0f)
|
||||
{
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Pets tend to run out of bounds and cause skeletons to spawn off the map
|
||||
// Pets also tend to pull adds from inside of the tower through the floor
|
||||
// This multiplier DOES NOT impact Hunter and Warlock pets
|
||||
// Hunter and Warlock pets are addressed in ControlPetAggressionAction
|
||||
float NightbaneDisablePetsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastForceOfNatureAction*>(action) ||
|
||||
dynamic_cast<CastFeralSpiritAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
|
||||
dynamic_cast<CastSummonWaterElementalAction*>(action) ||
|
||||
dynamic_cast<CastShadowfiendAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z)
|
||||
{
|
||||
if (dynamic_cast<PetAttackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Give the main tank 8 seconds to get aggro during phase transitions
|
||||
float NightbaneWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane || nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z)
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = nightbane->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
const uint8 dpsWaitSeconds = 8;
|
||||
|
||||
auto it = nightbaneDpsWaitTimer.find(instanceId);
|
||||
if (it == nightbaneDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// The "avoid aoe" strategy must be disabled for the main tank
|
||||
// Otherwise, the main tank will spin Nightbane to avoid Charred Earth and wipe the raid
|
||||
// It is also disabled for all bots during the flight phase
|
||||
float NightbaneDisableAvoidAoeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane)
|
||||
return 1.0f;
|
||||
|
||||
if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z || botAI->IsMainTank(bot))
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disable some movement actions that conflict with the strategies
|
||||
float NightbaneDisableMovementMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
// Disable CombatFormationMoveAction for all bots except:
|
||||
// (1) main tank and (2) only during the ground phase, other melee
|
||||
if (botAI->IsRanged(bot) ||
|
||||
(botAI->IsMelee(bot) && !botAI->IsMainTank(bot) &&
|
||||
nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
134
src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.h
Normal file
134
src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDKARAZHANMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class AttumenTheHuntsmanDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanDisableTankAssistMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman disable tank assist multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanStayStackedMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanStayStackedMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman stay stacked multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman wait for dps multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheCuratorDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheCuratorDisableTankAssistMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the curator disable tank assist multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheCuratorDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the curator delay bloodlust and heroism multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class ShadeOfAranArcaneExplosionDisableChargeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
ShadeOfAranArcaneExplosionDisableChargeMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "shade of aran arcane explosion disable charge multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class ShadeOfAranFlameWreathDisableMovementMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
ShadeOfAranFlameWreathDisableMovementMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "shade of aran flame wreath disable movement multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NetherspiteKeepBlockingBeamMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NetherspiteKeepBlockingBeamMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "netherspite keep blocking beam multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NetherspiteWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NetherspiteWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "netherspite wait for dps multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class PrinceMalchezaarDisableAvoidAoeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarDisableAvoidAoeMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar disable avoid aoe multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class PrinceMalchezaarEnfeebleKeepDistanceMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarEnfeebleKeepDistanceMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar enfeeble keep distance multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class PrinceMalchezaarDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar delay bloodlust and heroism multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NightbaneDisablePetsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NightbaneDisablePetsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable pets multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NightbaneWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NightbaneWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane wait for dps multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NightbaneDisableAvoidAoeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NightbaneDisableAvoidAoeMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable avoid aoe multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class NightbaneDisableMovementMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NightbaneDisableMovementMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable movement multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
263
src/Ai/Raid/Karazhan/RaidKarazhanActionContext.h
Normal file
263
src/Ai/Raid/Karazhan/RaidKarazhanActionContext.h
Normal file
@@ -0,0 +1,263 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDKARAZHANACTIONCONTEXT_H
|
||||
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidKarazhanActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidKarazhanActionContext()
|
||||
{
|
||||
// Trash
|
||||
creators["mana warp stun creature before warp breach"] =
|
||||
&RaidKarazhanActionContext::mana_warp_stun_creature_before_warp_breach;
|
||||
|
||||
// Attumen the Huntsman
|
||||
creators["attumen the huntsman mark target"] =
|
||||
&RaidKarazhanActionContext::attumen_the_huntsman_mark_target;
|
||||
|
||||
creators["attumen the huntsman split bosses"] =
|
||||
&RaidKarazhanActionContext::attumen_the_huntsman_split_bosses;
|
||||
|
||||
creators["attumen the huntsman stack behind"] =
|
||||
&RaidKarazhanActionContext::attumen_the_huntsman_stack_behind;
|
||||
|
||||
creators["attumen the huntsman manage dps timer"] =
|
||||
&RaidKarazhanActionContext::attumen_the_huntsman_manage_dps_timer;
|
||||
|
||||
// Moroes
|
||||
creators["moroes main tank attack boss"] =
|
||||
&RaidKarazhanActionContext::moroes_main_tank_attack_boss;
|
||||
|
||||
creators["moroes mark target"] =
|
||||
&RaidKarazhanActionContext::moroes_mark_target;
|
||||
|
||||
// Maiden of Virtue
|
||||
creators["maiden of virtue move boss to healer"] =
|
||||
&RaidKarazhanActionContext::maiden_of_virtue_move_boss_to_healer;
|
||||
|
||||
creators["maiden of virtue position ranged"] =
|
||||
&RaidKarazhanActionContext::maiden_of_virtue_position_ranged;
|
||||
|
||||
// The Big Bad Wolf
|
||||
creators["big bad wolf position boss"] =
|
||||
&RaidKarazhanActionContext::big_bad_wolf_position_boss;
|
||||
|
||||
creators["big bad wolf run away from boss"] =
|
||||
&RaidKarazhanActionContext::big_bad_wolf_run_away_from_boss;
|
||||
|
||||
// Romulo and Julianne
|
||||
creators["romulo and julianne mark target"] =
|
||||
&RaidKarazhanActionContext::romulo_and_julianne_mark_target;
|
||||
|
||||
// The Wizard of Oz
|
||||
creators["wizard of oz mark target"] =
|
||||
&RaidKarazhanActionContext::wizard_of_oz_mark_target;
|
||||
|
||||
creators["wizard of oz scorch strawman"] =
|
||||
&RaidKarazhanActionContext::wizard_of_oz_scorch_strawman;
|
||||
|
||||
// The Curator
|
||||
creators["the curator mark astral flare"] =
|
||||
&RaidKarazhanActionContext::the_curator_mark_astral_flare;
|
||||
|
||||
creators["the curator position boss"] =
|
||||
&RaidKarazhanActionContext::the_curator_position_boss;
|
||||
|
||||
creators["the curator spread ranged"] =
|
||||
&RaidKarazhanActionContext::the_curator_spread_ranged;
|
||||
|
||||
// Terestian Illhoof
|
||||
creators["terestian illhoof mark target"] =
|
||||
&RaidKarazhanActionContext::terestian_illhoof_mark_target;
|
||||
|
||||
// Shade of Aran
|
||||
creators["shade of aran run away from arcane explosion"] =
|
||||
&RaidKarazhanActionContext::shade_of_aran_run_away_from_arcane_explosion;
|
||||
|
||||
creators["shade of aran stop moving during flame wreath"] =
|
||||
&RaidKarazhanActionContext::shade_of_aran_stop_moving_during_flame_wreath;
|
||||
|
||||
creators["shade of aran mark conjured elemental"] =
|
||||
&RaidKarazhanActionContext::shade_of_aran_mark_conjured_elemental;
|
||||
|
||||
creators["shade of aran ranged maintain distance"] =
|
||||
&RaidKarazhanActionContext::shade_of_aran_ranged_maintain_distance;
|
||||
|
||||
// Netherspite
|
||||
creators["netherspite block red beam"] =
|
||||
&RaidKarazhanActionContext::netherspite_block_red_beam;
|
||||
|
||||
creators["netherspite block blue beam"] =
|
||||
&RaidKarazhanActionContext::netherspite_block_blue_beam;
|
||||
|
||||
creators["netherspite block green beam"] =
|
||||
&RaidKarazhanActionContext::netherspite_block_green_beam;
|
||||
|
||||
creators["netherspite avoid beam and void zone"] =
|
||||
&RaidKarazhanActionContext::netherspite_avoid_beam_and_void_zone;
|
||||
|
||||
creators["netherspite banish phase avoid void zone"] =
|
||||
&RaidKarazhanActionContext::netherspite_banish_phase_avoid_void_zone;
|
||||
|
||||
creators["netherspite manage timers and trackers"] =
|
||||
&RaidKarazhanActionContext::netherspite_manage_timers_and_trackers;
|
||||
|
||||
// Prince Malchezaar
|
||||
creators["prince malchezaar enfeebled avoid hazard"] =
|
||||
&RaidKarazhanActionContext::prince_malchezaar_enfeebled_avoid_hazard;
|
||||
|
||||
creators["prince malchezaar non tank avoid infernal"] =
|
||||
&RaidKarazhanActionContext::prince_malchezaar_non_tank_avoid_infernal;
|
||||
|
||||
creators["prince malchezaar main tank movement"] =
|
||||
&RaidKarazhanActionContext::prince_malchezaar_main_tank_movement;
|
||||
|
||||
// Nightbane
|
||||
creators["nightbane ground phase position boss"] =
|
||||
&RaidKarazhanActionContext::nightbane_ground_phase_position_boss;
|
||||
|
||||
creators["nightbane ground phase rotate ranged positions"] =
|
||||
&RaidKarazhanActionContext::nightbane_ground_phase_rotate_ranged_positions;
|
||||
|
||||
creators["nightbane cast fear ward on main tank"] =
|
||||
&RaidKarazhanActionContext::nightbane_cast_fear_ward_on_main_tank;
|
||||
|
||||
creators["nightbane control pet aggression"] =
|
||||
&RaidKarazhanActionContext::nightbane_control_pet_aggression;
|
||||
|
||||
creators["nightbane flight phase movement"] =
|
||||
&RaidKarazhanActionContext::nightbane_flight_phase_movement;
|
||||
|
||||
creators["nightbane manage timers and trackers"] =
|
||||
&RaidKarazhanActionContext::nightbane_manage_timers_and_trackers;
|
||||
}
|
||||
|
||||
private:
|
||||
// Trash
|
||||
static Action* mana_warp_stun_creature_before_warp_breach(
|
||||
PlayerbotAI* botAI) { return new ManaWarpStunCreatureBeforeWarpBreachAction(botAI); }
|
||||
|
||||
// Attumen the Huntsman
|
||||
static Action* attumen_the_huntsman_mark_target(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanMarkTargetAction(botAI); }
|
||||
|
||||
static Action* attumen_the_huntsman_split_bosses(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanSplitBossesAction(botAI); }
|
||||
|
||||
static Action* attumen_the_huntsman_stack_behind(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanStackBehindAction(botAI); }
|
||||
|
||||
static Action* attumen_the_huntsman_manage_dps_timer(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanManageDpsTimerAction(botAI); }
|
||||
|
||||
// Moroes
|
||||
static Action* moroes_main_tank_attack_boss(
|
||||
PlayerbotAI* botAI) { return new MoroesMainTankAttackBossAction(botAI); }
|
||||
|
||||
static Action* moroes_mark_target(
|
||||
PlayerbotAI* botAI) { return new MoroesMarkTargetAction(botAI); }
|
||||
|
||||
// Maiden of Virtue
|
||||
static Action* maiden_of_virtue_move_boss_to_healer(
|
||||
PlayerbotAI* botAI) { return new MaidenOfVirtueMoveBossToHealerAction(botAI); }
|
||||
|
||||
static Action* maiden_of_virtue_position_ranged(
|
||||
PlayerbotAI* botAI) { return new MaidenOfVirtuePositionRangedAction(botAI); }
|
||||
|
||||
// The Big Bad Wolf
|
||||
static Action* big_bad_wolf_position_boss(
|
||||
PlayerbotAI* botAI) { return new BigBadWolfPositionBossAction(botAI); }
|
||||
|
||||
static Action* big_bad_wolf_run_away_from_boss(
|
||||
PlayerbotAI* botAI) { return new BigBadWolfRunAwayFromBossAction(botAI); }
|
||||
|
||||
// Romulo and Julianne
|
||||
static Action* romulo_and_julianne_mark_target(
|
||||
PlayerbotAI* botAI) { return new RomuloAndJulianneMarkTargetAction(botAI); }
|
||||
|
||||
// The Wizard of Oz
|
||||
static Action* wizard_of_oz_mark_target(
|
||||
PlayerbotAI* botAI) { return new WizardOfOzMarkTargetAction(botAI); }
|
||||
|
||||
static Action* wizard_of_oz_scorch_strawman(
|
||||
PlayerbotAI* botAI) { return new WizardOfOzScorchStrawmanAction(botAI); }
|
||||
|
||||
// The Curator
|
||||
static Action* the_curator_mark_astral_flare(
|
||||
PlayerbotAI* botAI) { return new TheCuratorMarkAstralFlareAction(botAI); }
|
||||
|
||||
static Action* the_curator_position_boss(
|
||||
PlayerbotAI* botAI) { return new TheCuratorPositionBossAction(botAI); }
|
||||
|
||||
static Action* the_curator_spread_ranged(
|
||||
PlayerbotAI* botAI) { return new TheCuratorSpreadRangedAction(botAI); }
|
||||
|
||||
// Terestian Illhoof
|
||||
static Action* terestian_illhoof_mark_target(
|
||||
PlayerbotAI* botAI) { return new TerestianIllhoofMarkTargetAction(botAI); }
|
||||
|
||||
// Shade of Aran
|
||||
static Action* shade_of_aran_run_away_from_arcane_explosion(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranRunAwayFromArcaneExplosionAction(botAI); }
|
||||
|
||||
static Action* shade_of_aran_stop_moving_during_flame_wreath(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranStopMovingDuringFlameWreathAction(botAI); }
|
||||
|
||||
static Action* shade_of_aran_mark_conjured_elemental(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranMarkConjuredElementalAction(botAI); }
|
||||
|
||||
static Action* shade_of_aran_ranged_maintain_distance(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranRangedMaintainDistanceAction(botAI); }
|
||||
|
||||
// Netherspite
|
||||
static Action* netherspite_block_red_beam(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBlockRedBeamAction(botAI); }
|
||||
|
||||
static Action* netherspite_block_blue_beam(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBlockBlueBeamAction(botAI); }
|
||||
|
||||
static Action* netherspite_block_green_beam(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBlockGreenBeamAction(botAI); }
|
||||
|
||||
static Action* netherspite_avoid_beam_and_void_zone(
|
||||
PlayerbotAI* botAI) { return new NetherspiteAvoidBeamAndVoidZoneAction(botAI); }
|
||||
|
||||
static Action* netherspite_banish_phase_avoid_void_zone(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBanishPhaseAvoidVoidZoneAction(botAI); }
|
||||
|
||||
static Action* netherspite_manage_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new NetherspiteManageTimersAndTrackersAction(botAI); }
|
||||
|
||||
// Prince Malchezaar
|
||||
static Action* prince_malchezaar_enfeebled_avoid_hazard(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarEnfeebledAvoidHazardAction(botAI); }
|
||||
|
||||
static Action* prince_malchezaar_non_tank_avoid_infernal(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarNonTankAvoidInfernalAction(botAI); }
|
||||
|
||||
static Action* prince_malchezaar_main_tank_movement(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarMainTankMovementAction(botAI); }
|
||||
|
||||
// Nightbane
|
||||
static Action* nightbane_ground_phase_position_boss(
|
||||
PlayerbotAI* botAI) { return new NightbaneGroundPhasePositionBossAction(botAI); }
|
||||
|
||||
static Action* nightbane_ground_phase_rotate_ranged_positions(
|
||||
PlayerbotAI* botAI) { return new NightbaneGroundPhaseRotateRangedPositionsAction(botAI); }
|
||||
|
||||
static Action* nightbane_cast_fear_ward_on_main_tank(
|
||||
PlayerbotAI* botAI) { return new NightbaneCastFearWardOnMainTankAction(botAI); }
|
||||
|
||||
static Action* nightbane_control_pet_aggression(
|
||||
PlayerbotAI* botAI) { return new NightbaneControlPetAggressionAction(botAI); }
|
||||
|
||||
static Action* nightbane_flight_phase_movement(
|
||||
PlayerbotAI* botAI) { return new NightbaneFlightPhaseMovementAction(botAI); }
|
||||
|
||||
static Action* nightbane_manage_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new NightbaneManageTimersAndTrackersAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
263
src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h
Normal file
263
src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h
Normal file
@@ -0,0 +1,263 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidKarazhanTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class RaidKarazhanTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidKarazhanTriggerContext()
|
||||
{
|
||||
// Trash
|
||||
creators["mana warp is about to explode"] =
|
||||
&RaidKarazhanTriggerContext::mana_warp_is_about_to_explode;
|
||||
|
||||
// Attumen the Huntsman
|
||||
creators["attumen the huntsman need target priority"] =
|
||||
&RaidKarazhanTriggerContext::attumen_the_huntsman_need_target_priority;
|
||||
|
||||
creators["attumen the huntsman attumen spawned"] =
|
||||
&RaidKarazhanTriggerContext::attumen_the_huntsman_attumen_spawned;
|
||||
|
||||
creators["attumen the huntsman attumen is mounted"] =
|
||||
&RaidKarazhanTriggerContext::attumen_the_huntsman_attumen_is_mounted;
|
||||
|
||||
creators["attumen the huntsman boss wipes aggro when mounting"] =
|
||||
&RaidKarazhanTriggerContext::attumen_the_huntsman_boss_wipes_aggro_when_mounting;
|
||||
|
||||
// Moroes
|
||||
creators["moroes boss engaged by main tank"] =
|
||||
&RaidKarazhanTriggerContext::moroes_boss_engaged_by_main_tank;
|
||||
|
||||
creators["moroes need target priority"] =
|
||||
&RaidKarazhanTriggerContext::moroes_need_target_priority;
|
||||
|
||||
// Maiden of Virtue
|
||||
creators["maiden of virtue healers are stunned by repentance"] =
|
||||
&RaidKarazhanTriggerContext::maiden_of_virtue_healers_are_stunned_by_repentance;
|
||||
|
||||
creators["maiden of virtue holy wrath deals chain damage"] =
|
||||
&RaidKarazhanTriggerContext::maiden_of_virtue_holy_wrath_deals_chain_damage;
|
||||
|
||||
// The Big Bad Wolf
|
||||
creators["big bad wolf boss engaged by tank"] =
|
||||
&RaidKarazhanTriggerContext::big_bad_wolf_boss_engaged_by_tank;
|
||||
|
||||
creators["big bad wolf boss is chasing little red riding hood"] =
|
||||
&RaidKarazhanTriggerContext::big_bad_wolf_boss_is_chasing_little_red_riding_hood;
|
||||
|
||||
// Romulo and Julianne
|
||||
creators["romulo and julianne both bosses revived"] =
|
||||
&RaidKarazhanTriggerContext::romulo_and_julianne_both_bosses_revived;
|
||||
|
||||
// The Wizard of Oz
|
||||
creators["wizard of oz need target priority"] =
|
||||
&RaidKarazhanTriggerContext::wizard_of_oz_need_target_priority;
|
||||
|
||||
creators["wizard of oz strawman is vulnerable to fire"] =
|
||||
&RaidKarazhanTriggerContext::wizard_of_oz_strawman_is_vulnerable_to_fire;
|
||||
|
||||
// The Curator
|
||||
creators["the curator astral flare spawned"] =
|
||||
&RaidKarazhanTriggerContext::the_curator_astral_flare_spawned;
|
||||
|
||||
creators["the curator boss engaged by tanks"] =
|
||||
&RaidKarazhanTriggerContext::the_curator_boss_engaged_by_tanks;
|
||||
|
||||
creators["the curator astral flares cast arcing sear"] =
|
||||
&RaidKarazhanTriggerContext::the_curator_astral_flares_cast_arcing_sear;
|
||||
|
||||
// Terestian Illhoof
|
||||
creators["terestian illhoof need target priority"] =
|
||||
&RaidKarazhanTriggerContext::terestian_illhoof_need_target_priority;
|
||||
|
||||
// Shade of Aran
|
||||
creators["shade of aran arcane explosion is casting"] =
|
||||
&RaidKarazhanTriggerContext::shade_of_aran_arcane_explosion_is_casting;
|
||||
|
||||
creators["shade of aran flame wreath is active"] =
|
||||
&RaidKarazhanTriggerContext::shade_of_aran_flame_wreath_is_active;
|
||||
|
||||
creators["shade of aran conjured elementals summoned"] =
|
||||
&RaidKarazhanTriggerContext::shade_of_aran_conjured_elementals_summoned;
|
||||
|
||||
creators["shade of aran boss uses counterspell and blizzard"] =
|
||||
&RaidKarazhanTriggerContext::shade_of_aran_boss_uses_counterspell_and_blizzard;
|
||||
|
||||
// Netherspite
|
||||
creators["netherspite red beam is active"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_red_beam_is_active;
|
||||
|
||||
creators["netherspite blue beam is active"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_blue_beam_is_active;
|
||||
|
||||
creators["netherspite green beam is active"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_green_beam_is_active;
|
||||
|
||||
creators["netherspite bot is not beam blocker"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_bot_is_not_beam_blocker;
|
||||
|
||||
creators["netherspite boss is banished"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_boss_is_banished;
|
||||
|
||||
creators["netherspite need to manage timers and trackers"] =
|
||||
&RaidKarazhanTriggerContext::netherspite_need_to_manage_timers_and_trackers;
|
||||
|
||||
// Prince Malchezaar
|
||||
creators["prince malchezaar bot is enfeebled"] =
|
||||
&RaidKarazhanTriggerContext::prince_malchezaar_bot_is_enfeebled;
|
||||
|
||||
creators["prince malchezaar infernals are spawned"] =
|
||||
&RaidKarazhanTriggerContext::prince_malchezaar_infernals_are_spawned;
|
||||
|
||||
creators["prince malchezaar boss engaged by main tank"] =
|
||||
&RaidKarazhanTriggerContext::prince_malchezaar_boss_engaged_by_main_tank;
|
||||
|
||||
// Nightbane
|
||||
creators["nightbane boss engaged by main tank"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_boss_engaged_by_main_tank;
|
||||
|
||||
creators["nightbane ranged bots are in charred earth"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_ranged_bots_are_in_charred_earth;
|
||||
|
||||
creators["nightbane main tank is susceptible to fear"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_main_tank_is_susceptible_to_fear;
|
||||
|
||||
creators["nightbane pets ignore collision to chase flying boss"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_pets_ignore_collision_to_chase_flying_boss;
|
||||
|
||||
creators["nightbane boss is flying"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_boss_is_flying;
|
||||
|
||||
creators["nightbane need to manage timers and trackers"] =
|
||||
&RaidKarazhanTriggerContext::nightbane_need_to_manage_timers_and_trackers;
|
||||
}
|
||||
|
||||
private:
|
||||
// Trash
|
||||
static Trigger* mana_warp_is_about_to_explode(
|
||||
PlayerbotAI* botAI) { return new ManaWarpIsAboutToExplodeTrigger(botAI); }
|
||||
|
||||
// Attumen the Huntsman
|
||||
static Trigger* attumen_the_huntsman_need_target_priority(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanNeedTargetPriorityTrigger(botAI); }
|
||||
|
||||
static Trigger* attumen_the_huntsman_attumen_spawned(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanAttumenSpawnedTrigger(botAI); }
|
||||
|
||||
static Trigger* attumen_the_huntsman_attumen_is_mounted(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanAttumenIsMountedTrigger(botAI); }
|
||||
|
||||
static Trigger* attumen_the_huntsman_boss_wipes_aggro_when_mounting(
|
||||
PlayerbotAI* botAI) { return new AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger(botAI); }
|
||||
|
||||
// Moroes
|
||||
static Trigger* moroes_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new MoroesBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* moroes_need_target_priority(
|
||||
PlayerbotAI* botAI) { return new MoroesNeedTargetPriorityTrigger(botAI); }
|
||||
|
||||
// Maiden of Virtue
|
||||
static Trigger* maiden_of_virtue_healers_are_stunned_by_repentance(
|
||||
PlayerbotAI* botAI) { return new MaidenOfVirtueHealersAreStunnedByRepentanceTrigger(botAI); }
|
||||
|
||||
static Trigger* maiden_of_virtue_holy_wrath_deals_chain_damage(
|
||||
PlayerbotAI* botAI) { return new MaidenOfVirtueHolyWrathDealsChainDamageTrigger(botAI); }
|
||||
|
||||
// The Big Bad Wolf
|
||||
static Trigger* big_bad_wolf_boss_engaged_by_tank(
|
||||
PlayerbotAI* botAI) { return new BigBadWolfBossEngagedByTankTrigger(botAI); }
|
||||
|
||||
static Trigger* big_bad_wolf_boss_is_chasing_little_red_riding_hood(
|
||||
PlayerbotAI* botAI) { return new BigBadWolfBossIsChasingLittleRedRidingHoodTrigger(botAI); }
|
||||
|
||||
// Romulo and Julianne
|
||||
static Trigger* romulo_and_julianne_both_bosses_revived(
|
||||
PlayerbotAI* botAI) { return new RomuloAndJulianneBothBossesRevivedTrigger(botAI); }
|
||||
|
||||
// The Wizard of Oz
|
||||
static Trigger* wizard_of_oz_need_target_priority(
|
||||
PlayerbotAI* botAI) { return new WizardOfOzNeedTargetPriorityTrigger(botAI); }
|
||||
|
||||
static Trigger* wizard_of_oz_strawman_is_vulnerable_to_fire(
|
||||
PlayerbotAI* botAI) { return new WizardOfOzStrawmanIsVulnerableToFireTrigger(botAI); }
|
||||
|
||||
// The Curator
|
||||
static Trigger* the_curator_astral_flare_spawned(
|
||||
PlayerbotAI* botAI) { return new TheCuratorAstralFlareSpawnedTrigger(botAI); }
|
||||
|
||||
static Trigger* the_curator_boss_engaged_by_tanks(
|
||||
PlayerbotAI* botAI) { return new TheCuratorBossEngagedByTanksTrigger(botAI); }
|
||||
|
||||
static Trigger* the_curator_astral_flares_cast_arcing_sear(
|
||||
PlayerbotAI* botAI) { return new TheCuratorBossAstralFlaresCastArcingSearTrigger(botAI); }
|
||||
|
||||
// Terestian Illhoof
|
||||
static Trigger* terestian_illhoof_need_target_priority(
|
||||
PlayerbotAI* botAI) { return new TerestianIllhoofNeedTargetPriorityTrigger(botAI); }
|
||||
|
||||
// Shade of Aran
|
||||
static Trigger* shade_of_aran_arcane_explosion_is_casting(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranArcaneExplosionIsCastingTrigger(botAI); }
|
||||
|
||||
static Trigger* shade_of_aran_flame_wreath_is_active(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranFlameWreathIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* shade_of_aran_conjured_elementals_summoned(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranConjuredElementalsSummonedTrigger(botAI); }
|
||||
|
||||
static Trigger* shade_of_aran_boss_uses_counterspell_and_blizzard(
|
||||
PlayerbotAI* botAI) { return new ShadeOfAranBossUsesCounterspellAndBlizzardTrigger(botAI); }
|
||||
|
||||
// Netherspite
|
||||
static Trigger* netherspite_red_beam_is_active(
|
||||
PlayerbotAI* botAI) { return new NetherspiteRedBeamIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* netherspite_blue_beam_is_active(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBlueBeamIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* netherspite_green_beam_is_active(
|
||||
PlayerbotAI* botAI) { return new NetherspiteGreenBeamIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* netherspite_bot_is_not_beam_blocker(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBotIsNotBeamBlockerTrigger(botAI); }
|
||||
|
||||
static Trigger* netherspite_boss_is_banished(
|
||||
PlayerbotAI* botAI) { return new NetherspiteBossIsBanishedTrigger(botAI); }
|
||||
|
||||
static Trigger* netherspite_need_to_manage_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new NetherspiteNeedToManageTimersAndTrackersTrigger(botAI); }
|
||||
|
||||
// Prince Malchezaar
|
||||
static Trigger* prince_malchezaar_bot_is_enfeebled(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarBotIsEnfeebledTrigger(botAI); }
|
||||
|
||||
static Trigger* prince_malchezaar_infernals_are_spawned(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarInfernalsAreSpawnedTrigger(botAI); }
|
||||
|
||||
static Trigger* prince_malchezaar_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new PrinceMalchezaarBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
// Nightbane
|
||||
static Trigger* nightbane_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new NightbaneBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* nightbane_ranged_bots_are_in_charred_earth(
|
||||
PlayerbotAI* botAI) { return new NightbaneRangedBotsAreInCharredEarthTrigger(botAI); }
|
||||
|
||||
static Trigger* nightbane_main_tank_is_susceptible_to_fear(
|
||||
PlayerbotAI* botAI) { return new NightbaneMainTankIsSusceptibleToFearTrigger(botAI); }
|
||||
|
||||
static Trigger* nightbane_pets_ignore_collision_to_chase_flying_boss(
|
||||
PlayerbotAI* botAI) { return new NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* nightbane_boss_is_flying(
|
||||
PlayerbotAI* botAI) { return new NightbaneBossIsFlyingTrigger(botAI); }
|
||||
|
||||
static Trigger* nightbane_need_to_manage_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new NightbaneNeedToManageTimersAndTrackersTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
162
src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.cpp
Normal file
162
src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "RaidKarazhanStrategy.h"
|
||||
#include "RaidKarazhanMultipliers.h"
|
||||
|
||||
void RaidKarazhanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// Trash
|
||||
triggers.push_back(new TriggerNode("mana warp is about to explode",
|
||||
{ NextAction("mana warp stun creature before warp breach", ACTION_EMERGENCY + 6) }
|
||||
));
|
||||
|
||||
// Attumen the Huntsman
|
||||
triggers.push_back(new TriggerNode("attumen the huntsman need target priority",
|
||||
{ NextAction("attumen the huntsman mark target", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("attumen the huntsman attumen spawned",
|
||||
{ NextAction("attumen the huntsman split bosses", ACTION_RAID + 2) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("attumen the huntsman attumen is mounted",
|
||||
{ NextAction("attumen the huntsman stack behind", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("attumen the huntsman boss wipes aggro when mounting",
|
||||
{ NextAction("attumen the huntsman manage dps timer", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
// Moroes
|
||||
triggers.push_back(new TriggerNode("moroes boss engaged by main tank",
|
||||
{ NextAction("moroes main tank attack boss", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("moroes need target priority",
|
||||
{ NextAction("moroes mark target", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Maiden of Virtue
|
||||
triggers.push_back(new TriggerNode("maiden of virtue healers are stunned by repentance",
|
||||
{ NextAction("maiden of virtue move boss to healer", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("maiden of virtue holy wrath deals chain damage",
|
||||
{ NextAction("maiden of virtue position ranged", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// The Big Bad Wolf
|
||||
triggers.push_back(new TriggerNode("big bad wolf boss is chasing little red riding hood",
|
||||
{ NextAction("big bad wolf run away from boss", ACTION_EMERGENCY + 6) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("big bad wolf boss engaged by tank",
|
||||
{ NextAction("big bad wolf position boss", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Romulo and Julianne
|
||||
triggers.push_back(new TriggerNode("romulo and julianne both bosses revived",
|
||||
{ NextAction("romulo and julianne mark target", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// The Wizard of Oz
|
||||
triggers.push_back(new TriggerNode("wizard of oz need target priority",
|
||||
{ NextAction("wizard of oz mark target", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("wizard of oz strawman is vulnerable to fire",
|
||||
{ NextAction("wizard of oz scorch strawman", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
// The Curator
|
||||
triggers.push_back(new TriggerNode("the curator astral flare spawned",
|
||||
{ NextAction("the curator mark astral flare", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("the curator boss engaged by tanks",
|
||||
{ NextAction("the curator position boss", ACTION_RAID + 2) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("the curator astral flares cast arcing sear",
|
||||
{ NextAction("the curator spread ranged", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
// Terestian Illhoof
|
||||
triggers.push_back(new TriggerNode("terestian illhoof need target priority",
|
||||
{ NextAction("terestian illhoof mark target", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Shade of Aran
|
||||
triggers.push_back(new TriggerNode("shade of aran arcane explosion is casting",
|
||||
{ NextAction("shade of aran run away from arcane explosion", ACTION_EMERGENCY + 6) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("shade of aran flame wreath is active",
|
||||
{ NextAction("shade of aran stop moving during flame wreath", ACTION_EMERGENCY + 7) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("shade of aran conjured elementals summoned",
|
||||
{ NextAction("shade of aran mark conjured elemental", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("shade of aran boss uses counterspell and blizzard",
|
||||
{ NextAction("shade of aran ranged maintain distance", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
// Netherspite
|
||||
triggers.push_back(new TriggerNode("netherspite red beam is active",
|
||||
{ NextAction("netherspite block red beam", ACTION_EMERGENCY + 8) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("netherspite blue beam is active",
|
||||
{ NextAction("netherspite block blue beam", ACTION_EMERGENCY + 8) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("netherspite green beam is active",
|
||||
{ NextAction("netherspite block green beam", ACTION_EMERGENCY + 8) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("netherspite bot is not beam blocker",
|
||||
{ NextAction("netherspite avoid beam and void zone", ACTION_EMERGENCY + 7) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("netherspite boss is banished",
|
||||
{ NextAction("netherspite banish phase avoid void zone", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("netherspite need to manage timers and trackers",
|
||||
{ NextAction("netherspite manage timers and trackers", ACTION_EMERGENCY + 10) }
|
||||
));
|
||||
|
||||
// Prince Malchezaar
|
||||
triggers.push_back(new TriggerNode("prince malchezaar bot is enfeebled",
|
||||
{ NextAction("prince malchezaar enfeebled avoid hazard", ACTION_EMERGENCY + 6) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("prince malchezaar infernals are spawned",
|
||||
{ NextAction("prince malchezaar non tank avoid infernal", ACTION_EMERGENCY + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("prince malchezaar boss engaged by main tank",
|
||||
{ NextAction("prince malchezaar main tank movement", ACTION_EMERGENCY + 6) }
|
||||
));
|
||||
|
||||
// Nightbane
|
||||
triggers.push_back(new TriggerNode("nightbane boss engaged by main tank",
|
||||
{ NextAction("nightbane ground phase position boss", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("nightbane ranged bots are in charred earth",
|
||||
{ NextAction("nightbane ground phase rotate ranged positions", ACTION_EMERGENCY + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("nightbane main tank is susceptible to fear",
|
||||
{ NextAction("nightbane cast fear ward on main tank", ACTION_RAID + 2) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("nightbane pets ignore collision to chase flying boss",
|
||||
{ NextAction("nightbane control pet aggression", ACTION_RAID + 2) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("nightbane boss is flying",
|
||||
{ NextAction("nightbane flight phase movement", ACTION_RAID + 1) }
|
||||
));
|
||||
triggers.push_back(new TriggerNode("nightbane need to manage timers and trackers",
|
||||
{ NextAction("nightbane manage timers and trackers", ACTION_EMERGENCY + 10) }
|
||||
));
|
||||
}
|
||||
|
||||
void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
|
||||
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
|
||||
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
|
||||
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
|
||||
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));
|
||||
multipliers.push_back(new NetherspiteKeepBlockingBeamMultiplier(botAI));
|
||||
multipliers.push_back(new NetherspiteWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new PrinceMalchezaarDisableAvoidAoeMultiplier(botAI));
|
||||
multipliers.push_back(new PrinceMalchezaarEnfeebleKeepDistanceMultiplier(botAI));
|
||||
multipliers.push_back(new PrinceMalchezaarDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
multipliers.push_back(new NightbaneDisablePetsMultiplier(botAI));
|
||||
multipliers.push_back(new NightbaneWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new NightbaneDisableAvoidAoeMultiplier(botAI));
|
||||
multipliers.push_back(new NightbaneDisableMovementMultiplier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h
Normal file
18
src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_
|
||||
#define _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidKarazhanStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidKarazhanStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "karazhan"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
385
src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp
Normal file
385
src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
#include "RaidKarazhanTriggers.h"
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
bool ManaWarpIsAboutToExplodeTrigger::IsActive()
|
||||
{
|
||||
Unit* manaWarp = AI_VALUE2(Unit*, "find target", "mana warp");
|
||||
return manaWarp && manaWarp->GetHealthPct() < 15;
|
||||
}
|
||||
|
||||
bool AttumenTheHuntsmanNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||
return midnight != nullptr;
|
||||
}
|
||||
|
||||
bool AttumenTheHuntsmanAttumenSpawnedTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||
return false;
|
||||
|
||||
Unit* attumen = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN);
|
||||
return attumen != nullptr;
|
||||
}
|
||||
|
||||
bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||
return attumenMounted && attumenMounted->GetVictim() != bot;
|
||||
}
|
||||
|
||||
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||
return midnight != nullptr;
|
||||
}
|
||||
|
||||
bool MoroesBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes");
|
||||
return moroes != nullptr;
|
||||
}
|
||||
|
||||
bool MoroesNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsDps(bot))
|
||||
return false;
|
||||
|
||||
Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe");
|
||||
Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi");
|
||||
Unit* keira = AI_VALUE2(Unit*, "find target", "lady keira berrybuck");
|
||||
Unit* rafe = AI_VALUE2(Unit*, "find target", "baron rafe dreuger");
|
||||
Unit* robin = AI_VALUE2(Unit*, "find target", "lord robin daris");
|
||||
Unit* crispin = AI_VALUE2(Unit*, "find target", "lord crispin ference");
|
||||
|
||||
Unit* target = GetFirstAliveUnit({ dorothea, catriona, keira, rafe, robin, crispin });
|
||||
return target != nullptr;
|
||||
}
|
||||
|
||||
bool MaidenOfVirtueHealersAreStunnedByRepentanceTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
||||
return maiden && maiden->GetVictim() == bot;
|
||||
}
|
||||
|
||||
bool MaidenOfVirtueHolyWrathDealsChainDamageTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
||||
return maiden != nullptr;
|
||||
}
|
||||
|
||||
bool BigBadWolfBossEngagedByTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot) || bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
|
||||
return false;
|
||||
|
||||
Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
||||
return wolf != nullptr;
|
||||
}
|
||||
|
||||
bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
||||
{
|
||||
if (!bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
|
||||
return false;
|
||||
|
||||
Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
||||
return wolf != nullptr;
|
||||
}
|
||||
|
||||
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||
if (!romulo)
|
||||
return false;
|
||||
|
||||
Unit* julianne = AI_VALUE2(Unit*, "find target", "julianne");
|
||||
if (!julianne)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||
Unit* tito = AI_VALUE2(Unit*, "find target", "tito");
|
||||
Unit* roar = AI_VALUE2(Unit*, "find target", "roar");
|
||||
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
|
||||
Unit* tinhead = AI_VALUE2(Unit*, "find target", "tinhead");
|
||||
Unit* crone = AI_VALUE2(Unit*, "find target", "the crone");
|
||||
|
||||
Unit* target = GetFirstAliveUnit({ dorothee, tito, roar, strawman, tinhead, crone });
|
||||
return target != nullptr;
|
||||
}
|
||||
|
||||
bool WizardOfOzStrawmanIsVulnerableToFireTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_MAGE)
|
||||
return false;
|
||||
|
||||
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
|
||||
return strawman && strawman->IsAlive();
|
||||
}
|
||||
|
||||
bool TheCuratorAstralFlareSpawnedTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsDps(bot))
|
||||
return false;
|
||||
|
||||
Unit* flare = AI_VALUE2(Unit*, "find target", "astral flare");
|
||||
return flare != nullptr;
|
||||
}
|
||||
|
||||
bool TheCuratorBossEngagedByTanksTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0))
|
||||
return false;
|
||||
|
||||
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||
return curator != nullptr;
|
||||
}
|
||||
|
||||
bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||
return curator != nullptr;
|
||||
}
|
||||
|
||||
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||
return illhoof != nullptr;
|
||||
}
|
||||
|
||||
bool ShadeOfAranArcaneExplosionIsCastingTrigger::IsActive()
|
||||
{
|
||||
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||
return aran && aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION) &&
|
||||
!IsFlameWreathActive(botAI, bot);
|
||||
}
|
||||
|
||||
bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||
return aran && IsFlameWreathActive(botAI, bot);
|
||||
}
|
||||
|
||||
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||
return elemental && elemental->IsAlive() &&
|
||||
!elemental->HasAura(SPELL_WARLOCK_BANISH);
|
||||
}
|
||||
|
||||
bool ShadeOfAranBossUsesCounterspellAndBlizzardTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||
return aran && !(aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION)) &&
|
||||
!IsFlameWreathActive(botAI, bot);
|
||||
}
|
||||
|
||||
bool NetherspiteRedBeamIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return false;
|
||||
|
||||
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
||||
return redPortal != nullptr;
|
||||
}
|
||||
|
||||
bool NetherspiteBlueBeamIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return false;
|
||||
|
||||
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
||||
return bluePortal != nullptr;
|
||||
}
|
||||
|
||||
bool NetherspiteGreenBeamIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return false;
|
||||
|
||||
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
||||
return greenPortal != nullptr;
|
||||
}
|
||||
|
||||
bool NetherspiteBotIsNotBeamBlockerTrigger::IsActive()
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return false;
|
||||
|
||||
auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot);
|
||||
return bot != redBlocker && bot != blueBlocker && bot != greenBlocker;
|
||||
}
|
||||
|
||||
bool NetherspiteBossIsBanishedTrigger::IsActive()
|
||||
{
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
if (!netherspite || !netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
return false;
|
||||
|
||||
std::vector<Unit*> voidZones = GetAllVoidZones(botAI, bot);
|
||||
for (Unit* vz : voidZones)
|
||||
{
|
||||
if (bot->GetExactDist2d(vz) < 4.0f)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot))
|
||||
return false;
|
||||
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
return netherspite != nullptr;
|
||||
}
|
||||
|
||||
bool PrinceMalchezaarBotIsEnfeebledTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_ENFEEBLE);
|
||||
}
|
||||
|
||||
bool PrinceMalchezaarInfernalsAreSpawnedTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMainTank(bot) || bot->HasAura(SPELL_ENFEEBLE))
|
||||
return false;
|
||||
|
||||
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||
return malchezaar != nullptr;
|
||||
}
|
||||
|
||||
bool PrinceMalchezaarBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||
return malchezaar != nullptr;
|
||||
}
|
||||
|
||||
bool NightbaneBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
return nightbane && nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z;
|
||||
}
|
||||
|
||||
bool NightbaneRangedBotsAreInCharredEarthTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
return nightbane && nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z;
|
||||
}
|
||||
|
||||
bool NightbaneMainTankIsSusceptibleToFearTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_PRIEST)
|
||||
return false;
|
||||
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane)
|
||||
return false;
|
||||
|
||||
Player* mainTank = nullptr;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && botAI->IsMainTank(member))
|
||||
{
|
||||
mainTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainTank && !mainTank->HasAura(SPELL_FEAR_WARD) &&
|
||||
botAI->CanCastSpell("fear ward", mainTank);
|
||||
}
|
||||
|
||||
bool NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger::IsActive()
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane)
|
||||
return false;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
return pet && pet->IsAlive();
|
||||
}
|
||||
|
||||
bool NightbaneBossIsFlyingTrigger::IsActive()
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
if (!nightbane || nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z)
|
||||
return false;
|
||||
|
||||
const uint32 instanceId = nightbane->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
const uint8 flightPhaseDurationSeconds = 35;
|
||||
|
||||
return nightbaneFlightPhaseStartTimer.find(instanceId) != nightbaneFlightPhaseStartTimer.end() &&
|
||||
(now - nightbaneFlightPhaseStartTimer[instanceId] < flightPhaseDurationSeconds);
|
||||
}
|
||||
|
||||
bool NightbaneNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||
{
|
||||
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||
return nightbane != nullptr;
|
||||
}
|
||||
301
src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.h
Normal file
301
src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.h
Normal file
@@ -0,0 +1,301 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDKARAZHANTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
class ManaWarpIsAboutToExplodeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ManaWarpIsAboutToExplodeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "mana warp is about to explode") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanNeedTargetPriorityTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanNeedTargetPriorityTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman need target priority") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanAttumenSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanAttumenSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman attumen spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanAttumenIsMountedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanAttumenIsMountedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman attumen is mounted") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman boss wipes aggro when mounting") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MoroesBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MoroesBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "moroes boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MoroesNeedTargetPriorityTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MoroesNeedTargetPriorityTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "moroes need target priority") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MaidenOfVirtueHealersAreStunnedByRepentanceTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MaidenOfVirtueHealersAreStunnedByRepentanceTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "maiden of virtue healers are stunned by repentance") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MaidenOfVirtueHolyWrathDealsChainDamageTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MaidenOfVirtueHolyWrathDealsChainDamageTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "maiden of virtue holy wrath deals chain damage") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class BigBadWolfBossEngagedByTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BigBadWolfBossEngagedByTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "big bad wolf boss engaged by tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class BigBadWolfBossIsChasingLittleRedRidingHoodTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BigBadWolfBossIsChasingLittleRedRidingHoodTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "big bad wolf boss is chasing little red riding hood") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class RomuloAndJulianneBothBossesRevivedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RomuloAndJulianneBothBossesRevivedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "romulo and julianne both bosses revived") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class WizardOfOzNeedTargetPriorityTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
WizardOfOzNeedTargetPriorityTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "wizard of oz need target priority") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class WizardOfOzStrawmanIsVulnerableToFireTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
WizardOfOzStrawmanIsVulnerableToFireTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "wizard of oz strawman is vulnerable to fire") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
class TheCuratorAstralFlareSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheCuratorAstralFlareSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the curator astral flare spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheCuratorBossEngagedByTanksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheCuratorBossEngagedByTanksTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the curator boss engaged by tanks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheCuratorBossAstralFlaresCastArcingSearTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheCuratorBossAstralFlaresCastArcingSearTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the curator astral flares cast arcing sear") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TerestianIllhoofNeedTargetPriorityTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TerestianIllhoofNeedTargetPriorityTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "terestian illhoof need target priority") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ShadeOfAranArcaneExplosionIsCastingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ShadeOfAranArcaneExplosionIsCastingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran arcane explosion is casting") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ShadeOfAranFlameWreathIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ShadeOfAranFlameWreathIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran flame wreath is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ShadeOfAranConjuredElementalsSummonedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ShadeOfAranConjuredElementalsSummonedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran conjured elementals summoned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ShadeOfAranBossUsesCounterspellAndBlizzardTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ShadeOfAranBossUsesCounterspellAndBlizzardTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran boss uses counterspell and blizzard") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteRedBeamIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteRedBeamIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite red beam is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteBlueBeamIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteBlueBeamIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite blue beam is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteGreenBeamIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteGreenBeamIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite green beam is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteBotIsNotBeamBlockerTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteBotIsNotBeamBlockerTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite bot is not beam blocker") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteBossIsBanishedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteBossIsBanishedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite boss is banished") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NetherspiteNeedToManageTimersAndTrackersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NetherspiteNeedToManageTimersAndTrackersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "netherspite need to manage timers and trackers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarBotIsEnfeebledTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarBotIsEnfeebledTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar bot is enfeebled") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarInfernalsAreSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarInfernalsAreSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar infernals are spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class PrinceMalchezaarBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
PrinceMalchezaarBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbaneBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbaneBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbaneRangedBotsAreInCharredEarthTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbaneRangedBotsAreInCharredEarthTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane ranged bots are in charred earth") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbaneMainTankIsSusceptibleToFearTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbaneMainTankIsSusceptibleToFearTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane main tank is susceptible to fear") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane pets ignore collision to chase flying boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbaneBossIsFlyingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbaneBossIsFlyingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane boss is flying") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class NightbaneNeedToManageTimersAndTrackersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NightbaneNeedToManageTimersAndTrackersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "nightbane need to manage timers and trackers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
490
src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp
Normal file
490
src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace KarazhanHelpers
|
||||
{
|
||||
// Attumen the Huntsman
|
||||
std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||
// Big Bad Wolf
|
||||
std::unordered_map<ObjectGuid, uint8> bigBadWolfRunIndex;
|
||||
// Netherspite
|
||||
std::unordered_map<uint32, time_t> netherspiteDpsWaitTimer;
|
||||
std::unordered_map<ObjectGuid, time_t> redBeamMoveTimer;
|
||||
std::unordered_map<ObjectGuid, bool> lastBeamMoveSideways;
|
||||
// Nightbane
|
||||
std::unordered_map<uint32, time_t> nightbaneDpsWaitTimer;
|
||||
std::unordered_map<ObjectGuid, uint8> nightbaneTankStep;
|
||||
std::unordered_map<ObjectGuid, uint8> nightbaneRangedStep;
|
||||
std::unordered_map<uint32, time_t> nightbaneFlightPhaseStartTimer;
|
||||
std::unordered_map<ObjectGuid, bool> nightbaneRainOfBonesHit;
|
||||
|
||||
const Position MAIDEN_OF_VIRTUE_BOSS_POSITION = { -10945.881f, -2103.782f, 92.712f };
|
||||
const Position MAIDEN_OF_VIRTUE_RANGED_POSITION[8] =
|
||||
{
|
||||
{ -10931.178f, -2116.580f, 92.179f },
|
||||
{ -10925.828f, -2102.425f, 92.180f },
|
||||
{ -10933.089f, -2088.502f, 92.180f },
|
||||
{ -10947.590f, -2082.815f, 92.180f },
|
||||
{ -10960.912f, -2090.437f, 92.179f },
|
||||
{ -10966.017f, -2105.288f, 92.175f },
|
||||
{ -10959.242f, -2119.617f, 92.180f },
|
||||
{ -10944.495f, -2123.857f, 92.180f },
|
||||
};
|
||||
|
||||
const Position BIG_BAD_WOLF_BOSS_POSITION = { -10913.391f, -1773.508f, 90.477f };
|
||||
const Position BIG_BAD_WOLF_RUN_POSITION[4] =
|
||||
{
|
||||
{ -10875.456f, -1779.036f, 90.477f },
|
||||
{ -10872.281f, -1751.638f, 90.477f },
|
||||
{ -10910.492f, -1747.401f, 90.477f },
|
||||
{ -10913.391f, -1773.508f, 90.477f },
|
||||
};
|
||||
|
||||
const Position THE_CURATOR_BOSS_POSITION = { -11139.463f, -1884.645f, 165.765f };
|
||||
|
||||
const Position NIGHTBANE_TRANSITION_BOSS_POSITION = { -11160.646f, -1932.773f, 91.473f }; // near some ribs
|
||||
const Position NIGHTBANE_FINAL_BOSS_POSITION = { -11173.530f, -1940.707f, 91.473f };
|
||||
const Position NIGHTBANE_RANGED_POSITION1 = { -11145.949f, -1970.927f, 91.473f };
|
||||
const Position NIGHTBANE_RANGED_POSITION2 = { -11143.594f, -1954.981f, 91.473f };
|
||||
const Position NIGHTBANE_RANGED_POSITION3 = { -11159.778f, -1961.031f, 91.473f };
|
||||
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Only one bot is needed to set/reset instance-wide timers
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||
{
|
||||
for (Unit* unit : units)
|
||||
{
|
||||
if (unit && unit->IsAlive())
|
||||
return unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
||||
{
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||
return unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||
{
|
||||
Unit* nearestPlayer = nullptr;
|
||||
float nearestDistance = radius;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
|
||||
float distance = bot->GetExactDist2d(member);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
nearestPlayer = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
||||
Spell* currentSpell = aran ? aran->GetCurrentSpell(CURRENT_GENERIC_SPELL) : nullptr;
|
||||
|
||||
if (currentSpell && currentSpell->m_spellInfo &&
|
||||
currentSpell->m_spellInfo->Id == SPELL_FLAME_WREATH_CAST)
|
||||
return true;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (member->HasAura(SPELL_FLAME_WREATH_AURA))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Red beam blockers: tank bots, no Nether Exhaustion Red
|
||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
std::vector<Player*> redBlockers;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) ||
|
||||
member->HasAura(SPELL_NETHER_EXHAUSTION_RED))
|
||||
continue;
|
||||
|
||||
redBlockers.push_back(member);
|
||||
}
|
||||
}
|
||||
|
||||
return redBlockers;
|
||||
}
|
||||
|
||||
// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and <24 stacks of Blue Beam debuff
|
||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
std::vector<Player*> blueBlockers;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_BLUE);
|
||||
Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF);
|
||||
bool overStack = blueBuff && blueBuff->GetStackAmount() >= 24;
|
||||
|
||||
bool isDps = botAI->IsDps(member);
|
||||
bool isWarrior = member->getClass() == CLASS_WARRIOR;
|
||||
bool isRogue = member->getClass() == CLASS_ROGUE;
|
||||
|
||||
if (isDps && !isWarrior && !isRogue && !hasExhaustion && !overStack)
|
||||
blueBlockers.push_back(member);
|
||||
}
|
||||
}
|
||||
|
||||
return blueBlockers;
|
||||
}
|
||||
|
||||
// Green beam blockers:
|
||||
// (1) Prioritize Rogues and non-tank Warrior bots, no Nether Exhaustion Green
|
||||
// (2) Then assign Healer bots, no Nether Exhaustion Green and <24 stacks of Green Beam debuff
|
||||
std::vector<Player*> GetGreenBlockers(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
std::vector<Player*> greenBlockers;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
|
||||
bool isRogue = member->getClass() == CLASS_ROGUE;
|
||||
bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member);
|
||||
bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion;
|
||||
|
||||
if (eligibleRogueWarrior)
|
||||
greenBlockers.push_back(member);
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
|
||||
Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF);
|
||||
bool overStack = greenBuff && greenBuff->GetStackAmount() >= 24;
|
||||
bool isHealer = botAI->IsHeal(member);
|
||||
bool eligibleHealer = isHealer && !hasExhaustion && !overStack;
|
||||
|
||||
if (eligibleHealer)
|
||||
greenBlockers.push_back(member);
|
||||
}
|
||||
}
|
||||
|
||||
return greenBlockers;
|
||||
}
|
||||
|
||||
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
static ObjectGuid currentRedBlocker;
|
||||
static ObjectGuid currentGreenBlocker;
|
||||
static ObjectGuid currentBlueBlocker;
|
||||
|
||||
Player* redBlocker = nullptr;
|
||||
Player* greenBlocker = nullptr;
|
||||
Player* blueBlocker = nullptr;
|
||||
|
||||
std::vector<Player*> redBlockers = GetRedBlockers(botAI, bot);
|
||||
if (!redBlockers.empty())
|
||||
{
|
||||
auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* player)
|
||||
{
|
||||
return player && player->GetGUID() == currentRedBlocker;
|
||||
});
|
||||
|
||||
if (it != redBlockers.end())
|
||||
redBlocker = *it;
|
||||
else
|
||||
redBlocker = redBlockers.front();
|
||||
|
||||
currentRedBlocker = redBlocker ? redBlocker->GetGUID() : ObjectGuid::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRedBlocker = ObjectGuid::Empty;
|
||||
redBlocker = nullptr;
|
||||
}
|
||||
|
||||
std::vector<Player*> greenBlockers = GetGreenBlockers(botAI, bot);
|
||||
if (!greenBlockers.empty())
|
||||
{
|
||||
auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* player)
|
||||
{
|
||||
return player && player->GetGUID() == currentGreenBlocker;
|
||||
});
|
||||
|
||||
if (it != greenBlockers.end())
|
||||
greenBlocker = *it;
|
||||
else
|
||||
greenBlocker = greenBlockers.front();
|
||||
|
||||
currentGreenBlocker = greenBlocker ? greenBlocker->GetGUID() : ObjectGuid::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentGreenBlocker = ObjectGuid::Empty;
|
||||
greenBlocker = nullptr;
|
||||
}
|
||||
|
||||
std::vector<Player*> blueBlockers = GetBlueBlockers(botAI, bot);
|
||||
if (!blueBlockers.empty())
|
||||
{
|
||||
auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* player)
|
||||
{
|
||||
return player && player->GetGUID() == currentBlueBlocker;
|
||||
});
|
||||
|
||||
if (it != blueBlockers.end())
|
||||
blueBlocker = *it;
|
||||
else
|
||||
blueBlocker = blueBlockers.front();
|
||||
|
||||
currentBlueBlocker = blueBlocker ? blueBlocker->GetGUID() : ObjectGuid::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentBlueBlocker = ObjectGuid::Empty;
|
||||
blueBlocker = nullptr;
|
||||
}
|
||||
|
||||
return std::make_tuple(redBlocker, greenBlocker, blueBlocker);
|
||||
}
|
||||
|
||||
std::vector<Unit*> GetAllVoidZones(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
std::vector<Unit*> voidZones;
|
||||
const float radius = 30.0f;
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (!unit || unit->GetEntry() != NPC_VOID_ZONE)
|
||||
continue;
|
||||
|
||||
float dist = bot->GetExactDist2d(unit);
|
||||
if (dist < radius)
|
||||
voidZones.push_back(unit);
|
||||
}
|
||||
|
||||
return voidZones;
|
||||
}
|
||||
|
||||
bool IsSafePosition(float x, float y, float z, const std::vector<Unit*>& hazards, float hazardRadius)
|
||||
{
|
||||
for (Unit* hazard : hazards)
|
||||
{
|
||||
float dist = hazard->GetExactDist2d(x, y);
|
||||
if (dist < hazardRadius)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Unit*> GetSpawnedInfernals(PlayerbotAI* botAI)
|
||||
{
|
||||
std::vector<Unit*> infernals;
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->GetEntry() == NPC_NETHERSPITE_INFERNAL)
|
||||
infernals.push_back(unit);
|
||||
}
|
||||
|
||||
return infernals;
|
||||
}
|
||||
|
||||
bool IsStraightPathSafe(const Position& start, const Position& target, const std::vector<Unit*>& hazards,
|
||||
float hazardRadius, float stepSize)
|
||||
{
|
||||
float sx = start.GetPositionX();
|
||||
float sy = start.GetPositionY();
|
||||
float sz = start.GetPositionZ();
|
||||
float tx = target.GetPositionX();
|
||||
float ty = target.GetPositionY();
|
||||
float tz = target.GetPositionZ();
|
||||
|
||||
const float totalDist = start.GetExactDist2d(target.GetPositionX(), target.GetPositionY());
|
||||
if (totalDist == 0.0f)
|
||||
return true;
|
||||
|
||||
for (float checkDist = 0.0f; checkDist <= totalDist; checkDist += stepSize)
|
||||
{
|
||||
float t = checkDist / totalDist;
|
||||
float checkX = sx + (tx - sx) * t;
|
||||
float checkY = sy + (ty - sy) * t;
|
||||
float checkZ = sz + (tz - sz) * t;
|
||||
for (Unit* hazard : hazards)
|
||||
{
|
||||
const float hx = checkX - hazard->GetPositionX();
|
||||
const float hy = checkY - hazard->GetPositionY();
|
||||
if ((hx*hx + hy*hy) < hazardRadius * hazardRadius)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryFindSafePositionWithSafePath(
|
||||
Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ,
|
||||
const std::vector<Unit*>& hazards, float safeDistance, float stepSize, uint8 numAngles,
|
||||
float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ)
|
||||
{
|
||||
float bestMoveDist = std::numeric_limits<float>::max();
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < numAngles; ++i)
|
||||
{
|
||||
float angle = (2.0f * M_PI * i) / numAngles;
|
||||
float dx = cos(angle);
|
||||
float dy = sin(angle);
|
||||
|
||||
for (float dist = stepSize; dist <= maxSampleDist; dist += stepSize)
|
||||
{
|
||||
float x = centerX + dx * dist;
|
||||
float y = centerY + dy * dist;
|
||||
float z = centerZ;
|
||||
float destX = x, destY = y, destZ = z;
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, centerX, centerY, centerZ,
|
||||
destX, destY, destZ, true))
|
||||
continue;
|
||||
|
||||
if (!IsSafePosition(destX, destY, destZ, hazards, safeDistance))
|
||||
continue;
|
||||
|
||||
if (requireSafePath)
|
||||
{
|
||||
if (!IsStraightPathSafe(Position(originX, originY, originZ), Position(destX, destY, destZ),
|
||||
hazards, safeDistance, stepSize))
|
||||
continue;
|
||||
}
|
||||
|
||||
const float moveDist = Position(originX, originY, originZ).GetExactDist2d(destX, destY);
|
||||
if (moveDist < bestMoveDist)
|
||||
{
|
||||
bestMoveDist = moveDist;
|
||||
bestDestX = destX;
|
||||
bestDestY = destY;
|
||||
bestDestZ = destZ;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
136
src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h
Normal file
136
src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifndef _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
||||
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Position.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace KarazhanHelpers
|
||||
{
|
||||
enum KarazhanSpells
|
||||
{
|
||||
// Maiden of Virtue
|
||||
SPELL_REPENTANCE = 29511,
|
||||
|
||||
// Opera Event
|
||||
SPELL_LITTLE_RED_RIDING_HOOD = 30756,
|
||||
|
||||
// The Curator
|
||||
SPELL_CURATOR_EVOCATION = 30254,
|
||||
|
||||
// Shade of Aran
|
||||
SPELL_FLAME_WREATH_CAST = 30004,
|
||||
SPELL_FLAME_WREATH_AURA = 29946,
|
||||
SPELL_ARCANE_EXPLOSION = 29973,
|
||||
|
||||
// Netherspite
|
||||
SPELL_RED_BEAM_DEBUFF = 30421, // "Nether Portal - Perseverance" (player aura)
|
||||
SPELL_GREEN_BEAM_DEBUFF = 30422, // "Nether Portal - Serenity" (player aura)
|
||||
SPELL_BLUE_BEAM_DEBUFF = 30423, // "Nether Portal - Dominance" (player aura)
|
||||
SPELL_GREEN_BEAM_HEAL = 30467, // "Nether Portal - Serenity" (Netherspite aura)
|
||||
SPELL_NETHER_EXHAUSTION_RED = 38637,
|
||||
SPELL_NETHER_EXHAUSTION_GREEN = 38638,
|
||||
SPELL_NETHER_EXHAUSTION_BLUE = 38639,
|
||||
SPELL_NETHERSPITE_BANISHED = 39833, // "Vortex Shade Black"
|
||||
|
||||
// Prince Malchezaar
|
||||
SPELL_ENFEEBLE = 30843,
|
||||
|
||||
// Nightbane
|
||||
SPELL_CHARRED_EARTH = 30129,
|
||||
SPELL_BELLOWING_ROAR = 36922,
|
||||
SPELL_RAIN_OF_BONES = 37091,
|
||||
|
||||
// Warlock
|
||||
SPELL_WARLOCK_BANISH = 18647,
|
||||
|
||||
// Priest
|
||||
SPELL_FEAR_WARD = 6346,
|
||||
};
|
||||
|
||||
enum KarazhanNPCs
|
||||
{
|
||||
// Trash
|
||||
NPC_SPECTRAL_RETAINER = 16410,
|
||||
NPC_MANA_WARP = 16530,
|
||||
|
||||
// Attumen the Huntsman
|
||||
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||
|
||||
// Shade of Aran
|
||||
NPC_CONJURED_ELEMENTAL = 17167,
|
||||
|
||||
// Netherspite
|
||||
NPC_VOID_ZONE = 16697,
|
||||
NPC_GREEN_PORTAL = 17367, // "Nether Portal - Serenity <Healing Portal>"
|
||||
NPC_BLUE_PORTAL = 17368, // "Nether Portal - Dominance <Damage Portal>"
|
||||
NPC_RED_PORTAL = 17369, // "Nether Portal - Perseverance <Tanking Portal>"
|
||||
|
||||
// Prince Malchezaar
|
||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||
};
|
||||
|
||||
const uint32 KARAZHAN_MAP_ID = 532;
|
||||
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||
|
||||
// Attumen the Huntsman
|
||||
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||
// Big Bad Wolf
|
||||
extern std::unordered_map<ObjectGuid, uint8> bigBadWolfRunIndex;
|
||||
// Netherspite
|
||||
extern std::unordered_map<uint32, time_t> netherspiteDpsWaitTimer;
|
||||
extern std::unordered_map<ObjectGuid, time_t> redBeamMoveTimer;
|
||||
extern std::unordered_map<ObjectGuid, bool> lastBeamMoveSideways;
|
||||
// Nightbane
|
||||
extern std::unordered_map<uint32, time_t> nightbaneDpsWaitTimer;
|
||||
extern std::unordered_map<ObjectGuid, uint8> nightbaneTankStep;
|
||||
extern std::unordered_map<ObjectGuid, uint8> nightbaneRangedStep;
|
||||
extern std::unordered_map<uint32, time_t> nightbaneFlightPhaseStartTimer;
|
||||
extern std::unordered_map<ObjectGuid, bool> nightbaneRainOfBonesHit;
|
||||
|
||||
extern const Position MAIDEN_OF_VIRTUE_BOSS_POSITION;
|
||||
extern const Position MAIDEN_OF_VIRTUE_RANGED_POSITION[8];
|
||||
extern const Position BIG_BAD_WOLF_BOSS_POSITION;
|
||||
extern const Position BIG_BAD_WOLF_RUN_POSITION[4];
|
||||
extern const Position THE_CURATOR_BOSS_POSITION;
|
||||
extern const Position NIGHTBANE_TRANSITION_BOSS_POSITION;
|
||||
extern const Position NIGHTBANE_FINAL_BOSS_POSITION;
|
||||
extern const Position NIGHTBANE_RANGED_POSITION1;
|
||||
extern const Position NIGHTBANE_RANGED_POSITION2;
|
||||
extern const Position NIGHTBANE_RANGED_POSITION3;
|
||||
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
||||
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetGreenBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Unit*> GetAllVoidZones(PlayerbotAI *botAI, Player* bot);
|
||||
bool IsSafePosition (float x, float y, float z, const std::vector<Unit*>& hazards, float hazardRadius);
|
||||
std::vector<Unit*> GetSpawnedInfernals(PlayerbotAI* botAI);
|
||||
bool IsStraightPathSafe(
|
||||
const Position& start, const Position& target,
|
||||
const std::vector<Unit*>& hazards, float hazardRadius, float stepSize);
|
||||
bool TryFindSafePositionWithSafePath(
|
||||
Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ,
|
||||
const std::vector<Unit*>& hazards, float safeDistance, float stepSize, uint8 numAngles,
|
||||
float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ);
|
||||
}
|
||||
|
||||
#endif
|
||||
695
src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp
Normal file
695
src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp
Normal file
@@ -0,0 +1,695 @@
|
||||
#include "RaidMagtheridonActions.h"
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Creature.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return false;
|
||||
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
MarkTargetWithSquare(bot, channelerSquare);
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
MarkTargetWithStar(bot, channelerStar);
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
MarkTargetWithCircle(bot, channelerCircle);
|
||||
|
||||
// After first three channelers are dead, wait for Magtheridon to activate
|
||||
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()))
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
|
||||
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
return true;
|
||||
}
|
||||
|
||||
Creature* currentTarget = nullptr;
|
||||
std::string rtiName;
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
{
|
||||
currentTarget = channelerSquare;
|
||||
rtiName = "square";
|
||||
}
|
||||
else if (channelerStar && channelerStar->IsAlive())
|
||||
{
|
||||
currentTarget = channelerStar;
|
||||
rtiName = "star";
|
||||
}
|
||||
else if (channelerCircle && channelerCircle->IsAlive())
|
||||
{
|
||||
currentTarget = channelerCircle;
|
||||
rtiName = "circle";
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, rtiName, currentTarget);
|
||||
|
||||
if (currentTarget && bot->GetVictim() != currentTarget)
|
||||
return Attack(currentTarget);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (!channelerDiamond || !channelerDiamond->IsAlive())
|
||||
return false;
|
||||
|
||||
MarkTargetWithDiamond(bot, channelerDiamond);
|
||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||
|
||||
if (bot->GetVictim() != channelerDiamond)
|
||||
return Attack(channelerDiamond);
|
||||
|
||||
if (channelerDiamond->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (!channelerTriangle || !channelerTriangle->IsAlive())
|
||||
return false;
|
||||
|
||||
MarkTargetWithTriangle(bot, channelerTriangle);
|
||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||
|
||||
if (bot->GetVictim() != channelerTriangle)
|
||||
return Attack(channelerTriangle);
|
||||
|
||||
if (channelerTriangle->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Misdirect West & East Channelers to Main Tank
|
||||
bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
std::vector<Player*> hunters;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
|
||||
hunters.push_back(member);
|
||||
}
|
||||
|
||||
int hunterIndex = -1;
|
||||
for (size_t i = 0; i < hunters.size(); ++i)
|
||||
{
|
||||
if (hunters[i] == bot)
|
||||
{
|
||||
hunterIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Player* mainTank = nullptr;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsMainTank(member))
|
||||
{
|
||||
mainTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
if (mainTank && channelerStar && channelerStar->IsAlive() &&
|
||||
channelerStar->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
return botAI->CastSpell("misdirection", mainTank);
|
||||
|
||||
if (!bot->HasAura(SPELL_MISDIRECTION))
|
||||
return false;
|
||||
|
||||
if (botAI->CanCastSpell("steady shot", channelerStar))
|
||||
return botAI->CastSpell("steady shot", channelerStar);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
|
||||
channelerCircle->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
return botAI->CastSpell("misdirection", mainTank);
|
||||
|
||||
if (!bot->HasAura(SPELL_MISDIRECTION))
|
||||
return false;
|
||||
|
||||
if (botAI->CanCastSpell("steady shot", channelerCircle))
|
||||
return botAI->CastSpell("steady shot", channelerCircle);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Listed in order of priority
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
{
|
||||
SetRtiTarget(botAI, "square", channelerSquare);
|
||||
|
||||
if (bot->GetTarget() != channelerSquare->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerSquare->GetGUID());
|
||||
return Attack(channelerSquare);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
{
|
||||
SetRtiTarget(botAI, "star", channelerStar);
|
||||
|
||||
if (bot->GetTarget() != channelerStar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerStar->GetGUID());
|
||||
return Attack(channelerStar);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
{
|
||||
SetRtiTarget(botAI, "circle", channelerCircle);
|
||||
|
||||
if (bot->GetTarget() != channelerCircle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerCircle->GetGUID());
|
||||
return Attack(channelerCircle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (channelerDiamond && channelerDiamond->IsAlive())
|
||||
{
|
||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||
|
||||
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerDiamond->GetGUID());
|
||||
return Attack(channelerDiamond);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (channelerTriangle && channelerTriangle->IsAlive())
|
||||
{
|
||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||
|
||||
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerTriangle->GetGUID());
|
||||
return Attack(channelerTriangle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
||||
(!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()) &&
|
||||
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
|
||||
(!channelerTriangle || !channelerTriangle->IsAlive()))
|
||||
{
|
||||
SetRtiTarget(botAI, "cross", magtheridon);
|
||||
|
||||
if (bot->GetTarget() != magtheridon->GetGUID())
|
||||
{
|
||||
bot->SetSelection(magtheridon->GetGUID());
|
||||
return Attack(magtheridon);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign Burning Abyssals to Warlocks to Banish
|
||||
// Burning Abyssals in excess of Warlocks in party will be Feared
|
||||
bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
|
||||
std::vector<Unit*> abyssals;
|
||||
for (auto const& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_BURNING_ABYSSAL && unit->IsAlive())
|
||||
abyssals.push_back(unit);
|
||||
}
|
||||
|
||||
std::vector<Player*> warlocks;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member))
|
||||
warlocks.push_back(member);
|
||||
}
|
||||
|
||||
int warlockIndex = -1;
|
||||
for (size_t i = 0; i < warlocks.size(); ++i)
|
||||
{
|
||||
if (warlocks[i] == bot)
|
||||
{
|
||||
warlockIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
||||
{
|
||||
Unit* assignedAbyssal = abyssals[warlockIndex];
|
||||
if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true))
|
||||
return botAI->CastSpell("banish", assignedAbyssal);
|
||||
}
|
||||
|
||||
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
||||
{
|
||||
Unit* excessAbyssal = abyssals[i];
|
||||
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
|
||||
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
|
||||
return botAI->CastSpell("fear", excessAbyssal);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Main tank will back up to the Northern point of the room
|
||||
bool MagtheridonMainTankPositionBossAction::Execute(Event event)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return false;
|
||||
|
||||
MarkTargetWithCross(bot, magtheridon);
|
||||
SetRtiTarget(botAI, "cross", magtheridon);
|
||||
|
||||
if (bot->GetVictim() != magtheridon)
|
||||
return Attack(magtheridon);
|
||||
|
||||
if (magtheridon->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
|
||||
const float maxDistance = 2.0f;
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ranged DPS will remain within 25 yards of the center of the room
|
||||
// Healers will remain within 15 yards of a position that is between ranged DPS and the boss
|
||||
std::unordered_map<ObjectGuid, Position> MagtheridonSpreadRangedAction::initialPositions;
|
||||
std::unordered_map<ObjectGuid, bool> MagtheridonSpreadRangedAction::hasReachedInitialPosition;
|
||||
|
||||
bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
const uint32 instanceId = magtheridon->GetMap()->GetInstanceId();
|
||||
|
||||
// Wait for 6 seconds after Magtheridon activates to spread
|
||||
const uint8 spreadWaitSeconds = 6;
|
||||
auto it = spreadWaitTimer.find(instanceId);
|
||||
if (it == spreadWaitTimer.end() ||
|
||||
(time(nullptr) - it->second) < spreadWaitSeconds)
|
||||
return false;
|
||||
|
||||
auto cubeIt = botToCubeAssignment.find(bot->GetGUID());
|
||||
if (cubeIt != botToCubeAssignment.end())
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
auto timerIt = blastNovaTimer.find(instanceId);
|
||||
if (timerIt != blastNovaTimer.end())
|
||||
{
|
||||
time_t lastBlastNova = timerIt->second;
|
||||
if (now - lastBlastNova >= 49)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Player*> members;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive())
|
||||
members.push_back(member);
|
||||
}
|
||||
|
||||
bool isHealer = botAI->IsHeal(bot);
|
||||
const Location& center = isHealer
|
||||
? MagtheridonsLairLocations::HealerSpreadPosition
|
||||
: MagtheridonsLairLocations::RangedSpreadPosition;
|
||||
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
||||
float centerX = center.x;
|
||||
float centerY = center.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
const float radiusBuffer = 3.0f;
|
||||
|
||||
if (!initialPositions.count(bot->GetGUID()))
|
||||
{
|
||||
auto it = std::find(members.begin(), members.end(), bot);
|
||||
uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0;
|
||||
uint8 count = members.size();
|
||||
|
||||
float angle = 2 * M_PI * botIndex / count;
|
||||
float radius = static_cast<float>(rand()) / RAND_MAX * maxSpreadRadius;
|
||||
float targetX = centerX + radius * cos(angle);
|
||||
float targetY = centerY + radius * sin(angle);
|
||||
|
||||
initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ);
|
||||
hasReachedInitialPosition[bot->GetGUID()] = false;
|
||||
}
|
||||
|
||||
Position targetPosition = initialPositions[bot->GetGUID()];
|
||||
if (!hasReachedInitialPosition[bot->GetGUID()])
|
||||
{
|
||||
if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f))
|
||||
{
|
||||
float destX = targetPosition.GetPositionX();
|
||||
float destY = targetPosition.GetPositionY();
|
||||
float destZ = targetPosition.GetPositionZ();
|
||||
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(),
|
||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||
}
|
||||
|
||||
float distToCenter = bot->GetExactDist2d(centerX, centerY);
|
||||
|
||||
if (distToCenter > maxSpreadRadius + radiusBuffer)
|
||||
{
|
||||
float angle = static_cast<float>(rand()) / RAND_MAX * 2.0f * M_PI;
|
||||
float radius = static_cast<float>(rand()) / RAND_MAX * maxSpreadRadius;
|
||||
float targetX = centerX + radius * cos(angle);
|
||||
float targetY = centerY + radius * sin(angle);
|
||||
|
||||
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), targetX, targetY, centerZ))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// For bots that are assigned to click cubes
|
||||
// Magtheridon casts Blast Nova every 54.35 to 55.40s, with a 2s cast time
|
||||
bool MagtheridonUseManticronCubeAction::Execute(Event event)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return false;
|
||||
|
||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||
const CubeInfo& cubeInfo = it->second;
|
||||
GameObject* cube = botAI->GetGameObject(cubeInfo.guid);
|
||||
if (!cube)
|
||||
return false;
|
||||
|
||||
// Release cubes after Blast Nova is interrupted
|
||||
if (HandleCubeRelease(magtheridon, cube))
|
||||
return true;
|
||||
|
||||
// Check if cube logic should be active (49+ second rule)
|
||||
if (!ShouldActivateCubeLogic(magtheridon))
|
||||
return false;
|
||||
|
||||
// Handle active cube logic based on Blast Nova casting state
|
||||
bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||
|
||||
if (!blastNovaActive)
|
||||
// After 49 seconds, wait at safe distance from cube
|
||||
return HandleWaitingPhase(cubeInfo);
|
||||
else
|
||||
// Blast Nova is casting - move to and click cube
|
||||
return HandleCubeInteraction(cubeInfo, cube);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* cube)
|
||||
{
|
||||
if (bot->HasAura(SPELL_SHADOW_GRASP) &&
|
||||
!(magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA)))
|
||||
{
|
||||
uint32 delay = urand(200, 3000);
|
||||
botAI->AddTimedEvent(
|
||||
[this]
|
||||
{
|
||||
botAI->Reset();
|
||||
},
|
||||
delay);
|
||||
botAI->SetNextCheckDelay(delay + 50);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MagtheridonUseManticronCubeAction::ShouldActivateCubeLogic(Unit* magtheridon)
|
||||
{
|
||||
auto timerIt = blastNovaTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||
if (timerIt == blastNovaTimer.end())
|
||||
return false;
|
||||
|
||||
time_t now = time(nullptr);
|
||||
time_t lastBlastNova = timerIt->second;
|
||||
|
||||
return (now - lastBlastNova >= 49);
|
||||
}
|
||||
|
||||
bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeInfo)
|
||||
{
|
||||
const float safeWaitDistance = 8.0f;
|
||||
float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y);
|
||||
|
||||
if (fabs(cubeDist - safeWaitDistance) > 1.0f)
|
||||
{
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
float angle = i * M_PI / 6.0f;
|
||||
float targetX = cubeInfo.x + cos(angle) * safeWaitDistance;
|
||||
float targetY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||
float targetZ = bot->GetPositionZ();
|
||||
|
||||
if (IsSafeFromMagtheridonHazards(botAI, bot, targetX, targetY, targetZ))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
float angle = static_cast<float>(rand()) / RAND_MAX * 2.0f * M_PI;
|
||||
float fallbackX = cubeInfo.x + cos(angle) * safeWaitDistance;
|
||||
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||
float fallbackZ = bot->GetPositionZ();
|
||||
|
||||
return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube)
|
||||
{
|
||||
const float interactDistance = 1.0f;
|
||||
float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y);
|
||||
|
||||
if (cubeDist > interactDistance)
|
||||
{
|
||||
if (cubeDist <= interactDistance + 1.0f)
|
||||
{
|
||||
uint32 delay = urand(200, 1500);
|
||||
botAI->AddTimedEvent(
|
||||
[this, cube]
|
||||
{
|
||||
bot->StopMoving();
|
||||
cube->Use(bot);
|
||||
},
|
||||
delay);
|
||||
botAI->SetNextCheckDelay(delay + 50);
|
||||
return true;
|
||||
}
|
||||
|
||||
float angle = atan2(cubeInfo.y - bot->GetPositionY(), cubeInfo.x - bot->GetPositionX());
|
||||
float targetX = cubeInfo.x - cos(angle) * interactDistance;
|
||||
float targetY = cubeInfo.y - sin(angle) * interactDistance;
|
||||
float targetZ = bot->GetPositionZ();
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The Blast Nova timer resets when Magtheridon stops casting it, which is needed to ensure that bots use cubes.
|
||||
// However, Magtheridon's Blast Nova cooldown actually runs from when he starts casting it. This means that if a Blast Nova
|
||||
// is not interrupted or takes too long to interrupt, the timer will be thrown off for the rest of the encounter.
|
||||
// Correcting this issue is complicated and probably would need some rewriting--I have not done so and
|
||||
// and view the current solution as sufficient since in TBC a missed Blast Nova would be a guaranteed wipe anyway.
|
||||
bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return false;
|
||||
|
||||
const uint32 instanceId = magtheridon->GetMap()->GetInstanceId();
|
||||
const time_t now = time(nullptr);
|
||||
|
||||
bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||
bool lastBlastNova = lastBlastNovaState[instanceId];
|
||||
|
||||
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
|
||||
blastNovaTimer[instanceId] = now;
|
||||
|
||||
lastBlastNovaState[instanceId] = blastNovaActive;
|
||||
|
||||
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
{
|
||||
spreadWaitTimer.try_emplace(instanceId, now);
|
||||
blastNovaTimer.try_emplace(instanceId, now);
|
||||
dpsWaitTimer.try_emplace(instanceId, now);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MagtheridonSpreadRangedAction::initialPositions.clear();
|
||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
|
||||
botToCubeAssignment.clear();
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
{
|
||||
spreadWaitTimer.erase(instanceId);
|
||||
blastNovaTimer.erase(instanceId);
|
||||
dpsWaitTimer.erase(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
100
src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h
Normal file
100
src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H
|
||||
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonMainTankAttackFirstThreeChannelersAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank attack first three channelers") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonFirstAssistTankAttackNWChannelerAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonFirstAssistTankAttackNWChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon first assist tank attack nw channeler") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonSecondAssistTankAttackNEChannelerAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonSecondAssistTankAttackNEChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon second assist tank attack ne channeler") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonMisdirectHellfireChannelers : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonMisdirectHellfireChannelers(PlayerbotAI* botAI, std::string const name = "magtheridon misdirect hellfire channelers") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonAssignDPSPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "magtheridon assign dps priority") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonWarlockCCBurningAbyssalAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonWarlockCCBurningAbyssalAction(PlayerbotAI* botAI, std::string const name = "magtheridon warlock cc burning abyssal") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MagtheridonMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank position boss") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonSpreadRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
static std::unordered_map<ObjectGuid, Position> initialPositions;
|
||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||
|
||||
MagtheridonSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "magtheridon spread ranged") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MagtheridonUseManticronCubeAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MagtheridonUseManticronCubeAction(PlayerbotAI* botAI, std::string const name = "magtheridon use manticron cube") : MovementAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
||||
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
||||
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
|
||||
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
|
||||
};
|
||||
|
||||
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
||||
{
|
||||
public:
|
||||
MagtheridonManageTimersAndAssignmentsAction(PlayerbotAI* botAI, std::string const name = "magtheridon manage timers and assignments") : Action(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <unordered_map>
|
||||
#include <ctime>
|
||||
|
||||
#include "RaidMagtheridonMultipliers.h"
|
||||
#include "RaidMagtheridonActions.h"
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "WarlockActions.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
// Don't do anything other than clicking cubes when Magtheridon is casting Blast Nova
|
||||
float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon)
|
||||
return 1.0f;
|
||||
|
||||
if (magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA))
|
||||
{
|
||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||
if (it != botToCubeAssignment.end())
|
||||
{
|
||||
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Bots will wait for 6 seconds after Magtheridon becomes attackable before engaging
|
||||
float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
return 1.0f;
|
||||
|
||||
const uint8 dpsWaitSeconds = 6;
|
||||
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||
if (it == dpsWaitTimer.end() ||
|
||||
(time(nullptr) - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
|
||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// No tank assist for offtanks during the channeler phase
|
||||
// So they don't try to pull channelers from each other or the main tank
|
||||
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
if (!magtheridon)
|
||||
return 1.0f;
|
||||
|
||||
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class MagtheridonUseManticronCubeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MagtheridonUseManticronCubeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon use manticron cube multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class MagtheridonWaitToAttackMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MagtheridonWaitToAttackMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon wait to attack multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class MagtheridonDisableOffTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MagtheridonDisableOffTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon disable off tank assist multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h
Normal file
37
src/Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H
|
||||
|
||||
#include "RaidMagtheridonActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidMagtheridonActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidMagtheridonActionContext()
|
||||
{
|
||||
creators["magtheridon main tank attack first three channelers"] = &RaidMagtheridonActionContext::magtheridon_main_tank_attack_first_three_channelers;
|
||||
creators["magtheridon first assist tank attack nw channeler"] = &RaidMagtheridonActionContext::magtheridon_first_assist_tank_attack_nw_channeler;
|
||||
creators["magtheridon second assist tank attack ne channeler"] = &RaidMagtheridonActionContext::magtheridon_second_assist_tank_attack_ne_channeler;
|
||||
creators["magtheridon misdirect hellfire channelers"] = &RaidMagtheridonActionContext::magtheridon_misdirect_hellfire_channelers;
|
||||
creators["magtheridon assign dps priority"] = &RaidMagtheridonActionContext::magtheridon_assign_dps_priority;
|
||||
creators["magtheridon warlock cc burning abyssal"] = &RaidMagtheridonActionContext::magtheridon_warlock_cc_burning_abyssal;
|
||||
creators["magtheridon main tank position boss"] = &RaidMagtheridonActionContext::magtheridon_main_tank_position_boss;
|
||||
creators["magtheridon spread ranged"] = &RaidMagtheridonActionContext::magtheridon_spread_ranged;
|
||||
creators["magtheridon use manticron cube"] = &RaidMagtheridonActionContext::magtheridon_use_manticron_cube;
|
||||
creators["magtheridon manage timers and assignments"] = &RaidMagtheridonActionContext::magtheridon_manage_timers_and_assignments;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* magtheridon_main_tank_attack_first_three_channelers(PlayerbotAI* botAI) { return new MagtheridonMainTankAttackFirstThreeChannelersAction(botAI); }
|
||||
static Action* magtheridon_first_assist_tank_attack_nw_channeler(PlayerbotAI* botAI) { return new MagtheridonFirstAssistTankAttackNWChannelerAction(botAI); }
|
||||
static Action* magtheridon_second_assist_tank_attack_ne_channeler(PlayerbotAI* botAI) { return new MagtheridonSecondAssistTankAttackNEChannelerAction(botAI); }
|
||||
static Action* magtheridon_misdirect_hellfire_channelers(PlayerbotAI* botAI) { return new MagtheridonMisdirectHellfireChannelers(botAI); }
|
||||
static Action* magtheridon_assign_dps_priority(PlayerbotAI* botAI) { return new MagtheridonAssignDPSPriorityAction(botAI); }
|
||||
static Action* magtheridon_warlock_cc_burning_abyssal(PlayerbotAI* botAI) { return new MagtheridonWarlockCCBurningAbyssalAction(botAI); }
|
||||
static Action* magtheridon_main_tank_position_boss(PlayerbotAI* botAI) { return new MagtheridonMainTankPositionBossAction(botAI); }
|
||||
static Action* magtheridon_spread_ranged(PlayerbotAI* botAI) { return new MagtheridonSpreadRangedAction(botAI); }
|
||||
static Action* magtheridon_use_manticron_cube(PlayerbotAI* botAI) { return new MagtheridonUseManticronCubeAction(botAI); }
|
||||
static Action* magtheridon_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonManageTimersAndAssignmentsAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h
Normal file
37
src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidMagtheridonTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class RaidMagtheridonTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidMagtheridonTriggerContext() : NamedObjectContext<Trigger>()
|
||||
{
|
||||
creators["magtheridon first three channelers engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_first_three_channelers_engaged_by_main_tank;
|
||||
creators["magtheridon nw channeler engaged by first assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_nw_channeler_engaged_by_first_assist_tank;
|
||||
creators["magtheridon ne channeler engaged by second assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_ne_channeler_engaged_by_second_assist_tank;
|
||||
creators["magtheridon pulling west and east channelers"] = &RaidMagtheridonTriggerContext::magtheridon_pull_west_and_east_channelers;
|
||||
creators["magtheridon determining kill order"] = &RaidMagtheridonTriggerContext::magtheridon_determining_kill_order;
|
||||
creators["magtheridon burning abyssal spawned"] = &RaidMagtheridonTriggerContext::magtheridon_burning_abyssal_spawned;
|
||||
creators["magtheridon boss engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_main_tank;
|
||||
creators["magtheridon boss engaged by ranged"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_ranged;
|
||||
creators["magtheridon incoming blast nova"] = &RaidMagtheridonTriggerContext::magtheridon_incoming_blast_nova;
|
||||
creators["magtheridon need to manage timers and assignments"] = &RaidMagtheridonTriggerContext::magtheridon_need_to_manage_timers_and_assignments;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* magtheridon_first_three_channelers_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(botAI); }
|
||||
static Trigger* magtheridon_nw_channeler_engaged_by_first_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(botAI); }
|
||||
static Trigger* magtheridon_ne_channeler_engaged_by_second_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(botAI); }
|
||||
static Trigger* magtheridon_pull_west_and_east_channelers(PlayerbotAI* botAI) { return new MagtheridonPullingWestAndEastChannelersTrigger(botAI); }
|
||||
static Trigger* magtheridon_determining_kill_order(PlayerbotAI* botAI) { return new MagtheridonDeterminingKillOrderTrigger(botAI); }
|
||||
static Trigger* magtheridon_burning_abyssal_spawned(PlayerbotAI* botAI) { return new MagtheridonBurningAbyssalSpawnedTrigger(botAI); }
|
||||
static Trigger* magtheridon_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByMainTankTrigger(botAI); }
|
||||
static Trigger* magtheridon_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByRangedTrigger(botAI); }
|
||||
static Trigger* magtheridon_incoming_blast_nova(PlayerbotAI* botAI) { return new MagtheridonIncomingBlastNovaTrigger(botAI); }
|
||||
static Trigger* magtheridon_need_to_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonNeedToManageTimersAndAssignmentsTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
42
src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.cpp
Normal file
42
src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "RaidMagtheridonStrategy.h"
|
||||
#include "RaidMagtheridonMultipliers.h"
|
||||
|
||||
void RaidMagtheridonStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("magtheridon incoming blast nova", {
|
||||
NextAction("magtheridon use manticron cube", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon need to manage timers and assignments", {
|
||||
NextAction("magtheridon manage timers and assignments", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon burning abyssal spawned", {
|
||||
NextAction("magtheridon warlock cc burning abyssal", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon boss engaged by ranged", {
|
||||
NextAction("magtheridon spread ranged", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon pulling west and east channelers", {
|
||||
NextAction("magtheridon misdirect hellfire channelers", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon boss engaged by main tank", {
|
||||
NextAction("magtheridon main tank position boss", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon first three channelers engaged by main tank", {
|
||||
NextAction("magtheridon main tank attack first three channelers", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon nw channeler engaged by first assist tank", {
|
||||
NextAction("magtheridon first assist tank attack nw channeler", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon ne channeler engaged by second assist tank", {
|
||||
NextAction("magtheridon second assist tank attack ne channeler", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("magtheridon determining kill order", {
|
||||
NextAction("magtheridon assign dps priority", ACTION_RAID + 1) }));
|
||||
}
|
||||
|
||||
void RaidMagtheridonStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new MagtheridonUseManticronCubeMultiplier(botAI));
|
||||
multipliers.push_back(new MagtheridonWaitToAttackMultiplier(botAI));
|
||||
multipliers.push_back(new MagtheridonDisableOffTankAssistMultiplier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h
Normal file
18
src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidMagtheridonStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidMagtheridonStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "magtheridon"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
128
src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp
Normal file
128
src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "RaidMagtheridonTriggers.h"
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
bool MagtheridonFirstThreeChannelersEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
return magtheridon && botAI->IsMainTank(bot) &&
|
||||
magtheridon->HasAura(SPELL_SHADOW_CAGE);
|
||||
}
|
||||
|
||||
bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
||||
channelerDiamond && channelerDiamond->IsAlive();
|
||||
}
|
||||
|
||||
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
||||
channelerTriangle && channelerTriangle->IsAlive();
|
||||
}
|
||||
|
||||
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
|
||||
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
||||
((channelerStar && channelerStar->IsAlive()) ||
|
||||
(channelerCircle && channelerCircle->IsAlive()));
|
||||
}
|
||||
|
||||
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
|
||||
return false;
|
||||
|
||||
return (channeler && channeler->IsAlive()) || (magtheridon &&
|
||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||
}
|
||||
|
||||
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (!magtheridon || bot->getClass() != CLASS_WARLOCK)
|
||||
return false;
|
||||
|
||||
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
return std::any_of(npcs.begin(), npcs.end(), [this](const ObjectGuid& npc)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
return unit && unit->GetEntry() == NPC_BURNING_ABYSSAL;
|
||||
});
|
||||
}
|
||||
|
||||
bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
return magtheridon && botAI->IsMainTank(bot) &&
|
||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE);
|
||||
}
|
||||
|
||||
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
|
||||
return magtheridon && botAI->IsRanged(bot) &&
|
||||
!(channeler && channeler->IsAlive());
|
||||
}
|
||||
|
||||
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group || !magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
return false;
|
||||
|
||||
bool needsReassign = botToCubeAssignment.empty();
|
||||
if (!needsReassign)
|
||||
{
|
||||
for (auto const& pair : botToCubeAssignment)
|
||||
{
|
||||
Player* assigned = ObjectAccessor::FindPlayer(pair.first);
|
||||
if (!assigned || !assigned->IsAlive())
|
||||
{
|
||||
needsReassign = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsReassign)
|
||||
{
|
||||
std::vector<CubeInfo> cubes = GetAllCubeInfosByDbGuids(bot->GetMap(), MANTICRON_CUBE_DB_GUIDS);
|
||||
AssignBotsToCubesByGuidAndCoords(group, cubes, botAI);
|
||||
}
|
||||
|
||||
return botToCubeAssignment.find(bot->GetGUID()) != botToCubeAssignment.end();
|
||||
}
|
||||
|
||||
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
return magtheridon;
|
||||
}
|
||||
77
src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.h
Normal file
77
src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
class MagtheridonFirstThreeChannelersEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon first three channelers engaged by main tank") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonNWChannelerEngagedByFirstAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon nw channeler engaged by first assist tank") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonNEChannelerEngagedBySecondAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon ne channeler engaged by second assist tank") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonPullingWestAndEastChannelersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonPullingWestAndEastChannelersTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon pulling west and east channelers") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonDeterminingKillOrderTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon determining kill order") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonBurningAbyssalSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonBurningAbyssalSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon burning abyssal spawned") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by main tank") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by ranged") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonIncomingBlastNovaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonIncomingBlastNovaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon incoming blast nova") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MagtheridonNeedToManageTimersAndAssignmentsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MagtheridonNeedToManageTimersAndAssignmentsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon need to manage timers and assignments") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
226
src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp
Normal file
226
src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Creature.h"
|
||||
#include "GameObject.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Map.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||
}
|
||||
|
||||
// Identify channelers by their database GUIDs
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
||||
{
|
||||
Map* map = bot->GetMap();
|
||||
if (!map)
|
||||
return nullptr;
|
||||
|
||||
auto it = map->GetCreatureBySpawnIdStore().find(dbGuid);
|
||||
if (it == map->GetCreatureBySpawnIdStore().end())
|
||||
return nullptr;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||
}
|
||||
|
||||
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
||||
|
||||
// Get the positions of all Manticron Cubes by their database GUIDs
|
||||
std::vector<CubeInfo> GetAllCubeInfosByDbGuids(Map* map, const std::vector<uint32>& cubeDbGuids)
|
||||
{
|
||||
std::vector<CubeInfo> cubes;
|
||||
if (!map)
|
||||
return cubes;
|
||||
|
||||
for (uint32 dbGuid : cubeDbGuids)
|
||||
{
|
||||
auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid);
|
||||
if (bounds.first == bounds.second)
|
||||
continue;
|
||||
|
||||
GameObject* go = bounds.first->second;
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
CubeInfo info;
|
||||
info.guid = go->GetGUID();
|
||||
info.x = go->GetPositionX();
|
||||
info.y = go->GetPositionY();
|
||||
info.z = go->GetPositionZ();
|
||||
cubes.push_back(info);
|
||||
}
|
||||
|
||||
return cubes;
|
||||
}
|
||||
|
||||
std::unordered_map<ObjectGuid, CubeInfo> botToCubeAssignment;
|
||||
|
||||
void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector<CubeInfo>& cubes, PlayerbotAI* botAI)
|
||||
{
|
||||
botToCubeAssignment.clear();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
size_t cubeIndex = 0;
|
||||
std::vector<Player*> candidates;
|
||||
|
||||
// Assign ranged DPS (excluding Warlocks) to cubes first
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref && cubeIndex < cubes.size(); ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !botAI->IsRangedDps(member, true) ||
|
||||
member->getClass() == CLASS_WARLOCK || !GET_PLAYERBOT_AI(member))
|
||||
continue;
|
||||
|
||||
candidates.push_back(member);
|
||||
if (candidates.size() >= cubes.size())
|
||||
break;
|
||||
}
|
||||
|
||||
// If there are still cubes left, assign any other non-tank bots
|
||||
if (candidates.size() < cubes.size())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember();
|
||||
ref && candidates.size() < cubes.size(); ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || botAI->IsTank(member))
|
||||
continue;
|
||||
|
||||
if (std::find(candidates.begin(), candidates.end(), member) == candidates.end())
|
||||
candidates.push_back(member);
|
||||
}
|
||||
}
|
||||
|
||||
for (Player* member : candidates)
|
||||
{
|
||||
if (cubeIndex >= cubes.size())
|
||||
break;
|
||||
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
botToCubeAssignment[member->GetGUID()] = cubes[cubeIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<uint32, bool> lastBlastNovaState;
|
||||
std::unordered_map<uint32, time_t> blastNovaTimer;
|
||||
std::unordered_map<uint32, time_t> spreadWaitTimer;
|
||||
std::unordered_map<uint32, time_t> dpsWaitTimer;
|
||||
|
||||
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z)
|
||||
{
|
||||
// Debris
|
||||
std::vector<Unit*> debrisHazards;
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (!unit || unit->GetEntry() != NPC_TARGET_TRIGGER)
|
||||
continue;
|
||||
debrisHazards.push_back(unit);
|
||||
}
|
||||
for (Unit* hazard : debrisHazards)
|
||||
{
|
||||
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2));
|
||||
if (dist < 9.0f)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conflagration
|
||||
GuidVector gos = *botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest game objects");
|
||||
for (auto const& goGuid : gos)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(goGuid);
|
||||
if (!go || go->GetEntry() != GO_BLAZE)
|
||||
continue;
|
||||
|
||||
float dist = std::sqrt(std::pow(x - go->GetPositionX(), 2) + std::pow(y - go->GetPositionY(), 2));
|
||||
if (dist < 5.0f)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
90
src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h
Normal file
90
src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H
|
||||
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Group.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
enum MagtheridonSpells
|
||||
{
|
||||
// Magtheridon
|
||||
SPELL_SHADOW_CAGE = 30205,
|
||||
SPELL_BLAST_NOVA = 30616,
|
||||
SPELL_SHADOW_GRASP = 30410,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647,
|
||||
SPELL_FEAR = 6215,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
};
|
||||
|
||||
enum MagtheridonNPCs
|
||||
{
|
||||
NPC_BURNING_ABYSSAL = 17454,
|
||||
NPC_TARGET_TRIGGER = 17474,
|
||||
};
|
||||
|
||||
enum MagtheridonObjects
|
||||
{
|
||||
GO_BLAZE = 181832,
|
||||
};
|
||||
|
||||
constexpr uint32 SOUTH_CHANNELER = 90978;
|
||||
constexpr uint32 WEST_CHANNELER = 90979;
|
||||
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
||||
constexpr uint32 EAST_CHANNELER = 90982;
|
||||
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
||||
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z, orientation;
|
||||
};
|
||||
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
extern const Location WaitingForMagtheridonPosition;
|
||||
extern const Location MagtheridonTankPosition;
|
||||
extern const Location NWChannelerTankPosition;
|
||||
extern const Location NEChannelerTankPosition;
|
||||
extern const Location RangedSpreadPosition;
|
||||
extern const Location HealerSpreadPosition;
|
||||
}
|
||||
|
||||
struct CubeInfo
|
||||
{
|
||||
ObjectGuid guid;
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
extern const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS;
|
||||
extern std::unordered_map<ObjectGuid, CubeInfo> botToCubeAssignment;
|
||||
std::vector<CubeInfo> GetAllCubeInfosByDbGuids(Map* map, const std::vector<uint32>& cubeDbGuids);
|
||||
void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector<CubeInfo>& cubes, PlayerbotAI* botAI);
|
||||
extern std::unordered_map<uint32, bool> lastBlastNovaState;
|
||||
extern std::unordered_map<uint32, time_t> blastNovaTimer;
|
||||
extern std::unordered_map<uint32, time_t> spreadWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> dpsWaitTimer;
|
||||
}
|
||||
|
||||
#endif
|
||||
215
src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp
Normal file
215
src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "RaidMcActions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
#include "RaidMcTriggers.h"
|
||||
#include "RaidMcHelpers.h"
|
||||
|
||||
static constexpr float LIVING_BOMB_DISTANCE = 20.0f;
|
||||
static constexpr float INFERNO_DISTANCE = 20.0f;
|
||||
|
||||
// don't get hit by Arcane Explosion but still be in casting range
|
||||
static constexpr float ARCANE_EXPLOSION_DISTANCE = 26.0f;
|
||||
|
||||
// dedicated tank positions; prevents assist tanks from positioning Core Ragers on steep walls on pull
|
||||
static const Position GOLEMAGG_TANK_POSITION{795.7308, -994.8848, -207.18661};
|
||||
static const Position CORE_RAGER_TANK_POSITION{846.6453, -1019.0639, -198.9819};
|
||||
|
||||
static constexpr float GOLEMAGGS_TRUST_DISTANCE = 30.0f;
|
||||
static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f;
|
||||
|
||||
using namespace MoltenCoreHelpers;
|
||||
|
||||
bool McMoveFromGroupAction::Execute(Event event)
|
||||
{
|
||||
return MoveFromGroup(LIVING_BOMB_DISTANCE);
|
||||
}
|
||||
|
||||
bool McMoveFromBaronGeddonAction::Execute(Event event)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||
{
|
||||
float distToTravel = INFERNO_DISTANCE - bot->GetDistance2d(boss);
|
||||
if (distToTravel > 0)
|
||||
{
|
||||
// Stop current spell first
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
|
||||
return MoveAway(boss, distToTravel);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McShazzrahMoveAwayAction::Execute(Event event)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah"))
|
||||
{
|
||||
float distToTravel = ARCANE_EXPLOSION_DISTANCE - bot->GetDistance2d(boss);
|
||||
if (distToTravel > 0)
|
||||
return MoveAway(boss, distToTravel);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McGolemaggMarkBossAction::Execute(Event event)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentSkullGuid = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
||||
if (currentSkullGuid.IsEmpty() || currentSkullGuid != boss->GetGUID())
|
||||
{
|
||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance,
|
||||
float stepDistance)
|
||||
{
|
||||
if (bot->GetVictim() != target)
|
||||
return Attack(target);
|
||||
if (target->GetVictim() == bot)
|
||||
{
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.GetPositionX(), tankPosition.GetPositionY());
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.GetPositionX() - bot->GetPositionX();
|
||||
float dY = tankPosition.GetPositionY() - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * stepDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * stepDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false,
|
||||
false, false, MovementPriority::MOVEMENT_COMBAT, true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
else if (botAI->DoSpecificAction("taunt spell", Event(), true))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const
|
||||
{
|
||||
coreRager1 = coreRager2 = nullptr;
|
||||
for (auto const& target : AI_VALUE(GuidVector, "possible targets no los"))
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CORE_RAGER)
|
||||
{
|
||||
if (coreRager1 == nullptr)
|
||||
coreRager1 = unit;
|
||||
else if (coreRager2 == nullptr)
|
||||
{
|
||||
coreRager2 = unit;
|
||||
break; // There should be no third Core Rager.
|
||||
}
|
||||
}
|
||||
}
|
||||
return coreRager1 != nullptr && coreRager2 != nullptr;
|
||||
}
|
||||
|
||||
bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event)
|
||||
{
|
||||
// At this point, we know we are not the last living tank in the group.
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||
{
|
||||
Unit* coreRager1;
|
||||
Unit* coreRager2;
|
||||
if (!FindCoreRagers(coreRager1, coreRager2))
|
||||
return false; // safety check
|
||||
|
||||
// We only need to move if the Core Ragers still have Golemagg's Trust
|
||||
if (coreRager1->HasAura(SPELL_GOLEMAGGS_TRUST) || coreRager2->HasAura(SPELL_GOLEMAGGS_TRUST))
|
||||
return MoveUnitToPosition(boss, GOLEMAGG_TANK_POSITION, boss->GetCombatReach());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
// Step 0: Filter additional assist tanks. We only need 2.
|
||||
bool isFirstAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 0, true);
|
||||
bool isSecondAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 1, true);
|
||||
if (!isFirstAssistTank && !isSecondAssistTank)
|
||||
return Attack(boss);
|
||||
|
||||
// Step 1: Find both Core Ragers
|
||||
Unit* coreRager1;
|
||||
Unit* coreRager2;
|
||||
if (!FindCoreRagers(coreRager1, coreRager2))
|
||||
return false; // safety check
|
||||
|
||||
// Step 2: Assign Core Rager to bot
|
||||
Unit* myCoreRager = nullptr;
|
||||
Unit* otherCoreRager = nullptr;
|
||||
if (isFirstAssistTank)
|
||||
{
|
||||
myCoreRager = coreRager1;
|
||||
otherCoreRager = coreRager2;
|
||||
}
|
||||
else // isSecondAssistTank is always true here
|
||||
{
|
||||
myCoreRager = coreRager2;
|
||||
otherCoreRager = coreRager1;
|
||||
}
|
||||
|
||||
// Step 3: Select the right target
|
||||
if (myCoreRager->GetVictim() != bot)
|
||||
{
|
||||
// Step 3.1: My Core Rager isn't attacking me. Attack until it does.
|
||||
if (bot->GetVictim() != myCoreRager)
|
||||
return Attack(myCoreRager);
|
||||
return botAI->DoSpecificAction("taunt spell", event, true);
|
||||
}
|
||||
|
||||
Unit* otherCoreRagerVictim = otherCoreRager->GetVictim();
|
||||
if (otherCoreRagerVictim) // Core Rager victim can be NULL
|
||||
{
|
||||
// Step 3.2: Check if the other Core Rager isn't attacking its assist tank.
|
||||
Player* otherCoreRagerPlayerVictim = otherCoreRagerVictim->ToPlayer();
|
||||
if (otherCoreRagerPlayerVictim &&
|
||||
!PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 0, true) &&
|
||||
!PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 1, true))
|
||||
{
|
||||
// Assume we are the only assist tank or the other assist tank is dead => pick up other Core Rager!
|
||||
if (bot->GetVictim() != otherCoreRager)
|
||||
return Attack(otherCoreRager);
|
||||
return botAI->DoSpecificAction("taunt spell", event, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (bot->GetVictim() != myCoreRager)
|
||||
return Attack(myCoreRager); // Step 3.3: Attack our Core Rager in case we previously switched in 3.2.
|
||||
|
||||
// Step 4: Prevent Golemagg's Trust on Core Ragers
|
||||
if (myCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST) ||
|
||||
(otherCoreRagerVictim == bot && otherCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST)))
|
||||
{
|
||||
// Step 4.1: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position)
|
||||
float bossDistanceToCoreRagerTankPosition = boss->GetExactDist2d(
|
||||
CORE_RAGER_TANK_POSITION.GetPositionX(), CORE_RAGER_TANK_POSITION.GetPositionY());
|
||||
if (bossDistanceToCoreRagerTankPosition > GOLEMAGGS_TRUST_DISTANCE)
|
||||
{
|
||||
float distanceToTankPosition = bot->GetExactDist2d(CORE_RAGER_TANK_POSITION.GetPositionX(),
|
||||
CORE_RAGER_TANK_POSITION.GetPositionY());
|
||||
if (distanceToTankPosition > CORE_RAGER_STEP_DISTANCE)
|
||||
return MoveUnitToPosition(myCoreRager, CORE_RAGER_TANK_POSITION, CORE_RAGER_STEP_DISTANCE);
|
||||
}
|
||||
|
||||
// Step 4.2: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust
|
||||
return MoveAway(boss, CORE_RAGER_STEP_DISTANCE, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
67
src/Ai/Raid/MoltenCore/Action/RaidMcActions.h
Normal file
67
src/Ai/Raid/MoltenCore/Action/RaidMcActions.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCACTIONS_H
|
||||
#define _PLAYERBOT_RAIDMCACTIONS_H
|
||||
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class McMoveFromGroupAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
McMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc move from group")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class McMoveFromBaronGeddonAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
McMoveFromBaronGeddonAction(PlayerbotAI* botAI, std::string const name = "mc move from baron geddon")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class McShazzrahMoveAwayAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
McShazzrahMoveAwayAction(PlayerbotAI* botAI, std::string const name = "mc shazzrah move away")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class McGolemaggMarkBossAction : public Action
|
||||
{
|
||||
public:
|
||||
McGolemaggMarkBossAction(PlayerbotAI* botAI, std::string const name = "mc golemagg mark boss")
|
||||
: Action(botAI, name) {};
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class McGolemaggTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
McGolemaggTankAction(PlayerbotAI* botAI, std::string const name)
|
||||
: AttackAction(botAI, name) {}
|
||||
protected:
|
||||
bool MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, float stepDistance = 3.0f);
|
||||
bool FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const;
|
||||
};
|
||||
|
||||
class McGolemaggMainTankAttackGolemaggAction : public McGolemaggTankAction
|
||||
{
|
||||
public:
|
||||
McGolemaggMainTankAttackGolemaggAction(PlayerbotAI* botAI, std::string const name = "mc golemagg main tank attack golemagg")
|
||||
: McGolemaggTankAction(botAI, name) {};
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class McGolemaggAssistTankAttackCoreRagerAction : public McGolemaggTankAction
|
||||
{
|
||||
public:
|
||||
McGolemaggAssistTankAttackCoreRagerAction(PlayerbotAI* botAI, std::string const name = "mc golemagg assist tank attack core rager")
|
||||
: McGolemaggTankAction(botAI, name) {};
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
117
src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp
Normal file
117
src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "RaidMcMultipliers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "RaidMcActions.h"
|
||||
#include "RaidMcHelpers.h"
|
||||
|
||||
using namespace MoltenCoreHelpers;
|
||||
|
||||
static bool IsDpsBotWithAoeAction(Player* bot, Action* action)
|
||||
{
|
||||
if (PlayerbotAI::IsDps(bot))
|
||||
{
|
||||
if (dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastConsecrationAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<CastWhirlwindAction*>(action) ||
|
||||
dynamic_cast<CastMagmaTotemAction*>(action) || dynamic_cast<CastExplosiveTrapAction*>(action) ||
|
||||
dynamic_cast<CastDeathAndDecayAction*>(action))
|
||||
return true;
|
||||
|
||||
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||
{
|
||||
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float GarrDisableDpsAoeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "garr"))
|
||||
{
|
||||
if (IsDpsBotWithAoeAction(bot, action))
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
static bool IsAllowedGeddonMovementAction(Action* action)
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<McMoveFromGroupAction*>(action) &&
|
||||
!dynamic_cast<McMoveFromBaronGeddonAction*>(action))
|
||||
return false;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float BaronGeddonAbilityMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||
{
|
||||
if (boss->HasAura(SPELL_INFERNO))
|
||||
{
|
||||
if (!IsAllowedGeddonMovementAction(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// No check for Baron Geddon, because bots may have the bomb even after Geddon died.
|
||||
if (bot->HasAura(SPELL_LIVING_BOMB))
|
||||
{
|
||||
if (!IsAllowedGeddonMovementAction(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
static bool IsSingleLivingTankInGroup(Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
if (PlayerbotAI::IsTank(member))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float GolemaggMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||
{
|
||||
if (PlayerbotAI::IsTank(bot) && IsSingleLivingTankInGroup(bot))
|
||||
{
|
||||
// Only one tank => Pick up Golemagg and the two Core Ragers
|
||||
if (dynamic_cast<McGolemaggMainTankAttackGolemaggAction*>(action) ||
|
||||
dynamic_cast<McGolemaggAssistTankAttackCoreRagerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
if (PlayerbotAI::IsAssistTank(bot))
|
||||
{
|
||||
// The first two assist tanks manage the Core Ragers. The remaining assist tanks attack the boss.
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
if (IsDpsBotWithAoeAction(bot, action))
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
27
src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.h
Normal file
27
src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDMCMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class GarrDisableDpsAoeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GarrDisableDpsAoeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "garr disable dps aoe multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class BaronGeddonAbilityMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
BaronGeddonAbilityMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "baron geddon ability multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
class GolemaggMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GolemaggMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "golemagg multiplier") {}
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
48
src/Ai/Raid/MoltenCore/RaidMcActionContext.h
Normal file
48
src/Ai/Raid/MoltenCore/RaidMcActionContext.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcActions.h"
|
||||
|
||||
class RaidMcActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidMcActionContext()
|
||||
{
|
||||
creators["mc lucifron shadow resistance"] = &RaidMcActionContext::lucifron_shadow_resistance;
|
||||
creators["mc magmadar fire resistance"] = &RaidMcActionContext::magmadar_fire_resistance;
|
||||
creators["mc gehennas shadow resistance"] = &RaidMcActionContext::gehennas_shadow_resistance;
|
||||
creators["mc garr fire resistance"] = &RaidMcActionContext::garr_fire_resistance;
|
||||
creators["mc baron geddon fire resistance"] = &RaidMcActionContext::baron_geddon_fire_resistance;
|
||||
creators["mc move from group"] = &RaidMcActionContext::check_should_move_from_group;
|
||||
creators["mc move from baron geddon"] = &RaidMcActionContext::move_from_baron_geddon;
|
||||
creators["mc shazzrah move away"] = &RaidMcActionContext::shazzrah_move_away;
|
||||
creators["mc sulfuron harbinger fire resistance"] = &RaidMcActionContext::sulfuron_harbinger_fire_resistance;
|
||||
creators["mc golemagg fire resistance"] = &RaidMcActionContext::golemagg_fire_resistance;
|
||||
creators["mc golemagg mark boss"] = &RaidMcActionContext::golemagg_mark_boss;
|
||||
creators["mc golemagg main tank attack golemagg"] = &RaidMcActionContext::golemagg_main_tank_attack_golemagg;
|
||||
creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager;
|
||||
creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance;
|
||||
creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "lucifron"); }
|
||||
static Action* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "magmadar"); }
|
||||
static Action* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "gehennas"); }
|
||||
static Action* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "garr"); }
|
||||
static Action* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "baron geddon"); }
|
||||
static Action* check_should_move_from_group(PlayerbotAI* botAI) { return new McMoveFromGroupAction(botAI); }
|
||||
static Action* move_from_baron_geddon(PlayerbotAI* botAI) { return new McMoveFromBaronGeddonAction(botAI); }
|
||||
static Action* shazzrah_move_away(PlayerbotAI* botAI) { return new McShazzrahMoveAwayAction(botAI); }
|
||||
static Action* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "sulfuron harbinger"); }
|
||||
static Action* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "golemagg the incinerator"); }
|
||||
static Action* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossAction(botAI); }
|
||||
static Action* golemagg_main_tank_attack_golemagg(PlayerbotAI* botAI) { return new McGolemaggMainTankAttackGolemaggAction(botAI); }
|
||||
static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); }
|
||||
static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); }
|
||||
static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); }
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/Ai/Raid/MoltenCore/RaidMcHelpers.h
Normal file
22
src/Ai/Raid/MoltenCore/RaidMcHelpers.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCHELPERS_H
|
||||
#define _PLAYERBOT_RAIDMCHELPERS_H
|
||||
|
||||
namespace MoltenCoreHelpers
|
||||
{
|
||||
enum MoltenCoreNPCs
|
||||
{
|
||||
// Golemagg
|
||||
NPC_CORE_RAGER = 11672,
|
||||
};
|
||||
enum MoltenCoreSpells
|
||||
{
|
||||
// Baron Geddon
|
||||
SPELL_INFERNO = 19695,
|
||||
SPELL_LIVING_BOMB = 20475,
|
||||
|
||||
// Golemagg
|
||||
SPELL_GOLEMAGGS_TRUST = 20553,
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
48
src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h
Normal file
48
src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcTriggers.h"
|
||||
|
||||
class RaidMcTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidMcTriggerContext()
|
||||
{
|
||||
creators["mc lucifron shadow resistance"] = &RaidMcTriggerContext::lucifron_shadow_resistance;
|
||||
creators["mc magmadar fire resistance"] = &RaidMcTriggerContext::magmadar_fire_resistance;
|
||||
creators["mc gehennas shadow resistance"] = &RaidMcTriggerContext::gehennas_shadow_resistance;
|
||||
creators["mc garr fire resistance"] = &RaidMcTriggerContext::garr_fire_resistance;
|
||||
creators["mc baron geddon fire resistance"] = &RaidMcTriggerContext::baron_geddon_fire_resistance;
|
||||
creators["mc living bomb debuff"] = &RaidMcTriggerContext::living_bomb_debuff;
|
||||
creators["mc baron geddon inferno"] = &RaidMcTriggerContext::baron_geddon_inferno;
|
||||
creators["mc shazzrah ranged"] = &RaidMcTriggerContext::shazzrah_ranged;
|
||||
creators["mc sulfuron harbinger fire resistance"] = &RaidMcTriggerContext::sulfuron_harbinger_fire_resistance;
|
||||
creators["mc golemagg fire resistance"] = &RaidMcTriggerContext::golemagg_fire_resistance;
|
||||
creators["mc golemagg mark boss"] = &RaidMcTriggerContext::golemagg_mark_boss;
|
||||
creators["mc golemagg is main tank"] = &RaidMcTriggerContext::golemagg_is_main_tank;
|
||||
creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank;
|
||||
creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance;
|
||||
creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "lucifron"); }
|
||||
static Trigger* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "magmadar"); }
|
||||
static Trigger* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "gehennas"); }
|
||||
static Trigger* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "garr"); }
|
||||
static Trigger* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "baron geddon"); }
|
||||
static Trigger* living_bomb_debuff(PlayerbotAI* botAI) { return new McLivingBombDebuffTrigger(botAI); }
|
||||
static Trigger* baron_geddon_inferno(PlayerbotAI* botAI) { return new McBaronGeddonInfernoTrigger(botAI); }
|
||||
static Trigger* shazzrah_ranged(PlayerbotAI* botAI) { return new McShazzrahRangedTrigger(botAI); }
|
||||
static Trigger* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "sulfuron harbinger"); }
|
||||
static Trigger* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "golemagg the incinerator"); }
|
||||
static Trigger* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossTrigger(botAI); }
|
||||
static Trigger* golemagg_is_main_tank(PlayerbotAI* botAI) { return new McGolemaggIsMainTankTrigger(botAI); }
|
||||
static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); }
|
||||
static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); }
|
||||
static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); }
|
||||
};
|
||||
|
||||
#endif
|
||||
81
src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.cpp
Normal file
81
src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "RaidMcStrategy.h"
|
||||
|
||||
#include "RaidMcMultipliers.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidMcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// Lucifron
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc lucifron shadow resistance",
|
||||
{ NextAction("mc lucifron shadow resistance", ACTION_RAID) }));
|
||||
|
||||
// Magmadar
|
||||
// TODO: Fear ward / tremor totem, or general anti-fear strat development. Same as King Dred (Drak'Tharon) and faction commander (Nexus).
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc magmadar fire resistance",
|
||||
{ NextAction("mc magmadar fire resistance", ACTION_RAID) }));
|
||||
|
||||
// Gehennas
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc gehennas shadow resistance",
|
||||
{ NextAction("mc gehennas shadow resistance", ACTION_RAID) }));
|
||||
|
||||
// Garr
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc garr fire resistance",
|
||||
{ NextAction("mc garr fire resistance", ACTION_RAID) }));
|
||||
|
||||
// Baron Geddon
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc baron geddon fire resistance",
|
||||
{ NextAction("mc baron geddon fire resistance", ACTION_RAID) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc living bomb debuff",
|
||||
{ NextAction("mc move from group", ACTION_RAID) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc baron geddon inferno",
|
||||
{ NextAction("mc move from baron geddon", ACTION_RAID) }));
|
||||
|
||||
// Shazzrah
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc shazzrah ranged",
|
||||
{ NextAction("mc shazzrah move away", ACTION_RAID) }));
|
||||
|
||||
// Sulfuron Harbinger
|
||||
// Alternatively, shadow resistance is also possible.
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc sulfuron harbinger fire resistance",
|
||||
{ NextAction("mc sulfuron harbinger fire resistance", ACTION_RAID) }));
|
||||
|
||||
// Golemagg the Incinerator
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc golemagg fire resistance",
|
||||
{ NextAction("mc golemagg fire resistance", ACTION_RAID) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc golemagg mark boss",
|
||||
{ NextAction("mc golemagg mark boss", ACTION_RAID) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc golemagg is main tank",
|
||||
{ NextAction("mc golemagg main tank attack golemagg", ACTION_RAID) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc golemagg is assist tank",
|
||||
{ NextAction("mc golemagg assist tank attack core rager", ACTION_RAID) }));
|
||||
|
||||
// Majordomo Executus
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc majordomo shadow resistance",
|
||||
{ NextAction("mc majordomo shadow resistance", ACTION_RAID) }));
|
||||
|
||||
// Ragnaros
|
||||
triggers.push_back(
|
||||
new TriggerNode("mc ragnaros fire resistance",
|
||||
{ NextAction("mc ragnaros fire resistance", ACTION_RAID) }));
|
||||
}
|
||||
|
||||
void RaidMcStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new GarrDisableDpsAoeMultiplier(botAI));
|
||||
multipliers.push_back(new BaronGeddonAbilityMultiplier(botAI));
|
||||
multipliers.push_back(new GolemaggMultiplier(botAI));
|
||||
}
|
||||
17
src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h
Normal file
17
src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDMCSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidMcStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidMcStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
std::string const getName() override { return "moltencore"; }
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
40
src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp
Normal file
40
src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "RaidMcTriggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "RaidMcHelpers.h"
|
||||
|
||||
using namespace MoltenCoreHelpers;
|
||||
|
||||
bool McLivingBombDebuffTrigger::IsActive()
|
||||
{
|
||||
// No check for Baron Geddon, because bots may have the bomb even after Geddon died.
|
||||
return bot->HasAura(SPELL_LIVING_BOMB);
|
||||
}
|
||||
|
||||
bool McBaronGeddonInfernoTrigger::IsActive()
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||
return boss->HasAura(SPELL_INFERNO);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool McShazzrahRangedTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "shazzrah") && PlayerbotAI::IsRanged(bot);
|
||||
}
|
||||
|
||||
bool McGolemaggMarkBossTrigger::IsActive()
|
||||
{
|
||||
// any tank may mark the boss
|
||||
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsTank(bot);
|
||||
}
|
||||
|
||||
bool McGolemaggIsMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool McGolemaggIsAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot);
|
||||
}
|
||||
50
src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.h
Normal file
50
src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDMCTRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class McLivingBombDebuffTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McLivingBombDebuffTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc living bomb debuff") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class McBaronGeddonInfernoTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McBaronGeddonInfernoTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc baron geddon inferno") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class McShazzrahRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McShazzrahRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc shazzrah ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class McGolemaggMarkBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McGolemaggMarkBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg mark boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class McGolemaggIsMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McGolemaggIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class McGolemaggIsAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
McGolemaggIsAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
246
src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp
Normal file
246
src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "RaidOsActions.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool SartharionTankPositionAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
Unit* shadron = nullptr;
|
||||
Unit* tenebron = nullptr;
|
||||
Unit* vesperon = nullptr;
|
||||
|
||||
// Detect incoming drakes before they are on aggro table
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (!unit) { continue; }
|
||||
|
||||
switch (unit->GetEntry())
|
||||
{
|
||||
case NPC_SHADRON:
|
||||
shadron = unit;
|
||||
continue;
|
||||
case NPC_TENEBRON:
|
||||
tenebron = unit;
|
||||
continue;
|
||||
case NPC_VESPERON:
|
||||
vesperon = unit;
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Position currentPos = bot->GetPosition();
|
||||
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
|
||||
float looseDistance = 12.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
// Offtank grab drakes
|
||||
else if (shadron || tenebron || vesperon)
|
||||
{
|
||||
float triggerDistance = 100.0f;
|
||||
// Prioritise threat before positioning
|
||||
if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance &&
|
||||
tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron)
|
||||
{
|
||||
return Attack(tenebron);
|
||||
}
|
||||
if (shadron && bot->GetExactDist2d(shadron) < triggerDistance &&
|
||||
shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron)
|
||||
{
|
||||
return Attack(shadron);
|
||||
}
|
||||
if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance &&
|
||||
vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon)
|
||||
{
|
||||
return Attack(vesperon);
|
||||
}
|
||||
|
||||
bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) ||
|
||||
(shadron && bot->GetExactDist2d(shadron) < triggerDistance) ||
|
||||
(vesperon && bot->GetExactDist2d(vesperon) < triggerDistance);
|
||||
// Offtank has threat on drakes, check positioning
|
||||
if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvoidTwilightFissureAction::Execute(Event event)
|
||||
{
|
||||
const float radius = 5.0f;
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE)
|
||||
{
|
||||
float currentDistance = bot->GetDistance2d(unit);
|
||||
if (currentDistance < radius)
|
||||
{
|
||||
return MoveAway(unit, radius - currentDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvoidFlameTsunamiAction::Execute(Event event)
|
||||
{
|
||||
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
|
||||
float looseDistance = 4.0f;
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI)
|
||||
{
|
||||
Position currentPos = bot->GetPosition();
|
||||
|
||||
// I think these are centrepoints for the wave segments. Either way they uniquely identify the wave
|
||||
// direction as they have different coords for the left and right waves
|
||||
// int casting is not a mistake, need to avoid FP errors somehow.
|
||||
// I always saw these accurate to around 6 decimal places, but if there are issues,
|
||||
// can switch this to abs comparison of floats which would technically be more robust.
|
||||
int posY = (int) unit->GetPositionY();
|
||||
if (posY == 505 || posY == 555) // RIGHT WAVE
|
||||
{
|
||||
bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX();
|
||||
if (wavePassed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
else // LEFT WAVE
|
||||
{
|
||||
bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX();
|
||||
if (wavePassed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
else // Ranged/healers
|
||||
{
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SartharionAttackPriorityAction::Execute(Event event)
|
||||
{
|
||||
Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron");
|
||||
|
||||
Unit* target = nullptr;
|
||||
|
||||
if (acolyte)
|
||||
{
|
||||
target = acolyte;
|
||||
}
|
||||
else if (vesperon)
|
||||
{
|
||||
target = vesperon;
|
||||
}
|
||||
else if (tenebron)
|
||||
{
|
||||
target = tenebron;
|
||||
}
|
||||
else if (shadron)
|
||||
{
|
||||
target = shadron;
|
||||
}
|
||||
else if (sartharion)
|
||||
{
|
||||
target = sartharion;
|
||||
}
|
||||
|
||||
if (target && AI_VALUE(Unit*, "current target") != target)
|
||||
{
|
||||
return Attack(target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EnterTwilightPortalAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; }
|
||||
|
||||
GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f);
|
||||
if (!portal) { return false; }
|
||||
|
||||
if (!portal->IsAtInteractDistance(bot))
|
||||
{
|
||||
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Go through portal
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << portal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExitTwilightPortalAction::Execute(Event event)
|
||||
{
|
||||
GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f);
|
||||
if (!portal) { return false; }
|
||||
|
||||
if (!portal->IsAtInteractDistance(bot))
|
||||
{
|
||||
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Go through portal
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << portal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
return true;
|
||||
}
|
||||
64
src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h
Normal file
64
src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSACTIONS_H
|
||||
#define _PLAYERBOT_RAIDOSACTIONS_H
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f;
|
||||
const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f;
|
||||
const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f;
|
||||
const std::pair<float, float> SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f};
|
||||
const std::pair<float, float> SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f};
|
||||
const std::pair<float, float> SARTHARION_RANGED_POSITION = {3248.0f, 507.0f};
|
||||
|
||||
class SartharionTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidTwilightFissureAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidFlameTsunamiAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class SartharionAttackPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class EnterTwilightPortalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ExitTwilightPortalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
49
src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp
Normal file
49
src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "RaidOsMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "RaidOsActions.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float SartharionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
Unit* target = action->GetTarget();
|
||||
|
||||
if (botAI->IsMainTank(bot) && dynamic_cast<TankFaceAction*>(action))
|
||||
{
|
||||
// return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot) && target && target != boss &&
|
||||
(dynamic_cast<TankAssistAction*>(action) || dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsAssistTank(bot) && target && target == boss &&
|
||||
(dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
16
src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.h
Normal file
16
src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDOSMULTIPLIERS_H
|
||||
#define _PLAYERRBOT_RAIDOSMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class SartharionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
30
src/Ai/Raid/ObsidianSanctum/RaidOsActionContext.h
Normal file
30
src/Ai/Raid/ObsidianSanctum/RaidOsActionContext.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDOSACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOsActions.h"
|
||||
|
||||
class RaidOsActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidOsActionContext()
|
||||
{
|
||||
creators["sartharion tank position"] = &RaidOsActionContext::tank_position;
|
||||
creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure;
|
||||
creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami;
|
||||
creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority;
|
||||
creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal;
|
||||
creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); }
|
||||
static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); }
|
||||
static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); }
|
||||
static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); }
|
||||
static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); }
|
||||
static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h
Normal file
32
src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
class RaidOsTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidOsTriggerContext()
|
||||
{
|
||||
creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank;
|
||||
creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami;
|
||||
creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure;
|
||||
creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps;
|
||||
creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee;
|
||||
creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter;
|
||||
creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); }
|
||||
static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); }
|
||||
static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); }
|
||||
static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); }
|
||||
static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); }
|
||||
static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); }
|
||||
static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp
Normal file
32
src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidOsMultipliers.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidOsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("sartharion tank",
|
||||
{ NextAction("sartharion tank position", ACTION_MOVE) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("twilight fissure",
|
||||
{ NextAction("avoid twilight fissure", ACTION_RAID + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("flame tsunami",
|
||||
{ NextAction("avoid flame tsunami", ACTION_RAID + 1) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("sartharion dps",
|
||||
{ NextAction("sartharion attack priority", ACTION_RAID) }));
|
||||
// Flank dragon positioning
|
||||
triggers.push_back(new TriggerNode("sartharion melee positioning",
|
||||
{ NextAction("rear flank", ACTION_MOVE + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("twilight portal enter",
|
||||
{ NextAction("enter twilight portal", ACTION_RAID + 1) }));
|
||||
triggers.push_back(new TriggerNode("twilight portal exit",
|
||||
{ NextAction("exit twilight portal", ACTION_RAID + 1) }));
|
||||
}
|
||||
|
||||
void RaidOsStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new SartharionMultiplier(botAI));
|
||||
}
|
||||
17
src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h
Normal file
17
src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidOsStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidOsStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "wotlk-os"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
125
src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp
Normal file
125
src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool SartharionTankTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
bool FlameTsunamiTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsTank(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit)
|
||||
{
|
||||
if (unit->GetEntry() == NPC_FLAME_TSUNAMI)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TwilightFissureTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit)
|
||||
{
|
||||
if (unit->GetEntry() == NPC_TWILIGHT_FISSURE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SartharionDpsTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return botAI->IsDps(bot);
|
||||
}
|
||||
|
||||
bool SartharionMeleePositioningTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
|
||||
return !(shadron || tenebron || vesperon);
|
||||
}
|
||||
|
||||
bool TwilightPortalEnterTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; }
|
||||
|
||||
// In 25-man, take two healers in. Otherwise just take one
|
||||
// if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
// {
|
||||
// if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (botAI->IsHealAssistantOfIndex(bot, 0))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Don't enter portal until drakes are dead
|
||||
if (bot->HasAura(SPELL_POWER_OF_SHADRON) ||
|
||||
bot->HasAura(SPELL_POWER_OF_TENEBRON) ||
|
||||
bot->HasAura(SPELL_POWER_OF_VESPERON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
// GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||
// for (auto& object : objects)
|
||||
// {
|
||||
// GameObject* go = botAI->GetGameObject(object);
|
||||
// if (go && go->GetEntry() == GO_TWILIGHT_PORTAL)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f));
|
||||
}
|
||||
|
||||
bool TwilightPortalExitTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron");
|
||||
}
|
||||
120
src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.h
Normal file
120
src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDOSTRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum ObsidianSanctumIDs
|
||||
{
|
||||
// Bosses
|
||||
NPC_SARTHARION = 28860,
|
||||
NPC_SHADRON = 30451,
|
||||
NPC_TENEBRON = 30452,
|
||||
NPC_VESPERON = 30449,
|
||||
|
||||
// Mini-boss shared
|
||||
SPELL_SHADOW_BREATH = 57570,
|
||||
SPELL_SHADOW_FISSURE = 57579,
|
||||
SPELL_SUMMON_TWILIGHT_WHELP = 58035,
|
||||
SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835,
|
||||
SPELL_TWILIGHT_TORMENT_VESPERON = 57935,
|
||||
|
||||
// Sartharion
|
||||
SPELL_SARTHARION_CLEAVE = 56909,
|
||||
SPELL_SARTHARION_FLAME_BREATH = 56908,
|
||||
SPELL_SARTHARION_TAIL_LASH = 56910,
|
||||
SPELL_CYCLONE_AURA_PERIODIC = 57598,
|
||||
SPELL_LAVA_STRIKE_DUMMY = 57578,
|
||||
SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697,
|
||||
SPELL_LAVA_STRIKE_SUMMON = 57572,
|
||||
SPELL_SARTHARION_PYROBUFFET = 56916,
|
||||
SPELL_SARTHARION_BERSERK = 61632,
|
||||
SPELL_SARTHARION_TWILIGHT_REVENGE = 60639,
|
||||
|
||||
// Sartharion with drakes
|
||||
SPELL_WILL_OF_SARTHARION = 61254,
|
||||
SPELL_POWER_OF_TENEBRON = 61248,
|
||||
SPELL_POWER_OF_VESPERON = 61251,
|
||||
SPELL_POWER_OF_SHADRON = 58105,
|
||||
SPELL_GIFT_OF_TWILIGHT_FIRE = 58766,
|
||||
|
||||
// Visuals
|
||||
SPELL_EGG_MARKER_VISUAL = 58547,
|
||||
SPELL_FLAME_TSUNAMI_VISUAL = 57494,
|
||||
|
||||
// Misc
|
||||
SPELL_FADE_ARMOR = 60708,
|
||||
SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492,
|
||||
SPELL_FLAME_TSUNAMI_LEAP = 60241,
|
||||
SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557,
|
||||
|
||||
NPC_TWILIGHT_EGG = 30882,
|
||||
NPC_TWILIGHT_WHELP = 30890,
|
||||
NPC_DISCIPLE_OF_SHADRON = 30688,
|
||||
NPC_DISCIPLE_OF_VESPERON = 30858,
|
||||
NPC_ACOLYTE_OF_SHADRON = 31218,
|
||||
NPC_ACOLYTE_OF_VESPERON = 31219,
|
||||
|
||||
// Sartharion fight
|
||||
NPC_LAVA_BLAZE = 30643,
|
||||
NPC_FLAME_TSUNAMI = 30616,
|
||||
NPC_SAFE_AREA_TRIGGER = 30494,
|
||||
NPC_TWILIGHT_FISSURE = 30641,
|
||||
GO_TWILIGHT_PORTAL = 193988,
|
||||
GO_NORMAL_PORTAL = 193989,
|
||||
SPELL_TWILIGHT_SHIFT = 57874,
|
||||
};
|
||||
|
||||
const uint32 OS_MAP_ID = 615;
|
||||
|
||||
class SartharionTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FlameTsunamiTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightFissureTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class SartharionDpsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class SartharionMeleePositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightPortalEnterTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightPortalExitTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
147
src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp
Normal file
147
src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
// RaidOnyxiaActions.cpp
|
||||
#include "RaidOnyxiaActions.h"
|
||||
|
||||
#include "GenericSpellActions.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "MovementActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PositionAction.h"
|
||||
|
||||
bool RaidOnyxiaMoveToSideAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
float angleToBot = boss->GetAngle(bot);
|
||||
float bossFacing = boss->GetOrientation();
|
||||
float diff = fabs(angleToBot - bossFacing);
|
||||
if (diff > M_PI)
|
||||
diff = 2 * M_PI - diff;
|
||||
|
||||
float distance = bot->GetDistance(boss);
|
||||
|
||||
// Too close (30 yards) and either in front or behind
|
||||
if (distance <= 30.0f && (diff < M_PI / 4 || diff > 3 * M_PI / 4))
|
||||
{
|
||||
float offsetAngle = bossFacing + M_PI_2; // 90° to the right
|
||||
float offsetDist = 15.0f;
|
||||
|
||||
float sideX = boss->GetPositionX() + offsetDist * cos(offsetAngle);
|
||||
float sideY = boss->GetPositionY() + offsetDist * sin(offsetAngle);
|
||||
|
||||
// bot->Yell("Too close to front or tail — moving to side of Onyxia!", LANG_UNIVERSAL);
|
||||
return MoveTo(boss->GetMapId(), sideX, sideY, boss->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RaidOnyxiaSpreadOutAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia");
|
||||
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
Player* target = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL)->m_targets.GetUnitTarget()->ToPlayer();
|
||||
if (target != bot)
|
||||
return false;
|
||||
|
||||
// bot->Yell("Spreading out — I'm the Fireball target!", LANG_UNIVERSAL);
|
||||
return MoveFromGroup(9.0f); // move 9 yards
|
||||
}
|
||||
|
||||
bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (!currentSpell)
|
||||
return false;
|
||||
|
||||
uint32 spellId = currentSpell->m_spellInfo->Id;
|
||||
|
||||
std::vector<SafeZone> safeZones = GetSafeZonesForBreath(spellId);
|
||||
if (safeZones.empty())
|
||||
return false;
|
||||
|
||||
// Find closest safe zone
|
||||
SafeZone* bestZone = nullptr;
|
||||
float bestDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (auto& zone : safeZones)
|
||||
{
|
||||
float dist = bot->GetExactDist2d(zone.pos.GetPositionX(), zone.pos.GetPositionY());
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestDist = dist;
|
||||
bestZone = &zone;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestZone)
|
||||
return false;
|
||||
|
||||
if (bot->IsWithinDist2d(bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->radius))
|
||||
return false; // Already safe
|
||||
|
||||
// bot->Yell("Moving to Safe Zone!", LANG_UNIVERSAL);
|
||||
return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
bool RaidOnyxiaKillWhelpsAction::Execute(Event event)
|
||||
{
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
// If already attacking a whelp, don't swap targets
|
||||
if (currentTarget && currentTarget->GetEntry() == 11262)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
for (ObjectGuid guid : targets)
|
||||
{
|
||||
Creature* unit = botAI->GetCreature(guid);
|
||||
if (!unit || !unit->IsAlive() || !unit->IsInWorld())
|
||||
continue;
|
||||
|
||||
if (unit->GetEntry() == 11262) // Onyxia Whelp
|
||||
{
|
||||
// bot->Yell("Attacking Whelps!", LANG_UNIVERSAL);
|
||||
return Attack(unit);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OnyxiaAvoidEggsAction::Execute(Event event)
|
||||
{
|
||||
Position botPos = Position(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
||||
|
||||
float x, y;
|
||||
|
||||
// get safe zone slightly away from eggs (Can this be dynamic?)
|
||||
if (botPos.GetExactDist2d(-36.0f, -164.0f) <= 5.0f)
|
||||
{
|
||||
x = -10.0f;
|
||||
y = -180.0f;
|
||||
}
|
||||
else if (botPos.GetExactDist2d(-34.0f, -262.0f) <= 5.0f)
|
||||
{
|
||||
x = -16.0f;
|
||||
y = -250.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; // Not in danger zone
|
||||
}
|
||||
|
||||
// bot->Yell("Too close to eggs — backing off!", LANG_UNIVERSAL);
|
||||
|
||||
return MoveTo(bot->GetMapId(), x, y, bot->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
107
src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h
Normal file
107
src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// RaidOnyxiaActions.h
|
||||
#ifndef _PLAYERBOT_RAIDONYXIAACTIONS_H_
|
||||
#define _PLAYERBOT_RAIDONYXIAACTIONS_H_
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class RaidOnyxiaMoveToSideAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaMoveToSideAction(PlayerbotAI* botAI, std::string const name = "ony move to side")
|
||||
: MovementAction(botAI, name)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class RaidOnyxiaSpreadOutAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaSpreadOutAction(PlayerbotAI* botAI, std::string const name = "ony spread out")
|
||||
: MovementAction(botAI, name)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
struct SafeZone
|
||||
{
|
||||
Position pos;
|
||||
float radius;
|
||||
};
|
||||
|
||||
class RaidOnyxiaMoveToSafeZoneAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaMoveToSafeZoneAction(PlayerbotAI* botAI, std::string const name = "ony move to safe zone")
|
||||
: MovementAction(botAI, name)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
std::vector<SafeZone> GetSafeZonesForBreath(uint32 spellId)
|
||||
{
|
||||
// Define your safe zone coordinates based on the map
|
||||
// Example assumes Onyxia's lair map coordinates
|
||||
float z = bot->GetPositionZ(); // Stay at current height
|
||||
|
||||
switch (spellId)
|
||||
{
|
||||
case 17086: // N to S
|
||||
case 18351: // S to N
|
||||
return {SafeZone{Position(-10.0f, -180.0f, z), 5.0f},
|
||||
SafeZone{Position(-20.0f, -250.0f, z), 5.0f}}; // Bottom Safe Zone
|
||||
|
||||
case 18576: // E to W
|
||||
case 18609: // W to E
|
||||
return {
|
||||
SafeZone{Position(20.0f, -210.0f, z), 5.0f},
|
||||
SafeZone{Position(-75.0f, -210.0f, z), 5.0f},
|
||||
}; // Left Safe Zone
|
||||
|
||||
case 18564: // SE to NW
|
||||
case 18584: // NW to SE
|
||||
return {
|
||||
SafeZone{Position(-60.0f, -195.0f, z), 5.0f},
|
||||
SafeZone{Position(10.0f, -240.0f, z), 5.0f},
|
||||
}; // NW Safe Zone
|
||||
|
||||
case 18596: // SW to NE
|
||||
case 18617: // NE to SW
|
||||
return {
|
||||
SafeZone{Position(7.0f, -185.0f, z), 5.0f},
|
||||
SafeZone{Position(-60.0f, -240.0f, z), 5.0f},
|
||||
}; // NE Safe Zone
|
||||
|
||||
default:
|
||||
return {SafeZone{Position(0.0f, 0.0f, z), 5.0f}}; // Fallback center - shouldn't ever happen
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class RaidOnyxiaKillWhelpsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaKillWhelpsAction(PlayerbotAI* botAI, std::string const name = "ony kill whelps")
|
||||
: AttackAction(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class OnyxiaAvoidEggsAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
OnyxiaAvoidEggsAction(PlayerbotAI* botAI) : MovementAction(botAI, "ony avoid eggs move") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
28
src/Ai/Raid/Onyxia/RaidOnyxiaActionContext.h
Normal file
28
src/Ai/Raid/Onyxia/RaidOnyxiaActionContext.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _PLAYERBOT_RAIDONYXIAACTIONS_CONTEXT_H
|
||||
#define _PLAYERBOT_RAIDONYXIAACTIONS_CONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOnyxiaActions.h"
|
||||
|
||||
class RaidOnyxiaActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaActionContext()
|
||||
{
|
||||
creators["ony move to side"] = &RaidOnyxiaActionContext::move_to_side;
|
||||
creators["ony spread out"] = &RaidOnyxiaActionContext::spread_out;
|
||||
creators["ony move to safe zone"] = &RaidOnyxiaActionContext::move_to_safe_zone;
|
||||
creators["ony kill whelps"] = &RaidOnyxiaActionContext::kill_whelps;
|
||||
creators["ony avoid eggs move"] = &RaidOnyxiaActionContext::avoid_eggs;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* move_to_side(PlayerbotAI* ai) { return new RaidOnyxiaMoveToSideAction(ai); }
|
||||
static Action* spread_out(PlayerbotAI* ai) { return new RaidOnyxiaSpreadOutAction(ai); }
|
||||
static Action* move_to_safe_zone(PlayerbotAI* ai) { return new RaidOnyxiaMoveToSafeZoneAction(ai); }
|
||||
static Action* kill_whelps(PlayerbotAI* ai) { return new RaidOnyxiaKillWhelpsAction(ai); }
|
||||
static Action* avoid_eggs(PlayerbotAI* ai) { return new OnyxiaAvoidEggsAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
28
src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h
Normal file
28
src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOnyxiaTriggers.h"
|
||||
|
||||
class RaidOnyxiaTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidOnyxiaTriggerContext()
|
||||
{
|
||||
creators["ony near tail"] = &RaidOnyxiaTriggerContext::near_tail;
|
||||
creators["ony deep breath warning"] = &RaidOnyxiaTriggerContext::deep_breath;
|
||||
creators["ony fireball splash incoming"] = &RaidOnyxiaTriggerContext::fireball_splash;
|
||||
creators["ony whelps spawn"] = &RaidOnyxiaTriggerContext::whelps_spawn;
|
||||
creators["ony avoid eggs"] = &RaidOnyxiaTriggerContext::avoid_eggs;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* near_tail(PlayerbotAI* ai) { return new OnyxiaNearTailTrigger(ai); }
|
||||
static Trigger* deep_breath(PlayerbotAI* ai) { return new OnyxiaDeepBreathTrigger(ai); }
|
||||
static Trigger* fireball_splash(PlayerbotAI* ai) { return new RaidOnyxiaFireballSplashTrigger(ai); }
|
||||
static Trigger* whelps_spawn(PlayerbotAI* ai) { return new RaidOnyxiaWhelpsSpawnTrigger(ai); }
|
||||
static Trigger* avoid_eggs(PlayerbotAI* ai) { return new OnyxiaAvoidEggsTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user