diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp index b231f716..49f2e6e3 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp @@ -1,8 +1,11 @@ #include "RaidUlduarTriggers.h" #include "EventMap.h" +#include "GameObject.h" #include "Object.h" +#include "PlayerbotAI.h" #include "Playerbots.h" +#include "RaidUlduarBossHelper.h" #include "RaidUlduarScripts.h" #include "ScriptedCreature.h" #include "SharedDefines.h" @@ -43,3 +46,187 @@ bool FlameLeviathanVehicleNearTrigger::IsActive() return true; } + +bool RazorscaleFlyingAloneTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss) + { + return false; + } + + // Check if the boss is flying + if (boss->GetPositionZ() < RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) + { + return false; + } + + // Get the list of attackers + GuidVector attackers = context->GetValue("attackers")->Get(); + if (attackers.empty()) + { + return true; // No attackers implies flying alone + } + + std::vector dark_rune_adds; + + // Loop through attackers to find dark rune adds + for (ObjectGuid const& guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + + uint32 entry = unit->GetEntry(); + + // Check for valid dark rune entries + 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); + } + } + + // Return whether there are no dark rune adds + return dark_rune_adds.empty(); +} + + +bool RazorscaleDevouringFlamesTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss) + return false; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DEVOURING_FLAME) + { + return true; + } + } + + return false; +} + +bool RazorscaleAvoidSentinelTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss) + return false; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL) + { + return true; + } + } + + return false; +} + +bool RazorscaleAvoidWhirlwindTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss) + return false; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + 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))) + { + return true; + } + } + + return false; +} + +bool RazorscaleGroundedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss) + { + return false; + } + + // Check if the boss is flying + if (boss->GetPositionZ() < RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) + { + return true; + } + return false; +} + +bool RazorscaleHarpoonAvailableTrigger::IsActive() +{ + // Get harpoon data from the helper + const std::vector& harpoonData = RazorscaleBossHelper::GetHarpoonData(); + + // Get the boss entity + Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (!boss || !boss->IsAlive()) + { + return false; + } + + // Update the boss AI context in the helper + RazorscaleBossHelper razorscaleHelper(botAI); + + if (!razorscaleHelper.UpdateBossAI()) + { + return false; + } + + // Check each harpoon entry + for (const auto& harpoon : harpoonData) + { + // Skip harpoons whose chain spell is already active on the boss + if (razorscaleHelper.IsHarpoonFired(harpoon.chainSpellId)) + { + continue; + } + + // Find the nearest harpoon GameObject within 200 yards + if (GameObject* harpoonGO = bot->FindNearestGameObject(harpoon.gameObjectEntry, 200.0f)) + { + if (RazorscaleBossHelper::IsHarpoonReady(harpoonGO)) + { + return true; // At least one harpoon is available and ready to be fired + } + } + } + + // No harpoons are available or need to be fired + return false; +} + +bool RazorscaleFuseArmorTrigger::IsActive() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Iterate through group members to find the main tank with Fuse Armor + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (!member || !botAI->IsMainTank(member)) + continue; + + Aura* fuseArmor = member->GetAura(RazorscaleBossHelper::SPELL_FUSEARMOR); + if (fuseArmor && fuseArmor->GetStackAmount() >= RazorscaleBossHelper::FUSEARMOR_THRESHOLD) + return true; + } + + return false; +}