mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Merge pull request #2205 from mod-playerbots/test-staging
Test staging to master
This commit is contained in:
commit
4b7b0958dd
@ -43,21 +43,21 @@ any impact on performance, you may skip these question. If necessary, a maintain
|
|||||||
## Impact Assessment
|
## Impact Assessment
|
||||||
<!-- As a generic test, before and after measure of pmon (playerbot pmon tick) can help you here. -->
|
<!-- As a generic test, before and after measure of pmon (playerbot pmon tick) can help you here. -->
|
||||||
- Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots?
|
- Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots?
|
||||||
- [ ] No, not at all
|
- - [ ] No, not at all
|
||||||
- [ ] Minimal impact (**explain below**)
|
- - [ ] Minimal impact (**explain below**)
|
||||||
- [ ] Moderate impact (**explain below**)
|
- - [ ] Moderate impact (**explain below**)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- Does this change modify default bot behavior?
|
- Does this change modify default bot behavior?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain why**)
|
- - [ ] Yes (**explain why**)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- Does this change add new decision branches or increase maintenance complexity?
|
- Does this change add new decision branches or increase maintenance complexity?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain below**)
|
- - [ ] Yes (**explain below**)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -68,8 +68,8 @@ the message is in a translatable format, and list in the table the message_key a
|
|||||||
Search for GetBotTextOrDefault in the codebase for examples.
|
Search for GetBotTextOrDefault in the codebase for examples.
|
||||||
-->
|
-->
|
||||||
Does this change add bot messages to translate?
|
Does this change add bot messages to translate?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**list messages in the table**)
|
- - [ ] Yes (**list messages in the table**)
|
||||||
|
|
||||||
| Message key | Default message |
|
| Message key | Default message |
|
||||||
| --------------- | ------------------ |
|
| --------------- | ------------------ |
|
||||||
@ -82,8 +82,8 @@ AI assistance is allowed, but all submitted code must be fully understood, revie
|
|||||||
We expect contributors to be honest about what they do and do not understand.
|
We expect contributors to be honest about what they do and do not understand.
|
||||||
-->
|
-->
|
||||||
Was AI assistance used while working on this change?
|
Was AI assistance used while working on this change?
|
||||||
- [ ] No
|
- - [ ] No
|
||||||
- [ ] Yes (**explain below**)
|
- - [ ] Yes (**explain below**)
|
||||||
<!--
|
<!--
|
||||||
If yes, please specify:
|
If yes, please specify:
|
||||||
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation).
|
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation).
|
||||||
@ -94,10 +94,10 @@ If yes, please specify:
|
|||||||
|
|
||||||
## Final Checklist
|
## Final Checklist
|
||||||
|
|
||||||
- [ ] Stability is not compromised.
|
- - [ ] Stability is not compromised.
|
||||||
- [ ] Performance impact is understood, tested, and acceptable.
|
- - [ ] Performance impact is understood, tested, and acceptable.
|
||||||
- [ ] Added logic complexity is justified and explained.
|
- - [ ] Added logic complexity is justified and explained.
|
||||||
- [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
- - [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
||||||
|
|
||||||
## Notes for Reviewers
|
## Notes for Reviewers
|
||||||
<!-- Anything else that's helpful to review or test your pull request. -->
|
<!-- Anything else that's helpful to review or test your pull request. -->
|
||||||
|
|||||||
@ -5,18 +5,211 @@
|
|||||||
|
|
||||||
#include "FollowActions.h"
|
#include "FollowActions.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "Formations.h"
|
#include "Formations.h"
|
||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
|
#include "MotionMaster.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.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*/)
|
bool FollowAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||||
std::string const target = formation->GetTargetName();
|
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;
|
bool moved = false;
|
||||||
if (!target.empty())
|
if (!target.empty())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -37,10 +37,10 @@ public:
|
|||||||
uint32 GetCurrWaypoint() override;
|
uint32 GetCurrWaypoint() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GrobblulusMoveCenterAction : public MoveInsideAction
|
class GrobbulusMoveCenterAction : public MoveInsideAction
|
||||||
{
|
{
|
||||||
public:
|
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
|
class GrobbulusMoveAwayAction : public MovementAction
|
||||||
@ -173,26 +173,26 @@ private:
|
|||||||
RazuviousBossHelper helper;
|
RazuviousBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HorsemanAttractAlternativelyAction : public AttackAction
|
class FourHorsemenAttractAlternativelyAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FourhorsemanBossHelper helper;
|
FourHorsemenBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HorsemanAttactInOrderAction : public AttackAction
|
class FourHorsemenAttackInOrderAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FourhorsemanBossHelper helper;
|
FourHorsemenBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
// class SapphironGroundMainTankPositionAction : public MovementAction
|
// class SapphironGroundMainTankPositionAction : public MovementAction
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "RaidNaxxActions.h"
|
|
||||||
|
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidNaxxActions.h"
|
||||||
|
|
||||||
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
|
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -66,13 +65,10 @@ bool AnubrekhanPositionAction::Execute(Event /*event*/)
|
|||||||
{
|
{
|
||||||
uint32 nearest = FindNearestWaypoint();
|
uint32 nearest = FindNearestWaypoint();
|
||||||
uint32 next_point;
|
uint32 next_point;
|
||||||
if (inPhase)
|
|
||||||
next_point = (nearest + 1) % intervals;
|
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,
|
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
|
||||||
false, false, MovementPriority::MOVEMENT_COMBAT);
|
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
|
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
|
bool FourHorsemenAttractAlternativelyAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
if (!helper.UpdateBossAI())
|
if (!helper.UpdateBossAI())
|
||||||
return false;
|
return false;
|
||||||
@ -13,13 +13,13 @@ bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
Unit* attackTarget = helper.CurrentAttackTarget();
|
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 Attack(attackTarget);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HorsemanAttactInOrderAction::Execute(Event /*event*/)
|
bool FourHorsemenAttackInOrderAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
if (!helper.UpdateBossAI())
|
if (!helper.UpdateBossAI())
|
||||||
return false;
|
return false;
|
||||||
@ -70,7 +70,6 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
|
|||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Group::MemberSlotList const& slots = group->GetMemberSlots();
|
|
||||||
Player* playerWithIcebolt = nullptr;
|
Player* playerWithIcebolt = nullptr;
|
||||||
float minDistance;
|
float minDistance;
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
|||||||
@ -13,7 +13,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* target = helper.GetNearestPet();
|
Unit* target = helper.GetNearestPet();
|
||||||
if (!bot->IsWithinDistInMap(target, 50.0f))
|
if (!target || !bot->IsWithinDistInMap(target, 50.0f))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -22,7 +22,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
|
|||||||
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
|
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Unit* target = helper.GetNearestPet();
|
Unit* target = helper.GetNearestPet();
|
||||||
if (!bot->IsWithinLOSInMap(target))
|
if (!target || !bot->IsWithinLOSInMap(target))
|
||||||
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
|
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
|
||||||
|
|
||||||
if (AI_VALUE(Unit*, "current target") != target)
|
if (AI_VALUE(Unit*, "current target") != target)
|
||||||
|
|||||||
@ -245,7 +245,7 @@ float AnubrekhanGenericMultiplier::GetValue(Action* action)
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float FourhorsemanGenericMultiplier::GetValue(Action* action)
|
float FourHorsemenGenericMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
||||||
if (!boss)
|
if (!boss)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
#ifndef _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
|
||||||
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
#define _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
|
||||||
|
|
||||||
#include "Multiplier.h"
|
#include "Multiplier.h"
|
||||||
#include "RaidNaxxBossHelper.h"
|
#include "RaidNaxxBossHelper.h"
|
||||||
@ -84,10 +84,10 @@ public:
|
|||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FourhorsemanGenericMultiplier : public Multiplier
|
class FourHorsemenGenericMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {}
|
FourHorsemenGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "four horsemen generic") {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
|
|||||||
@ -31,8 +31,8 @@ public:
|
|||||||
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
|
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
|
||||||
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
|
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
|
||||||
|
|
||||||
creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively;
|
creators["four horsemen attract alternatively"] = &RaidNaxxActionContext::four_horsemen_attract_alternatively;
|
||||||
creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order;
|
creators["four horsemen attack in order"] = &RaidNaxxActionContext::four_horsemen_attack_in_order;
|
||||||
|
|
||||||
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
|
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
|
||||||
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
|
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
|
||||||
@ -56,7 +56,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
|
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
|
||||||
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(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* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); }
|
||||||
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
|
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
|
||||||
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
|
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
|
||||||
@ -70,11 +70,8 @@ private:
|
|||||||
{
|
{
|
||||||
return new RazuviousUseObedienceCrystalAction(ai);
|
return new RazuviousUseObedienceCrystalAction(ai);
|
||||||
}
|
}
|
||||||
static Action* horseman_attract_alternatively(PlayerbotAI* 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); }
|
||||||
return new HorsemanAttractAlternativelyAction(ai);
|
|
||||||
}
|
|
||||||
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new HorsemanAttactInOrderAction(ai); }
|
|
||||||
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
|
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
|
||||||
// SapphironGroundMainTankPositionAction(ai); }
|
// SapphironGroundMainTankPositionAction(ai); }
|
||||||
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(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 tank"] = &RaidNaxxTriggerContext::razuvious_tank;
|
||||||
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
|
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
|
||||||
|
|
||||||
creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors;
|
creators["four horsemen attractors"] = &RaidNaxxTriggerContext::four_horsemen_attractors;
|
||||||
creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors;
|
creators["four horsemen except attractors"] = &RaidNaxxTriggerContext::four_horsemen_except_attractors;
|
||||||
|
|
||||||
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
|
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
|
||||||
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
|
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
|
||||||
@ -66,8 +66,8 @@ private:
|
|||||||
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
|
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
|
||||||
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
|
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
|
||||||
|
|
||||||
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); }
|
static Trigger* four_horsemen_attractors(PlayerbotAI* ai) { return new FourHorsemenAttractorsTrigger(ai); }
|
||||||
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(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_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); }
|
||||||
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(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) }
|
{ NextAction("razuvious target", ACTION_RAID + 1) }
|
||||||
));
|
));
|
||||||
|
|
||||||
// four horseman
|
// four horsemen
|
||||||
triggers.push_back(new TriggerNode("horseman attractors",
|
triggers.push_back(new TriggerNode("four horsemen attractors",
|
||||||
{ NextAction("horseman attract alternatively", ACTION_RAID + 1) }
|
{ NextAction("four horsemen attract alternatively", ACTION_RAID + 1) }
|
||||||
));
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("horseman except attractors",
|
triggers.push_back(new TriggerNode("four horsemen except attractors",
|
||||||
{ NextAction("horseman attack in order", ACTION_RAID + 1) }
|
{ NextAction("four horsemen attack in order", ACTION_RAID + 1) }
|
||||||
));
|
));
|
||||||
|
|
||||||
// sapphiron
|
// sapphiron
|
||||||
@ -150,7 +150,7 @@ void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
|||||||
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
|
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
|
||||||
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
|
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
|
||||||
multipliers.push_back(new AnubrekhanGenericMultiplier(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 GothikGenericMultiplier(botAI));
|
||||||
multipliers.push_back(new GluthGenericMultiplier(botAI));
|
multipliers.push_back(new GluthGenericMultiplier(botAI));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,7 +114,7 @@ bool RazuviousNontankTrigger::IsActive()
|
|||||||
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
|
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HorsemanAttractorsTrigger::IsActive()
|
bool FourHorsemenAttractorsTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!helper.UpdateBossAI())
|
if (!helper.UpdateBossAI())
|
||||||
return false;
|
return false;
|
||||||
@ -122,7 +122,7 @@ bool HorsemanAttractorsTrigger::IsActive()
|
|||||||
return helper.IsAttracter(bot);
|
return helper.IsAttracter(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HorsemanExceptAttractorsTrigger::IsActive()
|
bool FourHorsemenExceptAttractorsTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!helper.UpdateBossAI())
|
if (!helper.UpdateBossAI())
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -186,24 +186,24 @@ private:
|
|||||||
ThaddiusBossHelper helper;
|
ThaddiusBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HorsemanAttractorsTrigger : public Trigger
|
class FourHorsemenAttractorsTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {}
|
FourHorsemenAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "four horsemen attractors"), helper(ai) {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FourhorsemanBossHelper helper;
|
FourHorsemenBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HorsemanExceptAttractorsTrigger : public Trigger
|
class FourHorsemenExceptAttractorsTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool IsActive() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FourhorsemanBossHelper helper;
|
FourHorsemenBossHelper helper;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SapphironGroundTrigger : public Trigger
|
class SapphironGroundTrigger : public Trigger
|
||||||
|
|||||||
@ -202,7 +202,7 @@ public:
|
|||||||
}
|
}
|
||||||
bool FindPosToAvoidChill(std::vector<float>& dest)
|
bool FindPosToAvoidChill(std::vector<float>& dest)
|
||||||
{
|
{
|
||||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25});
|
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill10, NaxxSpellIds::Chill25});
|
||||||
if (!aura)
|
if (!aura)
|
||||||
{
|
{
|
||||||
// Fallback to name for custom spell data.
|
// Fallback to name for custom spell data.
|
||||||
@ -363,13 +363,13 @@ private:
|
|||||||
Unit* _unit = nullptr;
|
Unit* _unit = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FourhorsemanBossHelper : public AiObject
|
class FourHorsemenBossHelper : public AiObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const float posZ = 241.27f;
|
const float posZ = 241.27f;
|
||||||
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
|
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
|
||||||
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
|
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
|
||||||
FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
FourHorsemenBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||||
bool UpdateBossAI()
|
bool UpdateBossAI()
|
||||||
{
|
{
|
||||||
if (!bot->IsInCombat())
|
if (!bot->IsInCombat())
|
||||||
@ -497,7 +497,8 @@ public:
|
|||||||
if (feugen && feugen->IsAlive())
|
if (feugen && feugen->IsAlive())
|
||||||
unit = feugen;
|
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;
|
unit = stalagg;
|
||||||
|
|
||||||
return unit;
|
return unit;
|
||||||
|
|||||||
@ -58,6 +58,7 @@ namespace NaxxSpellIds
|
|||||||
// Sapphiron
|
// Sapphiron
|
||||||
static constexpr uint32 Icebolt10 = 28522;
|
static constexpr uint32 Icebolt10 = 28522;
|
||||||
static constexpr uint32 Icebolt25 = 28526;
|
static constexpr uint32 Icebolt25 = 28526;
|
||||||
|
static constexpr uint32 Chill10 = 28547;
|
||||||
static constexpr uint32 Chill25 = 55699;
|
static constexpr uint32 Chill25 = 55699;
|
||||||
/*
|
/*
|
||||||
// Fight
|
// 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
|
#ifndef _PLAYERBOT_RAIDSSCACTIONS_H
|
||||||
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
||||||
|
|
||||||
@ -75,8 +80,8 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool TryMisdirectToFrostTank(Unit* hydross, Group* group);
|
bool TryMisdirectToFrostTank(Unit* hydross);
|
||||||
bool TryMisdirectToNatureTank(Unit* hydross, Group* group);
|
bool TryMisdirectToNatureTank(Unit* hydross);
|
||||||
};
|
};
|
||||||
|
|
||||||
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
||||||
@ -413,10 +418,10 @@ private:
|
|||||||
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
||||||
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||||
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||||
bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
bool IsFirstCorePasserInPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||||
bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
bool IsSecondCorePasserInPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||||
bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
bool IsThirdCorePasserInPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||||
bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
bool IsFourthCorePasserInPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||||
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
||||||
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
||||||
};
|
};
|
||||||
@ -428,19 +433,12 @@ public:
|
|||||||
bool Execute(Event event) override;
|
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
|
class LadyVashjAvoidToxicSporesAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
static std::vector<Unit*> GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot);
|
static std::vector<Unit*> GetAllSporeDropTriggers(Player* bot);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Position FindSafestNearbyPosition(const std::vector<Unit*>& spores, const Position& position, float maxRadius, float hazardRadius);
|
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 "RaidSSCMultipliers.h"
|
||||||
#include "RaidSSCActions.h"
|
#include "RaidSSCActions.h"
|
||||||
#include "RaidSSCHelpers.h"
|
#include "RaidSSCHelpers.h"
|
||||||
@ -28,12 +33,10 @@ using namespace SerpentShrineCavernHelpers;
|
|||||||
|
|
||||||
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_TOXIC_POOL))
|
if (bot->HasAura(SPELL_TOXIC_POOL) &&
|
||||||
{
|
dynamic_cast<MovementAction*>(action) &&
|
||||||
if (dynamic_cast<MovementAction*>(action) &&
|
|
||||||
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -53,16 +56,16 @@ float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action)
|
|||||||
dynamic_cast<CombatFormationMoveAction*>(action))
|
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
return 0.0f;
|
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) ||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
dynamic_cast<ReachTargetAction*>(action) ||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
(dynamic_cast<AttackAction*>(action) &&
|
(dynamic_cast<AttackAction*>(action) &&
|
||||||
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
||||||
!dynamic_cast<HydrossTheUnstablePositionNatureTankAction*>(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 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -97,14 +100,14 @@ float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
|||||||
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
||||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||||
|
|
||||||
if (justChanged || aboutToChange)
|
if (!justChanged && !aboutToChange)
|
||||||
{
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<AttackAction*>(action) ||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
(dynamic_cast<CastSpellAction*>(action) &&
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
{
|
{
|
||||||
@ -116,14 +119,14 @@ float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
|||||||
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
||||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||||
|
|
||||||
if (justChanged || aboutToChange)
|
if (!justChanged && !aboutToChange)
|
||||||
{
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<AttackAction*>(action) ||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
(dynamic_cast<CastSpellAction*>(action) &&
|
(dynamic_cast<CastSpellAction*>(action) &&
|
||||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -133,11 +136,9 @@ float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action)
|
|||||||
if (bot->getClass() != CLASS_HUNTER)
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
if (AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||||
{
|
dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -175,14 +176,14 @@ float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action)
|
|||||||
if (!botAI->IsRanged(bot))
|
if (!botAI->IsRanged(bot))
|
||||||
return 1.0f;
|
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) ||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
dynamic_cast<FleeAction*>(action) ||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
dynamic_cast<CastDisengageAction*>(action) ||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
dynamic_cast<CastBlinkBackAction*>(action))
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -215,11 +216,11 @@ float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action)
|
|||||||
++tankCount;
|
++tankCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tankCount >= 3)
|
if (tankCount < 3)
|
||||||
{
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<TankAssistAction*>(action))
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -234,14 +235,11 @@ float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
|||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
if (!leotherasHuman)
|
if (!leotheras || (!leotheras->HasAura(SPELL_WHIRLWIND) &&
|
||||||
|
!leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
|
||||||
(leotherasHuman->HasAura(SPELL_WHIRLWIND) ||
|
|
||||||
leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
|
||||||
{
|
|
||||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
@ -249,7 +247,6 @@ float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
|||||||
!dynamic_cast<AttackAction*>(action) &&
|
!dynamic_cast<AttackAction*>(action) &&
|
||||||
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -262,10 +259,10 @@ float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (GetPhase2LeotherasDemon(botAI) && dynamic_cast<AttackAction*>(action))
|
if (GetPhase2LeotherasDemon(bot) && dynamic_cast<AttackAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast<CastBerserkAction*>(action))
|
if (!GetPhase3LeotherasDemon(bot) && dynamic_cast<CastBerserkAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
@ -273,8 +270,9 @@ float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
|||||||
|
|
||||||
float LeotherasTheBlindFocusOnInnerDemonMultiplier::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) ||
|
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||||
dynamic_cast<DpsAssistAction*>(action) ||
|
dynamic_cast<DpsAssistAction*>(action) ||
|
||||||
dynamic_cast<CastHealingSpellAction*>(action) ||
|
dynamic_cast<CastHealingSpellAction*>(action) ||
|
||||||
@ -287,7 +285,6 @@ float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
|||||||
dynamic_cast<CastDireBearFormAction*>(action) ||
|
dynamic_cast<CastDireBearFormAction*>(action) ||
|
||||||
dynamic_cast<CastTreeFormAction*>(action))
|
dynamic_cast<CastTreeFormAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -297,19 +294,19 @@ float LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* actio
|
|||||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (!GetPhase2LeotherasDemon(botAI))
|
if (!GetPhase2LeotherasDemon(bot))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
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) ||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
dynamic_cast<ReachTargetAction*>(action) ||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
dynamic_cast<CastKillingSpreeAction*>(action))
|
dynamic_cast<CastKillingSpreeAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -330,8 +327,8 @@ float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
|||||||
const time_t now = std::time(nullptr);
|
const time_t now = std::time(nullptr);
|
||||||
|
|
||||||
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
||||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
Unit* leotherasHuman = GetLeotherasHuman(bot);
|
||||||
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI);
|
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(bot);
|
||||||
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||||
!leotherasPhase3Demon)
|
!leotherasPhase3Demon)
|
||||||
{
|
{
|
||||||
@ -350,7 +347,7 @@ float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
||||||
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI);
|
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(bot);
|
||||||
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
||||||
if (leotherasPhase2Demon)
|
if (leotherasPhase2Demon)
|
||||||
{
|
{
|
||||||
@ -398,12 +395,12 @@ float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* acti
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
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) ||
|
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||||
dynamic_cast<CastBloodlustAction*>(action))
|
dynamic_cast<CastBloodlustAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -447,14 +444,12 @@ float FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action)
|
|||||||
if (!botAI->IsDps(bot))
|
if (!botAI->IsDps(bot))
|
||||||
return 1.0f;
|
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 (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
|
||||||
{
|
auto castSpellAction = dynamic_cast<CastSpellAction*>(action);
|
||||||
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
if (castSpellAction && castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -464,11 +459,11 @@ float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action
|
|||||||
if (bot->getClass() != CLASS_HUNTER)
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
return 1.0f;
|
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))
|
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -505,12 +500,12 @@ float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue
|
|||||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||||
return 1.0f;
|
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) ||
|
if (dynamic_cast<FleeAction*>(action) ||
|
||||||
dynamic_cast<FollowAction*>(action))
|
dynamic_cast<FollowAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -526,12 +521,12 @@ float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* act
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||||
return 1.0f;
|
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) ||
|
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||||
dynamic_cast<CastBloodlustAction*>(action))
|
dynamic_cast<CastBloodlustAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -541,11 +536,11 @@ float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action)
|
|||||||
if (!botAI->IsMainTank(bot))
|
if (!botAI->IsMainTank(bot))
|
||||||
return 1.0f;
|
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))
|
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -556,17 +551,14 @@ float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* actio
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||||
if (!tidewalker)
|
if (!tidewalker || tidewalker->GetHealthPct() > 25.0f)
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (tidewalker->GetHealthPct() < 25.0f)
|
|
||||||
{
|
|
||||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
dynamic_cast<FleeAction*>(action) ||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
dynamic_cast<CastDisengageAction*>(action) ||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
dynamic_cast<CastBlinkBackAction*>(action))
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -580,18 +572,15 @@ float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (bot->getClass() == CLASS_SHAMAN)
|
if (bot->getClass() == CLASS_SHAMAN &&
|
||||||
{
|
!IsLadyVashjInPhase3(botAI) &&
|
||||||
if (IsLadyVashjInPhase3(botAI))
|
(dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsDps(bot) || !IsLadyVashjInPhase1(botAI))
|
||||||
return 1.0f;
|
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) ||
|
if (dynamic_cast<CastMetamorphosisAction*>(action) ||
|
||||||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
|
dynamic_cast<CastAdrenalineRushAction*>(action) ||
|
||||||
dynamic_cast<CastBladeFlurryAction*>(action) ||
|
dynamic_cast<CastBladeFlurryAction*>(action) ||
|
||||||
@ -614,8 +603,29 @@ float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
|||||||
dynamic_cast<CastBloodFuryAction*>(action) ||
|
dynamic_cast<CastBloodFuryAction*>(action) ||
|
||||||
dynamic_cast<UseTrinketAction*>(action))
|
dynamic_cast<UseTrinketAction*>(action))
|
||||||
return 0.0f;
|
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;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,15 +634,15 @@ float LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action)
|
|||||||
if (!botAI->IsRanged(bot))
|
if (!botAI->IsRanged(bot))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||||
IsLadyVashjInPhase1(botAI))
|
!IsLadyVashjInPhase1(botAI))
|
||||||
{
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
dynamic_cast<FleeAction*>(action) ||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
dynamic_cast<CastDisengageAction*>(action) ||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
dynamic_cast<CastBlinkBackAction*>(action))
|
dynamic_cast<CastBlinkBackAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -658,19 +668,18 @@ float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action)
|
|||||||
// Bots should not loot the core with normal looting logic
|
// Bots should not loot the core with normal looting logic
|
||||||
float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action)
|
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))
|
if (dynamic_cast<LootAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||||
!IsLadyVashjInPhase2(botAI))
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<WipeAction*>(action) ||
|
if (dynamic_cast<WipeAction*>(action) ||
|
||||||
@ -678,65 +687,43 @@ float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* acti
|
|||||||
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||||
if (!group)
|
|
||||||
return 1.0f;
|
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
bool isCoreHandler = false;
|
||||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
int myIndex = -1;
|
||||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
for (int i = 0; i < static_cast<int>(coreHandlers.size()); ++i)
|
||||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
{
|
||||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
if (coreHandlers[i] && coreHandlers[i] == bot)
|
||||||
|
{
|
||||||
|
isCoreHandler = true;
|
||||||
|
myIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isCoreHandler)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
auto hasCore = [](Player* player)
|
auto hasCore = [](Player* player)
|
||||||
{
|
{
|
||||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasCore(bot))
|
// If the bot actually has the core, only allow core handling
|
||||||
{
|
if (hasCore(bot) && !dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||||
if (!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (bot == designatedLooter)
|
// First and second passers block movement when the looter teleports to the elemental
|
||||||
{
|
Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||||
if (!hasCore(bot))
|
if (tainted && coreHandlers[0]->GetExactDist2d(tainted) < 5.0f &&
|
||||||
return 1.0f;
|
(bot == coreHandlers[1] || bot == coreHandlers[2]) &&
|
||||||
}
|
(dynamic_cast<MovementAction*>(action) &&
|
||||||
else if (bot == firstCorePasser)
|
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action)))
|
||||||
{
|
return 0.0f;
|
||||||
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;
|
|
||||||
|
|
||||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
// If any prior handler (including self) recently had the core, block other movement
|
||||||
(bot == firstCorePasser || bot == secondCorePasser))
|
if (AnyRecentCoreInInventory(botAI, bot) &&
|
||||||
{
|
dynamic_cast<MovementAction*>(action) &&
|
||||||
if (dynamic_cast<MovementAction*>(action) &&
|
|
||||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (AnyRecentCoreInInventory(group, botAI))
|
|
||||||
{
|
|
||||||
if (dynamic_cast<MovementAction*>(action) &&
|
|
||||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@ -745,7 +732,8 @@ float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* acti
|
|||||||
// So the standard target selection system must be disabled
|
// So the standard target selection system must be disabled
|
||||||
float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action)
|
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;
|
return 1.0f;
|
||||||
|
|
||||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
@ -755,24 +743,26 @@ float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *ac
|
|||||||
{
|
{
|
||||||
if (dynamic_cast<DpsAssistAction*>(action) ||
|
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||||
dynamic_cast<TankAssistAction*>(action) ||
|
dynamic_cast<TankAssistAction*>(action) ||
|
||||||
dynamic_cast<FollowAction*>(action) ||
|
|
||||||
dynamic_cast<FleeAction*>(action))
|
dynamic_cast<FleeAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
|
if (bot->GetExactDist2d(vashj) < 60.0f &&
|
||||||
|
dynamic_cast<FollowAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||||
if (enchanted && bot->GetVictim() == enchanted)
|
if (enchanted && bot->GetVictim() == enchanted &&
|
||||||
{
|
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (IsLadyVashjInPhase3(botAI))
|
if (IsLadyVashjInPhase3(botAI))
|
||||||
{
|
{
|
||||||
if (dynamic_cast<DpsAssistAction*>(action))
|
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||||
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
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");
|
Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite");
|
||||||
if (enchanted || strider || elite)
|
if (enchanted || strider || elite)
|
||||||
{
|
{
|
||||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
if (dynamic_cast<FollowAction*>(action) ||
|
||||||
dynamic_cast<FollowAction*>(action) ||
|
|
||||||
dynamic_cast<FleeAction*>(action))
|
dynamic_cast<FleeAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
if (enchanted && bot->GetVictim() == enchanted)
|
if (enchanted && bot->GetVictim() == enchanted &&
|
||||||
{
|
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
return 0.0f;
|
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
|
#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||||
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||||
|
|
||||||
@ -193,6 +198,14 @@ public:
|
|||||||
virtual float GetValue(Action* action);
|
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
|
class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
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
|
#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||||
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||||
|
|
||||||
@ -161,9 +166,6 @@ public:
|
|||||||
creators["lady vashj destroy tainted core"] =
|
creators["lady vashj destroy tainted core"] =
|
||||||
&RaidSSCActionContext::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"] =
|
creators["lady vashj avoid toxic spores"] =
|
||||||
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
||||||
|
|
||||||
@ -324,9 +326,6 @@ private:
|
|||||||
static Action* lady_vashj_destroy_tainted_core(
|
static Action* lady_vashj_destroy_tainted_core(
|
||||||
PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); }
|
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(
|
static Action* lady_vashj_avoid_toxic_spores(
|
||||||
PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); }
|
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
|
#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||||
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||||
|
|
||||||
@ -155,9 +160,6 @@ public:
|
|||||||
creators["lady vashj tainted core is unusable"] =
|
creators["lady vashj tainted core is unusable"] =
|
||||||
&RaidSSCTriggerContext::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"] =
|
creators["lady vashj toxic sporebats are spewing poison clouds"] =
|
||||||
&RaidSSCTriggerContext::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(
|
static Trigger* lady_vashj_tainted_core_is_unusable(
|
||||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); }
|
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(
|
static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds(
|
||||||
PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); }
|
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 "RaidSSCStrategy.h"
|
||||||
#include "RaidSSCMultipliers.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", {
|
triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", {
|
||||||
NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) }));
|
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", {
|
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) }));
|
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>
|
// Lady Vashj <Coilfang Matron>
|
||||||
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new LadyVashjMainTankGroupShamanUseGroundingTotemMultiplier(botAI));
|
||||||
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
||||||
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
||||||
multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(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_
|
#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||||
#define _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 "RaidSSCTriggers.h"
|
||||||
#include "RaidSSCHelpers.h"
|
#include "RaidSSCHelpers.h"
|
||||||
#include "RaidSSCActions.h"
|
#include "RaidSSCActions.h"
|
||||||
@ -26,35 +31,37 @@ bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive()
|
|||||||
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return botAI->IsDps(bot) &&
|
return botAI->IsDps(bot) &&
|
||||||
GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM);
|
AI_VALUE2(Unit*, "find target", "greyheart tidecaller");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hydross the Unstable <Duke of Currents>
|
// Hydross the Unstable <Duke of Currents>
|
||||||
|
|
||||||
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
return botAI->IsMainTank(bot) &&
|
||||||
botAI->IsMainTank(bot);
|
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
return botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||||
botAI->IsAssistTankOfIndex(bot, 0, true);
|
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (botAI->IsHeal(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
if (hydross && hydross->GetHealthPct() < 10.0f)
|
if (!hydross || hydross->GetHealthPct() < 10.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") &&
|
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||||
!AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) &&
|
return AI_VALUE2(Unit*, "find target", "pure spawn of hydross") ||
|
||||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
AI_VALUE2(Unit*, "find target", "tainted spawn of hydross");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
||||||
@ -71,19 +78,19 @@ bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive()
|
|||||||
|
|
||||||
bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::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 false;
|
||||||
|
|
||||||
return bot->getClass() != CLASS_HUNTER &&
|
return AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
!botAI->IsHeal(bot) &&
|
|
||||||
!botAI->IsMainTank(bot) &&
|
|
||||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Lurker Below
|
// The Lurker Below
|
||||||
@ -102,11 +109,11 @@ bool TheLurkerBelowSpoutIsActiveTrigger::IsActive()
|
|||||||
|
|
||||||
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
if (!botAI->IsMainTank(bot))
|
||||||
if (!lurker)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!botAI->IsMainTank(bot))
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
|
if (!lurker)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const time_t now = std::time(nullptr);
|
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
|
// Trigger will be active only if there are at least 3 tanks in the raid
|
||||||
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* mainTank = nullptr;
|
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||||
Player* firstAssistTank = nullptr;
|
Player* firstAssistTank = GetGroupAssistTank(botAI, bot, 0);
|
||||||
Player* secondAssistTank = nullptr;
|
Player* secondAssistTank = GetGroupAssistTank(botAI, bot, 1);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
||||||
return false;
|
return false;
|
||||||
@ -173,51 +161,55 @@ bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
|||||||
|
|
||||||
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "the lurker below") &&
|
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leotheras the Blind
|
// Leotheras the Blind
|
||||||
|
|
||||||
bool LeotherasTheBlindBossIsInactiveTrigger::IsActive()
|
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()
|
bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (bot->getClass() != CLASS_WARLOCK)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (GetLeotherasDemonFormTank(bot) != bot)
|
if (GetLeotherasDemonFormTank(bot) != bot)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GetActiveLeotherasDemon(botAI);
|
return GetActiveLeotherasDemon(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (botAI->IsRanged(bot) || !botAI->IsTank(bot))
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!GetLeotherasDemonFormTank(bot))
|
if (!GetLeotherasDemonFormTank(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GetPhase2LeotherasDemon(botAI);
|
return GetPhase2LeotherasDemon(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
if (!botAI->IsRanged(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!botAI->IsRanged(bot))
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
@ -231,14 +223,14 @@ bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
|||||||
|
|
||||||
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
if (botAI->IsTank(bot))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (botAI->IsTank(bot) && botAI->IsMelee(bot))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
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 false;
|
||||||
|
|
||||||
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
||||||
@ -247,10 +239,13 @@ bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
|||||||
|
|
||||||
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
if (botAI->IsRanged(bot))
|
||||||
return false;
|
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;
|
return false;
|
||||||
|
|
||||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||||
@ -260,7 +255,7 @@ bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
|||||||
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GetPhase2LeotherasDemon(botAI);
|
return GetPhase2LeotherasDemon(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||||
@ -271,89 +266,68 @@ bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
|||||||
|
|
||||||
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (botAI->IsHeal(bot))
|
if (botAI->IsHeal(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (GetLeotherasDemonFormTank(bot) == bot)
|
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GetPhase3LeotherasDemon(botAI) &&
|
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||||
GetLeotherasHuman(botAI);
|
return false;
|
||||||
|
|
||||||
|
if (bot->getClass() == CLASS_WARLOCK && GetLeotherasDemonFormTank(bot) == bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return GetPhase3LeotherasDemon(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
||||||
{
|
{
|
||||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (bot->getClass() != CLASS_HUNTER)
|
if (bot->getClass() != CLASS_HUNTER)
|
||||||
return false;
|
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()
|
bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind") &&
|
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fathom-Lord Karathress
|
// Fathom-Lord Karathress
|
||||||
|
|
||||||
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
return botAI->IsMainTank(bot) &&
|
||||||
botAI->IsMainTank(bot);
|
AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") &&
|
return botAI->IsAssistTankOfIndex(bot, 0, false) &&
|
||||||
botAI->IsAssistTankOfIndex(bot, 0, false);
|
AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") &&
|
return botAI->IsAssistTankOfIndex(bot, 1, false) &&
|
||||||
botAI->IsAssistTankOfIndex(bot, 1, false);
|
AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") &&
|
return botAI->IsAssistTankOfIndex(bot, 2, false) &&
|
||||||
botAI->IsAssistTankOfIndex(bot, 2, false);
|
AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
return botAI->IsAssistHealOfIndex(bot, 0, true) &&
|
||||||
if (!caribdis)
|
AI_VALUE2(Unit*, "find target", "fathom-guard 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||||
@ -367,10 +341,10 @@ bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
|||||||
|
|
||||||
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
if (botAI->IsHeal(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->IsHeal(bot))
|
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->IsDps(bot))
|
if (botAI->IsDps(bot))
|
||||||
@ -387,8 +361,8 @@ bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
|||||||
|
|
||||||
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID) &&
|
||||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Morogrim Tidewalker
|
// Morogrim Tidewalker
|
||||||
@ -404,8 +378,8 @@ bool MorogrimTidewalkerPullingBossTrigger::IsActive()
|
|||||||
|
|
||||||
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") &&
|
return botAI->IsMainTank(bot) &&
|
||||||
botAI->IsMainTank(bot);
|
AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||||
@ -421,8 +395,11 @@ bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
|||||||
|
|
||||||
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||||
!IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot);
|
!IsLadyVashjInPhase2(botAI);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
||||||
@ -439,10 +416,7 @@ bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive()
|
|||||||
IsLadyVashjInPhase2(botAI))
|
IsLadyVashjInPhase2(botAI))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsMainTankInSameSubgroup(bot))
|
return IsMainTankInSameSubgroup(botAI, bot);
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||||
@ -450,15 +424,16 @@ bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
Group* group = bot->GetGroup();
|
||||||
{
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -500,9 +475,10 @@ bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool taintedPresent = false;
|
bool taintedPresent = false;
|
||||||
Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
if (AI_VALUE2(Unit*, "find target", "tainted elemental"))
|
||||||
if (taintedUnit)
|
{
|
||||||
taintedPresent = true;
|
taintedPresent = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||||
@ -513,26 +489,20 @@ bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
|||||||
if (!object)
|
if (!object)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Creature* creature = object->ToCreature())
|
if (Creature* creature = object->ToCreature();
|
||||||
{
|
creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||||
if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
|
||||||
{
|
{
|
||||||
taintedPresent = true;
|
taintedPresent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!taintedPresent)
|
if (!taintedPresent)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
return GetDesignatedCoreLooter(botAI, bot) == bot &&
|
||||||
if (!group)
|
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
return false;
|
|
||||||
|
|
||||||
return (GetDesignatedCoreLooter(group, botAI) == bot &&
|
|
||||||
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||||
@ -540,54 +510,24 @@ bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||||
if (!group)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
bool isCoreHandler = false;
|
||||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
for (Player* handler : coreHandlers)
|
||||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
if (handler == bot)
|
||||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
isCoreHandler = true;
|
||||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
|
||||||
|
|
||||||
auto hasCore = [](Player* player) -> bool
|
if (!isCoreHandler)
|
||||||
{
|
|
||||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bot == designatedLooter)
|
|
||||||
{
|
|
||||||
if (!hasCore(bot))
|
|
||||||
return false;
|
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
|
// First and second passers move to positions as soon as the elemental appears
|
||||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
Unit* tainted = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||||
(bot == firstCorePasser || bot == secondCorePasser))
|
if (tainted && coreHandlers[0]->GetExactDist2d(tainted) < 5.0f &&
|
||||||
|
(bot == coreHandlers[1] || bot == coreHandlers[2]))
|
||||||
return true;
|
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()
|
bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||||
@ -599,18 +539,7 @@ bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
|||||||
if (!IsLadyVashjInPhase2(botAI))
|
if (!IsLadyVashjInPhase2(botAI))
|
||||||
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
auto coreHandlers = GetCoreHandlers(botAI, bot);
|
||||||
if (!group)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Player* coreHandlers[] =
|
|
||||||
{
|
|
||||||
GetDesignatedCoreLooter(group, botAI),
|
|
||||||
GetFirstTaintedCorePasser(group, botAI),
|
|
||||||
GetSecondTaintedCorePasser(group, botAI),
|
|
||||||
GetThirdTaintedCorePasser(group, botAI),
|
|
||||||
GetFourthTaintedCorePasser(group, botAI)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||||
{
|
{
|
||||||
@ -625,24 +554,6 @@ bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
|||||||
return false;
|
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()
|
bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return IsLadyVashjInPhase3(botAI);
|
return IsLadyVashjInPhase3(botAI);
|
||||||
@ -653,8 +564,10 @@ bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
|||||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
Group* group = bot->GetGroup();
|
||||||
{
|
if (!group)
|
||||||
|
return false;
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
@ -664,7 +577,6 @@ bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
|||||||
if (botAI->IsMelee(member))
|
if (botAI->IsMelee(member))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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
|
#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||||
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||||
|
|
||||||
@ -387,14 +392,6 @@ public:
|
|||||||
bool IsActive() override;
|
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
|
class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
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 "RaidSSCHelpers.h"
|
||||||
#include "AiFactory.h"
|
#include "AiFactory.h"
|
||||||
#include "Creature.h"
|
#include "Creature.h"
|
||||||
@ -79,61 +84,54 @@ namespace SerpentShrineCavernHelpers
|
|||||||
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||||
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||||
|
|
||||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI)
|
Unit* GetLeotherasHuman(Player* bot)
|
||||||
{
|
{
|
||||||
auto const& npcs =
|
constexpr float searchRadius = 100.0f;
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
Creature* leotheras =
|
||||||
for (auto const& guid : npcs)
|
bot->FindNearestCreature(NPC_LEOTHERAS_THE_BLIND, searchRadius, true);
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
if (leotheras && leotheras->IsInCombat() &&
|
||||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
!leotheras->HasAura(SPELL_METAMORPHOSIS))
|
||||||
unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS))
|
return leotheras;
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI)
|
Unit* GetPhase2LeotherasDemon(Player* bot)
|
||||||
{
|
{
|
||||||
auto const& npcs =
|
constexpr float searchRadius = 100.0f;
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
Creature* leotheras =
|
||||||
for (auto const& guid : npcs)
|
bot->FindNearestCreature(NPC_LEOTHERAS_THE_BLIND, searchRadius, true);
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
if (leotheras && leotheras->HasAura(SPELL_METAMORPHOSIS))
|
||||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
return leotheras;
|
||||||
unit->HasAura(SPELL_METAMORPHOSIS))
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI)
|
Unit* GetPhase3LeotherasDemon(Player* bot)
|
||||||
{
|
{
|
||||||
auto const& npcs =
|
constexpr float searchRadius = 100.0f;
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
return bot->FindNearestCreature(NPC_SHADOW_OF_LEOTHERAS, searchRadius, true);
|
||||||
for (auto const& guid : npcs)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS)
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI)
|
Unit* GetActiveLeotherasDemon(Player* bot)
|
||||||
{
|
{
|
||||||
Unit* phase2 = GetPhase2LeotherasDemon(botAI);
|
Unit* phase2 = GetPhase2LeotherasDemon(bot);
|
||||||
Unit* phase3 = GetPhase3LeotherasDemon(botAI);
|
Unit* phase3 = GetPhase3LeotherasDemon(bot);
|
||||||
return phase2 ? phase2 : phase3;
|
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)
|
Player* GetLeotherasDemonFormTank(Player* bot)
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
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())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
@ -142,21 +140,12 @@ namespace SerpentShrineCavernHelpers
|
|||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()))
|
if (group->IsAssistant(member->GetGUID()))
|
||||||
return member;
|
return member;
|
||||||
|
|
||||||
|
if (!fallbackWarlock && GET_PLAYERBOT_AI(member))
|
||||||
|
fallbackWarlock = member;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (2) Fall back to first found bot Warlock
|
return fallbackWarlock;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fathom-Lord Karathress
|
// Fathom-Lord Karathress
|
||||||
@ -182,16 +171,15 @@ namespace SerpentShrineCavernHelpers
|
|||||||
|
|
||||||
// Lady Vashj <Coilfang Matron>
|
// 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<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||||
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||||
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||||
std::unordered_map<uint32, time_t> lastImbueAttempt;
|
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();
|
Group* group = bot->GetGroup();
|
||||||
if (!group || !group->isRaidGroup())
|
if (!group || !group->isRaidGroup())
|
||||||
@ -210,12 +198,9 @@ namespace SerpentShrineCavernHelpers
|
|||||||
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member))
|
if (botAI->IsMainTank(member))
|
||||||
{
|
|
||||||
if (memberAI->IsMainTank(member))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -277,38 +262,9 @@ namespace SerpentShrineCavernHelpers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds)
|
Player* GetDesignatedCoreLooter(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@ -317,10 +273,15 @@ namespace SerpentShrineCavernHelpers
|
|||||||
if (!leaderGuid.IsEmpty())
|
if (!leaderGuid.IsEmpty())
|
||||||
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
||||||
|
|
||||||
|
// If cheats are disabled, the group leader will be the designated looter
|
||||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||||
return leader;
|
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())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
@ -331,22 +292,36 @@ namespace SerpentShrineCavernHelpers
|
|||||||
if (!memberAI)
|
if (!memberAI)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (memberAI->IsMelee(member) && memberAI->IsDps(member))
|
if (!meleeDpsAssistant && memberAI->IsMelee(member) &&
|
||||||
return member;
|
memberAI->IsDps(member) && group->IsAssistant(member->GetGUID()))
|
||||||
|
|
||||||
if (!fallback && memberAI->IsRangedDps(member))
|
|
||||||
fallback = member;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback ? fallback : leader;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
|
||||||
{
|
{
|
||||||
|
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)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
@ -355,32 +330,29 @@ namespace SerpentShrineCavernHelpers
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
if (!memberAI)
|
if (memberAI && memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||||
continue;
|
|
||||||
|
|
||||||
if (memberAI->IsAssistHealOfIndex(member, 0, true))
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||||
botAI->IsTank(member) || member == designatedLooter)
|
!botAI->IsTank(member) && member != designatedLooter)
|
||||||
continue;
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
Player* GetSecondTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
@ -390,34 +362,31 @@ namespace SerpentShrineCavernHelpers
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
if (!memberAI)
|
if (memberAI && memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||||
continue;
|
|
||||||
|
|
||||||
if (memberAI->IsAssistHealOfIndex(member, 1, true))
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||||
botAI->IsTank(member) || member == designatedLooter ||
|
!botAI->IsTank(member) && member != designatedLooter &&
|
||||||
member == firstCorePasser)
|
member != firstCorePasser)
|
||||||
continue;
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
Player* GetThirdTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
Player* secondCorePasser = GetSecondTaintedCorePasser(botAI, bot);
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
@ -427,35 +396,32 @@ namespace SerpentShrineCavernHelpers
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
if (!memberAI)
|
if (memberAI && memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||||
continue;
|
|
||||||
|
|
||||||
if (memberAI->IsAssistHealOfIndex(member, 2, true))
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||||
botAI->IsTank(member) || member == designatedLooter ||
|
!botAI->IsTank(member) && member != designatedLooter &&
|
||||||
member == firstCorePasser || member == secondCorePasser)
|
member != firstCorePasser && member != secondCorePasser)
|
||||||
continue;
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
Player* GetFourthTaintedCorePasser(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
Player* designatedLooter = GetDesignatedCoreLooter(botAI, bot);
|
||||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
Player* firstCorePasser = GetFirstTaintedCorePasser(botAI, bot);
|
||||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
Player* secondCorePasser = GetSecondTaintedCorePasser(botAI, bot);
|
||||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
Player* thirdCorePasser = GetThirdTaintedCorePasser(botAI, bot);
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
@ -466,27 +432,75 @@ namespace SerpentShrineCavernHelpers
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||||
if (!memberAI)
|
if (memberAI && memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||||
continue;
|
|
||||||
|
|
||||||
if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) &&
|
||||||
botAI->IsTank(member) || member == designatedLooter ||
|
!botAI->IsTank(member) && member != designatedLooter &&
|
||||||
member == firstCorePasser || member == secondCorePasser ||
|
member != firstCorePasser && member != secondCorePasser &&
|
||||||
member == thirdCorePasser)
|
member != thirdCorePasser)
|
||||||
continue;
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
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 =
|
const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS =
|
||||||
{
|
{
|
||||||
47482, // NW
|
47482, // NW
|
||||||
@ -510,10 +524,7 @@ namespace SerpentShrineCavernHelpers
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
GameObject* go = bounds.first->second;
|
GameObject* go = bounds.first->second;
|
||||||
if (!go)
|
if (!go || go->GetGoState() != GO_STATE_READY)
|
||||||
continue;
|
|
||||||
|
|
||||||
if (go->GetGoState() != GO_STATE_READY)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GeneratorInfo info;
|
GeneratorInfo info;
|
||||||
@ -529,7 +540,7 @@ namespace SerpentShrineCavernHelpers
|
|||||||
|
|
||||||
// Returns the nearest active Shield Generator to the bot
|
// Returns the nearest active Shield Generator to the bot
|
||||||
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
||||||
// which depawn after use
|
// which despawn after use
|
||||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
||||||
{
|
{
|
||||||
if (!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_
|
#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||||
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||||
|
|
||||||
@ -64,9 +69,6 @@ namespace SerpentShrineCavernHelpers
|
|||||||
|
|
||||||
// Warlock
|
// Warlock
|
||||||
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
||||||
|
|
||||||
// Item
|
|
||||||
SPELL_HEAVY_NETHERWEAVE_NET = 31368,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SerpentShrineCavernNPCs
|
enum SerpentShrineCavernNPCs
|
||||||
@ -105,9 +107,6 @@ namespace SerpentShrineCavernHelpers
|
|||||||
{
|
{
|
||||||
// Lady Vashj <Coilfang Matron>
|
// Lady Vashj <Coilfang Matron>
|
||||||
ITEM_TAINTED_CORE = 31088,
|
ITEM_TAINTED_CORE = 31088,
|
||||||
|
|
||||||
// Tailoring
|
|
||||||
ITEM_HEAVY_NETHERWEAVE_NET = 24269,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32 SSC_MAP_ID = 548;
|
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> leotherasHumanFormDpsWaitTimer;
|
||||||
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||||
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI);
|
Unit* GetLeotherasHuman(Player* bot);
|
||||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI);
|
Unit* GetPhase2LeotherasDemon(Player* bot);
|
||||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI);
|
Unit* GetPhase3LeotherasDemon(Player* bot);
|
||||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI);
|
Unit* GetActiveLeotherasDemon(Player* bot);
|
||||||
Player* GetLeotherasDemonFormTank(Player* bot);
|
Player* GetLeotherasDemonFormTank(Player* bot);
|
||||||
|
|
||||||
// Fathom-Lord Karathress
|
// Fathom-Lord Karathress
|
||||||
@ -158,25 +157,26 @@ namespace SerpentShrineCavernHelpers
|
|||||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||||
|
|
||||||
// Lady Vashj <Coilfang Matron>
|
// 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 const Position VASHJ_PLATFORM_CENTER_POSITION;
|
||||||
extern std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
|
||||||
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||||
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||||
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||||
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||||
extern std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
extern std::unordered_map<ObjectGuid, time_t> lastCoreInInventoryTime;
|
||||||
bool IsMainTankInSameSubgroup(Player* bot);
|
bool IsMainTankInSameSubgroup(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
||||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
||||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
||||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
||||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3);
|
Player* GetDesignatedCoreLooter(PlayerbotAI* botAI, Player* bot);
|
||||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI);
|
Player* GetFirstTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
Player* GetSecondTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
Player* GetThirdTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
Player* GetFourthTaintedCorePasser(PlayerbotAI* botAI, Player* bot);
|
||||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
std::array<Player*, 5> GetCoreHandlers(PlayerbotAI* botAI, Player* bot);
|
||||||
|
bool AnyRecentCoreInInventory(PlayerbotAI* botAI, Player* bot);
|
||||||
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
||||||
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
||||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||||
|
|||||||
@ -1796,7 +1796,7 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
|
|||||||
if (itr != _playerbotsAIMap.end())
|
if (itr != _playerbotsAIMap.end())
|
||||||
{
|
{
|
||||||
if (itr->second->IsBotAI())
|
if (itr->second->IsBotAI())
|
||||||
return reinterpret_cast<PlayerbotAI*>(itr->second);
|
return dynamic_cast<PlayerbotAI*>(itr->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -1812,7 +1812,7 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
|||||||
if (itr != _playerbotsMgrMap.end())
|
if (itr != _playerbotsMgrMap.end())
|
||||||
{
|
{
|
||||||
if (!itr->second->IsBotAI())
|
if (!itr->second->IsBotAI())
|
||||||
return reinterpret_cast<PlayerbotMgr*>(itr->second);
|
return dynamic_cast<PlayerbotMgr*>(itr->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@ -1070,7 +1070,7 @@ void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim)
|
|||||||
if (!victim->IsCreature())
|
if (!victim->IsCreature())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Creature* creature = reinterpret_cast<Creature*>(victim);
|
Creature* creature = dynamic_cast<Creature*>(victim);
|
||||||
if (!creature)
|
if (!creature)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@ -1753,9 +1753,8 @@ void TravelNodeMap::generateTransportNodes()
|
|||||||
for (auto const& itr : *sObjectMgr->GetGameObjectTemplates())
|
for (auto const& itr : *sObjectMgr->GetGameObjectTemplates())
|
||||||
{
|
{
|
||||||
GameObjectTemplate const* data = &itr.second;
|
GameObjectTemplate const* data = &itr.second;
|
||||||
if (data && (data->type == GAMEOBJECT_TYPE_TRANSPORT || data->type == GAMEOBJECT_TYPE_MO_TRANSPORT))
|
if (!data || (data->type != GAMEOBJECT_TYPE_TRANSPORT && data->type != GAMEOBJECT_TYPE_MO_TRANSPORT))
|
||||||
{
|
continue;
|
||||||
TransportAnimation const* animation = sTransportMgr->GetTransportAnimInfo(itr.first);
|
|
||||||
|
|
||||||
uint32 pathId = data->moTransport.taxiPathId;
|
uint32 pathId = data->moTransport.taxiPathId;
|
||||||
float moveSpeed = data->moTransport.moveSpeed;
|
float moveSpeed = data->moTransport.moveSpeed;
|
||||||
@ -1764,125 +1763,20 @@ void TravelNodeMap::generateTransportNodes()
|
|||||||
|
|
||||||
TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
|
TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
|
||||||
|
|
||||||
|
// Keep only transports with taxi paths (boats/zeppelins).
|
||||||
|
if (path.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
std::vector<WorldPosition> ppath;
|
std::vector<WorldPosition> ppath;
|
||||||
TravelNode* prevNode = nullptr;
|
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.
|
// Loop over the path and connect stop locations.
|
||||||
for (auto& p : path)
|
for (auto& p : path)
|
||||||
{
|
{
|
||||||
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
|
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)
|
if (prevNode)
|
||||||
{
|
|
||||||
ppath.push_back(pos);
|
ppath.push_back(pos);
|
||||||
}
|
|
||||||
|
|
||||||
if (p->delay > 0)
|
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.
|
// Continue from start until first stop and connect to end.
|
||||||
for (auto& p : path)
|
for (auto& p : path)
|
||||||
{
|
{
|
||||||
WorldPosition pos = WorldPosition(p->mapid, p->x, p->y, p->z, 0);
|
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);
|
ppath.push_back(pos);
|
||||||
|
|
||||||
if (p->delay > 0)
|
if (p->delay > 0)
|
||||||
@ -1925,20 +1814,16 @@ void TravelNodeMap::generateTransportNodes()
|
|||||||
|
|
||||||
if (node != prevNode)
|
if (node != prevNode)
|
||||||
{
|
{
|
||||||
TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first,
|
TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first, true);
|
||||||
true);
|
|
||||||
travelPath.setPathAndCost(ppath, moveSpeed);
|
travelPath.setPathAndCost(ppath, moveSpeed);
|
||||||
|
|
||||||
node->setPathTo(prevNode, travelPath);
|
node->setPathTo(prevNode, travelPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ppath.clear();
|
ppath.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TravelNodeMap::generateZoneMeanNodes()
|
void TravelNodeMap::generateZoneMeanNodes()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -52,15 +52,14 @@ public:
|
|||||||
PlayerbotsSecureLoginServerScript()
|
PlayerbotsSecureLoginServerScript()
|
||||||
: ServerScript("PlayerbotsSecureLoginServerScript", { SERVERHOOK_CAN_PACKET_RECEIVE }) {}
|
: ServerScript("PlayerbotsSecureLoginServerScript", { SERVERHOOK_CAN_PACKET_RECEIVE }) {}
|
||||||
|
|
||||||
bool CanPacketReceive(WorldSession* /*session*/, WorldPacket& packet) override
|
bool CanPacketReceive(WorldSession* /*session*/, WorldPacket const& packet) override
|
||||||
{
|
{
|
||||||
if (packet.GetOpcode() != CMSG_PLAYER_LOGIN)
|
if (packet.GetOpcode() != CMSG_PLAYER_LOGIN)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto const oldPos = packet.rpos();
|
WorldPacket pkt(packet);
|
||||||
ObjectGuid loginGuid;
|
ObjectGuid loginGuid;
|
||||||
packet >> loginGuid;
|
pkt >> loginGuid;
|
||||||
packet.rpos(oldPos);
|
|
||||||
|
|
||||||
if (!loginGuid)
|
if (!loginGuid)
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user