From 9c816b682a159afaa04f9a3e803d50895cc0c5f4 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Thu, 17 Apr 2025 05:50:38 +0200 Subject: [PATCH] Kologarn strategy (#1205) * - Added triggers and action for kologarn fight * - code refactoring * - Added nature resistance aura for kologarn --- .../raids/ulduar/RaidUlduarActionContext.h | 6 + .../raids/ulduar/RaidUlduarActions.cpp | 191 ++++++++++++++++-- src/strategy/raids/ulduar/RaidUlduarActions.h | 24 +++ .../raids/ulduar/RaidUlduarStrategy.cpp | 15 ++ .../raids/ulduar/RaidUlduarTriggerContext.h | 6 + .../raids/ulduar/RaidUlduarTriggers.cpp | 150 ++++++++++++-- .../raids/ulduar/RaidUlduarTriggers.h | 39 ++++ 7 files changed, 390 insertions(+), 41 deletions(-) diff --git a/src/strategy/raids/ulduar/RaidUlduarActionContext.h b/src/strategy/raids/ulduar/RaidUlduarActionContext.h index a801735e..49823169 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActionContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarActionContext.h @@ -26,6 +26,9 @@ public: creators["razorscale fuse armor action"] = &RaidUlduarActionContext::razorscale_fuse_armor_action; creators["iron assembly lightning tendrils action"] = &RaidUlduarActionContext::iron_assembly_lightning_tendrils_action; creators["iron assembly overload action"] = &RaidUlduarActionContext::iron_assembly_overload_action; + creators["kologarn mark dps target action"] = &RaidUlduarActionContext::kologarn_mark_dps_target_action; + creators["kologarn fall from floor action"] = &RaidUlduarActionContext::kologarn_fall_from_floor_action; + creators["kologarn nature resistance action"] = &RaidUlduarActionContext::kologarn_nature_resistance_action; creators["hodir move snowpacked icicle"] = &RaidUlduarActionContext::hodir_move_snowpacked_icicle; creators["hodir biting cold jump"] = &RaidUlduarActionContext::hodir_biting_cold_jump; creators["freya move away nature bomb"] = &RaidUlduarActionContext::freya_move_away_nature_bomb; @@ -44,6 +47,9 @@ private: static Action* razorscale_fuse_armor_action(PlayerbotAI* ai) { return new RazorscaleFuseArmorAction(ai); } static Action* iron_assembly_lightning_tendrils_action(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsAction(ai); } static Action* iron_assembly_overload_action(PlayerbotAI* ai) { return new IronAssemblyOverloadAction(ai); } + static Action* kologarn_mark_dps_target_action(PlayerbotAI* ai) { return new KologarnMarkDpsTargetAction(ai); } + static Action* kologarn_fall_from_floor_action(PlayerbotAI* ai) { return new KologarnFallFromFloorAction(ai); } + static Action* kologarn_nature_resistance_action(PlayerbotAI* ai) { return new KologarnNatureResistanceAction(ai); } static Action* hodir_move_snowpacked_icicle(PlayerbotAI* ai) { return new HodirMoveSnowpackedIcicleAction(ai); } static Action* hodir_biting_cold_jump(PlayerbotAI* ai) { return new HodirBitingColdJumpAction(ai); } static Action* freya_move_away_nature_bomb(PlayerbotAI* ai) { return new FreyaMoveAwayNatureBombAction(ai); } diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.cpp b/src/strategy/raids/ulduar/RaidUlduarActions.cpp index 33759ae4..4cb492c7 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarActions.cpp @@ -12,7 +12,6 @@ #include "ObjectGuid.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" -#include "Player.h" #include "Playerbots.h" #include "Position.h" #include "RaidUlduarBossHelper.h" @@ -25,6 +24,7 @@ #include "SharedDefines.h" #include "Unit.h" #include "Vehicle.h" +#include const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, @@ -33,6 +33,9 @@ const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED const std::vector corners = { {183.53f, 66.53f, 409.80f}, {383.03f, 75.10f, 411.71f}, {379.74f, -133.05f, 410.88f}, {158.67f, -137.54f, 409.80f}}; +const Position ULDUAR_KOLOGARN_CRUNCH_ARMOR_RESET_SPOT = Position(1752.4803f, -43.44299f, 448.805f); +const Position ULDUAR_KOLOGARN_RESTORE_POSITION = Position(1764.3749f, -24.02903f, 448.0f, 0.00087690353); + bool FlameLeviathanVehicleAction::Execute(Event event) { vehicleBase_ = bot->GetVehicleBase(); @@ -273,14 +276,14 @@ bool FlameLeviathanEnterVehicleAction::Execute(Event event) if (!ShouldEnter(vehicleBase)) continue; - + if (!vehicleToEnter || bot->GetExactDist(vehicleToEnter) > bot->GetExactDist(vehicleBase)) vehicleToEnter = vehicleBase; } if (!vehicleToEnter) return false; - + if (EnterVehicle(vehicleToEnter, true)) return true; @@ -290,7 +293,7 @@ bool FlameLeviathanEnterVehicleAction::Execute(Event event) bool FlameLeviathanEnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) { float dist = bot->GetDistance(vehicleBase); - + if (dist > INTERACTION_DISTANCE && !moveIfFar) return false; @@ -517,7 +520,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event) // Check if the main tank is a human player Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; - + if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player { // Iterate through the first 3 bot tanks to assign the Skull marker @@ -530,7 +533,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event) { int8 skullIndex = 7; // Skull ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex); - + // If there's no skull set yet, or the skull is on a different target, set the sentinel if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget)) { @@ -548,7 +551,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event) { int8 skullIndex = 7; // Skull ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex); - + // If there's no skull set yet, or the skull is on a different target, set the sentinel if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget)) { @@ -566,13 +569,13 @@ bool RazorscaleAvoidSentinelAction::isUseful() bool isMainTank = botAI->IsMainTank(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; - + // If this bot is the main tank, it should always try to mark if (isMainTank) { return true; } - + // If the main tank is a human, check if this bot is one of the first three valid bot tanks if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player { @@ -586,7 +589,7 @@ bool RazorscaleAvoidSentinelAction::isUseful() } bool isRanged = botAI->IsRanged(bot); - const float radius = 8.0f; + const float radius = 8.0f; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) @@ -596,7 +599,7 @@ bool RazorscaleAvoidSentinelAction::isUseful() { if (isRanged && bot->GetDistance2d(unit) < radius) { - return true; + return true; } } } @@ -647,7 +650,7 @@ bool RazorscaleAvoidWhirlwindAction::isUseful() { if (bot->GetDistance2d(unit) < radius) { - return true; + return true; } } } @@ -679,7 +682,7 @@ bool RazorscaleIgnoreBossAction::isUseful() { return false; } - + Group* group = bot->GetGroup(); if (!group) { @@ -843,7 +846,7 @@ bool RazorscaleGroundedAction::isUseful() if (mainTank) { - constexpr float maxDistance = 2.0f; + constexpr float maxDistance = 2.0f; float distanceToMainTank = bot->GetDistance2d(mainTank); return (distanceToMainTank > maxDistance); } @@ -899,7 +902,7 @@ bool RazorscaleGroundedAction::Execute(Event event) Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; - + if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player { // Iterate through the first 3 bot tanks to handle the moon marker @@ -909,7 +912,7 @@ bool RazorscaleGroundedAction::Execute(Event event) { int8 moonIndex = 4; ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); - + // If the moon marker is set to the boss, reset it if (currentMoonTarget == boss->GetGUID()) { @@ -924,7 +927,7 @@ bool RazorscaleGroundedAction::Execute(Event event) { int8 moonIndex = 4; ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); - + // If the moon marker is set to the boss, reset it if (currentMoonTarget == boss->GetGUID()) { @@ -934,12 +937,10 @@ bool RazorscaleGroundedAction::Execute(Event event) } } - if (mainTank && (botAI->IsTank(bot) && !botAI->IsMainTank(bot))) { - - constexpr float followDistance = 2.0f; - return MoveNear(mainTank, followDistance, MovementPriority::MOVEMENT_COMBAT); + constexpr float followDistance = 2.0f; + return MoveNear(mainTank, followDistance, MovementPriority::MOVEMENT_COMBAT); } if (botAI->IsRanged(bot)) @@ -1214,6 +1215,154 @@ bool IronAssemblyOverloadAction::Execute(Event event) return false; } +bool KologarnMarkDpsTargetAction::isUseful() +{ + KologarnMarkDpsTargetTrigger kologarnMarkDpsTargetTrigger(botAI); + return kologarnMarkDpsTargetTrigger.IsActive(); +} + +bool KologarnMarkDpsTargetAction::Execute(Event event) +{ + Unit* targetToMark = nullptr; + Unit* targetToCcMark = nullptr; + int8 skullIndex = 7; // Skull + int8 moonIndex = 4; // Moon + + // Check that there is rubble to mark + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + Unit* target = nullptr; + for (auto i = targets.begin(); i != targets.end(); ++i) + { + target = botAI->GetUnit(*i); + if (!target) + continue; + + uint32 creatureId = target->GetEntry(); + if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) + { + targetToMark = target; + } + } + + if (!targetToMark) + { + Unit* rightArm = AI_VALUE2(Unit*, "find target", "right arm"); + if (rightArm && rightArm->IsAlive()) + { + targetToMark = rightArm; + } + } + + if (!targetToMark) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn"); + if (boss && boss->IsAlive()) + { + targetToMark = boss; + } + } + + if (!targetToMark) + { + return false; // No target to mark + } + + Unit* leftArm = AI_VALUE2(Unit*, "find target", "left arm"); + if (leftArm && leftArm->IsAlive()) + { + targetToCcMark = leftArm; + } + + bool isMainTank = botAI->IsMainTank(bot); + Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); + Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; + + if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player + { + // Iterate through the first 3 bot tanks to assign the Skull marker + for (int i = 0; i < 3; ++i) + { + if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + { + Group* group = bot->GetGroup(); + if (group) + { + group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); + if (targetToCcMark) + { + group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); + } + + return true; + } + break; // Stop after finding the first valid bot tank + } + } + } + else if (isMainTank && bot->IsAlive()) // Bot is the main tank + { + Group* group = bot->GetGroup(); + if (group) + { + group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); + if (targetToCcMark) + { + group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); + } + return true; + } + } + else + { + for (int i = 0; i < 3; ++i) + { + if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot) && bot->IsAlive()) // Bot is a valid tank + { + Group* group = bot->GetGroup(); + if (group) + { + group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); + if (targetToCcMark) + { + group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); + } + return true; + } + break; // Stop after finding the first valid bot tank + } + } + } + + return false; +} + +bool KologarnFallFromFloorAction::Execute(Event event) +{ + return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), + ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(), + ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(), + ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation()); +} + +bool KologarnFallFromFloorAction::isUseful() +{ + KologarnFallFromFloorTrigger kologarnFallFromFloorTrigger(botAI); + return kologarnFallFromFloorTrigger.IsActive(); +} + +bool KologarnNatureResistanceAction::Execute(Event event) +{ + HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI); + botAI->ChangeStrategy(std::string("+") + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); + return true; +} + +bool KologarnNatureResistanceAction::isUseful() +{ + KologarnNatureResistanceTrigger kologarnNatureResistanceTrigger(botAI); + return kologarnNatureResistanceTrigger.IsActive(); +} + bool HodirMoveSnowpackedIcicleAction::isUseful() { // Check boss and it is alive diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.h b/src/strategy/raids/ulduar/RaidUlduarActions.h index d0a362e2..afad5121 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.h +++ b/src/strategy/raids/ulduar/RaidUlduarActions.h @@ -130,6 +130,30 @@ public: bool isUseful() override; }; +class KologarnMarkDpsTargetAction : public Action +{ +public: + KologarnMarkDpsTargetAction(PlayerbotAI* botAI) : Action(botAI, "kologarn mark dps target action") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class KologarnFallFromFloorAction : public Action +{ +public: + KologarnFallFromFloorAction(PlayerbotAI* botAI) : Action(botAI, "kologarn fall from floor action") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class KologarnNatureResistanceAction : public Action +{ +public: + KologarnNatureResistanceAction(PlayerbotAI* botAI) : Action(botAI, "kologarn nature resistance action") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + class HodirBitingColdJumpAction : public MovementAction { public: diff --git a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp index a873768b..aedd246b 100644 --- a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp @@ -57,6 +57,21 @@ void RaidUlduarStrategy::InitTriggers(std::vector& triggers) "iron assembly overload trigger", NextAction::array(0, new NextAction("iron assembly overload action", ACTION_RAID), nullptr))); + // + // Kologarn + // + triggers.push_back(new TriggerNode( + "kologarn fall from floor trigger", + NextAction::array(0, new NextAction("kologarn fall from floor action", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode( + "kologarn mark dps target trigger", + NextAction::array(0, new NextAction("kologarn mark dps target action", ACTION_RAID), nullptr))); + + triggers.push_back(new TriggerNode( + "kologarn nature resistance trigger", + NextAction::array(0, new NextAction("kologarn nature resistance action", ACTION_RAID), nullptr))); + // // Hodir // diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h index 953d5eda..91dd1719 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h @@ -26,6 +26,9 @@ public: creators["razorscale fuse armor trigger"] = &RaidUlduarTriggerContext::razorscale_fuse_armor_trigger; creators["iron assembly lightning tendrils trigger"] = &RaidUlduarTriggerContext::iron_assembly_lightning_tendrils_trigger; creators["iron assembly overload trigger"] = &RaidUlduarTriggerContext::iron_assembly_overload_trigger; + creators["kologarn mark dps target trigger"] = &RaidUlduarTriggerContext::kologarn_mark_dps_target_trigger; + creators["kologarn fall from floor trigger"] = &RaidUlduarTriggerContext::kologarn_fall_from_floor_trigger; + creators["kologarn nature resistance trigger"] = &RaidUlduarTriggerContext::kologarn_nature_resistance_trigger; creators["hodir biting cold"] = &RaidUlduarTriggerContext::hodir_biting_cold; creators["hodir near snowpacked icicle"] = &RaidUlduarTriggerContext::hodir_near_snowpacked_icicle; creators["freya near nature bomb"] = &RaidUlduarTriggerContext::freya_near_nature_bomb; @@ -44,6 +47,9 @@ private: static Trigger* razorscale_fuse_armor_trigger(PlayerbotAI* ai) { return new RazorscaleFuseArmorTrigger(ai); } static Trigger* iron_assembly_lightning_tendrils_trigger(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsTrigger(ai); } static Trigger* iron_assembly_overload_trigger(PlayerbotAI* ai) { return new IronAssemblyOverloadTrigger(ai); } + static Trigger* kologarn_mark_dps_target_trigger(PlayerbotAI* ai) { return new KologarnMarkDpsTargetTrigger(ai); } + static Trigger* kologarn_fall_from_floor_trigger(PlayerbotAI* ai) { return new KologarnFallFromFloorTrigger(ai); } + static Trigger* kologarn_nature_resistance_trigger(PlayerbotAI* ai) { return new KologarnNatureResistanceTrigger(ai); } static Trigger* hodir_biting_cold(PlayerbotAI* ai) { return new HodirBitingColdTrigger(ai); } static Trigger* hodir_near_snowpacked_icicle(PlayerbotAI* ai) { return new HodirNearSnowpackedIcicleTrigger(ai); } static Trigger* freya_near_nature_bomb(PlayerbotAI* ai) { return new FreyaNearNatureBombTrigger(ai); } diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp index 61ccdf6a..1c339913 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp @@ -11,6 +11,7 @@ #include "SharedDefines.h" #include "Trigger.h" #include "Vehicle.h" +#include const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, @@ -36,11 +37,11 @@ bool FlameLeviathanVehicleNearTrigger::IsActive() { if (bot->GetVehicle()) return false; - + Player* master = botAI->GetMaster(); if (!master) return false; - + if (!master->GetVehicle()) return false; @@ -50,22 +51,22 @@ bool FlameLeviathanVehicleNearTrigger::IsActive() bool RazorscaleFlyingAloneTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); - if (!boss) + if (!boss) { - return false; + return false; } - + // Check if the boss is flying if (boss->GetPositionZ() < RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) { - return false; + return false; } // Get the list of attackers GuidVector attackers = context->GetValue("attackers")->Get(); if (attackers.empty()) { - return true; // No attackers implies flying alone + return true; // No attackers implies flying alone } std::vector dark_rune_adds; @@ -80,8 +81,8 @@ bool RazorscaleFlyingAloneTrigger::IsActive() uint32 entry = unit->GetEntry(); // Check for valid dark rune entries - if (entry == RazorscaleBossHelper::UNIT_DARK_RUNE_WATCHER || - entry == RazorscaleBossHelper::UNIT_DARK_RUNE_GUARDIAN || + if (entry == RazorscaleBossHelper::UNIT_DARK_RUNE_WATCHER || + entry == RazorscaleBossHelper::UNIT_DARK_RUNE_GUARDIAN || entry == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL) { dark_rune_adds.push_back(unit); @@ -92,11 +93,10 @@ bool RazorscaleFlyingAloneTrigger::IsActive() return dark_rune_adds.empty(); } - bool RazorscaleDevouringFlamesTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); - if (!boss) + if (!boss) return false; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -115,7 +115,7 @@ bool RazorscaleDevouringFlamesTrigger::IsActive() bool RazorscaleAvoidSentinelTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); - if (!boss) + if (!boss) return false; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -134,7 +134,7 @@ bool RazorscaleAvoidSentinelTrigger::IsActive() bool RazorscaleAvoidWhirlwindTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); - if (!boss) + if (!boss) return false; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -142,7 +142,8 @@ bool RazorscaleAvoidWhirlwindTrigger::IsActive() { Unit* unit = botAI->GetUnit(npc); if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL && - (unit->HasAura(RazorscaleBossHelper::SPELL_SENTINEL_WHIRLWIND) || unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL))) + (unit->HasAura(RazorscaleBossHelper::SPELL_SENTINEL_WHIRLWIND) || + unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL))) { return true; } @@ -154,15 +155,15 @@ bool RazorscaleAvoidWhirlwindTrigger::IsActive() bool RazorscaleGroundedTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); - if (!boss) + if (!boss) { - return false; + return false; } - + // Check if the boss is flying if (boss->GetPositionZ() < RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) { - return true; + return true; } return false; } @@ -201,7 +202,7 @@ bool RazorscaleHarpoonAvailableTrigger::IsActive() { if (RazorscaleBossHelper::IsHarpoonReady(harpoonGO)) { - return true; // At least one harpoon is available and ready to be fired + return true; // At least one harpoon is available and ready to be fired } } } @@ -277,6 +278,115 @@ bool IronAssemblyOverloadTrigger::IsActive() boss->HasAura(SPELL_OVERLOAD_10_MAN_2) || boss->HasAura(SPELL_OVERLOAD_25_MAN_2); } +bool KologarnMarkDpsTargetTrigger::IsActive() +{ + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn"); + if (!boss || !boss->IsAlive()) + return false; + + // Only tank bot can mark target + if (!botAI->IsTank(bot)) + return false; + + // Get current raid dps target + Group* group = bot->GetGroup(); + if (!group) + return false; + + int8 skullIndex = 7; + ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex); + Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); + + // Check that rubble is marked + if (currentSkullUnit && currentSkullUnit->IsAlive() && currentSkullUnit->GetEntry() == NPC_RUBBLE) + { + return false; // Skull marker is already set on rubble + } + + // Check that there is rubble to mark + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + Unit* target = nullptr; + for (auto i = targets.begin(); i != targets.end(); ++i) + { + target = botAI->GetUnit(*i); + if (!target) + continue; + + uint32 creatureId = target->GetEntry(); + if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) + { + return true; // Found a rubble to mark + } + } + + // Check that right arm is marked + if (currentSkullUnit && currentSkullUnit->IsAlive() && currentSkullUnit->GetEntry() == NPC_RIGHT_ARM) + { + return false; // Skull marker is already set on right arm + } + + // Check that there is right arm to mark + Unit* rightArm = AI_VALUE2(Unit*, "find target", "right arm"); + if (rightArm && rightArm->IsAlive()) + { + return true; // Found a right arm to mark + } + + // Check that main body is marked + if (currentSkullUnit && currentSkullUnit->IsAlive() && currentSkullUnit->GetEntry() == NPC_KOLOGARN) + { + return false; // Skull marker is already set on main body + } + + // Main body is not marked + return true; +} + +bool KologarnFallFromFloorTrigger::IsActive() +{ + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn"); + if (!boss || !boss->IsAlive()) + { + return false; + } + + // Check if bot is on the floor + return bot->GetPositionZ() < ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT; +} + +bool KologarnNatureResistanceTrigger::IsActive() +{ + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn"); + if (!boss || !boss->IsAlive()) + return false; + + // Check if bot is alive + if (!bot->IsAlive()) + return false; + + // Check if bot is hunter + if (bot->getClass() != CLASS_HUNTER) + return false; + + // Check if bot have nature resistance aura + if (bot->HasAura(SPELL_ASPECT_OF_THE_WILD)) + return false; + + // Check if bot dont have already setted nature resistance aura + HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI); + if (botAI->HasStrategy(hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT)) + return false; + + // Check that the bot actually knows Aspect of the Wild + if (!bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD)) + return false; + + return true; +} + bool HodirBitingColdTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "hodir"); @@ -295,7 +405,7 @@ bool HodirBitingColdTrigger::IsActive() !botAI->GetAura("biting cold", master, false, false, 2); } -//Snowpacked Icicle Target +// Snowpacked Icicle Target bool HodirNearSnowpackedIcicleTrigger::IsActive() { // Check boss and it is alive diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.h b/src/strategy/raids/ulduar/RaidUlduarTriggers.h index 06d145d2..2c097dd2 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.h @@ -17,6 +17,16 @@ enum UlduarIDs SPELL_OVERLOAD_10_MAN_2 = 63485, SPELL_OVERLOAD_25_MAN_2 = 61886, + //Kologarn + NPC_RIGHT_ARM = 32934, + NPC_RUBBLE = 33768, + SPELL_CRUNCH_ARMOR = 64002, + + SPELL_FOCUSED_EYEBEAM_10_2 = 63346, + SPELL_FOCUSED_EYEBEAM_10 = 63347, + SPELL_FOCUSED_EYEBEAM_25_2 = 63976, + SPELL_FOCUSED_EYEBEAM_25 = 63977, + // Hodir NPC_SNOWPACKED_ICICLE = 33174, NPC_TOASTY_FIRE = 33342, @@ -26,8 +36,13 @@ enum UlduarIDs // Freya NPC_EONARS_GIFT = 33228, GOBJECT_NATURE_BOMB = 194902, + + // Buffs + SPELL_ASPECT_OF_THE_WILD = 49071, }; +const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f; + // // Flame Levi // @@ -114,6 +129,30 @@ public: bool IsActive() override; }; +// +// Kologarn +// +class KologarnMarkDpsTargetTrigger : public Trigger +{ +public: + KologarnMarkDpsTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn mark dps target trigger") {} + bool IsActive() override; +}; + +class KologarnFallFromFloorTrigger : public Trigger +{ +public: + KologarnFallFromFloorTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn fall from floor trigger") {} + bool IsActive() override; +}; + +class KologarnNatureResistanceTrigger : public Trigger +{ +public: + KologarnNatureResistanceTrigger(PlayerbotAI* ai) : Trigger(ai, "kologarn nature resistance trigger") {} + bool IsActive() override; +}; + // // Hodir //