mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge pull request #504 from fuzzdeveloper/aq20-ossirian-strat
AQ20 Ossirian Strat
This commit is contained in:
@@ -1507,6 +1507,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 409:
|
||||
strategyName = "mc";
|
||||
break;
|
||||
case 509:
|
||||
strategyName = "aq20";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "raids/moltencore/RaidMcActionContext.h"
|
||||
#include "raids/moltencore/RaidMcTriggerContext.h"
|
||||
#include "raids/aq20/RaidAq20ActionContext.h"
|
||||
#include "raids/aq20/RaidAq20TriggerContext.h"
|
||||
|
||||
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
{
|
||||
@@ -40,6 +42,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidMcActionContext());
|
||||
actionContexts.Add(new RaidAq20ActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
@@ -48,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidMcTriggerContext());
|
||||
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
|
||||
@@ -762,7 +762,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
||||
// return true;
|
||||
}
|
||||
|
||||
bool MovementAction::MoveTo(Unit* target, float distance, MovementPriority priority)
|
||||
bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriority priority)
|
||||
{
|
||||
if (!IsMovingAllowed(target))
|
||||
return false;
|
||||
@@ -874,7 +874,7 @@ float MovementAction::GetFollowAngle()
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MovementAction::IsMovingAllowed(Unit* target)
|
||||
bool MovementAction::IsMovingAllowed(WorldObject* target)
|
||||
{
|
||||
if (!target)
|
||||
return false;
|
||||
@@ -1272,6 +1272,8 @@ bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle)
|
||||
|
||||
// bot->GetMotionMaster()->Clear();
|
||||
bot->GetMotionMaster()->MoveChase((Unit*)obj, distance);
|
||||
|
||||
// TODO shouldnt this use "last movement" value?
|
||||
WaitForReach(bot->GetExactDist2d(obj) - distance);
|
||||
return true;
|
||||
}
|
||||
@@ -1295,6 +1297,7 @@ float MovementAction::MoveDelay(float distance)
|
||||
return delay;
|
||||
}
|
||||
|
||||
// TODO should this be removed? (or modified to use "last movement" value?)
|
||||
void MovementAction::WaitForReach(float distance)
|
||||
{
|
||||
float delay = 1000.0f * MoveDelay(distance);
|
||||
@@ -1313,6 +1316,15 @@ void MovementAction::WaitForReach(float distance)
|
||||
botAI->SetNextCheckDelay((uint32)delay);
|
||||
}
|
||||
|
||||
// similiar to botAI->SetNextCheckDelay() but only stops movement
|
||||
void MovementAction::SetNextMovementDelay(float delayMillis)
|
||||
{
|
||||
AI_VALUE(LastMovement&, "last movement")
|
||||
.Set(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetOrientation(),
|
||||
delayMillis,
|
||||
MovementPriority::MOVEMENT_FORCED);
|
||||
}
|
||||
|
||||
bool MovementAction::Flee(Unit* target)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
|
||||
@@ -30,7 +30,7 @@ protected:
|
||||
bool MoveToLOS(WorldObject* target, bool ranged = false);
|
||||
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
|
||||
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
bool MoveTo(Unit* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
float GetFollowAngle();
|
||||
bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance);
|
||||
@@ -39,7 +39,8 @@ protected:
|
||||
bool ReachCombatTo(Unit* target, float distance = 0.0f);
|
||||
float MoveDelay(float distance);
|
||||
void WaitForReach(float distance);
|
||||
bool IsMovingAllowed(Unit* target);
|
||||
void SetNextMovementDelay(float delayMillis);
|
||||
bool IsMovingAllowed(WorldObject* target);
|
||||
bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
|
||||
bool IsDuplicateMove(uint32 mapId, float x, float y, float z);
|
||||
bool IsWaitingForLastMove(MovementPriority priority);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "RaidBwlStrategy.h"
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidMcStrategy.h"
|
||||
#include "RaidAq20Strategy.h"
|
||||
|
||||
class RaidStrategyContext : public NamedObjectContext<Strategy>
|
||||
{
|
||||
@@ -19,6 +20,7 @@ public:
|
||||
creators["bwl"] = &RaidStrategyContext::bwl;
|
||||
creators["uld"] = &RaidStrategyContext::uld;
|
||||
creators["mc"] = &RaidStrategyContext::mc;
|
||||
creators["aq20"] = &RaidStrategyContext::aq20;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -26,6 +28,7 @@ private:
|
||||
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
||||
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
20
src/strategy/raids/aq20/RaidAq20ActionContext.h
Normal file
20
src/strategy/raids/aq20/RaidAq20ActionContext.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidAq20Actions.h"
|
||||
|
||||
class RaidAq20ActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidAq20ActionContext()
|
||||
{
|
||||
creators["aq20 use crystal"] = &RaidAq20ActionContext::use_crystal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* use_crystal(PlayerbotAI* ai) { return new Aq20UseCrystalAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
49
src/strategy/raids/aq20/RaidAq20Actions.cpp
Normal file
49
src/strategy/raids/aq20/RaidAq20Actions.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "RaidAq20Actions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
|
||||
bool Aq20UseCrystalAction::Execute(Event event)
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||
{
|
||||
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||
{
|
||||
float botDist = bot->GetDistance(crystal);
|
||||
if (botDist > INTERACTION_DISTANCE)
|
||||
return MoveTo(bot->GetMapId(),
|
||||
crystal->GetPositionX() + frand(-3.5f, 3.5f),
|
||||
crystal->GetPositionY() + frand(-3.5f, 3.5f),
|
||||
crystal->GetPositionZ());
|
||||
|
||||
// if we're already in range just wait here until it's time to activate crystal
|
||||
SetNextMovementDelay(500);
|
||||
|
||||
// don't activate crystal if boss too far or its already been activated
|
||||
if (boss->GetDistance(crystal) > 25.0f ||
|
||||
crystal->HasGameObjectFlag(GO_FLAG_IN_USE))
|
||||
return false;
|
||||
|
||||
// don't activate crystal if boss doesn't have buff yet AND isn't going to have it soon
|
||||
// (though ideally bot should activate it ~5 seconds early due to time it takes for
|
||||
// crystal to activate and remove buff)
|
||||
if (!RaidAq20Utils::IsOssirianBuffActive(boss) &&
|
||||
RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss) > 5000)
|
||||
return false;
|
||||
|
||||
// this makes crystal do its animation (then disappear after)
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << crystal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
// this makes crystal actually remove the buff and put on debuff (took a while to figure that out)
|
||||
WorldPacket data2(CMSG_GAMEOBJ_USE);
|
||||
data2 << crystal->GetGUID();
|
||||
bot->GetSession()->HandleGameobjectReportUse(data2);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
14
src/strategy/raids/aq20/RaidAq20Actions.h
Normal file
14
src/strategy/raids/aq20/RaidAq20Actions.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||
#define _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class Aq20UseCrystalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
Aq20UseCrystalAction(PlayerbotAI* botAI, std::string const name = "aq20 use crystal") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
#endif
|
||||
11
src/strategy/raids/aq20/RaidAq20Strategy.cpp
Normal file
11
src/strategy/raids/aq20/RaidAq20Strategy.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "RaidAq20Strategy.h"
|
||||
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidAq20Strategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("aq20 move to crystal",
|
||||
NextAction::array(0, new NextAction("aq20 use crystal", ACTION_RAID), nullptr)));
|
||||
|
||||
}
|
||||
17
src/strategy/raids/aq20/RaidAq20Strategy.h
Normal file
17
src/strategy/raids/aq20/RaidAq20Strategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
#define _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidAq20Strategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidAq20Strategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "aq20"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
// virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
20
src/strategy/raids/aq20/RaidAq20TriggerContext.h
Normal file
20
src/strategy/raids/aq20/RaidAq20TriggerContext.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidAq20Triggers.h"
|
||||
|
||||
class RaidAq20TriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidAq20TriggerContext()
|
||||
{
|
||||
creators["aq20 move to crystal"] = &RaidAq20TriggerContext::move_to_crystal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* move_to_crystal(PlayerbotAI* ai) { return new Aq20MoveToCrystalTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/strategy/raids/aq20/RaidAq20Triggers.cpp
Normal file
37
src/strategy/raids/aq20/RaidAq20Triggers.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "RaidAq20Triggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
|
||||
bool Aq20MoveToCrystalTrigger::IsActive()
|
||||
{
|
||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||
{
|
||||
if (boss->IsInCombat())
|
||||
{
|
||||
// if buff is active move to crystal
|
||||
if (RaidAq20Utils::IsOssirianBuffActive(boss))
|
||||
return true;
|
||||
|
||||
// if buff is not active a debuff will be, buff becomes active once debuff expires
|
||||
// so move to crystal when debuff almost done, or based debuff time left and
|
||||
// distance bot is from crystal (ie: start moving early enough to make it)
|
||||
int32 debuffTimeRemaining = RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss);
|
||||
if (debuffTimeRemaining < 5000)
|
||||
return true;
|
||||
if (debuffTimeRemaining < 30000)
|
||||
{
|
||||
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||
{
|
||||
float botDist = bot->GetDistance(crystal);
|
||||
float timeToReach = botDist / bot->GetSpeed(MOVE_RUN);
|
||||
// bot should ideally activate crystal a ~5 seconds early (due to time it takes for crystal
|
||||
// to activate) so aim to get there in time to do so
|
||||
return debuffTimeRemaining - 5000 < timeToReach * 1000.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
14
src/strategy/raids/aq20/RaidAq20Triggers.h
Normal file
14
src/strategy/raids/aq20/RaidAq20Triggers.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class Aq20MoveToCrystalTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
Aq20MoveToCrystalTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq20 move to crystal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
#endif
|
||||
38
src/strategy/raids/aq20/RaidAq20Utils.cpp
Normal file
38
src/strategy/raids/aq20/RaidAq20Utils.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "RaidAq20Utils.h"
|
||||
|
||||
#include "SpellAuras.h"
|
||||
|
||||
uint32 const OSSIRIAN_BUFF = 25176;
|
||||
uint32 const OSSIRIAN_DEBUFFS[] = {25177, 25178, 25180, 25181, 25183};
|
||||
uint32 const OSSIRIAN_CRYSTAL_GO_ENTRY = 180619;
|
||||
|
||||
bool RaidAq20Utils::IsOssirianBuffActive(Unit* ossirian)
|
||||
{
|
||||
return ossirian && ossirian->HasAura(OSSIRIAN_BUFF);
|
||||
}
|
||||
|
||||
int32 RaidAq20Utils::GetOssirianDebuffTimeRemaining(Unit* ossirian)
|
||||
{
|
||||
int32 retVal = 0xffffff;
|
||||
if (ossirian)
|
||||
{
|
||||
for (uint32 debuff : OSSIRIAN_DEBUFFS)
|
||||
{
|
||||
if (AuraApplication* auraApplication = ossirian->GetAuraApplication(debuff))
|
||||
{
|
||||
if (Aura* aura = auraApplication->GetBase())
|
||||
{
|
||||
int32 duration = aura->GetDuration();
|
||||
if (retVal > duration)
|
||||
retVal = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
GameObject* RaidAq20Utils::GetNearestCrystal(Unit* ossirian)
|
||||
{
|
||||
return ossirian ? ossirian->FindNearestGameObject(OSSIRIAN_CRYSTAL_GO_ENTRY, 200.0f) : nullptr;
|
||||
}
|
||||
15
src/strategy/raids/aq20/RaidAq20Utils.h
Normal file
15
src/strategy/raids/aq20/RaidAq20Utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20UTILS_H
|
||||
#define _PLAYERBOT_RAIDAQ20UTILS_H
|
||||
|
||||
#include "GameObject.h"
|
||||
#include "Unit.h"
|
||||
|
||||
class RaidAq20Utils
|
||||
{
|
||||
public:
|
||||
static bool IsOssirianBuffActive(Unit* ossirian);
|
||||
static int32 GetOssirianDebuffTimeRemaining(Unit* ossirian);
|
||||
static GameObject* GetNearestCrystal(Unit* ossirian);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user