From 4d7fba0dcc15fe59970c6e4ddeed97b6f6738876 Mon Sep 17 00:00:00 2001 From: avirar Date: Wed, 18 Dec 2024 16:09:58 +1100 Subject: [PATCH] Create RaidUlduarBossHelper.cpp --- .../raids/ulduar/RaidUlduarBossHelper.cpp | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp diff --git a/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp b/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp new file mode 100644 index 00000000..f57843eb --- /dev/null +++ b/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp @@ -0,0 +1,188 @@ +#include "RaidUlduarBossHelper.h" +#include "ObjectAccessor.h" +#include "GameObject.h" +#include "Group.h" +#include "ScriptedCreature.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "World.h" + +std::unordered_map RazorscaleBossHelper::_harpoonCooldowns; + +bool RazorscaleBossHelper::UpdateBossAI() +{ + _boss = AI_VALUE2(Unit*, "find target", "razorscale"); + if (_boss) + { + Group* group = bot->GetGroup(); + if (group && !AreRolesAssigned()) + { + AssignRolesBasedOnHealth(); + } + return true; + } + return false; +} + +Unit* RazorscaleBossHelper::GetBoss() const +{ + return _boss; +} + +bool RazorscaleBossHelper::IsGroundPhase() const +{ + return _boss && _boss->IsAlive() && + (_boss->GetPositionZ() <= RAZORSCALE_FLYING_Z_THRESHOLD) && + (_boss->GetHealthPct() < 50.0f) && + !_boss->HasAura(SPELL_STUN_AURA); +} + +bool RazorscaleBossHelper::IsFlyingPhase() const +{ + return _boss && (!IsGroundPhase() || _boss->GetPositionZ() >= RAZORSCALE_FLYING_Z_THRESHOLD); +} + +bool RazorscaleBossHelper::IsHarpoonFired(uint32 chainSpellId) const +{ + return _boss && _boss->HasAura(chainSpellId); +} + +bool RazorscaleBossHelper::IsHarpoonReady(GameObject* harpoonGO) +{ + if (!harpoonGO) + return false; + + auto it = _harpoonCooldowns.find(harpoonGO->GetGUID()); + if (it != _harpoonCooldowns.end()) + { + time_t currentTime = std::time(nullptr); + time_t elapsedTime = currentTime - it->second; + if (elapsedTime < HARPOON_COOLDOWN_DURATION) + return false; + } + + return harpoonGO->GetGoState() == GO_STATE_READY; +} + +void RazorscaleBossHelper::SetHarpoonOnCooldown(GameObject* harpoonGO) +{ + if (!harpoonGO) + return; + + time_t currentTime = std::time(nullptr); + _harpoonCooldowns[harpoonGO->GetGUID()] = currentTime; +} + +GameObject* RazorscaleBossHelper::FindNearestHarpoon(float x, float y, float z) const +{ + GameObject* nearestHarpoon = nullptr; + float minDistanceSq = std::numeric_limits::max(); + + for (const auto& harpoon : GetHarpoonData()) + { + if (GameObject* harpoonGO = bot->FindNearestGameObject(harpoon.gameObjectEntry, 200.0f)) + { + float dx = harpoonGO->GetPositionX() - x; + float dy = harpoonGO->GetPositionY() - y; + float dz = harpoonGO->GetPositionZ() - z; + float distanceSq = dx * dx + dy * dy + dz * dz; + + if (distanceSq < minDistanceSq) + { + minDistanceSq = distanceSq; + nearestHarpoon = harpoonGO; + } + } + } + + return nearestHarpoon; +} + +const std::vector& RazorscaleBossHelper::GetHarpoonData() +{ + static const std::vector harpoonData = + { + { GO_RAZORSCALE_HARPOON_1, SPELL_CHAIN_1 }, + { GO_RAZORSCALE_HARPOON_2, SPELL_CHAIN_2 }, + { GO_RAZORSCALE_HARPOON_3, SPELL_CHAIN_3 }, + { GO_RAZORSCALE_HARPOON_4, SPELL_CHAIN_4 }, + }; + return harpoonData; +} + +bool RazorscaleBossHelper::AreRolesAssigned() const +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (botAI->IsMainTank(member)) + return true; + } + + return false; +} + +bool RazorscaleBossHelper::CanSwapRoles() const +{ + std::time_t currentTime = std::time(nullptr); + return (currentTime - _lastRoleSwapTime) >= _roleSwapCooldown; +} + +void RazorscaleBossHelper::AssignRolesBasedOnHealth() +{ + if (!CanSwapRoles()) + return; + + Group* group = bot->GetGroup(); + if (!group) + return; + + // Gather all tank bots in the group, excluding those with Fuse Armor + std::vector tankBots; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !botAI->IsTank(member) || !member->IsAlive()) + continue; + + Aura* fuseArmor = member->GetAura(SPELL_FUSEARMOR); + if (fuseArmor && fuseArmor->GetStackAmount() >= FUSEARMOR_THRESHOLD) + continue; + + tankBots.push_back(member); + } + + if (tankBots.empty()) + return; + + // Sort tanks by max health descending + std::sort(tankBots.begin(), tankBots.end(), + [](Player* a, Player* b) + { + return a->GetMaxHealth() > b->GetMaxHealth(); + } + ); + + Player* newMainTank = tankBots[0]; + + // Remove all MAINTANK flags + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && botAI->IsMainTank(member)) + group->SetGroupMemberFlag(member->GetGUID(), false, MEMBER_FLAG_MAINTANK); + } + + // Assign the single main tank + group->SetGroupMemberFlag(newMainTank->GetGUID(), true, MEMBER_FLAG_MAINTANK); + + _lastRoleSwapTime = std::time(nullptr); +}