From e522570bd07b376f3c6924734bbcce298a48a4ed Mon Sep 17 00:00:00 2001 From: insunaa Date: Mon, 7 Oct 2024 09:55:15 +0200 Subject: [PATCH] Ulduar: Convert Hodir to BossAI Closes https://github.com/cmangos/mangos-wotlk/pull/441 Squashed commit of the following: commit a703a5621c88f8cd1330dfd8b64c8b35a24ee4ba Author: killerwife Date: Mon Oct 7 09:49:20 2024 +0200 Ulduar: Add different leashing check for hodir commit 9fdb136bad58f82d88b3b332bb06c17bab254812 Author: insunaa Date: Sun Oct 6 15:58:59 2024 +0200 Hodir: Respawn 30s after evade commit f494356ec85d3799785d7b349d2c62f0cc03e3a5 Author: insunaa Date: Sun Oct 6 15:53:05 2024 +0200 Hodir: Despawn on Evade commit 6e2947500734ba1b4e26c61034d4387e14cedb4d Author: insunaa Date: Sun Oct 6 15:28:52 2024 +0200 Hodir: Fix evade and FlashFreeze stacking commit 0193a7e4fa36ebf9dccf2ff169c684ca5bccf83c Author: killerwife Date: Sun Oct 6 15:08:11 2024 +0200 Hodir: Add some fixes commit 4fad15f12004deaf5d8a001779e2615199d82cce Author: insunaa Date: Wed Feb 22 13:25:14 2023 +0100 Ulduar: Convert Hodir to BossAI --- sql/scriptdev2/spell.sql | 6 + src/game/AI/ScriptDevAI/base/BossAI.cpp | 2 +- .../northrend/ulduar/ulduar/boss_hodir.cpp | 561 ++++++++++-------- .../northrend/ulduar/ulduar/ulduar.cpp | 29 + .../scripts/northrend/ulduar/ulduar/ulduar.h | 1 + src/game/Spells/SpellAuras.cpp | 29 - src/game/Spells/SpellEffects.cpp | 7 - 7 files changed, 354 insertions(+), 281 deletions(-) diff --git a/sql/scriptdev2/spell.sql b/sql/scriptdev2/spell.sql index 1e0640de1f4..25d9724dede 100644 --- a/sql/scriptdev2/spell.sql +++ b/sql/scriptdev2/spell.sql @@ -953,10 +953,14 @@ INSERT INTO spell_scripts(Id, ScriptName) VALUES (61546,'spell_shatter'), (61830,'spell_drink'), (61916,'spell_lightning_whirl'), +(61968,'spell_flash_freeze'), +(62038,'spell_biting_cold'), +(62039,'spell_biting_cold_damage'), (62108,'spell_tails_up_summon_female_frost_leopard'), (62116,'spell_tails_up_summon_female_icepaw_bear'), (62138,'spell_teleport_inside_violet_hold'), (62382,'spell_ignis_brittle'), +(62457,'spell_ice_shards'), (62789,'spell_heart_overload'), (62826,'spell_energy_orb_dummy'), (62828,'spell_recharge_robot'), @@ -971,6 +975,7 @@ INSERT INTO spell_scripts(Id, ScriptName) VALUES (62717,'spell_slag_pot'), (63474,'spell_ignis_scorch'), (63482,'spell_lightning_whirl_heroic'), +(63499,'spell_hodir_dispel_magic'), (63845,'spell_create_lance'), (64203,'spell_void_zone_xt'), (64209,'spell_consumption_xt'), @@ -983,6 +988,7 @@ INSERT INTO spell_scripts(Id, ScriptName) VALUES (64503,'spell_ignis_water'), (64568,'spell_blood_reserve_enchant'), (65121,'spell_searing_light'), +(65272,'spell_shatter_chest'), (65667,'spell_ignis_heat'), (65869,'spell_disengage'), (65932,'spell_retaliation_dummy_creature'), diff --git a/src/game/AI/ScriptDevAI/base/BossAI.cpp b/src/game/AI/ScriptDevAI/base/BossAI.cpp index 11d5e0abb3e..cdf5221cf9a 100644 --- a/src/game/AI/ScriptDevAI/base/BossAI.cpp +++ b/src/game/AI/ScriptDevAI/base/BossAI.cpp @@ -115,4 +115,4 @@ void BossAI::AddCastOnDeath(QueuedCast cast) void BossAI::AddRespawnOnEvade(std::chrono::seconds delay) { m_respawnDelay = delay.count(); -} \ No newline at end of file +} diff --git a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/boss_hodir.cpp b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/boss_hodir.cpp index a15cc7dad00..5a43bc83f88 100644 --- a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/boss_hodir.cpp +++ b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/boss_hodir.cpp @@ -23,29 +23,30 @@ EndScriptData */ #include "AI/ScriptDevAI/include/sc_common.h" #include "ulduar.h" -#include "Entities/TemporarySpawn.h" +#include "AI/ScriptDevAI/base/BossAI.h" enum { - SAY_AGGRO = -1603086, - SAY_SLAY_1 = -1603087, - SAY_SLAY_2 = -1603088, - SAY_FLASH_FREEZE = -1603089, - SAY_FROZEN_BLOWS = -1603090, - SAY_EPILOGUE = -1603091, - SAY_BERSERK = -1603092, + SAY_AGGRO = 34336, + SAY_SLAY_1 = 34337, + SAY_SLAY_2 = 34338, + SAY_EPILOGUE = 33484, + SAY_BERSERK = 34340, - EMOTE_FLASH_FREEZE = -1603094, - EMOTE_FROZEN_BLOWS = -1603095, + SAY_FLASH_FREEZE = 34339, + EMOTE_FLASH_FREEZE = 33314, // spells SPELL_BERSERK = 26662, SPELL_HODIR_CREDIT = 64899, // kill credit spell; added in spell_template SPELL_SHATTER_CHEST = 65272, // hard mode timer until chest is shattered; triggers 62501 which will send event 20907 if completed + SPELL_SHATTER_CHEST_TRIGGERED = 62501, SPELL_FROZEN_BLOWS = 62478, SPELL_FROZEN_BLOWS_H = 63512, SPELL_FREEZE = 62469, SPELL_BITTING_COLD = 62038, // triggers 62039 and 62188 + SPELL_BITING_COLD_AURA = 62039, + SPELL_BITING_COLD_STACK = 62188, SPELL_ICICLE_AURA = 62227, // periodic targeting aura; triggers the spell which summons npc 33169 SPELL_ICICLE_SNOWPACK = 62476, // cast right before Flash Freeze; triggers the spell which summons npc 33173 SPELL_ICICLE_SNOWPACK_H = 62477, @@ -60,10 +61,11 @@ enum // snowpacked icicle target spells SPELL_SAFE_AREA = 65705, // grant immunity from flash freeze + SPELL_AURA_SAFE_AREA = 62464, // flash freeze related spells // SPELL_FLASH_FREEZE_VISUAL = 62148, // cast by npc 30298 (handled by event 20896) - // SPELL_FLASH_FREEZE_SUMMON = 61970, // cast by all Flash Freeze targets; summons 32926 + SPELL_FLASH_FREEZE_SUMMON = 61970, // cast by all Flash Freeze targets; summons 32926 // SPELL_FLASH_FREEZE_SUMMON_NPC = 61989, // used to flash freeze all npc targets before the encounter; summons 32938 // SPELL_FLASH_FREEZE_STUN = 64175, // use and purpose unk // SPELL_FLASH_FREEZE_FRIENDLY = 64176, // use and purpose unk @@ -76,81 +78,93 @@ enum SPELL_FLASH_FREEZE_AURA_NPC = 61990, // stuns the summoner (npc) SPELL_FLASH_FREEZE_INITIAL = 62878, // trigger aggro on Hodir if damaged; sends event 21045 + // npc spells + SPELL_TOASTY_FIRE = 62821, + // npcs NPC_ICICLE = 33169, // NPC_SNOWPACKED_ICICLE = 33173, // entry used to generate npc 33173 and go 194173; handled in eventAI // NPC_SNOWPACKED_ICICLE_TARGET = 33174, // entry used to handle safe area aura from Flash Freeze; handled in eventAI NPC_FLASH_FREEZE = 32926, // entry used during the encounter NPC_FLASH_FREEZE_NPC = 32938, // entry which stuns the friendly npcs before the actual fight + NPC_TOASTY_FIRE = 33342, + GO_TOASTY_FIRE = 194300, // GO_SNOWDRIFT = 194173, EVENT_ID_ATTACK_START = 21045, EVENT_ID_SHATTER_CHEST = 20907, FACTION_ID_FRIENDLY = 35, + + VALUE_BITING_COLD_BASE_DAMAGE = 400, }; /*###### ## boss_hodir ######*/ -struct boss_hodirAI : public ScriptedAI +enum HodirActions { - boss_hodirAI(Creature* pCreature) : ScriptedAI(pCreature) - { - m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); - m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty(); - m_bEventFinished = false; - m_uiEpilogueTimer = 0; - m_uiEpilogueStage = 0; - Reset(); - } - - instance_ulduar* m_pInstance; - bool m_bIsRegularMode; - bool m_bEventFinished; - - uint32 m_uiEpilogueTimer; - uint32 m_uiBerserkTimer; - uint32 m_uiFlashFreezeTimer; - uint32 m_uiFrozenBlowsTimer; - uint32 m_uiFreezeTimer; - uint8 m_uiEpilogueStage; - - void Reset() override - { - m_uiBerserkTimer = 8 * MINUTE * IN_MILLISECONDS; - m_uiFlashFreezeTimer = 50000; - m_uiFrozenBlowsTimer = 70000; - m_uiFreezeTimer = urand(25000, 30000); - } + HODIR_AGGRO_SPELLS, + HODIR_FLASH_FREEZE, + HODIR_FREEZE, + HODIR_BERSERK, + HODIR_ACTIONS_MAX, + HODIR_EPILOGUE, +}; - void Aggro(Unit* /*pWho*/) override +struct boss_hodirAI : public BossAI +{ + boss_hodirAI(Creature* creature) : BossAI(creature, HODIR_ACTIONS_MAX), + m_instance(dynamic_cast(creature->GetInstanceData())), + m_isRegularMode(creature->GetMap()->IsRegularDifficulty()), + m_eventFinished(false), + m_epilogueStage(0) { - if (m_pInstance) + SetDataType(TYPE_HODIR); + AddOnAggroText(SAY_AGGRO); + AddOnKillText(SAY_SLAY_1, SAY_SLAY_2); + AddCombatAction(HODIR_BERSERK, 8min); + AddTimerlessCombatAction(HODIR_AGGRO_SPELLS, true); + AddCustomAction(HODIR_EPILOGUE, true, [&]() + { + switch (m_epilogueStage) + { + case 0: + if (m_instance) + m_instance->SetData(TYPE_HODIR, DONE); + + DoBroadcastText(SAY_EPILOGUE, m_creature); + ResetTimer(HODIR_EPILOGUE, 10s); + break; + case 1: + if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) + { + m_creature->ForcedDespawn(2000); + } + break; + } + ++m_epilogueStage; + }); + SetDeathPrevention(true); + if (m_instance) { - m_pInstance->SetData(TYPE_HODIR, IN_PROGRESS); - m_pInstance->SetData(TYPE_HODIR_HARD, DONE); + m_creature->GetCombatManager().SetLeashingCheck([](Unit* unit, float x, float y, float z) + { + return static_cast(unit->GetInstanceData())->GetPlayerInMap(true, false) == nullptr; + }); } - - DoScriptText(SAY_AGGRO, m_creature); - DoCastSpellIfCan(m_creature, SPELL_BITTING_COLD, CAST_TRIGGERED); - DoCastSpellIfCan(m_creature, SPELL_ICICLE_AURA, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); - DoCastSpellIfCan(m_creature, SPELL_SHATTER_CHEST, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); } - void AttackStart(Unit* pWho) override - { - // don't attack again after being defeated - if (m_bEventFinished) - return; - - ScriptedAI::AttackStart(pWho); - } + instance_ulduar* m_instance; + bool m_isRegularMode; + bool m_eventFinished; + uint8 m_epilogueStage; - void JustReachedHome() override + void Aggro(Unit* who) override { - if (m_pInstance) - m_pInstance->SetData(TYPE_HODIR, FAIL); + BossAI::Aggro(who); + if (m_instance) + m_instance->SetData(TYPE_HODIR_HARD, DONE); } void EnterEvadeMode() override @@ -158,201 +172,138 @@ struct boss_hodirAI : public ScriptedAI m_creature->RemoveAllAurasOnEvade(); m_creature->CombatStop(true); - if (m_creature->IsAlive() && !m_bEventFinished) - m_creature->GetMotionMaster()->MoveTargetedHome(); - - m_creature->SetLootRecipient(nullptr); + const std::vector* tmpHelpers = m_creature->GetMap()->GetCreatures("ULDUAR_HODIR_HELPERS"); - Reset(); - } + if (!tmpHelpers || tmpHelpers->empty()) + return; - void DamageTaken(Unit* /*pDealer*/, uint32& uiDamage, DamageEffectType /*damagetype*/, SpellEntry const* spellInfo) override - { - if (uiDamage >= m_creature->GetHealth()) + for (Creature* helper : *tmpHelpers) { - uiDamage = 0; - - if (!m_bEventFinished) - { - // Inform the faction helpers that the fight is over - ThreatList const& threatList = m_creature->getThreatManager().getThreatList(); - for (auto itr : threatList) - { - // only check creatures - if (!itr->getUnitGuid().IsCreature()) - continue; - - if (Creature* pTarget = m_creature->GetMap()->GetCreature(itr->getUnitGuid())) - pTarget->AI()->EnterEvadeMode(); - } - - m_uiEpilogueTimer = 10000; - m_creature->CastSpell(m_creature, SPELL_HODIR_CREDIT, TRIGGERED_OLD_TRIGGERED); - m_creature->SetFactionTemporary(FACTION_ID_FRIENDLY, TEMPFACTION_NONE); - m_bEventFinished = true; - EnterEvadeMode(); - } + if (!m_eventFinished && helper && helper->IsAlive()) + helper->Suicide(); } - } - - void KilledUnit(Unit* pVictim) override - { - if (pVictim->GetTypeId() != TYPEID_PLAYER) - return; - - DoScriptText(urand(0, 1) ? SAY_SLAY_1 : SAY_SLAY_2, m_creature); - } - void JustSummoned(Creature* pSummoned) override - { - if (pSummoned->GetEntry() == NPC_ICICLE) + if (m_creature->IsAlive() && !m_eventFinished) { - pSummoned->CastSpell(pSummoned, SPELL_ICICLE_DUMMY, TRIGGERED_NONE); - pSummoned->CastSpell(pSummoned, SPELL_ICICLE, TRIGGERED_OLD_TRIGGERED); - pSummoned->ForcedDespawn(5000); + m_creature->SetRespawnDelay(30s, true); + m_creature->ForcedDespawn(); } } - void UpdateAI(const uint32 uiDiff) override + void JustPreventedDeath(Unit* attacker) override { - if (m_uiEpilogueTimer) + // Inform the faction helpers that the fight is over + const std::vector* tmpHelpers = m_creature->GetMap()->GetCreatures("ULDUAR_HODIR_HELPERS"); + if (tmpHelpers && !tmpHelpers->empty()) { - if (m_uiEpilogueTimer <= uiDiff) + for (Creature* helper : *tmpHelpers) { - switch (m_uiEpilogueStage) - { - case 0: - if (m_pInstance) - m_pInstance->SetData(TYPE_HODIR, DONE); - - DoScriptText(SAY_EPILOGUE, m_creature); - m_uiEpilogueTimer = 10000; - break; - case 1: - if (DoCastSpellIfCan(m_creature, SPELL_TELEPORT) == CAST_OK) - { - m_creature->ForcedDespawn(2000); - m_uiEpilogueTimer = 0; - } - break; - } - ++m_uiEpilogueStage; + if (!m_eventFinished && helper && helper->AI() && helper->IsAlive()) + helper->AI()->EnterEvadeMode(); } - else - m_uiEpilogueTimer -= uiDiff; } - if (!m_creature->SelectHostileTarget() || !m_creature->GetVictim()) - return; + ResetTimer(HODIR_EPILOGUE, 10s); + m_creature->CastSpell(m_creature, SPELL_HODIR_CREDIT, TRIGGERED_OLD_TRIGGERED); + m_creature->SetFactionTemporary(FACTION_ID_FRIENDLY, TEMPFACTION_NONE); + m_eventFinished = true; + SetCombatScriptStatus(true); + m_creature->SetCanEnterCombat(false); + m_creature->SetImmuneToNPC(true); + EnterEvadeMode(); + } - if (m_uiBerserkTimer) + void ExecuteAction(uint32 action) override + { + switch (action) { - if (m_uiBerserkTimer <= uiDiff) + case HODIR_AGGRO_SPELLS: + { + DoCastSpellIfCan(m_creature, SPELL_BITTING_COLD, CAST_TRIGGERED); + DoCastSpellIfCan(m_creature, SPELL_ICICLE_AURA, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + DoCastSpellIfCan(m_creature, SPELL_SHATTER_CHEST, CAST_TRIGGERED | CAST_AURA_NOT_PRESENT); + DisableCombatAction(action); + return; + } + case HODIR_BERSERK: { if (DoCastSpellIfCan(m_creature, SPELL_BERSERK) == CAST_OK) { - DoScriptText(SAY_BERSERK, m_creature); - m_uiBerserkTimer = 0; + DoBroadcastText(SAY_BERSERK, m_creature); + DisableCombatAction(action); } + return; } - else - m_uiBerserkTimer -= uiDiff; } - - if (m_uiFlashFreezeTimer < uiDiff) - { - if (DoCastSpellIfCan(m_creature, SPELL_FLASH_FREEZE) == CAST_OK) - { - DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ICICLE_SNOWPACK : SPELL_ICICLE_SNOWPACK_H, CAST_TRIGGERED); - DoScriptText(EMOTE_FLASH_FREEZE, m_creature); - DoScriptText(SAY_FLASH_FREEZE, m_creature); - m_uiFlashFreezeTimer = 50000; - } - } - else - m_uiFlashFreezeTimer -= uiDiff; - - if (m_uiFrozenBlowsTimer < uiDiff) - { - if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_FROZEN_BLOWS : SPELL_FROZEN_BLOWS_H) == CAST_OK) - { - DoScriptText(SAY_FROZEN_BLOWS, m_creature); - DoScriptText(EMOTE_FROZEN_BLOWS, m_creature); - m_uiFrozenBlowsTimer = 60000; - } - } - else - m_uiFrozenBlowsTimer -= uiDiff; - - if (m_uiFreezeTimer < uiDiff) - { - if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) - { - if (DoCastSpellIfCan(pTarget, SPELL_FREEZE) == CAST_OK) - m_uiFreezeTimer = 15000; - } - } - else - m_uiFreezeTimer -= uiDiff; - - DoMeleeAttackIfReady(); } }; -UnitAI* GetAI_boss_hodir(Creature* pCreature) -{ - return new boss_hodirAI(pCreature); -} - /*###### ## npc_flash_freeze ######*/ -struct npc_flash_freezeAI : public Scripted_NoMovementAI +struct npc_flash_freezeAI : public ScriptedAI { - npc_flash_freezeAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) + npc_flash_freezeAI(Creature* creature) : ScriptedAI(creature), + m_instance(dynamic_cast(creature->GetInstanceData())), + m_freezeInit(false) { - m_pInstance = (instance_ulduar*)pCreature->GetInstanceData(); - Reset(); + SetAIImmobilizedState(true); + SetReactState(ReactStates::REACT_PASSIVE); } - instance_ulduar* m_pInstance; - - bool m_bFreezeInit; - void Reset() override { - m_bFreezeInit = false; + ScriptedAI::Reset(); + SetCombatMovement(false); } - void AttackStart(Unit* /*pWho*/) override { } - void MoveInLineOfSight(Unit* /*pWho*/) override { } + instance_ulduar* m_instance; + + bool m_freezeInit; + + inline Unit* GetSummoner() const + { + return m_creature->GetSpawner(); + } - void JustDied(Unit* /*pKiller*/) override + void JustDied(Unit* /*killer*/) override { // On Flash Freeze death, the owner should attack Hodir - if (m_creature->GetEntry() == NPC_FLASH_FREEZE_NPC && m_creature->IsTemporarySummon() && m_pInstance) + if (m_creature->GetEntry() == NPC_FLASH_FREEZE_NPC && m_creature->IsTemporarySummon() && m_instance) { - if (Creature* pHodir = m_pInstance->GetSingleCreatureFromStorage(NPC_HODIR)) + if (Creature* hodir = m_instance->GetSingleCreatureFromStorage(NPC_HODIR)) { // ignore if event already completed - if (pHodir->GetFaction() == FACTION_ID_FRIENDLY) + if (hodir->GetFaction() == FACTION_ID_FRIENDLY) return; - if (Creature* pSummoner = m_creature->GetMap()->GetCreature(m_creature->GetSpawnerGuid())) - pSummoner->AI()->AttackStart(pHodir); + if (Unit* summoner = GetSummoner()) + summoner->AI()->AttackStart(hodir); } } } - void UpdateAI(const uint32 /*uiDiff*/) override + void UpdateAI(const uint32 /*diff*/) override { // Flash Freeze npcs should be always be summoned if (!m_creature->IsTemporarySummon()) return; + if (Unit* summoner = GetSummoner()) + { + if (!summoner->IsAlive()) + { + if (summoner->IsCreature()) + static_cast(summoner)->ForcedDespawn(5000); + m_creature->ForcedDespawn(); + summoner = nullptr; + return; + } + } + // do the freezing on the first update tick - if (!m_bFreezeInit) + if (!m_freezeInit) { // Flash Freeze npc will always stun or kill the summoner if (m_creature->GetEntry() == NPC_FLASH_FREEZE_NPC) @@ -362,111 +313,233 @@ struct npc_flash_freezeAI : public Scripted_NoMovementAI } else if (m_creature->GetEntry() == NPC_FLASH_FREEZE) { - if (Unit* pSummoner = m_creature->GetMap()->GetUnit(m_creature->GetSpawnerGuid())) + if (Unit* summoner = GetSummoner()) { // kill frozen players - if (pSummoner->HasAura(SPELL_FREEZE)) - DoCastSpellIfCan(pSummoner, SPELL_FLASH_FREEZE_KILL); + if (summoner->HasAura(SPELL_FREEZE)) + DoCastSpellIfCan(summoner, SPELL_FLASH_FREEZE_KILL); else DoCastSpellIfCan(m_creature, SPELL_FLASH_FREEZE_AURA); - if (pSummoner->GetTypeId() == TYPEID_PLAYER && m_pInstance) - m_pInstance->SetSpecialAchievementCriteria(TYPE_ACHIEV_CHEESE_FREEZE, false); + if (summoner->GetTypeId() == TYPEID_PLAYER && m_instance) + m_instance->SetSpecialAchievementCriteria(TYPE_ACHIEV_CHEESE_FREEZE, false); } } - - m_bFreezeInit = true; + m_freezeInit = true; } } }; -UnitAI* GetAI_npc_flash_freeze(Creature* pCreature) +/*###### +## npc_snowpack_target +######*/ + +struct npc_snowpack_targetAI : public ScriptedAI { - return new npc_flash_freezeAI(pCreature); -} + npc_snowpack_targetAI(Creature* creature) : ScriptedAI(creature) + { + SetAIImmobilizedState(true); + SetCombatMovement(false); + } + + void JustRespawned() override + { + m_creature->CastSpell(nullptr, SPELL_SAFE_AREA, TriggerCastFlags::TRIGGERED_NONE); + } + + void EnterEvadeMode() override {} +}; /*###### ## event_boss_hodir ######*/ -bool ProcessEventId_event_boss_hodir(uint32 uiEventId, Object* pSource, Object* /*pTarget*/, bool /*bIsStart*/) +bool ProcessEventId_event_boss_hodir(uint32 eventId, Object* source, Object* /*target*/, bool /*isStart*/) { - if (pSource->GetTypeId() == TYPEID_UNIT) + if (source->GetTypeId() == TYPEID_UNIT) { - instance_ulduar* pInstance = (instance_ulduar*)((Creature*)pSource)->GetInstanceData(); - if (!pInstance) + instance_ulduar* instance = dynamic_cast(static_cast(source)->GetInstanceData()); + if (!instance) return true; - if (uiEventId == EVENT_ID_SHATTER_CHEST) + if (eventId == EVENT_ID_SHATTER_CHEST) { // Mark hard mode as failed and despawn the Rare cache - pInstance->SetData(TYPE_HODIR_HARD, FAIL); + instance->SetData(TYPE_HODIR_HARD, FAIL); - if (GameObject* pChest = pInstance->GetSingleGameObjectFromStorage(pInstance->instance->IsRegularDifficulty() ? GO_CACHE_OF_RARE_WINTER_10 : GO_CACHE_OF_RARE_WINTER_25)) - pChest->SetLootState(GO_JUST_DEACTIVATED); + if (GameObject* chest = instance->GetSingleGameObjectFromStorage(instance->instance->IsRegularDifficulty() ? GO_CACHE_OF_RARE_WINTER_10 : GO_CACHE_OF_RARE_WINTER_25)) + chest->SetLootState(GO_JUST_DEACTIVATED); } - else if (uiEventId == EVENT_ID_ATTACK_START) + else if (eventId == EVENT_ID_ATTACK_START) { // Start encounter - if (Creature* pHodir = pInstance->GetSingleCreatureFromStorage(NPC_HODIR)) + if (Creature* hodir = instance->GetSingleCreatureFromStorage(NPC_HODIR)) { // ignore if event already completed - if (pHodir->GetFaction() == FACTION_ID_FRIENDLY) + if (hodir->GetFaction() == FACTION_ID_FRIENDLY) return true; - - pHodir->SetInCombatWithZone(); + hodir->SetInCombatWithZone(); } } - return true; } - return false; } -/*###### -## npc_icicle_target -######*/ +// 61968 - Flash Freeze +struct FlashFreeze : public AuraScript, public SpellScript +{ + void OnSuccessfulStart(Spell* spell) const override + { + Unit* caster = spell->GetCaster(); + if (!caster || !caster->IsCreature()) + return; + DoBroadcastText(SAY_FLASH_FREEZE, caster); + DoBroadcastText(EMOTE_FLASH_FREEZE, caster); + caster->CastSpell(nullptr, caster->GetMap()->IsRegularDifficulty() ? SPELL_ICICLE_SNOWPACK : SPELL_ICICLE_SNOWPACK_H, TriggerCastFlags::TRIGGERED_IGNORE_CURRENT_CASTED_SPELL); + } + + void OnSuccessfulFinish(Spell* spell) const override + { + const std::vector* tmpFires = spell->GetCaster()->GetMap()->GetCreatures("ULDUAR_HODIR_FIRES"); -// TODO Remove this 'script' when combat can be proper prevented from core-side -struct npc_icicle_targetAI : public Scripted_NoMovementAI + if (!tmpFires || tmpFires->empty()) + return; + + for (Creature* fire : *tmpFires) + { + if (fire && fire->IsAlive()) + { + GameObject* fireGob = GetClosestGameObjectWithEntry(fire, GO_TOASTY_FIRE, 1.f); + if (fireGob) + fireGob->ForcedDespawn(); + fire->ForcedDespawn(); + } + } + } + + void OnPeriodicDummy(Aura* aura) const override + { + Unit* target = aura->GetTarget(); + if (!target) + return; + if (aura->GetAuraTicks() == 1 && !target->HasAura(SPELL_AURA_SAFE_AREA)) + { + if (target->IsCreature() && target->HasAura(SPELL_FLASH_FREEZE_AURA_NPC)) + return; + else if (!target->HasAura(SPELL_FLASH_FREEZE_AURA)) + target->CastSpell(nullptr, SPELL_FLASH_FREEZE_SUMMON, TRIGGERED_INSTANT_CAST | + TRIGGERED_IGNORE_CURRENT_CASTED_SPELL | TRIGGERED_IGNORE_CASTER_AURA_STATE | TRIGGERED_IGNORE_GCD, nullptr, aura); + } + } +}; + +// 65272 - Shatter Chest +struct ShatterChest : public AuraScript { - npc_icicle_targetAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature) { Reset(); } + void OnPeriodicDummy(Aura* aura) const override + { + Unit* target = aura->GetTarget(); + if (!target) + return; + target->CastSpell(target, SPELL_SHATTER_CHEST_TRIGGERED, TRIGGERED_OLD_TRIGGERED, nullptr, aura); + } +}; - void Reset() override +// 62038 - Biting Cold +struct BitingCold : public AuraScript +{ + void OnPeriodicDummy(Aura* aura) const override { - DoCastSpellIfCan(m_creature, SPELL_SAFE_AREA); + Player* target = dynamic_cast(aura->GetTarget()); + if (!target) + return; + if (target->IsMoving() || target->HasAura(SPELL_TOASTY_FIRE)) + target->RemoveAuraHolderFromStack(SPELL_BITING_COLD_AURA); + else if (!(aura->GetAuraTicks() % 3) && !target->HasAura(SPELL_TOASTY_FIRE)) + target->CastSpell(target, SPELL_BITING_COLD_AURA, TRIGGERED_OLD_TRIGGERED, nullptr, aura); + return; } +}; - void AttackStart(Unit* /*pWho*/) override { } - void MoveInLineOfSight(Unit* /*pWho*/) override { } - void UpdateAI(const uint32 /*uiDiff*/) override { } +// 62039 - Biting Cold +struct BitingColdDamage : public AuraScript +{ + void OnPeriodicDummy(Aura* aura) const override + { + Unit* target = aura->GetTarget(); + if (!target) + return; + int32 damage = VALUE_BITING_COLD_BASE_DAMAGE * std::pow(2, aura->GetStackAmount() - 1); + target->CastCustomSpell(target, SPELL_BITING_COLD_STACK, &damage, nullptr, nullptr, TRIGGERED_OLD_TRIGGERED); + } }; -UnitAI* GetAI_npc_icicle_target(Creature* pCreature) +// 62457 - Ice Shards + +struct IceShards : public SpellScript { - return new npc_icicle_targetAI(pCreature); -} + void OnEffectExecute(Spell* spell, SpellEffectIndex effIdx) const override + { + if (effIdx != EFFECT_INDEX_2) + return; + Creature* target = dynamic_cast(spell->GetUnitTarget()); + if (target && target->GetEntry() == NPC_TOASTY_FIRE) + { + if(GameObject* toastyFire = GetClosestGameObjectWithEntry(target, GO_TOASTY_FIRE, 1.f)) + toastyFire->ForcedDespawn(); + target->ForcedDespawn(); + } + } +}; + +// 63499 - Dispel Magic +struct HodirDispelMagic : public SpellScript +{ + bool OnCheckTarget(const Spell* spell, Unit* target, SpellEffectIndex /*eff*/) const override + { + if (target && (target->IsPlayer() || target->HasAura(spell->m_spellInfo->CalculateSimpleValue(SpellEffectIndex::EFFECT_INDEX_1)))) + return true; + return false; + } + + void OnEffectExecute(Spell* spell, SpellEffectIndex effIdx) const override + { + if (effIdx != SpellEffectIndex::EFFECT_INDEX_1) + return; + Unit* unitTarget = spell->GetUnitTarget(); + if (!unitTarget) + return; + unitTarget->RemoveAurasDueToSpell(spell->m_spellInfo->CalculateSimpleValue(effIdx)); + } +}; void AddSC_boss_hodir() { Script* pNewScript = new Script; pNewScript->Name = "boss_hodir"; - pNewScript->GetAI = GetAI_boss_hodir; + pNewScript->GetAI = &GetNewAIInstance; pNewScript->RegisterSelf(); pNewScript = new Script; pNewScript->Name = "npc_flash_freeze"; - pNewScript->GetAI = GetAI_npc_flash_freeze; + pNewScript->GetAI = &GetNewAIInstance; pNewScript->RegisterSelf(); pNewScript = new Script; - pNewScript->Name = "event_boss_hodir"; - pNewScript->pProcessEventId = &ProcessEventId_event_boss_hodir; + pNewScript->Name = "npc_snowpack_target"; + pNewScript->GetAI = &GetNewAIInstance; pNewScript->RegisterSelf(); pNewScript = new Script; - pNewScript->Name = "npc_icicle_target"; - pNewScript->GetAI = GetAI_npc_icicle_target; + pNewScript->Name = "event_boss_hodir"; + pNewScript->pProcessEventId = &ProcessEventId_event_boss_hodir; pNewScript->RegisterSelf(); + + RegisterSpellScript("spell_flash_freeze"); + RegisterSpellScript("spell_shatter_chest"); + RegisterSpellScript("spell_biting_cold"); + RegisterSpellScript("spell_biting_cold_damage"); + RegisterSpellScript("spell_ice_shards"); + RegisterSpellScript("spell_hodir_dispel_magic"); } diff --git a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.cpp b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.cpp index 9d609047461..6fa1b2e9c99 100644 --- a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.cpp +++ b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.cpp @@ -298,6 +298,24 @@ void instance_ulduar::OnCreatureCreate(Creature* pCreature) else m_vaporVezaxBunnyGuid = pCreature->GetObjectGuid(); return; + case NPC_DRUID_HORDE_N: + case NPC_DRUID_HORDE_H: + case NPC_SHAMAN_HORDE_N: + case NPC_SHAMAN_HORDE_H: + case NPC_MAGE_HORDE_N: + case NPC_MAGE_HORDE_H: + case NPC_PRIEST_HORDE_N: + case NPC_PRIEST_HORDE_H: + case NPC_DRUID_ALLIANCE_N: + case NPC_DRUID_ALLIANCE_H: + case NPC_SHAMAN_ALLIANCE_N: + case NPC_SHAMAN_ALLIANCE_H: + case NPC_MAGE_ALLIANCE_N: + case NPC_MAGE_ALLIANCE_H: + case NPC_PRIEST_ALLIANCE_N: + case NPC_PRIEST_ALLIANCE_H: + pCreature->SetImmuneToNPC(false); + return; default: return; @@ -1752,6 +1770,17 @@ bool ProcessEventId_event_ulduar(uint32 uiEventId, Object* pSource, Object* /*pT } } } + else if (uiEventId == EVENT_ID_FLASH_FREEZE) + { + if (pSource->GetTypeId() == TYPEID_PLAYER) + { + if (instance_ulduar* instance = static_cast(static_cast(pSource)->GetInstanceData())) + { + instance->SetSpecialAchievementCriteria(TYPE_ACHIEV_CHEESE_FREEZE, false); + return true; + } + } + } return false; } diff --git a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.h b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.h index 9f2245e8a0b..98f228d9b20 100644 --- a/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.h +++ b/src/game/AI/ScriptDevAI/scripts/northrend/ulduar/ulduar/ulduar.h @@ -371,6 +371,7 @@ enum EVENT_ID_TOWER_FLAME = 21033, EVENT_ID_TOWER_FROST = 21032, EVENT_ID_TOWER_STORMS = 21031, + EVENT_ID_FLASH_FREEZE = 20896, // EVENT_ID_TRAM_MIMIRON = 21393, // tram reached Mimiron // EVENT_ID_TRAM_CENTER = 21394, // tram reached Center diff --git a/src/game/Spells/SpellAuras.cpp b/src/game/Spells/SpellAuras.cpp index adf06238581..951db217b88 100755 --- a/src/game/Spells/SpellAuras.cpp +++ b/src/game/Spells/SpellAuras.cpp @@ -8926,12 +8926,6 @@ void Aura::PeriodicDummyTick() case 2: target->CastSpell(target, 55739, TRIGGERED_OLD_TRIGGERED); break; } return; - case 61968: // Flash Freeze - { - if (GetAuraTicks() == 1 && !target->HasAura(62464)) - target->CastSpell(target, 61970, TRIGGERED_OLD_TRIGGERED, nullptr, this); - return; - } case 62018: // Collapse { // lose 1% of health every second @@ -8943,24 +8937,6 @@ void Aura::PeriodicDummyTick() target->CastSpell(target, 62020, TRIGGERED_OLD_TRIGGERED, nullptr, this); return; } - case 62038: // Biting Cold - { - if (target->GetTypeId() != TYPEID_PLAYER) - return; - - // if player is moving remove one aura stack - if (target->IsMoving()) - target->RemoveAuraHolderFromStack(62039); - // otherwise add one aura stack each 3 seconds - else if (GetAuraTicks() % 3 && !target->HasAura(62821)) - target->CastSpell(target, 62039, TRIGGERED_OLD_TRIGGERED, nullptr, this); - return; - } - case 62039: // Biting Cold - { - target->CastSpell(target, 62188, TRIGGERED_OLD_TRIGGERED); - return; - } case 62566: // Healthy Spore Summon Periodic { target->CastSpell(target, 62582, TRIGGERED_OLD_TRIGGERED); @@ -9028,11 +9004,6 @@ void Aura::PeriodicDummyTick() } return; } - case 65272: // Shatter Chest - { - target->CastSpell(target, 62501, TRIGGERED_OLD_TRIGGERED, nullptr, this); - return; - } case 66798: // Death's Respite { Unit* caster = GetCaster(); diff --git a/src/game/Spells/SpellEffects.cpp b/src/game/Spells/SpellEffects.cpp index edd9e51f69c..33aed1bb91f 100644 --- a/src/game/Spells/SpellEffects.cpp +++ b/src/game/Spells/SpellEffects.cpp @@ -3469,13 +3469,6 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) return; } - case 63499: // Dispel Magic - { - if (unitTarget) - unitTarget->RemoveAurasDueToSpell(m_spellInfo->CalculateSimpleValue(eff_idx)); - - return; - } case 63545: // Icicle { if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)