diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 1e30be99..1acbd662 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1606,6 +1606,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 615: strategyName = "wotlk-os"; // Obsidian Sanctum break; + case 616: + strategyName = "wotlk-eoe"; // Eye Of Eternity + break; case 619: strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 10e9e187..12e741b6 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -26,6 +26,8 @@ #include "raids/naxxramas/RaidNaxxTriggerContext.h" #include "raids/obsidiansanctum/RaidOsActionContext.h" #include "raids/obsidiansanctum/RaidOsTriggerContext.h" +#include "raids/eyeofeternity/RaidEoEActionContext.h" +#include "raids/eyeofeternity/RaidEoETriggerContext.h" #include "raids/moltencore/RaidMcActionContext.h" #include "raids/moltencore/RaidMcTriggerContext.h" #include "raids/aq20/RaidAq20ActionContext.h" @@ -51,6 +53,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidAq20ActionContext()); actionContexts.Add(new RaidNaxxActionContext()); actionContexts.Add(new RaidOsActionContext()); + actionContexts.Add(new RaidEoEActionContext()); actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); @@ -75,6 +78,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidAq20TriggerContext()); triggerContexts.Add(new RaidNaxxTriggerContext()); triggerContexts.Add(new RaidOsTriggerContext()); + triggerContexts.Add(new RaidEoETriggerContext()); triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h index 38cd640b..fc22fdbb 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h @@ -21,8 +21,8 @@ class WotlkDungeonOccActionContext : public NamedObjectContext 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* fly_drake(PlayerbotAI* ai) { return new OccFlyDrakeAction(ai); } + static Action* drake_attack(PlayerbotAI* ai) { return new OccDrakeAttackAction(ai); } static Action* avoid_arcane_explosion(PlayerbotAI* ai) { return new AvoidArcaneExplosionAction(ai); } static Action* time_bomb_spread(PlayerbotAI* ai) { return new TimeBombSpreadAction(ai); } }; diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp b/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp index 115181e2..608ff618 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp @@ -111,7 +111,7 @@ bool DismountDrakeAction::Execute(Event event) return false; } -bool FlyDrakeAction::Execute(Event event) +bool OccFlyDrakeAction::Execute(Event event) { Player* master = botAI->GetMaster(); if (!master) { return false; } @@ -152,7 +152,7 @@ bool FlyDrakeAction::Execute(Event event) return false; } -bool DrakeAttackAction::Execute(Event event) +bool OccDrakeAttackAction::Execute(Event event) { vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) { return false; } @@ -188,7 +188,7 @@ bool DrakeAttackAction::Execute(Event event) return false; } -bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) +bool OccDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) { if (botAI->CanCastVehicleSpell(spellId, target)) if (botAI->CastVehicleSpell(spellId, target)) @@ -199,7 +199,7 @@ bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint3 return false; } -bool DrakeAttackAction::AmberDrakeAction(Unit* target) +bool OccDrakeAttackAction::AmberDrakeAction(Unit* target) { Aura* shockCharges = target->GetAura(SPELL_SHOCK_CHARGE, vehicleBase->GetGUID()); if (shockCharges && shockCharges->GetStackAmount() > 8) @@ -225,7 +225,7 @@ bool DrakeAttackAction::AmberDrakeAction(Unit* target) return false; } -bool DrakeAttackAction::EmeraldDrakeAction(Unit* target) +bool OccDrakeAttackAction::EmeraldDrakeAction(Unit* target) { Aura* poisonStacks = target->GetAura(SPELL_LEECHING_POISON, vehicleBase->GetGUID()); if (!poisonStacks || (poisonStacks->GetStackAmount() < 3 || @@ -286,7 +286,7 @@ bool DrakeAttackAction::EmeraldDrakeAction(Unit* target) return false; } -bool DrakeAttackAction::RubyDrakeAction(Unit* target) +bool OccDrakeAttackAction::RubyDrakeAction(Unit* target) { Aura* evasiveCharges = vehicleBase->GetAura(SPELL_EVASIVE_CHARGES); Aura* evasiveManeuvers = vehicleBase->GetAura(SPELL_EVASIVE_MANEUVERS); diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActions.h b/src/strategy/dungeons/wotlk/oculus/OculusActions.h index 2111461a..6b26b9dd 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActions.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusActions.h @@ -38,17 +38,17 @@ public: bool Execute(Event event) override; }; -class FlyDrakeAction : public MovementAction +class OccFlyDrakeAction : public MovementAction { public: - FlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "fly drake") {} + OccFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "occ fly drake") {} bool Execute(Event event) override; }; -class DrakeAttackAction : public Action +class OccDrakeAttackAction : public Action { public: - DrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "drake attack") {} + OccDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "occ drake attack") {} bool Execute(Event event) override; protected: @@ -57,7 +57,6 @@ protected: bool AmberDrakeAction(Unit* target); bool EmeraldDrakeAction(Unit* target); bool RubyDrakeAction(Unit* target); - }; class AvoidArcaneExplosionAction : public MovementAction diff --git a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp index 023d5256..b30c4db1 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp @@ -26,12 +26,12 @@ float MountingDrakeMultiplier::GetValue(Action* action) return 1.0f; } -float FlyingMultiplier::GetValue(Action* action) +float OccFlyingMultiplier::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(action) && !dynamic_cast(action)) + if (dynamic_cast(action) && !dynamic_cast(action)) { return 0.0f; } @@ -103,7 +103,7 @@ 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(action))) + if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast(action))) { return 0.0f; } diff --git a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h index f2f86f79..423364b0 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h @@ -21,10 +21,10 @@ class MountingDrakeMultiplier : public Multiplier virtual float GetValue(Action* action); }; -class FlyingMultiplier : public Multiplier +class OccFlyingMultiplier : public Multiplier { public: - FlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flying drake") {} + OccFlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "occ flying drake") {} public: virtual float GetValue(Action* action); diff --git a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp index 2b98e7da..9c1786b5 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp @@ -36,7 +36,7 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) void WotlkDungeonOccStrategy::InitMultipliers(std::vector &multipliers) { multipliers.push_back(new MountingDrakeMultiplier(botAI)); - multipliers.push_back(new FlyingMultiplier(botAI)); + multipliers.push_back(new OccFlyingMultiplier(botAI)); multipliers.push_back(new UromMultiplier(botAI)); multipliers.push_back(new EregosMultiplier(botAI)); } diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index 31675f7f..d863aa0a 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -6,6 +6,7 @@ #include "RaidBwlStrategy.h" #include "RaidNaxxStrategy.h" #include "RaidOsStrategy.h" +#include "RaidEoEStrategy.h" #include "RaidMcStrategy.h" #include "RaidAq20Strategy.h" #include "RaidIccStrategy.h" @@ -23,6 +24,7 @@ public: creators["aq20"] = &RaidStrategyContext::aq20; creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; + creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["uld"] = &RaidStrategyContext::uld; creators["icc"] = &RaidStrategyContext::icc; } @@ -33,6 +35,7 @@ private: static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } + static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); } }; diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h new file mode 100644 index 00000000..ba4d841d --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h @@ -0,0 +1,30 @@ +#ifndef _PLAYERBOT_RAIDEOEACTIONCONTEXT_H +#define _PLAYERBOT_RAIDEOEACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidEoEActions.h" + +class RaidEoEActionContext : public NamedObjectContext +{ +public: + RaidEoEActionContext() + { + creators["malygos position"] = &RaidEoEActionContext::position; + creators["malygos target"] = &RaidEoEActionContext::target; + // creators["pull power spark"] = &RaidEoEActionContext::pull_power_spark; + // creators["kill power spark"] = &RaidEoEActionContext::kill_power_spark; + creators["fly drake"] = &RaidEoEActionContext::fly_drake; + creators["drake attack"] = &RaidEoEActionContext::drake_attack; + } + +private: + static Action* position(PlayerbotAI* ai) { return new MalygosPositionAction(ai); } + static Action* target(PlayerbotAI* ai) { return new MalygosTargetAction(ai); } + // static Action* pull_power_spark(PlayerbotAI* ai) { return new PullPowerSparkAction(ai); } + // static Action* kill_power_spark(PlayerbotAI* ai) { return new KillPowerSparkAction(ai); } + static Action* fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); } + static Action* drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); } +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp new file mode 100644 index 00000000..4bacf89e --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp @@ -0,0 +1,390 @@ +#include "RaidEoEActions.h" +#include "RaidEoETriggers.h" + +#include "Playerbots.h" + + +bool MalygosPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + + float distance = 5.0f; + + if (phase == 1) + { + Unit* spark = nullptr; + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_POWER_SPARK) + { + spark = unit; + break; + } + } + + // Position tank + if (botAI->IsMainTank(bot)) + { + if (bot->GetDistance2d(MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second) > distance) + { + return MoveTo(EOE_MAP_ID, MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + // Position DK for spark pull + // else if (spark && bot->IsClass(CLASS_DEATH_KNIGHT)) + // { + // if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > distance) + // { + // bot->Yell("SPARK SPAWNED, MOVING TO STACK", LANG_UNIVERSAL); + // return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(), + // false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + // } + // return false; + // } + else if (spark) + { + return false; + } + else if (!bot->IsClass(CLASS_HUNTER)) + { + if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > (distance * 3.0f)) + { + return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + } + + return false; +} + +bool MalygosTargetAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + + if (phase == 1) + { + if (botAI->IsHeal(bot)) { return false; } + + Unit* newTarget = boss; + Unit* spark = nullptr; + + // GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + // for (auto& target : targets) + // { + // Unit* unit = botAI->GetUnit(target); + // if (unit && unit->GetEntry() == NPC_POWER_SPARK) + // { + // spark = unit; + // break; + // } + // } + + // if (spark && botAI->IsRangedDps(bot)) + // { + // newTarget = spark; + // } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + + if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry()) + { + return Attack(newTarget); + } + } + else if (phase == 2) + { + if (botAI->IsHeal(bot)) { return false; } + + Unit* newTarget = nullptr; + Unit* nexusLord = nullptr; + Unit* scionOfEternity = nullptr; + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (!unit) { continue; } + + if (unit->GetEntry() == NPC_NEXUS_LORD) + { + nexusLord = unit; + } + else if (unit->GetEntry() == NPC_SCION_OF_ETERNITY) + { + scionOfEternity = unit; + } + } + + if (botAI->IsRangedDps(bot) && scionOfEternity) + { + newTarget = scionOfEternity; + } + else + { + newTarget = nexusLord; + } + + if (!newTarget) { return false; } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry()) + { + return Attack(newTarget); + } + } + + // else if (phase == 3) + // {} + + return false; +} + +// bool PullPowerSparkAction::Execute(Event event) +// { +// Unit* spark = nullptr; + +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) +// { +// Unit* unit = botAI->GetUnit(target); +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) +// { +// spark = unit; +// break; +// } +// } + +// if (!spark) { return false; } + +// if (spark->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > 3.0f) +// { +// bot->Yell("GRIPPING SPARK", LANG_UNIVERSAL); +// return botAI->CastSpell("death grip", spark); +// } + +// return false; +// } + +// bool PullPowerSparkAction::isPossible() +// { +// Unit* spark = nullptr; + +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) +// { +// Unit* unit = botAI->GetUnit(target); +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) +// { +// spark = unit; +// break; +// } +// } + +// return botAI->CanCastSpell(spell, spark); +// } + +// bool PullPowerSparkAction::isUseful() +// { +// Unit* spark = nullptr; + +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) +// { +// Unit* unit = botAI->GetUnit(target); +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) +// { +// spark = unit; +// break; +// } +// } + +// if (!spark) +// return false; + +// if (!spark->IsInWorld() || spark->GetMapId() != bot->GetMapId()) +// return false; + +// return bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) < 3.0f; +// } + +// bool KillPowerSparkAction::Execute(Event event) +// { +// return false; +// } + +bool EoEFlyDrakeAction::isPossible() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); +} +bool EoEFlyDrakeAction::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", "malygos"); + if (boss && false) + { + // 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) > 5.0f) + { + uint8 numPlayers; + bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numPlayers = 25 : numPlayers = 10; + // 3/4 of a circle, with frontal cone 90 deg unobstructed + float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2; + // float angle = M_PI; + vehicleBase->SetCanFly(true); + mm->MoveFollow(masterVehicle, 3.0f, angle); + vehicleBase->SendMovementFlagUpdate(); + return true; + } + return false; +} + +bool EoEDrakeAttackAction::isPossible() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); +} +bool EoEDrakeAttackAction::Execute(Event event) +{ + vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + // Unit* target = AI_VALUE(Unit*, "current target"); + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + // if (!boss) { return false; } + + if (!boss) + { + GuidVector npcs = AI_VALUE(GuidVector, "possible targets"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || unit->GetEntry() != NPC_MALYGOS) { continue; } + + boss = unit; + break; + } + } + // Check this again to see if a target was assigned + if (!boss) { return false; } + + if (botAI->IsHeal(bot)) + { + return DrakeHealAction(); + } + else + { + return DrakeDpsAction(boss); + } + + return false; +} + +bool EoEDrakeAttackAction::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 EoEDrakeAttackAction::DrakeDpsAction(Unit* target) +{ + Unit* vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + Vehicle* veh = bot->GetVehicle(); + + uint8 comboPoints = vehicleBase->GetComboPoints(target); + if (comboPoints >= 2) + { + return CastDrakeSpellAction(target, SPELL_ENGULF_IN_FLAMES, 0); + } + else + { + return CastDrakeSpellAction(target, SPELL_FLAME_SPIKE, 0); + } +} + +bool EoEDrakeAttackAction::DrakeHealAction() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + Unit* target = vehicleBase->GetComboTarget(); + if (!target) + { + // Unit* newTarget = nullptr; + Unit* newTarget = vehicleBase; + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + Unit* unit = botAI->GetUnit(member); + if (!unit) + { + continue; + } + + Unit* drake = unit->GetVehicleBase(); + if (!drake || drake->IsFullHealth()) { continue; } + + if (!newTarget || drake->GetHealthPct() < newTarget->GetHealthPct() - 5.0f) + { + newTarget = drake; + } + } + target = newTarget; + } + + uint8 comboPoints = vehicleBase->GetComboPoints(target); + if (comboPoints >= 5) + { + return CastDrakeSpellAction(target, SPELL_LIFE_BURST, 0); + } + else + { + // "Revivify" may be bugged server-side: + // "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes. + // Forcing the cast attempt seems to succeed, not sure what's going on here. + // return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0); + return botAI->CastVehicleSpell(SPELL_REVIVIFY, target); + } +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.h b/src/strategy/raids/eyeofeternity/RaidEoEActions.h new file mode 100644 index 00000000..d2d158c7 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.h @@ -0,0 +1,69 @@ +#ifndef _PLAYERBOT_RAIDEOEACTIONS_H +#define _PLAYERBOT_RAIDEOEACTIONS_H + +#include "MovementActions.h" +#include "AttackAction.h" +#include "GenericSpellActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +const std::pair MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f}; +const std::pair MALYGOS_STACK_POSITION = {755.0f, 1301.0f}; + +class MalygosPositionAction : public MovementAction +{ +public: + MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class MalygosTargetAction : public AttackAction +{ +public: + MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class PullPowerSparkAction : public CastSpellAction +{ +public: + PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark") + : CastSpellAction(botAI, "death grip") {} + bool Execute(Event event) override; + bool isPossible() override; + bool isUseful() override; +}; + +class KillPowerSparkAction : public AttackAction +{ +public: + KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class EoEFlyDrakeAction : public MovementAction +{ +public: + EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {} + bool Execute(Event event) override; + bool isPossible() override; +}; + +class EoEDrakeAttackAction : public Action +{ +public: + EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {} + bool Execute(Event event) override; + bool isPossible() override; + +protected: + Unit* vehicleBase; + bool CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown); + bool DrakeDpsAction(Unit* target); + bool DrakeHealAction(); +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp new file mode 100644 index 00000000..0866bfb1 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp @@ -0,0 +1,114 @@ +#include "RaidEoEMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "RaidEoEActions.h" +#include "RaidEoETriggers.h" +#include "ReachTargetActions.h" +#include "ScriptedCreature.h" +#include "WarriorActions.h" + +// float SartharionMultiplier::GetValue(Action* action) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return 1.0f; } + +// Unit* target = action->GetTarget(); + +// if (botAI->IsMainTank(bot) && dynamic_cast(action)) +// { +// // return 0.0f; +// } + +// if (botAI->IsDps(bot) && dynamic_cast(action)) +// { +// return 0.0f; +// } + +// if (botAI->IsMainTank(bot) && target && target != boss && +// (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || +// dynamic_cast(action) || dynamic_cast(action))) +// { +// return 0.0f; +// } + +// if (botAI->IsAssistTank(bot) && target && target == boss && +// (dynamic_cast(action) || dynamic_cast(action) || +// dynamic_cast(action) || dynamic_cast(action))) +// { +// return 0.0f; +// } +// return 1.0f; +// } + +float MalygosMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + if (phase == 0) { return 1.0f; } + + if (phase == 1) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsRangedDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (!botAI->IsMainTank(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + // if (dynamic_cast(action) && !dynamic_cast(action)) + // { + // return 0.0f; + // } + } + else if (phase == 2) + { + if (botAI->IsDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (dynamic_cast(action)) + { + Unit* target = action->GetTarget(); + if (target && target->GetEntry() == NPC_SCION_OF_ETERNITY) + return 0.0f; + } + } + else if (phase == 3) + { + // Suppresses FollowAction as well as some attack-based movements + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + + return 1.0f; +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h new file mode 100644 index 00000000..05db5ee9 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h @@ -0,0 +1,16 @@ + +#ifndef _PLAYERRBOT_RAIDEOEMULTIPLIERS_H +#define _PLAYERRBOT_RAIDEOEMULTIPLIERS_H + +#include "Multiplier.h" + +class MalygosMultiplier : public Multiplier +{ +public: + MalygosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "malygos") {} + +public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp new file mode 100644 index 00000000..f30cf30a --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp @@ -0,0 +1,25 @@ +#include "RaidEoEStrategy.h" +#include "RaidEoEMultipliers.h" +#include "Strategy.h" + +void RaidEoEStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("malygos", + NextAction::array(0, new NextAction("malygos position", ACTION_MOVE), nullptr))); + triggers.push_back(new TriggerNode("malygos", + NextAction::array(0, new NextAction("malygos target", ACTION_RAID + 1), nullptr))); + // triggers.push_back(new TriggerNode("power spark", + // NextAction::array(0, new NextAction("pull power spark", ACTION_RAID + 2), nullptr))); + // triggers.push_back(new TriggerNode("power spark", + // NextAction::array(0, new NextAction("kill power spark", ACTION_RAID + 3), 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))); +} + +void RaidEoEStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new MalygosMultiplier(botAI)); +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h new file mode 100644 index 00000000..eb7a147b --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_RAIDEOESTRATEGY_H +#define _PLAYERBOT_RAIDEOESTRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidEoEStrategy : public Strategy +{ +public: + RaidEoEStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "wotlk-eoe"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h new file mode 100644 index 00000000..0c58f6cb --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidEoETriggers.h" + +class RaidEoETriggerContext : public NamedObjectContext +{ +public: + RaidEoETriggerContext() + { + creators["malygos"] = &RaidEoETriggerContext::malygos; + creators["power spark"] = &RaidEoETriggerContext::power_spark; + } + +private: + static Trigger* power_spark(PlayerbotAI* ai) { return new PowerSparkTrigger(ai); } + static Trigger* malygos(PlayerbotAI* ai) { return new MalygosTrigger(ai); } +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp new file mode 100644 index 00000000..2506ea1f --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp @@ -0,0 +1,53 @@ +#include "RaidEoETriggers.h" + +#include "SharedDefines.h" + +uint8 MalygosTrigger::getPhase(Player* bot, Unit* boss) +{ + uint8 phase = 0; + Unit* vehicle = bot->GetVehicleBase(); + if (bot->GetMapId() != EOE_MAP_ID) { return phase; } + + if (vehicle && vehicle->GetEntry() == NPC_WYRMREST_SKYTALON) + { + phase = 3; + } + else if (boss && boss->HealthAbovePct(50)) + { + phase = 1; + } + else if (boss) + { + phase = 2; + } + + return phase; +} + +bool MalygosTrigger::IsActive() +{ + return bool(AI_VALUE2(Unit*, "find target", "malygos")); +} + +bool PowerSparkTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + if (bot->getClass() != CLASS_DEATH_KNIGHT) + { + return false; + } + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_POWER_SPARK) + { + return true; + } + } + + return false; +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.h b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h new file mode 100644 index 00000000..9262c114 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h @@ -0,0 +1,58 @@ +#ifndef _PLAYERBOT_RAIDEOETRIGGERS_H +#define _PLAYERBOT_RAIDEOETRIGGERS_H + +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Trigger.h" + +enum EyeOfEternityIDs +{ + NPC_MALYGOS = 28859, + NPC_POWER_SPARK = 30084, + NPC_NEXUS_LORD = 30245, + NPC_SCION_OF_ETERNITY = 30249, + NPC_WYRMREST_SKYTALON = 30161, + + SPELL_POWER_SPARK_VISUAL = 55845, + SPELL_POWER_SPARK_GROUND_BUFF = 55852, + SPELL_POWER_SPARK_MALYGOS_BUFF = 56152, + + SPELL_TELEPORT_VISUAL = 52096, + + SPELL_SCION_ARCANE_BARRAGE = 56397, + SPELL_ARCANE_SHOCK_N = 57058, + SPELL_ARCANE_SHOCK_H = 60073, + SPELL_HASTE = 57060, + + SPELL_ALEXSTRASZA_GIFT = 61028, + + // Drake Abilities: + // DPS + SPELL_FLAME_SPIKE = 56091, + SPELL_ENGULF_IN_FLAMES = 56092, + // Healing + SPELL_REVIVIFY = 57090, + SPELL_LIFE_BURST = 57143, + // Utility + SPELL_FLAME_SHIELD = 57108, + SPELL_BLAZING_SPEED = 57092, +}; + +const uint32 EOE_MAP_ID = 616; + +class MalygosTrigger : public Trigger +{ +public: + MalygosTrigger(PlayerbotAI* botAI) : Trigger(botAI, "malygos") {} + bool IsActive() override; + uint8 static getPhase(Player* bot, Unit* boss); +}; + +class PowerSparkTrigger : public Trigger +{ +public: + PowerSparkTrigger(PlayerbotAI* botAI) : Trigger(botAI, "power spark") {} + bool IsActive() override; +}; + +#endif