mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
4 Commits
2925f248a6
...
a695ac77fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a695ac77fa | ||
|
|
5e7613f719 | ||
|
|
dca0be2932 | ||
|
|
ca19548cc5 |
@ -5,18 +5,211 @@
|
||||
|
||||
#include "FollowActions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
||||
#include "Event.h"
|
||||
#include "Formations.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Transport.h"
|
||||
#include "Map.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Transport* GetTransportForPosTolerant(Map* map, WorldObject* ref, uint32 phaseMask, float x, float y, float z)
|
||||
{
|
||||
if (!map || !ref)
|
||||
return nullptr;
|
||||
|
||||
std::array<float, 4> const probes = { z, z + 0.5f, z + 1.5f, z - 0.5f };
|
||||
for (float const pz : probes)
|
||||
{
|
||||
if (Transport* t = map->GetTransportForPos(phaseMask, x, y, pz, ref))
|
||||
return t;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Attempts to find a point on the leader's transport that is closer to the bot,
|
||||
// by probing along the segment from master -> bot and returning the last point
|
||||
// that is still detected as being on the expected transport.
|
||||
bool FindBoardingPointOnTransport(Map* map, Transport* expectedTransport, WorldObject* ref,
|
||||
float masterX, float masterY, float masterZ,
|
||||
float botX, float botY, float botZ,
|
||||
float& outX, float& outY, float& outZ)
|
||||
{
|
||||
if (!map || !expectedTransport || !ref)
|
||||
return false;
|
||||
|
||||
uint32 const phaseMask = ref->GetPhaseMask();
|
||||
|
||||
// Ensure master is actually detected on that transport (tolerant).
|
||||
if (GetTransportForPosTolerant(map, ref, phaseMask, masterX, masterY, masterZ) != expectedTransport)
|
||||
return false;
|
||||
|
||||
// The raycast in GetTransportForPos starts at (z + 2). Probe with a safe Z.
|
||||
float const probeZ = std::max(masterZ, botZ);
|
||||
|
||||
// Adaptive step count: small platforms need tighter sampling.
|
||||
float const dx2 = botX - masterX;
|
||||
float const dy2 = botY - masterY;
|
||||
float const dist2d = std::sqrt(dx2 * dx2 + dy2 * dy2);
|
||||
int32 const steps = std::clamp(static_cast<int32>(dist2d / 0.75f), 10, 28);
|
||||
|
||||
float const dx = (botX - masterX) / static_cast<float>(steps);
|
||||
float const dy = (botY - masterY) / static_cast<float>(steps);
|
||||
|
||||
// Master must actually be on the expected transport for this to work.
|
||||
if (map->GetTransportForPos(ref->GetPhaseMask(), masterX, masterY, probeZ, ref) != expectedTransport)
|
||||
return false;
|
||||
|
||||
float lastX = masterX;
|
||||
float lastY = masterY;
|
||||
bool found = false;
|
||||
|
||||
for (int32 i = 1; i <= steps; ++i)
|
||||
{
|
||||
float const px = masterX + dx * i;
|
||||
float const py = masterY + dy * i;
|
||||
|
||||
Transport* const t = GetTransportForPosTolerant(map, ref, phaseMask, px, py, probeZ);
|
||||
if (t != expectedTransport)
|
||||
break;
|
||||
|
||||
lastX = px;
|
||||
lastY = py;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
outX = lastX;
|
||||
outY = lastY;
|
||||
outZ = masterZ; // keep deck-level Z to encourage stepping onto the platform/boat
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FollowAction::Execute(Event /*event*/)
|
||||
{
|
||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||
std::string const target = formation->GetTargetName();
|
||||
|
||||
// Transport handling for moving transports only (boats/zeppelins).
|
||||
Player* master = botAI->GetMaster();
|
||||
if (master && master->IsInWorld() && bot->IsInWorld() && bot->GetMapId() == master->GetMapId())
|
||||
{
|
||||
Map* map = master->GetMap();
|
||||
uint32 const mapId = bot->GetMapId();
|
||||
Transport* transport = nullptr;
|
||||
bool masterOnTransport = false;
|
||||
|
||||
if (master->GetTransport())
|
||||
{
|
||||
transport = master->GetTransport();
|
||||
masterOnTransport = true;
|
||||
}
|
||||
else if (map)
|
||||
{
|
||||
transport = GetTransportForPosTolerant(map, master, master->GetPhaseMask(),
|
||||
master->GetPositionX(), master->GetPositionY(), master->GetPositionZ());
|
||||
masterOnTransport = (transport != nullptr);
|
||||
}
|
||||
|
||||
// Ignore static transports (elevators/trams): only keep boats/zeppelins here.
|
||||
if (transport && transport->IsStaticTransport())
|
||||
transport = nullptr;
|
||||
|
||||
if (transport && map && bot->GetTransport() != transport)
|
||||
{
|
||||
float const botProbeZ = std::max(bot->GetPositionZ(), transport->GetPositionZ());
|
||||
Transport* botSurfaceTransport = GetTransportForPosTolerant(map, bot, bot->GetPhaseMask(),
|
||||
bot->GetPositionX(), bot->GetPositionY(), botProbeZ);
|
||||
|
||||
if (botSurfaceTransport == transport)
|
||||
{
|
||||
transport->AddPassenger(bot, true);
|
||||
bot->StopMovingOnCurrentPos();
|
||||
return true;
|
||||
}
|
||||
|
||||
float const boardingAssistDistance = 60.0f;
|
||||
float const dist2d = ServerFacade::instance().GetDistance2d(bot, master);
|
||||
bool const inAssist = ServerFacade::instance().IsDistanceLessOrEqualThan(dist2d, boardingAssistDistance);
|
||||
|
||||
if (inAssist)
|
||||
{
|
||||
float destX = masterOnTransport ? master->GetPositionX() : transport->GetPositionX();
|
||||
float destY = masterOnTransport ? master->GetPositionY() : transport->GetPositionY();
|
||||
float destZ = masterOnTransport ? master->GetPositionZ() : transport->GetPositionZ();
|
||||
float edgeX = 0.0f;
|
||||
float edgeY = 0.0f;
|
||||
float edgeZ = 0.0f;
|
||||
|
||||
if (masterOnTransport &&
|
||||
FindBoardingPointOnTransport(map, transport, master,
|
||||
master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(),
|
||||
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||
edgeX, edgeY, edgeZ))
|
||||
{
|
||||
destX = edgeX;
|
||||
destY = edgeY;
|
||||
destZ = edgeZ;
|
||||
}
|
||||
|
||||
MovementPriority const priority = botAI->GetState() == BOT_STATE_COMBAT
|
||||
? MovementPriority::MOVEMENT_COMBAT
|
||||
: MovementPriority::MOVEMENT_NORMAL;
|
||||
|
||||
bool const movingAllowed = IsMovingAllowed(mapId, destX, destY, destZ);
|
||||
bool const dupMove = IsDuplicateMove(mapId, destX, destY, destZ);
|
||||
bool const waiting = IsWaitingForLastMove(priority);
|
||||
|
||||
if (movingAllowed && !dupMove && !waiting)
|
||||
{
|
||||
if (bot->IsSitState())
|
||||
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
|
||||
if (bot->IsNonMeleeSpellCast(true))
|
||||
{
|
||||
bot->CastStop();
|
||||
botAI->InterruptSpell();
|
||||
}
|
||||
|
||||
if (MotionMaster* mm = bot->GetMotionMaster())
|
||||
{
|
||||
mm->MovePoint(
|
||||
/*id*/ 0,
|
||||
/*coords*/ destX, destY, destZ,
|
||||
/*forcedMovement*/ FORCED_MOVEMENT_NONE,
|
||||
/*speed*/ 0.0f,
|
||||
/*orientation*/ 0.0f,
|
||||
/*generatePath*/ false,
|
||||
/*forceDestination*/ false);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
float delay = 1000.0f * MoveDelay(bot->GetExactDist(destX, destY, destZ));
|
||||
delay = std::clamp(delay, 0.0f, static_cast<float>(sPlayerbotAIConfig.maxWaitForMove));
|
||||
|
||||
AI_VALUE(LastMovement&, "last movement")
|
||||
.Set(mapId, destX, destY, destZ, bot->GetOrientation(), delay, priority);
|
||||
ClearIdleState();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// end unified transport handling
|
||||
|
||||
bool moved = false;
|
||||
if (!target.empty())
|
||||
{
|
||||
|
||||
@ -37,10 +37,10 @@ public:
|
||||
uint32 GetCurrWaypoint() override;
|
||||
};
|
||||
|
||||
class GrobblulusMoveCenterAction : public MoveInsideAction
|
||||
class GrobbulusMoveCenterAction : public MoveInsideAction
|
||||
{
|
||||
public:
|
||||
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
|
||||
GrobbulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
|
||||
};
|
||||
|
||||
class GrobbulusMoveAwayAction : public MovementAction
|
||||
@ -173,26 +173,26 @@ private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttractAlternativelyAction : public AttackAction
|
||||
class FourHorsemenAttractAlternativelyAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai)
|
||||
FourHorsemenAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attract alternatively"), helper(ai)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
FourhorsemanBossHelper helper;
|
||||
FourHorsemenBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttactInOrderAction : public AttackAction
|
||||
class FourHorsemenAttackInOrderAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {}
|
||||
FourHorsemenAttackInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attack in order"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
FourhorsemanBossHelper helper;
|
||||
FourHorsemenBossHelper helper;
|
||||
};
|
||||
|
||||
// class SapphironGroundMainTankPositionAction : public MovementAction
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
@ -66,13 +65,10 @@ bool AnubrekhanPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
uint32 nearest = FindNearestWaypoint();
|
||||
uint32 next_point;
|
||||
if (inPhase)
|
||||
next_point = (nearest + 1) % intervals;
|
||||
else
|
||||
next_point = nearest;
|
||||
|
||||
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false,
|
||||
false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
|
||||
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
|
||||
bool FourHorsemenAttractAlternativelyAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
@ -13,13 +13,13 @@ bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
|
||||
return true;
|
||||
|
||||
Unit* attackTarget = helper.CurrentAttackTarget();
|
||||
if (context->GetValue<Unit*>("current target")->Get() != attackTarget)
|
||||
if (attackTarget && context->GetValue<Unit*>("current target")->Get() != attackTarget)
|
||||
return Attack(attackTarget);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HorsemanAttactInOrderAction::Execute(Event /*event*/)
|
||||
bool FourHorsemenAttackInOrderAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
@ -70,7 +70,6 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Group::MemberSlotList const& slots = group->GetMemberSlots();
|
||||
Player* playerWithIcebolt = nullptr;
|
||||
float minDistance;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
|
||||
@ -13,7 +13,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
|
||||
return false;
|
||||
|
||||
Unit* target = helper.GetNearestPet();
|
||||
if (!bot->IsWithinDistInMap(target, 50.0f))
|
||||
if (!target || !bot->IsWithinDistInMap(target, 50.0f))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -22,7 +22,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
|
||||
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* target = helper.GetNearestPet();
|
||||
if (!bot->IsWithinLOSInMap(target))
|
||||
if (!target || !bot->IsWithinLOSInMap(target))
|
||||
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
if (AI_VALUE(Unit*, "current target") != target)
|
||||
|
||||
@ -245,7 +245,7 @@ float AnubrekhanGenericMultiplier::GetValue(Action* action)
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FourhorsemanGenericMultiplier::GetValue(Action* action)
|
||||
float FourHorsemenGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
||||
if (!boss)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
||||
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
||||
#ifndef _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
@ -84,10 +84,10 @@ public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FourhorsemanGenericMultiplier : public Multiplier
|
||||
class FourHorsemenGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {}
|
||||
FourHorsemenGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "four horsemen generic") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
@ -31,8 +31,8 @@ public:
|
||||
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
|
||||
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
|
||||
|
||||
creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively;
|
||||
creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order;
|
||||
creators["four horsemen attract alternatively"] = &RaidNaxxActionContext::four_horsemen_attract_alternatively;
|
||||
creators["four horsemen attack in order"] = &RaidNaxxActionContext::four_horsemen_attack_in_order;
|
||||
|
||||
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
|
||||
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
|
||||
@ -56,7 +56,7 @@ public:
|
||||
private:
|
||||
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
|
||||
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(ai); }
|
||||
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobblulusMoveCenterAction(ai); }
|
||||
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobbulusMoveCenterAction(ai); }
|
||||
static Action* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); }
|
||||
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
|
||||
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
|
||||
@ -70,11 +70,8 @@ private:
|
||||
{
|
||||
return new RazuviousUseObedienceCrystalAction(ai);
|
||||
}
|
||||
static Action* horseman_attract_alternatively(PlayerbotAI* ai)
|
||||
{
|
||||
return new HorsemanAttractAlternativelyAction(ai);
|
||||
}
|
||||
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new HorsemanAttactInOrderAction(ai); }
|
||||
static Action* four_horsemen_attract_alternatively(PlayerbotAI* ai) { return new FourHorsemenAttractAlternativelyAction(ai); }
|
||||
static Action* four_horsemen_attack_in_order(PlayerbotAI* ai) { return new FourHorsemenAttackInOrderAction(ai); }
|
||||
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
|
||||
// SapphironGroundMainTankPositionAction(ai); }
|
||||
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); }
|
||||
|
||||
@ -30,8 +30,8 @@ public:
|
||||
creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank;
|
||||
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
|
||||
|
||||
creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors;
|
||||
creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors;
|
||||
creators["four horsemen attractors"] = &RaidNaxxTriggerContext::four_horsemen_attractors;
|
||||
creators["four horsemen except attractors"] = &RaidNaxxTriggerContext::four_horsemen_except_attractors;
|
||||
|
||||
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
|
||||
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
|
||||
@ -66,8 +66,8 @@ private:
|
||||
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
|
||||
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
|
||||
|
||||
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); }
|
||||
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); }
|
||||
static Trigger* four_horsemen_attractors(PlayerbotAI* ai) { return new FourHorsemenAttractorsTrigger(ai); }
|
||||
static Trigger* four_horsemen_except_attractors(PlayerbotAI* ai) { return new FourHorsemenExceptAttractorsTrigger(ai); }
|
||||
|
||||
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); }
|
||||
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); }
|
||||
|
||||
@ -97,13 +97,13 @@ void RaidNaxxStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{ NextAction("razuvious target", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// four horseman
|
||||
triggers.push_back(new TriggerNode("horseman attractors",
|
||||
{ NextAction("horseman attract alternatively", ACTION_RAID + 1) }
|
||||
// four horsemen
|
||||
triggers.push_back(new TriggerNode("four horsemen attractors",
|
||||
{ NextAction("four horsemen attract alternatively", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("horseman except attractors",
|
||||
{ NextAction("horseman attack in order", ACTION_RAID + 1) }
|
||||
triggers.push_back(new TriggerNode("four horsemen except attractors",
|
||||
{ NextAction("four horsemen attack in order", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// sapphiron
|
||||
@ -150,7 +150,7 @@ void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
|
||||
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
|
||||
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
|
||||
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI));
|
||||
multipliers.push_back(new FourHorsemenGenericMultiplier(botAI));
|
||||
// multipliers.push_back(new GothikGenericMultiplier(botAI));
|
||||
multipliers.push_back(new GluthGenericMultiplier(botAI));
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ bool RazuviousNontankTrigger::IsActive()
|
||||
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
|
||||
}
|
||||
|
||||
bool HorsemanAttractorsTrigger::IsActive()
|
||||
bool FourHorsemenAttractorsTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
@ -122,7 +122,7 @@ bool HorsemanAttractorsTrigger::IsActive()
|
||||
return helper.IsAttracter(bot);
|
||||
}
|
||||
|
||||
bool HorsemanExceptAttractorsTrigger::IsActive()
|
||||
bool FourHorsemenExceptAttractorsTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
@ -186,24 +186,24 @@ private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttractorsTrigger : public Trigger
|
||||
class FourHorsemenAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {}
|
||||
FourHorsemenAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "four horsemen attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
FourhorsemanBossHelper helper;
|
||||
FourHorsemenBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanExceptAttractorsTrigger : public Trigger
|
||||
class FourHorsemenExceptAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {}
|
||||
FourHorsemenExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "four horsemen except attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
FourhorsemanBossHelper helper;
|
||||
FourHorsemenBossHelper helper;
|
||||
};
|
||||
|
||||
class SapphironGroundTrigger : public Trigger
|
||||
|
||||
@ -202,7 +202,7 @@ public:
|
||||
}
|
||||
bool FindPosToAvoidChill(std::vector<float>& dest)
|
||||
{
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25});
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill10, NaxxSpellIds::Chill25});
|
||||
if (!aura)
|
||||
{
|
||||
// Fallback to name for custom spell data.
|
||||
@ -363,13 +363,13 @@ private:
|
||||
Unit* _unit = nullptr;
|
||||
};
|
||||
|
||||
class FourhorsemanBossHelper : public AiObject
|
||||
class FourHorsemenBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const float posZ = 241.27f;
|
||||
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
|
||||
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
|
||||
FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
FourHorsemenBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
@ -497,7 +497,8 @@ public:
|
||||
if (feugen && feugen->IsAlive())
|
||||
unit = feugen;
|
||||
|
||||
if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen)))
|
||||
if (stalagg && stalagg->IsAlive() &&
|
||||
(!feugen || !feugen->IsAlive() || bot->GetDistance(stalagg) < bot->GetDistance(feugen)))
|
||||
unit = stalagg;
|
||||
|
||||
return unit;
|
||||
|
||||
@ -58,6 +58,7 @@ namespace NaxxSpellIds
|
||||
// Sapphiron
|
||||
static constexpr uint32 Icebolt10 = 28522;
|
||||
static constexpr uint32 Icebolt25 = 28526;
|
||||
static constexpr uint32 Chill10 = 28547;
|
||||
static constexpr uint32 Chill25 = 55699;
|
||||
/*
|
||||
// Fight
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
|
||||
@ -75,8 +80,8 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool TryMisdirectToFrostTank(Unit* hydross, Group* group);
|
||||
bool TryMisdirectToNatureTank(Unit* hydross, Group* group);
|
||||
bool TryMisdirectToFrostTank(Unit* hydross);
|
||||
bool TryMisdirectToNatureTank(Unit* hydross);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
||||
@ -413,10 +418,10 @@ private:
|
||||
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||
bool IsFirstCorePasserInPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool IsSecondCorePasserInPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool IsThirdCorePasserInPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFourthCorePasserInPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
||||
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
||||
};
|
||||
@ -428,19 +433,12 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjEraseCorePassingTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjEraseCorePassingTrackersAction(PlayerbotAI* botAI, std::string const name = "lady vashj erase core passing trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjAvoidToxicSporesAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
static std::vector<Unit*> GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot);
|
||||
static std::vector<Unit*> GetAllSporeDropTriggers(Player* bot);
|
||||
|
||||
private:
|
||||
Position FindSafestNearbyPosition(const std::vector<Unit*>& spores, const Position& position, float maxRadius, float hazardRadius);
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidSSCMultipliers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
@ -28,12 +33,10 @@ using namespace SerpentShrineCavernHelpers;
|
||||
|
||||
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_TOXIC_POOL))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
if (bot->HasAura(SPELL_TOXIC_POOL) &&
|
||||
dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -53,16 +56,16 @@ float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if ((botAI->IsMainTank(bot) && !hydross->HasAura(SPELL_CORRUPTION)) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0, true) && hydross->HasAura(SPELL_CORRUPTION)))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
(dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionNatureTankAction*>(action)))
|
||||
{
|
||||
if ((botAI->IsMainTank(bot) && hydross->HasAura(SPELL_CORRUPTION)) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0, true) && !hydross->HasAura(SPELL_CORRUPTION)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -97,14 +100,14 @@ float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
||||
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (!justChanged && !aboutToChange)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
{
|
||||
@ -116,14 +119,14 @@ float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
||||
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (!justChanged && !aboutToChange)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -133,11 +136,9 @@ float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
{
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
if (AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -175,14 +176,14 @@ float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action)
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "the lurker below"))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "the lurker below"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -215,11 +216,11 @@ float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
++tankCount;
|
||||
}
|
||||
|
||||
if (tankCount >= 3)
|
||||
{
|
||||
if (tankCount < 3)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -234,14 +235,11 @@ float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
if (!leotherasHuman)
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras || (!leotheras->HasAura(SPELL_WHIRLWIND) &&
|
||||
!leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||
return 1.0f;
|
||||
|
||||
if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
(leotherasHuman->HasAura(SPELL_WHIRLWIND) ||
|
||||
leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
@ -249,7 +247,6 @@ float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -262,10 +259,10 @@ float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return 1.0f;
|
||||
|
||||
if (GetPhase2LeotherasDemon(botAI) && dynamic_cast<AttackAction*>(action))
|
||||
if (GetPhase2LeotherasDemon(bot) && dynamic_cast<AttackAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast<CastBerserkAction*>(action))
|
||||
if (!GetPhase3LeotherasDemon(bot) && dynamic_cast<CastBerserkAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
@ -273,8 +270,9 @@ float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
|
||||
float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
{
|
||||
if (!bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<CastHealingSpellAction*>(action) ||
|
||||
@ -287,7 +285,6 @@ float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
||||
dynamic_cast<CastDireBearFormAction*>(action) ||
|
||||
dynamic_cast<CastTreeFormAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -297,19 +294,19 @@ float LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* actio
|
||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (!GetPhase2LeotherasDemon(botAI))
|
||||
if (!GetPhase2LeotherasDemon(bot))
|
||||
return 1.0f;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
if (chaosBlast && chaosBlast->GetStackAmount() >= 5)
|
||||
{
|
||||
if (!chaosBlast || chaosBlast->GetStackAmount() < 5)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -330,8 +327,8 @@ float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI);
|
||||
Unit* leotherasHuman = GetLeotherasHuman(bot);
|
||||
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(bot);
|
||||
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
!leotherasPhase3Demon)
|
||||
{
|
||||
@ -350,7 +347,7 @@ float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
||||
}
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
||||
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI);
|
||||
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(bot);
|
||||
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
||||
if (leotherasPhase2Demon)
|
||||
{
|
||||
@ -398,12 +395,12 @@ float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* acti
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (leotheras && leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
{
|
||||
if (!leotheras || !leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -447,14 +444,12 @@ float FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action)
|
||||
if (!botAI->IsDps(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||
{
|
||||
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return 1.0f;
|
||||
|
||||
auto castSpellAction = dynamic_cast<CastSpellAction*>(action);
|
||||
if (castSpellAction && castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -464,11 +459,11 @@ float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -505,12 +500,12 @@ float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -526,12 +521,12 @@ float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* act
|
||||
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "tidewalker lurker"))
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "tidewalker lurker"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -541,11 +536,11 @@ float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -556,17 +551,14 @@ float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* actio
|
||||
return 1.0f;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
if (!tidewalker)
|
||||
if (!tidewalker || tidewalker->GetHealthPct() > 25.0f)
|
||||
return 1.0f;
|
||||
|
||||
if (tidewalker->GetHealthPct() < 25.0f)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -580,18 +572,15 @@ float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->getClass() == CLASS_SHAMAN)
|
||||
{
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
if (bot->getClass() == CLASS_SHAMAN &&
|
||||
!IsLadyVashjInPhase3(botAI) &&
|
||||
(dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
if (!botAI->IsDps(bot) || !IsLadyVashjInPhase1(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (dynamic_cast<CastMetamorphosisAction*>(action) ||
|
||||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
|
||||
dynamic_cast<CastBladeFlurryAction*>(action) ||
|
||||
@ -614,8 +603,29 @@ float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
||||
dynamic_cast<CastBloodFuryAction*>(action) ||
|
||||
dynamic_cast<UseTrinketAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjMainTankGroupShamanUseGroundingTotemMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (!IsMainTankInSameSubgroup(botAI, bot))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastWindfuryTotemAction*>(action) ||
|
||||
dynamic_cast<SetWindfuryTotemAction*>(action) ||
|
||||
dynamic_cast<CastWrathOfAirTotemAction*>(action) ||
|
||||
dynamic_cast<SetWrathOfAirTotemAction*>(action) ||
|
||||
dynamic_cast<CastNatureResistanceTotemAction*>(action) ||
|
||||
dynamic_cast<SetNatureResistanceTotemAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@ -624,15 +634,15 @@ float LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action)
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
!IsLadyVashjInPhase1(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -658,19 +668,18 @@ float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action)
|
||||
// Bots should not loot the core with normal looting logic
|
||||
float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<LootAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
!IsLadyVashjInPhase2(botAI))
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<WipeAction*>(action) ||
|
||||
@ -678,65 +687,43 @@ float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* acti
|
||||
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
bool isCoreHandler = false;
|
||||
int myIndex = -1;
|
||||
for (int i = 0; i < static_cast<int>(coreHandlers.size()); ++i)
|
||||
{
|
||||
if (coreHandlers[i] && coreHandlers[i] == bot)
|
||||
{
|
||||
isCoreHandler = true;
|
||||
myIndex = i;
|
||||
}
|
||||
}
|
||||
if (!isCoreHandler)
|
||||
return 1.0f;
|
||||
|
||||
auto hasCore = [](Player* player)
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (hasCore(bot))
|
||||
{
|
||||
if (!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
// If the bot actually has the core, only allow core handling
|
||||
if (hasCore(bot) && !dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return 1.0f;
|
||||
// First and second passers block movement when the looter teleports to the elemental
|
||||
Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||
if (tainted && coreHandlers[0]->GetExactDist2d(tainted) < 5.0f &&
|
||||
(bot == coreHandlers[1] || bot == coreHandlers[2]) &&
|
||||
(dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
// If any prior handler (including self) recently had the core, block other movement
|
||||
if (AnyRecentCoreInInventory(botAI, bot) &&
|
||||
dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@ -745,7 +732,8 @@ float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* acti
|
||||
// So the standard target selection system must be disabled
|
||||
float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
@ -755,24 +743,26 @@ float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *ac
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (bot->GetExactDist2d(vashj) < 60.0f &&
|
||||
dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
if (enchanted && bot->GetVictim() == enchanted &&
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
@ -780,17 +770,14 @@ float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *ac
|
||||
Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite");
|
||||
if (enchanted || strider || elite)
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
if (dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
if (enchanted && bot->GetVictim() == enchanted &&
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
|
||||
@ -193,6 +198,14 @@ public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjMainTankGroupShamanUseGroundingTotemMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjMainTankGroupShamanUseGroundingTotemMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj main tank group shaman use grounding totem") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
|
||||
@ -161,9 +166,6 @@ public:
|
||||
creators["lady vashj destroy tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_destroy_tainted_core;
|
||||
|
||||
creators["lady vashj erase core passing trackers"] =
|
||||
&RaidSSCActionContext::lady_vashj_erase_core_passing_trackers;
|
||||
|
||||
creators["lady vashj avoid toxic spores"] =
|
||||
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
||||
|
||||
@ -324,9 +326,6 @@ private:
|
||||
static Action* lady_vashj_destroy_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_erase_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjEraseCorePassingTrackersAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_avoid_toxic_spores(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); }
|
||||
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
|
||||
@ -155,9 +160,6 @@ public:
|
||||
creators["lady vashj tainted core is unusable"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_core_is_unusable;
|
||||
|
||||
creators["lady vashj need to reset core passing trackers"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_need_to_reset_core_passing_trackers;
|
||||
|
||||
creators["lady vashj toxic sporebats are spewing poison clouds"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_toxic_sporebats_are_spewing_poison_clouds;
|
||||
|
||||
@ -312,9 +314,6 @@ private:
|
||||
static Trigger* lady_vashj_tainted_core_is_unusable(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_need_to_reset_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjNeedToResetCorePassingTrackersTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds(
|
||||
PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); }
|
||||
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidSSCMultipliers.h"
|
||||
|
||||
@ -144,9 +149,6 @@ void RaidSSCStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", {
|
||||
NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj need to reset core passing trackers", {
|
||||
NextAction("lady vashj erase core passing trackers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj adds spawn in phase 2 and phase 3", {
|
||||
NextAction("lady vashj assign phase 2 and phase 3 dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
@ -198,6 +200,7 @@ void RaidSSCStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjMainTankGroupShamanUseGroundingTotemMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(botAI));
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
#define _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
@ -26,35 +31,37 @@ bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive()
|
||||
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsDps(bot) &&
|
||||
GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM);
|
||||
AI_VALUE2(Unit*, "find target", "greyheart tidecaller");
|
||||
}
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsMainTank(bot);
|
||||
return botAI->IsMainTank(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
return botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (hydross && hydross->GetHealthPct() < 10.0f)
|
||||
if (!hydross || hydross->GetHealthPct() < 10.0f)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") &&
|
||||
!AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"))
|
||||
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
return false;
|
||||
|
||||
return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
return AI_VALUE2(Unit*, "find target", "pure spawn of hydross") ||
|
||||
AI_VALUE2(Unit*, "find target", "tainted spawn of hydross");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
||||
@ -71,19 +78,19 @@ bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive()
|
||||
|
||||
bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
if (bot->getClass() == CLASS_HUNTER ||
|
||||
botAI->IsHeal(bot) ||
|
||||
botAI->IsMainTank(bot) ||
|
||||
botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
return false;
|
||||
|
||||
return bot->getClass() != CLASS_HUNTER &&
|
||||
!botAI->IsHeal(bot) &&
|
||||
!botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
@ -102,11 +109,11 @@ bool TheLurkerBelowSpoutIsActiveTrigger::IsActive()
|
||||
|
||||
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
if (!botAI->IsMainTank(bot))
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
@ -135,35 +142,16 @@ bool TheLurkerBelowBossCastsGeyserTrigger::IsActive()
|
||||
// Trigger will be active only if there are at least 3 tanks in the raid
|
||||
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||
return false;
|
||||
|
||||
Player* mainTank = nullptr;
|
||||
Player* firstAssistTank = nullptr;
|
||||
Player* secondAssistTank = nullptr;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (!mainTank && memberAI->IsMainTank(member))
|
||||
mainTank = member;
|
||||
else if (!firstAssistTank && memberAI->IsAssistTankOfIndex(member, 0, true))
|
||||
firstAssistTank = member;
|
||||
else if (!secondAssistTank && memberAI->IsAssistTankOfIndex(member, 1, true))
|
||||
secondAssistTank = member;
|
||||
}
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
Player* firstAssistTank = GetGroupAssistTank(botAI, bot, 0);
|
||||
Player* secondAssistTank = GetGroupAssistTank(botAI, bot, 1);
|
||||
|
||||
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
||||
return false;
|
||||
@ -173,51 +161,55 @@ bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||
|
||||
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "the lurker below") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||
AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
bool LeotherasTheBlindBossIsInactiveTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "greyheart spellbinder");
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||
AI_VALUE2(Unit*, "find target", "greyheart spellbinder");
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_WARLOCK)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) != bot)
|
||||
return false;
|
||||
|
||||
return GetActiveLeotherasDemon(botAI);
|
||||
return GetActiveLeotherasDemon(bot);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot) || !botAI->IsTank(bot))
|
||||
if (!botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!GetLeotherasDemonFormTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
return GetPhase2LeotherasDemon(bot);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
if (!botAI->IsRanged(bot))
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
@ -231,14 +223,14 @@ bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||
|
||||
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) && botAI->IsMelee(bot))
|
||||
if (botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras || leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
if (!leotheras)
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
||||
@ -247,10 +239,13 @@ bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||
|
||||
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
if (botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
if (botAI->IsRanged(bot))
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
@ -260,7 +255,7 @@ bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
return GetPhase2LeotherasDemon(bot);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||
@ -271,89 +266,68 @@ bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||
|
||||
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) == bot)
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
return GetPhase3LeotherasDemon(botAI) &&
|
||||
GetLeotherasHuman(botAI);
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() == CLASS_WARLOCK && GetLeotherasDemonFormTank(bot) == bot)
|
||||
return false;
|
||||
|
||||
return GetPhase3LeotherasDemon(bot);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
return !bot->HasAura(SPELL_INSIDIOUS_WHISPER);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||
AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
botAI->IsMainTank(bot);
|
||||
return botAI->IsMainTank(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, false);
|
||||
return botAI->IsAssistTankOfIndex(bot, 0, false) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
}
|
||||
|
||||
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 1, false);
|
||||
return botAI->IsAssistTankOfIndex(bot, 1, false) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis");
|
||||
}
|
||||
|
||||
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 2, false);
|
||||
return botAI->IsAssistTankOfIndex(bot, 2, false) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess");
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
||||
{
|
||||
Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
if (!caribdis)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return false;
|
||||
|
||||
Player* firstAssistTank = nullptr;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->IsAssistTankOfIndex(member, 0, false))
|
||||
{
|
||||
firstAssistTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstAssistTank;
|
||||
return botAI->IsAssistHealOfIndex(bot, 0, true) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
}
|
||||
|
||||
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||
@ -367,10 +341,10 @@ bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||
|
||||
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return false;
|
||||
|
||||
if (botAI->IsDps(bot))
|
||||
@ -387,8 +361,8 @@ bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||
|
||||
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||
AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
}
|
||||
|
||||
// Morogrim Tidewalker
|
||||
@ -404,8 +378,8 @@ bool MorogrimTidewalkerPullingBossTrigger::IsActive()
|
||||
|
||||
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") &&
|
||||
botAI->IsMainTank(bot);
|
||||
return botAI->IsMainTank(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
}
|
||||
|
||||
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||
@ -421,8 +395,11 @@ bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||
|
||||
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
!IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot);
|
||||
!IsLadyVashjInPhase2(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
||||
@ -439,10 +416,7 @@ bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive()
|
||||
IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
if (!IsMainTankInSameSubgroup(bot))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return IsMainTankInSameSubgroup(botAI, bot);
|
||||
}
|
||||
|
||||
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||
@ -450,15 +424,16 @@ bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -500,9 +475,10 @@ bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
||||
return false;
|
||||
|
||||
bool taintedPresent = false;
|
||||
Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||
if (taintedUnit)
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental"))
|
||||
{
|
||||
taintedPresent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||
@ -513,26 +489,20 @@ bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
||||
if (!object)
|
||||
continue;
|
||||
|
||||
if (Creature* creature = object->ToCreature())
|
||||
{
|
||||
if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||
if (Creature* creature = object->ToCreature();
|
||||
creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||
{
|
||||
taintedPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!taintedPresent)
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return (GetDesignatedCoreLooter(group, botAI) == bot &&
|
||||
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false));
|
||||
return GetDesignatedCoreLooter(botAI, bot) == bot &&
|
||||
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||
@ -540,54 +510,24 @@ bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
bool isCoreHandler = false;
|
||||
for (Player* handler : coreHandlers)
|
||||
if (handler == bot)
|
||||
isCoreHandler = true;
|
||||
|
||||
auto hasCore = [](Player* player) -> bool
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
if (!isCoreHandler)
|
||||
return false;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return false;
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
return true;
|
||||
|
||||
// First and second passers move to positions as soon as the elemental appears
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||
if (tainted && coreHandlers[0]->GetExactDist2d(tainted) < 5.0f &&
|
||||
(bot == coreHandlers[1] || bot == coreHandlers[2]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
// Main logic: run if core is in play for this bot or a prior handler
|
||||
return AnyRecentCoreInInventory(botAI, bot);
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||
@ -599,18 +539,7 @@ bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||
if (!IsLadyVashjInPhase2(botAI))
|
||||
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* coreHandlers[] =
|
||||
{
|
||||
GetDesignatedCoreLooter(group, botAI),
|
||||
GetFirstTaintedCorePasser(group, botAI),
|
||||
GetSecondTaintedCorePasser(group, botAI),
|
||||
GetThirdTaintedCorePasser(group, botAI),
|
||||
GetFourthTaintedCorePasser(group, botAI)
|
||||
};
|
||||
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||
|
||||
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
{
|
||||
@ -625,24 +554,6 @@ bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjNeedToResetCorePassingTrackersTrigger::IsActive()
|
||||
{
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj || IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr) ||
|
||||
GetDesignatedCoreLooter(group, botAI) == bot ||
|
||||
GetFirstTaintedCorePasser(group, botAI) == bot ||
|
||||
GetSecondTaintedCorePasser(group, botAI) == bot ||
|
||||
GetThirdTaintedCorePasser(group, botAI) == bot ||
|
||||
GetFourthTaintedCorePasser(group, botAI) == bot;
|
||||
}
|
||||
|
||||
bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive()
|
||||
{
|
||||
return IsLadyVashjInPhase3(botAI);
|
||||
@ -653,8 +564,10 @@ bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
@ -664,7 +577,6 @@ bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
||||
if (botAI->IsMelee(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
|
||||
@ -387,14 +392,6 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjNeedToResetCorePassingTrackersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjNeedToResetCorePassingTrackersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj need to reset core passing trackers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Creature.h"
|
||||
@ -79,61 +84,54 @@ namespace SerpentShrineCavernHelpers
|
||||
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI)
|
||||
Unit* GetLeotherasHuman(Player* bot)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
constexpr float searchRadius = 100.0f;
|
||||
Creature* leotheras =
|
||||
bot->FindNearestCreature(NPC_LEOTHERAS_THE_BLIND, searchRadius, true);
|
||||
|
||||
if (leotheras && leotheras->IsInCombat() &&
|
||||
!leotheras->HasAura(SPELL_METAMORPHOSIS))
|
||||
return leotheras;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI)
|
||||
Unit* GetPhase2LeotherasDemon(Player* bot)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
constexpr float searchRadius = 100.0f;
|
||||
Creature* leotheras =
|
||||
bot->FindNearestCreature(NPC_LEOTHERAS_THE_BLIND, searchRadius, true);
|
||||
|
||||
if (leotheras && leotheras->HasAura(SPELL_METAMORPHOSIS))
|
||||
return leotheras;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI)
|
||||
Unit* GetPhase3LeotherasDemon(Player* bot)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS)
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
constexpr float searchRadius = 100.0f;
|
||||
return bot->FindNearestCreature(NPC_SHADOW_OF_LEOTHERAS, searchRadius, true);
|
||||
}
|
||||
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI)
|
||||
Unit* GetActiveLeotherasDemon(Player* bot)
|
||||
{
|
||||
Unit* phase2 = GetPhase2LeotherasDemon(botAI);
|
||||
Unit* phase3 = GetPhase3LeotherasDemon(botAI);
|
||||
Unit* phase2 = GetPhase2LeotherasDemon(bot);
|
||||
Unit* phase3 = GetPhase3LeotherasDemon(bot);
|
||||
return phase2 ? phase2 : phase3;
|
||||
}
|
||||
|
||||
// (1) First priority is an assistant Warlock (real player or bot)
|
||||
// (2) If no assistant Warlock, then look for any Warlock bot
|
||||
Player* GetLeotherasDemonFormTank(Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
// (1) First loop: Return the first assistant Warlock (real player or bot)
|
||||
Player* fallbackWarlock = nullptr;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
@ -142,21 +140,12 @@ namespace SerpentShrineCavernHelpers
|
||||
|
||||
if (group->IsAssistant(member->GetGUID()))
|
||||
return member;
|
||||
|
||||
if (!fallbackWarlock && GET_PLAYERBOT_AI(member))
|
||||
fallbackWarlock = member;
|
||||
}
|
||||
|
||||
// (2) Fall back to first found bot Warlock
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
member->getClass() != CLASS_WARLOCK)
|
||||
continue;
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
// (3) Return nullptr if none found
|
||||
return nullptr;
|
||||
return fallbackWarlock;
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
@ -182,16 +171,15 @@ namespace SerpentShrineCavernHelpers
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.985f };
|
||||
const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.902f };
|
||||
|
||||
std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
std::unordered_map<ObjectGuid, time_t> lastCoreInInventoryTime;
|
||||
|
||||
bool IsMainTankInSameSubgroup(Player* bot)
|
||||
bool IsMainTankInSameSubgroup(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group || !group->isRaidGroup())
|
||||
@ -210,12 +198,9 @@ namespace SerpentShrineCavernHelpers
|
||||
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
||||
continue;
|
||||
|
||||
if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member))
|
||||
{
|
||||
if (memberAI->IsMainTank(member))
|
||||
if (botAI->IsMainTank(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -277,38 +262,9 @@ namespace SerpentShrineCavernHelpers
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 instanceId = vashj->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lastCoreInInventoryTime.find(instanceId);
|
||||
if (it != lastCoreInInventoryTime.end())
|
||||
{
|
||||
if ((now - it->second) <= static_cast<time_t>(graceSeconds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI)
|
||||
Player* GetDesignatedCoreLooter(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
@ -317,10 +273,15 @@ namespace SerpentShrineCavernHelpers
|
||||
if (!leaderGuid.IsEmpty())
|
||||
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
||||
|
||||
// If cheats are disabled, the group leader will be the designated looter
|
||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||
return leader;
|
||||
|
||||
Player* fallback = leader;
|
||||
// Priority: (1) assistant melee DPS, (2) other melee DPS, (3) any ranged DPS
|
||||
Player* meleeDpsAssistant = nullptr;
|
||||
Player* meleeDps = nullptr;
|
||||
Player* rangedDps = nullptr;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
@ -331,22 +292,36 @@ namespace SerpentShrineCavernHelpers
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsMelee(member) && memberAI->IsDps(member))
|
||||
return member;
|
||||
|
||||
if (!fallback && memberAI->IsRangedDps(member))
|
||||
fallback = member;
|
||||
}
|
||||
|
||||
return fallback ? fallback : leader;
|
||||
}
|
||||
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
if (!meleeDpsAssistant && memberAI->IsMelee(member) &&
|
||||
memberAI->IsDps(member) && group->IsAssistant(member->GetGUID()))
|
||||
{
|
||||
meleeDpsAssistant = member;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!meleeDps && memberAI->IsMelee(member) && memberAI->IsDps(member))
|
||||
meleeDps = member;
|
||||
|
||||
if (!rangedDps && memberAI->IsRangedDps(member))
|
||||
rangedDps = member;
|
||||
}
|
||||
|
||||
if (meleeDpsAssistant)
|
||||
return meleeDpsAssistant;
|
||||
if (meleeDps)
|
||||
return meleeDps;
|
||||
if (rangedDps)
|
||||
return rangedDps;
|
||||
return leader;
|
||||
}
|
||||
|
||||
Player* GetFirstTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
@ -355,32 +330,29 @@ namespace SerpentShrineCavernHelpers
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||
if (memberAI && memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter)
|
||||
continue;
|
||||
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||
!botAI->IsTank(member) && member != designatedLooter)
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
Player* GetSecondTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
@ -390,34 +362,31 @@ namespace SerpentShrineCavernHelpers
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||
if (memberAI && memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser)
|
||||
continue;
|
||||
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||
!botAI->IsTank(member) && member != designatedLooter &&
|
||||
member != firstCorePasser)
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
Player* GetThirdTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(botAI, bot);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
@ -427,35 +396,32 @@ namespace SerpentShrineCavernHelpers
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||
if (memberAI && memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser)
|
||||
continue;
|
||||
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||
!botAI->IsTank(member) && member != designatedLooter &&
|
||||
member != firstCorePasser && member != secondCorePasser)
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
Player* GetFourthTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(botAI, bot);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(botAI, bot);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
@ -466,27 +432,75 @@ namespace SerpentShrineCavernHelpers
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||
if (memberAI && memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser ||
|
||||
member == thirdCorePasser)
|
||||
continue;
|
||||
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||
!botAI->IsTank(member) && member != designatedLooter &&
|
||||
member != firstCorePasser && member != secondCorePasser &&
|
||||
member != thirdCorePasser)
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<Player*, 5> GetCoreHandlers(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
return
|
||||
{
|
||||
GetDesignatedCoreLooter(botAI, bot),
|
||||
GetFirstTaintedCorePasser(botAI, bot),
|
||||
GetSecondTaintedCorePasser(botAI, bot),
|
||||
GetThirdTaintedCorePasser(botAI, bot),
|
||||
GetFourthTaintedCorePasser(botAI, bot)
|
||||
};
|
||||
}
|
||||
|
||||
// Checks if any bot from earlier in the passing sequence has the Tainted Core or
|
||||
// had it within the prior 3 seconds so the chain is not broken when the Core is in transit
|
||||
bool AnyRecentCoreInInventory(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||
|
||||
int8 myIndex = -1;
|
||||
for (int8 i = 0; i < 5; ++i)
|
||||
if (coreHandlers[i] && coreHandlers[i] == bot)
|
||||
myIndex = i;
|
||||
|
||||
if (myIndex == -1)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
constexpr uint8 lookbackSeconds = 3;
|
||||
|
||||
for (int8 i = 0; i <= myIndex; ++i)
|
||||
{
|
||||
Player* handler = coreHandlers[i];
|
||||
if (!handler)
|
||||
continue;
|
||||
|
||||
if (handler->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
return true;
|
||||
|
||||
auto it = lastCoreInInventoryTime.find(handler->GetGUID());
|
||||
if (it != lastCoreInInventoryTime.end() &&
|
||||
(now - it->second) <= static_cast<time_t>(lookbackSeconds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS =
|
||||
{
|
||||
47482, // NW
|
||||
@ -510,10 +524,7 @@ namespace SerpentShrineCavernHelpers
|
||||
continue;
|
||||
|
||||
GameObject* go = bounds.first->second;
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
if (go->GetGoState() != GO_STATE_READY)
|
||||
if (!go || go->GetGoState() != GO_STATE_READY)
|
||||
continue;
|
||||
|
||||
GeneratorInfo info;
|
||||
@ -529,7 +540,7 @@ namespace SerpentShrineCavernHelpers
|
||||
|
||||
// Returns the nearest active Shield Generator to the bot
|
||||
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
||||
// which depawn after use
|
||||
// which despawn after use
|
||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
||||
{
|
||||
if (!reference)
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
|
||||
@ -64,9 +69,6 @@ namespace SerpentShrineCavernHelpers
|
||||
|
||||
// Warlock
|
||||
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
||||
|
||||
// Item
|
||||
SPELL_HEAVY_NETHERWEAVE_NET = 31368,
|
||||
};
|
||||
|
||||
enum SerpentShrineCavernNPCs
|
||||
@ -105,9 +107,6 @@ namespace SerpentShrineCavernHelpers
|
||||
{
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
ITEM_TAINTED_CORE = 31088,
|
||||
|
||||
// Tailoring
|
||||
ITEM_HEAVY_NETHERWEAVE_NET = 24269,
|
||||
};
|
||||
|
||||
constexpr uint32 SSC_MAP_ID = 548;
|
||||
@ -134,10 +133,10 @@ namespace SerpentShrineCavernHelpers
|
||||
extern std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI);
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetLeotherasHuman(Player* bot);
|
||||
Unit* GetPhase2LeotherasDemon(Player* bot);
|
||||
Unit* GetPhase3LeotherasDemon(Player* bot);
|
||||
Unit* GetActiveLeotherasDemon(Player* bot);
|
||||
Player* GetLeotherasDemonFormTank(Player* bot);
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
@ -158,25 +157,26 @@ namespace SerpentShrineCavernHelpers
|
||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
constexpr float VASHJ_PLATFORM_Z = 42.985f;
|
||||
constexpr float VASHJ_PLATFORM_CENTER_Z = 42.902f;
|
||||
constexpr float VASHJ_PLATFORM_EDGE_Z = 41.097f;
|
||||
extern const Position VASHJ_PLATFORM_CENTER_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
extern std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
bool IsMainTankInSameSubgroup(Player* bot);
|
||||
extern std::unordered_map<ObjectGuid, time_t> lastCoreInInventoryTime;
|
||||
bool IsMainTankInSameSubgroup(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3);
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetDesignatedCoreLooter(PlayerbotAI* botAI, Player* bot);
|
||||
Player* GetFirstTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||
Player* GetSecondTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||
Player* GetThirdTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||
Player* GetFourthTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||
std::array<Player*, 5> GetCoreHandlers(PlayerbotAI* botAI, Player* bot);
|
||||
bool AnyRecentCoreInInventory(PlayerbotAI* botAI, Player* bot);
|
||||
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
||||
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||
|
||||
@ -1796,7 +1796,7 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
|
||||
if (itr != _playerbotsAIMap.end())
|
||||
{
|
||||
if (itr->second->IsBotAI())
|
||||
return reinterpret_cast<PlayerbotAI*>(itr->second);
|
||||
return dynamic_cast<PlayerbotAI*>(itr->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -1812,7 +1812,7 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
||||
if (itr != _playerbotsMgrMap.end())
|
||||
{
|
||||
if (!itr->second->IsBotAI())
|
||||
return reinterpret_cast<PlayerbotMgr*>(itr->second);
|
||||
return dynamic_cast<PlayerbotMgr*>(itr->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@ -1070,7 +1070,7 @@ void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim)
|
||||
if (!victim->IsCreature())
|
||||
return;
|
||||
|
||||
Creature* creature = reinterpret_cast<Creature*>(victim);
|
||||
Creature* creature = dynamic_cast<Creature*>(victim);
|
||||
if (!creature)
|
||||
return;
|
||||
|
||||
|
||||
@ -1753,9 +1753,8 @@ void TravelNodeMap::generateTransportNodes()
|
||||
for (auto const& itr : *sObjectMgr->GetGameObjectTemplates())
|
||||
{
|
||||
GameObjectTemplate const* data = &itr.second;
|
||||
if (data && (data->type == GAMEOBJECT_TYPE_TRANSPORT || data->type == GAMEOBJECT_TYPE_MO_TRANSPORT))
|
||||
{
|
||||
TransportAnimation const* animation = sTransportMgr->GetTransportAnimInfo(itr.first);
|
||||
if (!data || (data->type != GAMEOBJECT_TYPE_TRANSPORT && data->type != GAMEOBJECT_TYPE_MO_TRANSPORT))
|
||||
continue;
|
||||
|
||||
uint32 pathId = data->moTransport.taxiPathId;
|
||||
float moveSpeed = data->moTransport.moveSpeed;
|
||||
@ -1764,125 +1763,20 @@ void TravelNodeMap::generateTransportNodes()
|
||||
|
||||
TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
|
||||
|
||||
// Keep only transports with taxi paths (boats/zeppelins).
|
||||
if (path.empty())
|
||||
continue;
|
||||
|
||||
std::vector<WorldPosition> ppath;
|
||||
TravelNode* prevNode = nullptr;
|
||||
|
||||
// Elevators/Trams
|
||||
if (path.empty())
|
||||
{
|
||||
if (animation)
|
||||
{
|
||||
TransportPathContainer aPath = animation->Path;
|
||||
float timeStart;
|
||||
|
||||
for (auto& transport : WorldPosition().getGameObjectsNear(0, itr.first))
|
||||
{
|
||||
prevNode = nullptr;
|
||||
WorldPosition basePos(transport->mapid, transport->posX, transport->posY, transport->posZ,
|
||||
transport->orientation);
|
||||
WorldPosition lPos = WorldPosition();
|
||||
|
||||
for (auto& p : aPath)
|
||||
{
|
||||
float dx = -1 * p.second->X;
|
||||
float dy = -1 * p.second->Y;
|
||||
|
||||
WorldPosition pos =
|
||||
WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx,
|
||||
basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z,
|
||||
basePos.GetOrientation());
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
ppath.push_back(pos);
|
||||
}
|
||||
|
||||
if (pos.distance(&lPos) == 0)
|
||||
{
|
||||
TravelNode* node =
|
||||
TravelNodeMap::instance().addNode(pos, data->name, true, true, true, itr.first);
|
||||
|
||||
if (!prevNode)
|
||||
{
|
||||
ppath.push_back(pos);
|
||||
timeStart = p.second->TimeSeg;
|
||||
}
|
||||
else
|
||||
{
|
||||
float totalTime = (p.second->TimeSeg - timeStart) / 1000.0f;
|
||||
|
||||
TravelNodePath travelPath(0.1f, totalTime, (uint8)TravelNodePathType::transport,
|
||||
itr.first, true);
|
||||
node->setPathTo(prevNode, travelPath);
|
||||
ppath.clear();
|
||||
ppath.push_back(pos);
|
||||
timeStart = p.second->TimeSeg;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
}
|
||||
|
||||
lPos = pos;
|
||||
}
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
for (auto& p : aPath)
|
||||
{
|
||||
float dx = -1 * p.second->X;
|
||||
float dy = -1 * p.second->Y;
|
||||
WorldPosition pos =
|
||||
WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx,
|
||||
basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z,
|
||||
basePos.GetOrientation());
|
||||
|
||||
ppath.push_back(pos);
|
||||
|
||||
if (pos.distance(&lPos) == 0)
|
||||
{
|
||||
TravelNode* node =
|
||||
TravelNodeMap::instance().addNode(pos, data->name, true, true, true, itr.first);
|
||||
if (node != prevNode)
|
||||
{
|
||||
if (p.second->TimeSeg < timeStart)
|
||||
timeStart = 0;
|
||||
|
||||
float totalTime = (p.second->TimeSeg - timeStart) / 1000.0f;
|
||||
|
||||
TravelNodePath travelPath(0.1f, totalTime, (uint8)TravelNodePathType::transport,
|
||||
itr.first, true);
|
||||
travelPath.setPath(ppath);
|
||||
node->setPathTo(prevNode, travelPath);
|
||||
ppath.clear();
|
||||
ppath.push_back(pos);
|
||||
timeStart = p.second->TimeSeg;
|
||||
}
|
||||
}
|
||||
|
||||
lPos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
ppath.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Boats/Zepelins
|
||||
{
|
||||
// Loop over the path and connect stop locations.
|
||||
for (auto& p : path)
|
||||
{
|
||||
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
|
||||
|
||||
// if (data->displayId == 3015)
|
||||
// pos.setZ(pos.getZ() + 6.0f);
|
||||
// else if (data->displayId == 3031)
|
||||
// pos.setZ(pos.getZ() - 17.0f);
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
ppath.push_back(pos);
|
||||
}
|
||||
|
||||
if (p->delay > 0)
|
||||
{
|
||||
@ -1905,18 +1799,13 @@ void TravelNodeMap::generateTransportNodes()
|
||||
}
|
||||
}
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
if (!prevNode)
|
||||
continue;
|
||||
|
||||
// Continue from start until first stop and connect to end.
|
||||
for (auto& p : path)
|
||||
{
|
||||
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
|
||||
|
||||
// if (data->displayId == 3015)
|
||||
// pos.setZ(pos.getZ() + 6.0f);
|
||||
// else if (data->displayId == 3031)
|
||||
// pos.setZ(pos.getZ() - 17.0f);
|
||||
|
||||
ppath.push_back(pos);
|
||||
|
||||
if (p->delay > 0)
|
||||
@ -1925,20 +1814,16 @@ void TravelNodeMap::generateTransportNodes()
|
||||
|
||||
if (node != prevNode)
|
||||
{
|
||||
TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first,
|
||||
true);
|
||||
TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first, true);
|
||||
travelPath.setPathAndCost(ppath, moveSpeed);
|
||||
|
||||
node->setPathTo(prevNode, travelPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ppath.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TravelNodeMap::generateZoneMeanNodes()
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user