mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
@@ -59,6 +59,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonGDActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoLActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
|
||||
@@ -80,6 +81,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonGDTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoLTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
|
||||
|
||||
@@ -11,14 +11,13 @@
|
||||
#include "wotlk/gundrak/GundrakStrategy.h"
|
||||
#include "wotlk/hallsofstone/HallsOfStoneStrategy.h"
|
||||
#include "wotlk/hallsoflightning/HallsOfLightningStrategy.h"
|
||||
#include "wotlk/oculus/OculusStrategy.h"
|
||||
#include "wotlk/utgardepinnacle/UtgardePinnacleStrategy.h"
|
||||
#include "wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h"
|
||||
|
||||
/*
|
||||
Full list/TODO:
|
||||
|
||||
The Oculus - Occ
|
||||
Drakos the Interrogator, Varos Cloudstrider, Mage-Lord Urom, Ley-Guardian Eregos
|
||||
Trial of the Champion - ToC
|
||||
Alliance Champions: Deathstalker Visceri, Eressea Dawnsinger, Mokra the Skullcrusher, Runok Wildmane, Zul'tore
|
||||
Horde Champions: Ambrose Boltspark, Colosos, Jacob Alerius, Jaelyne Evensong, Lana Stouthammer
|
||||
@@ -74,12 +73,11 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonGDStrategy(botAI); }
|
||||
static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonHoSStrategy(botAI); }
|
||||
static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonHoLStrategy(botAI); }
|
||||
// static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonOccStrategy(botAI); }
|
||||
static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonOccStrategy(botAI); }
|
||||
static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUPStrategy(botAI); }
|
||||
static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonCoSStrategy(botAI); }
|
||||
|
||||
static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } // NYI from here down
|
||||
// NYI from here down
|
||||
static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_hor(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_fos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "gundrak/GundrakActionContext.h"
|
||||
#include "hallsofstone/HallsOfStoneActionContext.h"
|
||||
#include "hallsoflightning/HallsOfLightningActionContext.h"
|
||||
// #include "oculus/OculusActionContext.h"
|
||||
#include "oculus/OculusActionContext.h"
|
||||
#include "utgardepinnacle/UtgardePinnacleActionContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeActionContext.h"
|
||||
// #include "trialofthechampion/TrialOfTheChampionActionContext.h"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "gundrak/GundrakTriggerContext.h"
|
||||
#include "hallsofstone/HallsOfStoneTriggerContext.h"
|
||||
#include "hallsoflightning/HallsOfLightningTriggerContext.h"
|
||||
// #include "oculus/OculusTriggerContext.h"
|
||||
#include "oculus/OculusTriggerContext.h"
|
||||
#include "utgardepinnacle/UtgardePinnacleTriggerContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeTriggerContext.h"
|
||||
// #include "trialofthechampion/TrialOfTheChampionTriggerContext.h"
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
bool ShatterSpreadAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus");
|
||||
float radius = 40.0f;
|
||||
if (!boss) { return false; }
|
||||
|
||||
float radius = 40.0f;
|
||||
Unit* closestMember = nullptr;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
|
||||
30
src/strategy/dungeons/wotlk/oculus/OculusActionContext.h
Normal file
30
src/strategy/dungeons/wotlk/oculus/OculusActionContext.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "OculusActions.h"
|
||||
|
||||
class WotlkDungeonOccActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonOccActionContext() {
|
||||
creators["avoid unstable sphere"] = &WotlkDungeonOccActionContext::avoid_unstable_sphere;
|
||||
creators["mount drake"] = &WotlkDungeonOccActionContext::mount_drake;
|
||||
creators["dismount drake"] = &WotlkDungeonOccActionContext::dismount_drake;
|
||||
creators["fly drake"] = &WotlkDungeonOccActionContext::fly_drake;
|
||||
creators["drake attack"] = &WotlkDungeonOccActionContext::drake_attack;
|
||||
creators["avoid arcane explosion"] = &WotlkDungeonOccActionContext::avoid_arcane_explosion;
|
||||
creators["time bomb spread"] = &WotlkDungeonOccActionContext::time_bomb_spread;
|
||||
}
|
||||
private:
|
||||
static Action* avoid_unstable_sphere(PlayerbotAI* ai) { return new AvoidUnstableSphereAction(ai); }
|
||||
static Action* mount_drake(PlayerbotAI* ai) { return new MountDrakeAction(ai); }
|
||||
static Action* dismount_drake(PlayerbotAI* ai) { return new DismountDrakeAction(ai); }
|
||||
static Action* fly_drake(PlayerbotAI* ai) { return new FlyDrakeAction(ai); }
|
||||
static Action* drake_attack(PlayerbotAI* ai) { return new DrakeAttackAction(ai); }
|
||||
static Action* avoid_arcane_explosion(PlayerbotAI* ai) { return new AvoidArcaneExplosionAction(ai); }
|
||||
static Action* time_bomb_spread(PlayerbotAI* ai) { return new TimeBombSpreadAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
355
src/strategy/dungeons/wotlk/oculus/OculusActions.cpp
Normal file
355
src/strategy/dungeons/wotlk/oculus/OculusActions.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include "Playerbots.h"
|
||||
#include "OculusActions.h"
|
||||
#include "OculusStrategy.h"
|
||||
#include "LastSpellCastValue.h"
|
||||
|
||||
bool AvoidUnstableSphereAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "drakos the interrogator");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float radius = 12.0f;
|
||||
float extraDistance = 1.0f;
|
||||
Unit* closestSphere = nullptr;
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_UNSTABLE_SPHERE && !unit->isMoving())
|
||||
{
|
||||
if (!closestSphere || bot->GetExactDist2d(unit) < bot->GetExactDist2d(closestSphere))
|
||||
{
|
||||
closestSphere = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closestSphere && bot->GetExactDist2d(closestSphere) < radius + extraDistance)
|
||||
{
|
||||
return MoveAway(closestSphere, fmin(3.0f, bot->GetExactDist2d(closestSphere) - radius + extraDistance));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MountDrakeAction::isPossible() { return bot->GetMapId() == OCULUS_MAP_ID; }
|
||||
bool MountDrakeAction::Execute(Event event)
|
||||
{
|
||||
std::map<int32, int32> drakeAssignments;
|
||||
// Composition can be adjusted - both 3/1/1 and 2/2/1 are good default comps
|
||||
// {Amber, Emerald, Ruby}
|
||||
std::vector<uint8> composition = {2, 2, 1};
|
||||
// std::vector<uint8> composition = {3, 1, 1};
|
||||
int32 myIndex = botAI->GetGroupSlotIndex(bot);
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
Unit* vehicle = master->GetVehicleBase();
|
||||
if (!vehicle) { return false; }
|
||||
|
||||
// Subtract the player's chosen mount type from the composition so player can play whichever they prefer
|
||||
switch (vehicle->GetEntry())
|
||||
{
|
||||
case NPC_AMBER_DRAKE:
|
||||
composition[0]--;
|
||||
break;
|
||||
case NPC_EMERALD_DRAKE:
|
||||
composition[1]--;
|
||||
break;
|
||||
case NPC_RUBY_DRAKE:
|
||||
composition[2]--;
|
||||
break;
|
||||
}
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Player* player = botAI->GetPlayer(member);
|
||||
if (!player) { continue; }
|
||||
|
||||
for (int i = 0; i < composition.size(); i++)
|
||||
{
|
||||
if (composition[i] > 0)
|
||||
{
|
||||
drakeAssignments[botAI->GetGroupSlotIndex(player)] = DRAKE_ITEMS[i];
|
||||
composition[i]--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Correct/update the drake items in inventories incase assignments have changed
|
||||
for (uint32 itemId : DRAKE_ITEMS)
|
||||
{
|
||||
Item* item = bot->GetItemByEntry(itemId);
|
||||
if (!item) { continue; }
|
||||
|
||||
if (itemId == drakeAssignments[myIndex])
|
||||
{
|
||||
// Use our assigned drake
|
||||
return UseItemAuto(item);
|
||||
}
|
||||
// Else assigned drake is different, destroy old drake
|
||||
uint32 count = 1;
|
||||
bot->DestroyItemCount(item, count, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Bot does not have the correct drake item
|
||||
bot->AddItem(drakeAssignments[myIndex], 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DismountDrakeAction::Execute(Event event)
|
||||
{
|
||||
if (bot->GetVehicle())
|
||||
{
|
||||
bot->ExitVehicle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlyDrakeAction::Execute(Event event)
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
Unit* masterVehicle = master->GetVehicleBase();
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase || !masterVehicle) { return false; }
|
||||
|
||||
MotionMaster* mm = vehicleBase->GetMotionMaster();
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos");
|
||||
if (boss && !boss->HasAura(SPELL_PLANAR_SHIFT))
|
||||
{
|
||||
// Handle as boss encounter instead of formation flight
|
||||
mm->Clear(false);
|
||||
float distance = vehicleBase->GetExactDist(boss);
|
||||
float range = 55.0f; // Drake range is 60yd
|
||||
if (distance > range)
|
||||
{
|
||||
mm->MoveForwards(boss, range - distance);
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
vehicleBase->SetFacingToObject(boss);
|
||||
mm->MoveIdle();
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vehicleBase->GetExactDist(masterVehicle) > 20.0f)
|
||||
{
|
||||
// 3/4 of a circle, with frontal cone 90 deg unobstructed
|
||||
float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/5 + M_PI_2;
|
||||
vehicleBase->SetCanFly(true);
|
||||
mm->MoveFollow(masterVehicle, 15.0f, angle);
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeAttackAction::Execute(Event event)
|
||||
{
|
||||
vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase) { return false; }
|
||||
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
|
||||
if (!target)
|
||||
{
|
||||
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
|
||||
for (auto& attacker : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(attacker);
|
||||
if (!unit) { continue; }
|
||||
|
||||
SET_AI_VALUE(Unit*, "current target", unit);
|
||||
target = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target) { return false; }
|
||||
|
||||
switch (vehicleBase->GetEntry())
|
||||
{
|
||||
case NPC_AMBER_DRAKE:
|
||||
return AmberDrakeAction(target);
|
||||
case NPC_EMERALD_DRAKE:
|
||||
return EmeraldDrakeAction(target);
|
||||
case NPC_RUBY_DRAKE:
|
||||
return RubyDrakeAction(target);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown)
|
||||
{
|
||||
if (botAI->CanCastVehicleSpell(spellId, target))
|
||||
if (botAI->CastVehicleSpell(spellId, target))
|
||||
{
|
||||
vehicleBase->AddSpellCooldown(spellId, 0, cooldown);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeAttackAction::AmberDrakeAction(Unit* target)
|
||||
{
|
||||
Aura* shockCharges = target->GetAura(SPELL_SHOCK_CHARGE, vehicleBase->GetGUID());
|
||||
if (shockCharges && shockCharges->GetStackAmount() > 8)
|
||||
{
|
||||
// At 9 charges, better to detonate and re-channel rather than stacking the last charge due to gcd
|
||||
// If stacking Amber drakes, may need to drop this even lower as the charges stack so fast
|
||||
return CastDrakeSpellAction(target, SPELL_SHOCK_LANCE, 0);
|
||||
}
|
||||
|
||||
// Deal with enrage after shock charges, as Stop Time adds 5 charges and they may get wasted
|
||||
if (target->HasAura(SPELL_ENRAGED_ASSAULT) &&
|
||||
!target->HasAura(SPELL_STOP_TIME) &&
|
||||
!vehicleBase->HasSpellCooldown(SPELL_STOP_TIME))
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_STOP_TIME, 60000);
|
||||
}
|
||||
|
||||
if (!vehicleBase->FindCurrentSpellBySpellId(SPELL_TEMPORAL_RIFT))
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_TEMPORAL_RIFT, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeAttackAction::EmeraldDrakeAction(Unit* target)
|
||||
{
|
||||
Aura* poisonStacks = target->GetAura(SPELL_LEECHING_POISON, vehicleBase->GetGUID());
|
||||
if (!poisonStacks || (poisonStacks->GetStackAmount() < 3 ||
|
||||
poisonStacks->GetDuration() < 4000))
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_LEECHING_POISON, 0);
|
||||
}
|
||||
|
||||
if (!vehicleBase->HasSpellCooldown(SPELL_TOUCH_THE_NIGHTMARE) &&
|
||||
(!target->HasAura(SPELL_TOUCH_THE_NIGHTMARE) || vehicleBase->HealthAbovePct(90)))
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_TOUCH_THE_NIGHTMARE, 10000);
|
||||
}
|
||||
|
||||
Unit* healingTarget = nullptr;
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || bot->GetGUID() == member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Unit* drake = unit->GetVehicleBase();
|
||||
if (!drake || drake->IsFullHealth()) { continue; }
|
||||
|
||||
if (!healingTarget || drake->GetHealthPct() < healingTarget->GetHealthPct() - 15.0f)
|
||||
{
|
||||
healingTarget = drake;
|
||||
}
|
||||
}
|
||||
|
||||
Spell* currentSpell = vehicleBase->FindCurrentSpellBySpellId(SPELL_DREAM_FUNNEL);
|
||||
if (healingTarget)
|
||||
{
|
||||
if (!currentSpell || currentSpell->m_targets.GetUnitTarget() != healingTarget)
|
||||
{
|
||||
float distance = vehicleBase->GetExactDist(healingTarget);
|
||||
float range = 55.0f;
|
||||
if (distance > range)
|
||||
{
|
||||
MotionMaster* mm = vehicleBase->GetMotionMaster();
|
||||
mm->Clear(false);
|
||||
mm->MoveForwards(healingTarget, distance - range - 10.0f);
|
||||
vehicleBase->SendMovementFlagUpdate();
|
||||
return false;
|
||||
}
|
||||
return CastDrakeSpellAction(healingTarget, SPELL_DREAM_FUNNEL, 0);
|
||||
}
|
||||
}
|
||||
// Fill GCDs with Leeching Poison to refresh timer, rather than idling
|
||||
if (!currentSpell)
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_LEECHING_POISON, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeAttackAction::RubyDrakeAction(Unit* target)
|
||||
{
|
||||
Aura* evasiveCharges = vehicleBase->GetAura(SPELL_EVASIVE_CHARGES);
|
||||
Aura* evasiveManeuvers = vehicleBase->GetAura(SPELL_EVASIVE_MANEUVERS);
|
||||
|
||||
if (evasiveCharges)
|
||||
{
|
||||
if (evasiveManeuvers &&
|
||||
!vehicleBase->HasSpellCooldown(SPELL_MARTYR) &&
|
||||
evasiveManeuvers->GetDuration() > 10000 &&
|
||||
evasiveCharges->GetStackAmount() >= 5)
|
||||
{
|
||||
return CastDrakeSpellAction(vehicleBase, SPELL_MARTYR, 10000);
|
||||
}
|
||||
|
||||
if (!vehicleBase->HasSpellCooldown(SPELL_EVASIVE_MANEUVERS) &&
|
||||
evasiveCharges->GetStackAmount() >= 10)
|
||||
{
|
||||
return CastDrakeSpellAction(vehicleBase, SPELL_EVASIVE_MANEUVERS, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
return CastDrakeSpellAction(target, SPELL_SEARING_WRATH, 0);
|
||||
}
|
||||
|
||||
bool AvoidArcaneExplosionAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom");
|
||||
if (!boss) { return false; }
|
||||
|
||||
const Position* closestPos = nullptr;
|
||||
|
||||
for (auto& position : uromSafePositions)
|
||||
{
|
||||
if (!closestPos || bot->GetExactDist(position) < bot->GetExactDist(closestPos))
|
||||
{
|
||||
closestPos = &position;
|
||||
}
|
||||
}
|
||||
|
||||
if (!closestPos) { return false; }
|
||||
|
||||
return MoveNear(bot->GetMapId(), closestPos->GetPositionX(), closestPos->GetPositionY(), closestPos->GetPositionZ(), 2.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
bool TimeBombSpreadAction::Execute(Event event)
|
||||
{
|
||||
float radius = 10.0f;
|
||||
float distanceExtra = 2.0f;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
if (bot->GetGUID() == member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (unit && bot->GetExactDist2d(unit) < radius)
|
||||
{
|
||||
return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
77
src/strategy/dungeons/wotlk/oculus/OculusActions.h
Normal file
77
src/strategy/dungeons/wotlk/oculus/OculusActions.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "OculusTriggers.h"
|
||||
#include "UseItemAction.h"
|
||||
#include "GenericSpellActions.h"
|
||||
|
||||
const Position uromSafePositions[3] =
|
||||
{
|
||||
Position(1138.88f, 1052.22f, 508.36f),
|
||||
Position(1084.62f, 1079.71f, 508.36f),
|
||||
Position(1087.42f, 1020.132f, 508.36f)
|
||||
};
|
||||
|
||||
class AvoidUnstableSphereAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidUnstableSphereAction(PlayerbotAI* ai) : MovementAction(ai, "avoid unstable sphere") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MountDrakeAction : public UseItemAction
|
||||
{
|
||||
public:
|
||||
MountDrakeAction(PlayerbotAI* ai) : UseItemAction(ai, "mount drake") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
};
|
||||
|
||||
class DismountDrakeAction : public Action
|
||||
{
|
||||
public:
|
||||
DismountDrakeAction(PlayerbotAI* ai) : Action(ai, "dismount drake") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FlyDrakeAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
FlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "fly drake") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class DrakeAttackAction : public Action
|
||||
{
|
||||
public:
|
||||
DrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "drake attack") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
Unit* vehicleBase;
|
||||
bool CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown);
|
||||
bool AmberDrakeAction(Unit* target);
|
||||
bool EmeraldDrakeAction(Unit* target);
|
||||
bool RubyDrakeAction(Unit* target);
|
||||
|
||||
};
|
||||
|
||||
class AvoidArcaneExplosionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidArcaneExplosionAction(PlayerbotAI* ai) : MovementAction(ai, "avoid arcane explosion") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TimeBombSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TimeBombSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "time bomb spread") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
109
src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp
Normal file
109
src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "OculusMultipliers.h"
|
||||
#include "OculusActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "OculusTriggers.h"
|
||||
#include "FollowActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
|
||||
float MountingDrakeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
// P.I.T.A bug where the bots will somehow interrupt their item spell use,
|
||||
// even though the 0.5 sec cast goes off, it puts the drake essence on 15 sec cd
|
||||
// and no drake comes down.
|
||||
// It seems like this is due to moving/other actions being processed during the 0.5 secs.
|
||||
// If we suppress everything, they seem to mount properly. A bit of a ham-fisted solution but it works
|
||||
Player* master = botAI->GetMaster();
|
||||
if (bot->GetMapId() != OCULUS_MAP_ID || !master->GetVehicleBase() || bot->GetVehicleBase()) { return 1.0f; }
|
||||
|
||||
if (!dynamic_cast<MountDrakeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FlyingMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->GetMapId() != OCULUS_MAP_ID || !bot->GetVehicleBase()) { return 1.0f; }
|
||||
|
||||
// Suppresses FollowAction as well as some attack-based movements
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<FlyDrakeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float UromMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if(GetPhaseByCurrentPosition(bot) < 3)
|
||||
{
|
||||
Unit* target = action->GetTarget();
|
||||
if (target && target->GetEntry() == NPC_MAGE_LORD_UROM)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
// REAL BOSS FIGHT
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) &&
|
||||
boss->FindCurrentSpellBySpellId(SPELL_EMPOWERED_ARCANE_EXPLOSION))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<AvoidArcaneExplosionAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't bother avoiding Frostbomb for melee
|
||||
if (botAI->IsMelee(bot))
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (bot->HasAura(SPELL_TIME_BOMB))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<TimeBombSpreadAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
uint8 UromMultiplier::GetPhaseByCurrentPosition(Unit* unit)
|
||||
{
|
||||
// Distance to return a positive match for spawn platforms, tweak slightly if needed/
|
||||
// Make sure this doesn't get too large and reach the central ring as well
|
||||
float distance = 60.0f;
|
||||
|
||||
for (uint8 i = 0; i < 3; ++i)
|
||||
{
|
||||
if (unit->GetDistance(uromCoords[i][0], uromCoords[i][1], uromCoords[i][2]) < distance)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
float EregosMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast<DrakeAttackAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
53
src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h
Normal file
53
src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "Unit.h"
|
||||
|
||||
const float uromCoords[4][4] =
|
||||
{ // Platform coordinates
|
||||
{1177.47f, 937.722f, 527.405f, 2.21657f},
|
||||
{968.66f, 1042.53f, 527.32f, 0.077f},
|
||||
{1164.02f, 1170.85f, 527.321f, 3.66f},
|
||||
{1118.31f, 1080.377f, 508.361f, 4.25f} // Inner ring, actual boss fight
|
||||
};
|
||||
|
||||
class MountingDrakeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MountingDrakeMultiplier(PlayerbotAI* ai) : Multiplier(ai, "mounting drake") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FlyingMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flying drake") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class UromMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
UromMultiplier(PlayerbotAI* ai) : Multiplier(ai, "mage-lord urom") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
protected:
|
||||
uint8 GetPhaseByCurrentPosition(Unit* boss);
|
||||
};
|
||||
|
||||
class EregosMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
EregosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ley-guardian eregos") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
42
src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp
Normal file
42
src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "OculusStrategy.h"
|
||||
#include "OculusMultipliers.h"
|
||||
|
||||
|
||||
void WotlkDungeonOccStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Drakos the Interrogator
|
||||
// TODO: May need work, TBA.
|
||||
triggers.push_back(new TriggerNode("unstable sphere",
|
||||
NextAction::array(0, new NextAction("avoid unstable sphere", ACTION_MOVE + 5), nullptr)));
|
||||
|
||||
// DRAKES
|
||||
triggers.push_back(new TriggerNode("drake mount",
|
||||
NextAction::array(0, new NextAction("mount drake", ACTION_RAID + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("drake dismount",
|
||||
NextAction::array(0, new NextAction("dismount drake", ACTION_RAID + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("group flying",
|
||||
NextAction::array(0, new NextAction("fly drake", ACTION_NORMAL + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("drake combat",
|
||||
NextAction::array(0, new NextAction("drake attack", ACTION_NORMAL + 5), nullptr)));
|
||||
|
||||
// Varos Cloudstrider
|
||||
// Seems to be no way to identify the marked cores, may need to hook boss AI..
|
||||
// triggers.push_back(new TriggerNode("varos cloudstrider",
|
||||
// NextAction::array(0, new NextAction("avoid energize cores", ACTION_RAID + 5), nullptr)));
|
||||
|
||||
// Mage-Lord Urom
|
||||
triggers.push_back(new TriggerNode("arcane explosion",
|
||||
NextAction::array(0, new NextAction("avoid arcane explosion", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("time bomb",
|
||||
NextAction::array(0, new NextAction("time bomb spread", ACTION_MOVE + 4), nullptr)));
|
||||
|
||||
// Ley-Guardian Eregos
|
||||
}
|
||||
|
||||
void WotlkDungeonOccStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new MountingDrakeMultiplier(botAI));
|
||||
multipliers.push_back(new FlyingMultiplier(botAI));
|
||||
multipliers.push_back(new UromMultiplier(botAI));
|
||||
multipliers.push_back(new EregosMultiplier(botAI));
|
||||
}
|
||||
18
src/strategy/dungeons/wotlk/oculus/OculusStrategy.h
Normal file
18
src/strategy/dungeons/wotlk/oculus/OculusStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCSTRATEGY_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
|
||||
class WotlkDungeonOccStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonOccStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "oculus"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
33
src/strategy/dungeons/wotlk/oculus/OculusTriggerContext.h
Normal file
33
src/strategy/dungeons/wotlk/oculus/OculusTriggerContext.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "OculusTriggers.h"
|
||||
|
||||
class WotlkDungeonOccTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonOccTriggerContext()
|
||||
{
|
||||
creators["unstable sphere"] = &WotlkDungeonOccTriggerContext::unstable_sphere;
|
||||
creators["drake mount"] = &WotlkDungeonOccTriggerContext::drake_mount;
|
||||
creators["drake dismount"] = &WotlkDungeonOccTriggerContext::drake_dismount;
|
||||
creators["group flying"] = &WotlkDungeonOccTriggerContext::group_flying;
|
||||
creators["drake combat"] = &WotlkDungeonOccTriggerContext::drake_combat;
|
||||
creators["varos cloudstrider"] = &WotlkDungeonOccTriggerContext::varos_cloudstrider;
|
||||
creators["arcane explosion"] = &WotlkDungeonOccTriggerContext::arcane_explosion;
|
||||
creators["time bomb"] = &WotlkDungeonOccTriggerContext::time_bomb;
|
||||
}
|
||||
private:
|
||||
static Trigger* unstable_sphere(PlayerbotAI* ai) { return new DrakosUnstableSphereTrigger(ai); }
|
||||
static Trigger* drake_mount(PlayerbotAI* ai) { return new DrakeMountTrigger(ai); }
|
||||
static Trigger* drake_dismount(PlayerbotAI* ai) { return new DrakeDismountTrigger(ai); }
|
||||
static Trigger* group_flying(PlayerbotAI* ai) { return new GroupFlyingTrigger(ai); }
|
||||
static Trigger* drake_combat(PlayerbotAI* ai) { return new DrakeCombatTrigger(ai); }
|
||||
static Trigger* varos_cloudstrider(PlayerbotAI* ai) { return new VarosCloudstriderTrigger(ai); }
|
||||
static Trigger* arcane_explosion(PlayerbotAI* ai) { return new UromArcaneExplosionTrigger(ai); }
|
||||
static Trigger* time_bomb(PlayerbotAI* ai) { return new UromTimeBombTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
85
src/strategy/dungeons/wotlk/oculus/OculusTriggers.cpp
Normal file
85
src/strategy/dungeons/wotlk/oculus/OculusTriggers.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "Playerbots.h"
|
||||
#include "OculusTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Unit.h"
|
||||
|
||||
bool DrakosUnstableSphereTrigger::IsActive()
|
||||
{
|
||||
// Doesn't seem to be much point trying to get melee to dodge this,
|
||||
// they get hit anyway and it just causes a lot of running around and chaos
|
||||
// if (botAI->IsMelee(bot)) { return false; }
|
||||
if (botAI->IsTank(bot)) { return false; }
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (unit && unit->GetEntry() == NPC_UNSTABLE_SPHERE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrakeMountTrigger::IsActive()
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
|
||||
return master->GetVehicleBase() && !bot->GetVehicleBase();
|
||||
}
|
||||
|
||||
bool DrakeDismountTrigger::IsActive()
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
|
||||
return !master->GetVehicleBase() && bot->GetVehicleBase();
|
||||
}
|
||||
|
||||
bool GroupFlyingTrigger::IsActive()
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master) { return false; }
|
||||
|
||||
return master->GetVehicleBase() && bot->GetVehicleBase();
|
||||
}
|
||||
|
||||
bool DrakeCombatTrigger::IsActive()
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase) { return false; }
|
||||
|
||||
GuidVector attackers = AI_VALUE(GuidVector, "attackers");
|
||||
for (auto& attacker : attackers)
|
||||
{
|
||||
Unit* target = botAI->GetUnit(attacker);
|
||||
if (!target) { continue; }
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VarosCloudstriderTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "varos cloudstrider");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UromArcaneExplosionTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_EMPOWERED_ARCANE_EXPLOSION);
|
||||
}
|
||||
|
||||
bool UromTimeBombTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_TIME_BOMB);
|
||||
}
|
||||
129
src/strategy/dungeons/wotlk/oculus/OculusTriggers.h
Normal file
129
src/strategy/dungeons/wotlk/oculus/OculusTriggers.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONOCCTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONOCCTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum OculusIDs
|
||||
{
|
||||
// Drakos the Interrogator
|
||||
NPC_UNSTABLE_SPHERE = 28166,
|
||||
SPELL_UNSTABLE_SPHERE_PASSIVE = 50756,
|
||||
SPELL_UNSTABLE_SPHERE_PULSE = 50757,
|
||||
SPELL_UNSTABLE_SPHERE_TIMER = 50758,
|
||||
|
||||
// Drakes
|
||||
NPC_AMBER_DRAKE = 27755,
|
||||
NPC_EMERALD_DRAKE = 27692,
|
||||
NPC_RUBY_DRAKE = 27756,
|
||||
ITEM_AMBER_ESSENCE = 37859,
|
||||
ITEM_EMERALD_ESSENCE = 37815,
|
||||
ITEM_RUBY_ESSENCE = 37860,
|
||||
SPELL_AMBER_ESSENCE = 49461,
|
||||
SPELL_EMERALD_ESSENCE = 49345,
|
||||
SPELL_RUBY_ESSENCE = 49462,
|
||||
// Abilities:
|
||||
// Amber
|
||||
SPELL_SHOCK_LANCE = 49840,
|
||||
SPELL_SHOCK_CHARGE = 49836,
|
||||
SPELL_STOP_TIME = 49838,
|
||||
SPELL_TEMPORAL_RIFT = 49592,
|
||||
// Emerald
|
||||
SPELL_LEECHING_POISON = 50328,
|
||||
SPELL_TOUCH_THE_NIGHTMARE = 50341,
|
||||
SPELL_DREAM_FUNNEL = 50344,
|
||||
// Ruby
|
||||
SPELL_SEARING_WRATH = 50232,
|
||||
SPELL_EVASIVE_MANEUVERS = 50240,
|
||||
SPELL_EVASIVE_CHARGES = 50241,
|
||||
SPELL_MARTYR = 50253,
|
||||
|
||||
// Varos Cloudstrider
|
||||
NPC_CENTRIFUGE_CORE = 28183,
|
||||
|
||||
// Mage-Lord Urom
|
||||
NPC_MAGE_LORD_UROM = 27655,
|
||||
SPELL_TIME_BOMB_N = 51121,
|
||||
SPELL_TIME_BOMB_H = 59376,
|
||||
SPELL_EMPOWERED_ARCANE_EXPLOSION_N = 51110,
|
||||
SPELL_EMPOWERED_ARCANE_EXPLOSION_H = 59377,
|
||||
|
||||
// Ley-Guardian Eregos
|
||||
SPELL_ENRAGED_ASSAULT = 51170,
|
||||
SPELL_PLANAR_SHIFT = 51162,
|
||||
};
|
||||
|
||||
#define SPELL_EMPOWERED_ARCANE_EXPLOSION DUNGEON_MODE(bot, SPELL_EMPOWERED_ARCANE_EXPLOSION_N, SPELL_EMPOWERED_ARCANE_EXPLOSION_H)
|
||||
#define SPELL_TIME_BOMB DUNGEON_MODE(bot, SPELL_TIME_BOMB_N, SPELL_TIME_BOMB_H)
|
||||
|
||||
const std::vector<uint32> DRAKE_ITEMS = {ITEM_AMBER_ESSENCE, ITEM_EMERALD_ESSENCE, ITEM_RUBY_ESSENCE};
|
||||
const std::vector<uint32> DRAKE_SPELLS = {SPELL_AMBER_ESSENCE, SPELL_EMERALD_ESSENCE, SPELL_RUBY_ESSENCE};
|
||||
const uint32 OCULUS_MAP_ID = 578;
|
||||
|
||||
// const float uromCoords[4][4] =
|
||||
// {
|
||||
// {1177.47f, 937.722f, 527.405f, 2.21657f},
|
||||
// {968.66f, 1042.53f, 527.32f, 0.077f},
|
||||
// {1164.02f, 1170.85f, 527.321f, 3.66f},
|
||||
// {1118.31f, 1080.377f, 508.361f, 4.25f} // Inner ring, actual boss fight
|
||||
// };
|
||||
|
||||
class DrakosUnstableSphereTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
DrakosUnstableSphereTrigger(PlayerbotAI* ai) : Trigger(ai, "drakos unstable sphere") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class DrakeMountTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
DrakeMountTrigger(PlayerbotAI* ai) : Trigger(ai, "drake mount") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class DrakeDismountTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
DrakeDismountTrigger(PlayerbotAI* ai) : Trigger(ai, "drake dismount") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GroupFlyingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GroupFlyingTrigger(PlayerbotAI* ai) : Trigger(ai, "drake fly") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class DrakeCombatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
DrakeCombatTrigger(PlayerbotAI* ai) : Trigger(ai, "drake combat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class VarosCloudstriderTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
VarosCloudstriderTrigger(PlayerbotAI* ai) : Trigger(ai, "varos cloudstrider") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class UromArcaneExplosionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
UromArcaneExplosionTrigger(PlayerbotAI* ai) : Trigger(ai, "urom arcane explosion") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class UromTimeBombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
UromTimeBombTrigger(PlayerbotAI* ai) : Trigger(ai, "urom time bomb") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -46,10 +46,11 @@ bool AvoidShadowCrashAction::Execute(Event event)
|
||||
// Could check all enemy units in range as it's possible to pull multiple of these mobs.
|
||||
// They should really be killed 1 by 1, multipulls are messy so we just handle singles for now
|
||||
Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one");
|
||||
if (!unit) { return false; }
|
||||
|
||||
Unit* victim = nullptr;
|
||||
float radius = 10.0f;
|
||||
float targetDist = radius + 2.0f;
|
||||
if (!unit) { return false; }
|
||||
|
||||
// Actively move if targeted by a shadow crash.
|
||||
// Spell check not needed, they don't have any other non-instant casts
|
||||
@@ -58,13 +59,11 @@ bool AvoidShadowCrashAction::Execute(Event event)
|
||||
// This doesn't seem to avoid casts very well, perhaps because this isn't checked while allies are casting.
|
||||
// TODO: Revisit if this is an issue in heroics, otherwise ignore shadow crashes for the most part.
|
||||
victim = botAI->GetUnit(unit->GetTarget());
|
||||
if (!victim)
|
||||
{
|
||||
return false; // Exit early if no victim is found
|
||||
}
|
||||
if (victim && bot->GetExactDist2d(victim) < radius)
|
||||
float distance = bot->GetExactDist2d(victim->GetPosition());
|
||||
|
||||
if (victim && distance < radius)
|
||||
{
|
||||
return MoveAway(victim, targetDist - bot->GetExactDist2d(victim->GetPosition()));
|
||||
return MoveAway(victim, targetDist - distance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,21 +71,13 @@ bool AvoidShadowCrashAction::Execute(Event event)
|
||||
if (botAI->IsMelee(bot)) { return false; }
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
if (members.empty())
|
||||
{
|
||||
return false; // Exit early if no group members are found
|
||||
}
|
||||
for (auto& member : members)
|
||||
{
|
||||
if (bot->GetGUID() == member)
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || bot->GetGUID() == member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Unit* memberUnit = botAI->GetUnit(member);
|
||||
if (!memberUnit)
|
||||
{
|
||||
continue; // Skip if the memberUnit is null
|
||||
}
|
||||
float currentDist = bot->GetExactDist2d(botAI->GetUnit(member));
|
||||
if (currentDist < radius)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user