mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-18 01:24:35 +00:00
[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)
This commit is contained in:
172
src/Ai/Dungeon/Nexus/Action/NexusActions.cpp
Normal file
172
src/Ai/Dungeon/Nexus/Action/NexusActions.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "Playerbots.h"
|
||||
#include "NexusActions.h"
|
||||
#include "NexusStrategy.h"
|
||||
|
||||
bool MoveFromWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = nullptr;
|
||||
uint8 faction = bot->GetTeamId();
|
||||
float targetDist = 10.0f; // Whirlwind has a range of 8, adding a safety buffer
|
||||
|
||||
switch (bot->GetMap()->GetDifficulty())
|
||||
{
|
||||
case DUNGEON_DIFFICULTY_NORMAL:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||
}
|
||||
else // TEAM_HORDE
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||
}
|
||||
break;
|
||||
case DUNGEON_DIFFICULTY_HEROIC:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||
}
|
||||
else // TEAM_HORDE
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure boss is valid before accessing its methods
|
||||
if (!boss)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float bossDistance = bot->GetExactDist2d(boss->GetPosition());
|
||||
|
||||
// Check if the bot is already at a safe distance
|
||||
if (bossDistance > targetDist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move away from the boss to avoid Whirlwind
|
||||
return MoveAway(boss, targetDist - bossDistance);
|
||||
}
|
||||
|
||||
bool FirebombSpreadAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||
float radius = 5.0f;
|
||||
float targetDist = radius + 1.0f;
|
||||
if (!boss) { return false; }
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || bot->GetGUID() == member) { continue; }
|
||||
|
||||
if (bot->GetExactDist2d(unit) < targetDist)
|
||||
{
|
||||
return MoveAway(unit, targetDist);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TelestraSplitTargetAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||
bool TelestraSplitTargetAction::Execute(Event event)
|
||||
{
|
||||
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
|
||||
Unit* splitTargets[3] = {nullptr, nullptr, nullptr};
|
||||
|
||||
for (auto& attacker : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(attacker);
|
||||
if (!unit) { continue; }
|
||||
|
||||
switch (unit->GetEntry())
|
||||
{
|
||||
// Focus arcane clone first
|
||||
case NPC_ARCANE_MAGUS:
|
||||
splitTargets[0] = unit;
|
||||
break;
|
||||
// Then the frost clone
|
||||
case NPC_FROST_MAGUS:
|
||||
splitTargets[1] = unit;
|
||||
break;
|
||||
// Fire clone last
|
||||
case NPC_FIRE_MAGUS:
|
||||
splitTargets[2] = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Unit* target : splitTargets)
|
||||
{
|
||||
// Attack the first valid split target in the priority list
|
||||
if (target)
|
||||
{
|
||||
if (AI_VALUE(Unit*, "current target") != target)
|
||||
{
|
||||
return Attack(target);
|
||||
}
|
||||
// Don't continue loop here, the target exists so we don't
|
||||
// want to move down the prio list. We just don't need to send attack
|
||||
// command again, just return false and exit the loop that way
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChaoticRiftTargetAction::isUseful() { return !botAI->IsHeal(bot); }
|
||||
bool ChaoticRiftTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* chaoticRift = nullptr;
|
||||
|
||||
// Target is not findable from threat table using AI_VALUE2(),
|
||||
// therefore need to search manually for the unit name
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
|
||||
for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (unit && unit->GetName() == "Chaotic Rift")
|
||||
{
|
||||
chaoticRift = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!chaoticRift || AI_VALUE(Unit*, "current target") == chaoticRift)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Attack(chaoticRift);
|
||||
}
|
||||
|
||||
bool DodgeSpikesAction::isUseful()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return bot->GetExactDist2d(boss) > 0.5f;
|
||||
}
|
||||
bool DodgeSpikesAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f);
|
||||
}
|
||||
|
||||
bool IntenseColdJumpAction::Execute(Event event)
|
||||
{
|
||||
// This needs improving but maybe it should be done in the playerbot core.
|
||||
// Jump doesn't seem to support zero offset (eg. jump on the spot) so need to add a tiny delta.
|
||||
// This does a tiny bunnyhop that takes a couple of ms, it doesn't do a natural jump.
|
||||
// Adding extra Z offset causes floating, and appears to scale the jump speed based on Z difference.
|
||||
// Probably best to revisit once bot movement is improved
|
||||
return JumpTo(bot->GetMap()->GetId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 0.01f);
|
||||
// bot->GetMotionMaster()->MoveFall();
|
||||
}
|
||||
55
src/Ai/Dungeon/Nexus/Action/NexusActions.h
Normal file
55
src/Ai/Dungeon/Nexus/Action/NexusActions.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "NexusTriggers.h"
|
||||
|
||||
class MoveFromWhirlwindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MoveFromWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "move from whirlwind") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FirebombSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
FirebombSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "firebomb spread") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TelestraSplitTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TelestraSplitTargetAction(PlayerbotAI* ai) : AttackAction(ai, "telestra split target") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class ChaoticRiftTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
ChaoticRiftTargetAction(PlayerbotAI* ai) : AttackAction(ai, "chaotic rift target") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class DodgeSpikesAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
DodgeSpikesAction(PlayerbotAI* ai) : MovementAction(ai, "dodge spikes") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class IntenseColdJumpAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IntenseColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "intense cold jump") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
96
src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp
Normal file
96
src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "NexusMultipliers.h"
|
||||
#include "NexusActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "NexusTriggers.h"
|
||||
|
||||
float FactionCommanderMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = nullptr;
|
||||
uint8 faction = bot->GetTeamId();
|
||||
|
||||
switch (bot->GetMap()->GetDifficulty())
|
||||
{
|
||||
case DUNGEON_DIFFICULTY_NORMAL:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||
}
|
||||
else //if (faction == TEAM_HORDE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||
}
|
||||
break;
|
||||
case DUNGEON_DIFFICULTY_HEROIC:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||
}
|
||||
else //if (faction == TEAM_HORDE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (boss && boss->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND))
|
||||
{
|
||||
// Prevent movement actions other than flee during a whirlwind, to prevent running back in early.
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<MoveFromWhirlwindAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float TelestraMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||
if (boss && boss->GetEntry() != NPC_TELESTRA)
|
||||
{
|
||||
// boss is split into clones, do not auto acquire target
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AnomalusMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus");
|
||||
if (boss && boss->HasAura(BUFF_RIFT_SHIELD))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float OrmorokMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
// These are used for auto ranged repositioning, need to suppress so ranged dps don't ping-pong
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// This boss is annoying and shuffles around a lot. Don't let tank move once fight has started.
|
||||
// Extra checks are to allow the tank to close distance and engage the boss initially
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<DodgeSpikesAction*>(action)
|
||||
&& botAI->IsTank(bot) && bot->IsWithinMeleeRange(boss)
|
||||
&& AI_VALUE2(bool, "facing", "current target"))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
42
src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.h
Normal file
42
src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class FactionCommanderMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FactionCommanderMultiplier(PlayerbotAI* ai) : Multiplier(ai, "faction commander") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TelestraMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TelestraMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grand magus telestra") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AnomalusMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AnomalusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anomalus") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class OrmorokMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
OrmorokMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ormorok the tree-shaper") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
28
src/Ai/Dungeon/Nexus/NexusActionContext.h
Normal file
28
src/Ai/Dungeon/Nexus/NexusActionContext.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "NexusActions.h"
|
||||
|
||||
class WotlkDungeonNexActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonNexActionContext() {
|
||||
creators["move from whirlwind"] = &WotlkDungeonNexActionContext::move_from_whirlwind;
|
||||
creators["firebomb spread"] = &WotlkDungeonNexActionContext::firebomb_spread;
|
||||
creators["telestra split target"] = &WotlkDungeonNexActionContext::telestra_split_target;
|
||||
creators["chaotic rift target"] = &WotlkDungeonNexActionContext::chaotic_rift_target;
|
||||
creators["dodge spikes"] = &WotlkDungeonNexActionContext::dodge_spikes;
|
||||
creators["intense cold jump"] = &WotlkDungeonNexActionContext::intense_cold_jump;
|
||||
}
|
||||
private:
|
||||
static Action* move_from_whirlwind(PlayerbotAI* ai) { return new MoveFromWhirlwindAction(ai); }
|
||||
static Action* firebomb_spread(PlayerbotAI* ai) { return new FirebombSpreadAction(ai); }
|
||||
static Action* telestra_split_target(PlayerbotAI* ai) { return new TelestraSplitTargetAction(ai); }
|
||||
static Action* chaotic_rift_target(PlayerbotAI* ai) { return new ChaoticRiftTargetAction(ai); }
|
||||
static Action* dodge_spikes(PlayerbotAI* ai) { return new DodgeSpikesAction(ai); }
|
||||
static Action* intense_cold_jump(PlayerbotAI* ai) { return new IntenseColdJumpAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
33
src/Ai/Dungeon/Nexus/NexusTriggerContext.h
Normal file
33
src/Ai/Dungeon/Nexus/NexusTriggerContext.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NexusTriggers.h"
|
||||
|
||||
class WotlkDungeonNexTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonNexTriggerContext()
|
||||
{
|
||||
creators["faction commander whirlwind"] = &WotlkDungeonNexTriggerContext::faction_commander_whirlwind;
|
||||
creators["telestra firebomb"] = &WotlkDungeonNexTriggerContext::telestra_firebomb;
|
||||
creators["telestra split phase"] = &WotlkDungeonNexTriggerContext::telestra_split_phase;
|
||||
creators["chaotic rift"] = &WotlkDungeonNexTriggerContext::chaotic_rift;
|
||||
creators["ormorok spikes"] = &WotlkDungeonNexTriggerContext::ormorok_spikes;
|
||||
creators["ormorok stack"] = &WotlkDungeonNexTriggerContext::ormorok_stack;
|
||||
creators["intense cold"] = &WotlkDungeonNexTriggerContext::intense_cold;
|
||||
creators["keristrasza positioning"] = &WotlkDungeonNexTriggerContext::keristrasza_positioning;
|
||||
}
|
||||
private:
|
||||
static Trigger* faction_commander_whirlwind(PlayerbotAI* ai) { return new FactionCommanderWhirlwindTrigger(ai); }
|
||||
static Trigger* telestra_firebomb(PlayerbotAI* ai) { return new TelestraFirebombTrigger(ai); }
|
||||
static Trigger* telestra_split_phase(PlayerbotAI* ai) { return new TelestraSplitPhaseTrigger(ai); }
|
||||
static Trigger* chaotic_rift(PlayerbotAI* ai) { return new ChaoticRiftTrigger(ai); }
|
||||
static Trigger* ormorok_spikes(PlayerbotAI* ai) { return new OrmorokSpikesTrigger(ai); }
|
||||
static Trigger* ormorok_stack(PlayerbotAI* ai) { return new OrmorokStackTrigger(ai); }
|
||||
static Trigger* intense_cold(PlayerbotAI* ai) { return new IntenseColdTrigger(ai); }
|
||||
static Trigger* keristrasza_positioning(PlayerbotAI* ai) { return new KeristraszaPositioningTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
52
src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp
Normal file
52
src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "NexusStrategy.h"
|
||||
#include "NexusMultipliers.h"
|
||||
|
||||
void WotlkDungeonNexStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Horde Commander (Alliance N)/Commander Kolurg (Alliance H)
|
||||
// or
|
||||
// Alliance Commander (Horde N)/Commander Stoutbeard (Horde H)
|
||||
triggers.push_back(new TriggerNode("faction commander whirlwind",
|
||||
{ NextAction("move from whirlwind", ACTION_MOVE + 5) }));
|
||||
// TODO: Handle fear? (tremor totems, fear ward etc.)
|
||||
|
||||
// Grand Magus Telestra
|
||||
triggers.push_back(new TriggerNode("telestra firebomb",
|
||||
{ NextAction("firebomb spread", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("telestra split phase",
|
||||
{ NextAction("telestra split target", ACTION_RAID + 1) }));
|
||||
// TODO: Add priority interrupt on the frost split's Blizzard casts
|
||||
|
||||
// Anomalus
|
||||
triggers.push_back(new TriggerNode("chaotic rift",
|
||||
{ NextAction("chaotic rift target", ACTION_RAID + 1) }));
|
||||
|
||||
// Ormorok the Tree-Shaper
|
||||
// Tank trigger to stack inside boss. Can also add return action to prevent boss repositioning
|
||||
// if it becomes too much of a problem. He usually dies before he's up against a wall though
|
||||
triggers.push_back(new TriggerNode("ormorok spikes",
|
||||
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
|
||||
// Non-tank trigger to stack. Avoiding the spikes at range is.. harder than it seems.
|
||||
// TODO: This turns hunters into melee marshmallows, have not come up with a better solution yet
|
||||
triggers.push_back(new TriggerNode("ormorok stack",
|
||||
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
|
||||
// TODO: Add handling for spell reflect... best to spam low level/weak spells but don't want
|
||||
// to hardcode spells per class, might be difficult to dynamically generate this.
|
||||
// Will revisit if I find my altbots killing themselves in heroic, just heal through it for now
|
||||
|
||||
// Keristrasza
|
||||
triggers.push_back(new TriggerNode("intense cold",
|
||||
{ NextAction("intense cold jump", ACTION_MOVE + 5) }));
|
||||
// Flank dragon positioning
|
||||
triggers.push_back(new TriggerNode("keristrasza positioning",
|
||||
{ NextAction("rear flank", ACTION_MOVE + 4) }));
|
||||
// TODO: Add frost resist aura for paladins?
|
||||
}
|
||||
|
||||
void WotlkDungeonNexStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new FactionCommanderMultiplier(botAI));
|
||||
multipliers.push_back(new TelestraMultiplier(botAI));
|
||||
multipliers.push_back(new AnomalusMultiplier(botAI));
|
||||
multipliers.push_back(new OrmorokMultiplier(botAI));
|
||||
}
|
||||
17
src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h
Normal file
17
src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXSTRATEGY_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class WotlkDungeonNexStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonNexStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "nexus"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
107
src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp
Normal file
107
src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "Playerbots.h"
|
||||
#include "NexusTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
bool FactionCommanderWhirlwindTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = nullptr;
|
||||
uint8 faction = bot->GetTeamId();
|
||||
|
||||
switch (bot->GetMap()->GetDifficulty())
|
||||
{
|
||||
case DUNGEON_DIFFICULTY_NORMAL:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "horde commander");
|
||||
}
|
||||
else //if (faction == TEAM_HORDE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
|
||||
}
|
||||
break;
|
||||
case DUNGEON_DIFFICULTY_HEROIC:
|
||||
if (faction == TEAM_ALLIANCE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
|
||||
}
|
||||
else //if (faction == TEAM_HORDE)
|
||||
{
|
||||
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (boss && boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
if (boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TelestraFirebombTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMelee(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||
// Avoid split phase with the fake Telestra units, only match the true boss id
|
||||
return boss && boss->GetEntry() == NPC_TELESTRA;
|
||||
}
|
||||
|
||||
bool TelestraSplitPhaseTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra");
|
||||
// Only match split phase with the fake Telestra units
|
||||
return boss && boss->GetEntry() != NPC_TELESTRA;
|
||||
}
|
||||
|
||||
bool ChaoticRiftTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "anomalus");
|
||||
return boss && boss->HasAura(BUFF_RIFT_SHIELD);
|
||||
}
|
||||
|
||||
bool OrmorokSpikesTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||
if (!boss || !botAI->IsTank(bot)) { return false; }
|
||||
|
||||
GuidVector objects = AI_VALUE(GuidVector, "closest game objects");
|
||||
for (auto i = objects.begin(); i != objects.end(); ++i)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(*i);
|
||||
if (go && go->GetEntry() == GO_CRYSTAL_SPIKE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OrmorokStackTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper");
|
||||
return (boss && !botAI->IsTank(bot));
|
||||
}
|
||||
|
||||
bool IntenseColdTrigger::IsActive()
|
||||
{
|
||||
// Adjust as needed - too much interrupting loses dps time,
|
||||
// but too many stacks is deadly. Assuming 3-5 is a good number to clear
|
||||
int stackThreshold = 5;
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||
return boss && botAI->GetAura("intense cold", bot, false, false, stackThreshold);
|
||||
}
|
||||
|
||||
bool KeristraszaPositioningTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza");
|
||||
// Include healers here for now, otherwise they stand in things
|
||||
return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot);
|
||||
// return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot);
|
||||
}
|
||||
89
src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.h
Normal file
89
src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONNEXTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum NexusIDs
|
||||
{
|
||||
// Faction Commander
|
||||
NPC_ALLIANCE_COMMANDER = 27949,
|
||||
NPC_HORDE_COMMANDER = 27947,
|
||||
NPC_COMMANDER_STOUTBEARD = 26796,
|
||||
NPC_COMMANDER_KOLURG = 26798,
|
||||
// SPELL_FRIGHTENING_SHOUT = 19134,
|
||||
SPELL_WHIRLWIND = 38618,
|
||||
|
||||
// Grand Magus Telestra
|
||||
NPC_TELESTRA = 26731,
|
||||
NPC_FIRE_MAGUS = 26928,
|
||||
NPC_FROST_MAGUS = 26930,
|
||||
NPC_ARCANE_MAGUS = 26929,
|
||||
|
||||
// Anomalus
|
||||
BUFF_RIFT_SHIELD = 47748,
|
||||
|
||||
// Ormorok the Tree Shaper
|
||||
// NPC_CRYSTAL_SPIKE = 27099,
|
||||
GO_CRYSTAL_SPIKE = 188537,
|
||||
};
|
||||
|
||||
class FactionCommanderWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FactionCommanderWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "faction commander whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TelestraFirebombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TelestraFirebombTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra firebomb spread") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TelestraSplitPhaseTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TelestraSplitPhaseTrigger(PlayerbotAI* ai) : Trigger(ai, "telestra split phase") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ChaoticRiftTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ChaoticRiftTrigger(PlayerbotAI* ai) : Trigger(ai, "chaotic rift") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class OrmorokSpikesTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
OrmorokSpikesTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok spikes") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class OrmorokStackTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
OrmorokStackTrigger(PlayerbotAI* ai) : Trigger(ai, "ormorok stack") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IntenseColdTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IntenseColdTrigger(PlayerbotAI* ai) : Trigger(ai, "intense cold") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KeristraszaPositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KeristraszaPositioningTrigger(PlayerbotAI* ai) : Trigger(ai, "keristrasza positioning") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user