diff options
author | treeston <treeston.mmoc@gmail.com> | 2016-03-23 20:17:17 +0100 |
---|---|---|
committer | treeston <treeston.mmoc@gmail.com> | 2016-03-23 20:17:17 +0100 |
commit | f37682b7edd0d711e1120cbdd9d627fb5b9dbde1 (patch) | |
tree | ce7725df07befb526c776010521de3e59dfa4770 | |
parent | a463a704f269da6db5bec604f043c9c484335ef3 (diff) | |
parent | f4f7e6324d6c99335bf3479f212edea1e5572f88 (diff) |
Merge branch '3.3.5-naxxcleanup' into 3.3.5. (PR #16524)
19 files changed, 841 insertions, 532 deletions
diff --git a/sql/updates/world/2016_03_23_00_world.sql b/sql/updates/world/2016_03_23_00_world.sql new file mode 100644 index 00000000000..9195b652288 --- /dev/null +++ b/sql/updates/world/2016_03_23_00_world.sql @@ -0,0 +1,164 @@ +-- Naxxramas instance cleanup +SET @OGUID = 5517; -- gameobject GUID (1 object) used for everything except heigan + +-- ======== -- +-- Faerlina -- +-- ======== -- +-- Move probability of SAY_SLAY to DB +UPDATE `creature_text` SET `probability`=16 WHERE `entry`=15953 and `groupid`=2; + +-- ================== -- +-- Heigan the Unclean -- +-- ================== -- +-- Completely re-do eruption fissure spawns +SET @OGUID2 = 84980; -- gameobject GUID (76 objects); needs to match the constant in boss_heigan.cpp +DELETE FROM `gameobject` WHERE `id` between 181510 and 181552; +INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`,`VerifiedBuild`) VALUES + (@OGUID2+0,181510,533,3,1,2787.255000,-3654.130000,274.316700,3.534301,0.000000,0.000000,-0.980784,0.195095,0,0,1,15354), + (@OGUID2+1,181526,533,3,1,2781.556000,-3670.999000,274.351800,3.153633,0.000000,0.000000,-0.999982,0.006020,0,0,1,15354), + (@OGUID2+2,181511,533,3,1,2782.403000,-3660.402000,274.314800,2.110888,0.000000,0.000000,0.870119,0.492841,0,0,1,15354), + (@OGUID2+3,181517,533,3,1,2793.238000,-3664.132000,274.316700,0.635802,0.000000,0.000000,0.312573,0.949894,0,0,1,15354), + (@OGUID2+4,181518,533,3,1,2802.508000,-3664.726000,274.316700,0.635802,0.000000,0.000000,0.312573,0.949894,0,0,1,15354), + (@OGUID2+5,181523,533,3,1,2795.809000,-3677.562000,274.072800,2.853153,0.000000,0.000000,0.989618,0.143721,0,0,1,15354), + (@OGUID2+6,181519,533,3,1,2811.998000,-3671.979000,274.072800,5.809874,0.000000,0.000000,-0.234453,0.972127,0,0,1,15354), + (@OGUID2+7,181524,533,3,1,2792.327000,-3684.134000,274.316700,4.976158,0.000000,0.000000,-0.607980,0.793952,0,0,1,15354), + (@OGUID2+8,181520,533,3,1,2810.557000,-3680.581000,274.316700,3.186728,0.000000,0.000000,-0.999745,0.022566,0,0,1,15354), + (@OGUID2+9,181521,533,3,1,2800.146000,-3682.706000,274.351800,1.038823,0.000000,0.000000,0.496369,0.868111,0,0,1,15354), + (@OGUID2+10,181523,533,3,1,2795.809000,-3677.562000,274.072800,2.853153,0.000000,0.000000,0.989618,0.143721,0,0,1,15354), + (@OGUID2+11,181524,533,3,1,2792.327000,-3684.134000,274.316700,4.976158,0.000000,0.000000,-0.607980,0.793952,0,0,1,15354), + (@OGUID2+12,181520,533,3,1,2810.557000,-3680.581000,274.316700,3.186728,0.000000,0.000000,-0.999745,0.022566,0,0,1,15354), + (@OGUID2+13,181521,533,3,1,2800.146000,-3682.706000,274.351800,1.038823,0.000000,0.000000,0.496369,0.868111,0,0,1,15354), + (@OGUID2+14,181522,533,3,1,2805.961000,-3691.666000,274.316700,4.434372,0.000000,0.000000,-0.798264,0.602308,0,0,1,15354), + + (@OGUID2+15,181515,533,3,1,2755.239000,-3649.898000,274.316700,3.396841,0.000000,0.000000,-0.991867,0.127278,0,0,1,15354), + (@OGUID2+16,181516,533,3,1,2763.548000,-3654.044000,274.316700,3.399228,0.000000,0.000000,-0.991715,0.128462,0,0,1,15354), + (@OGUID2+17,181531,533,3,1,2749.335000,-3662.211000,274.351800,3.636871,0.000000,0.000000,-0.969494,0.245116,0,0,1,15354), + (@OGUID2+18,181514,533,3,1,2757.844000,-3659.562000,274.316700,1.971156,0.000000,0.000000,0.833592,0.552381,0,0,1,15354), + (@OGUID2+19,181512,533,3,1,2778.426000,-3651.314000,274.316700,3.540596,0.000000,0.000000,-0.980166,0.198181,0,0,1,15354), + (@OGUID2+20,181516,533,3,1,2763.548000,-3654.044000,274.316700,3.399228,0.000000,0.000000,-0.991715,0.128462,0,0,1,15354), + (@OGUID2+21,181530,533,3,1,2758.163000,-3667.129000,274.351800,3.138830,0.000000,0.000000,0.999999,0.001381,0,0,1,15354), + (@OGUID2+22,181514,533,3,1,2757.844000,-3659.562000,274.316700,1.971156,0.000000,0.000000,0.833592,0.552381,0,0,1,15354), + (@OGUID2+23,181512,533,3,1,2778.426000,-3651.314000,274.316700,3.540596,0.000000,0.000000,-0.980166,0.198181,0,0,1,15354), + (@OGUID2+24,181529,533,3,1,2763.326000,-3680.528000,274.351800,3.146377,0.000000,0.000000,-0.999997,0.002392,0,0,1,15354), + (@OGUID2+25,181513,533,3,1,2774.297000,-3660.655000,274.316700,6.099252,0.000000,0.000000,-0.091837,0.995774,0,0,1,15354), + (@OGUID2+26,181528,533,3,1,2769.250000,-3671.420000,274.422200,5.859179,0.000000,0.000000,-0.210419,0.977611,0,0,1,15354), + (@OGUID2+27,181527,533,3,1,2777.413000,-3677.635000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+28,181527,533,3,1,2777.413000,-3677.635000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+29,181529,533,3,1,2763.326000,-3680.528000,274.351800,3.146377,0.000000,0.000000,-0.999997,0.002392,0,0,1,15354), + (@OGUID2+30,181525,533,3,1,2783.359000,-3688.357000,274.385100,3.161319,0.000000,0.000000,-0.999951,0.009863,0,0,1,15354), + (@OGUID2+31,181528,533,3,1,2769.250000,-3671.420000,274.422200,5.859179,0.000000,0.000000,-0.210419,0.977611,0,0,1,15354), + (@OGUID2+32,181528,533,3,1,2769.250000,-3671.420000,274.422200,5.859179,0.000000,0.000000,-0.210419,0.977611,0,0,1,15354), + (@OGUID2+33,181529,533,3,1,2763.326000,-3680.528000,274.351800,3.146377,0.000000,0.000000,-0.999997,0.002392,0,0,1,15354), + (@OGUID2+34,181516,533,3,1,2763.548000,-3654.044000,274.316700,3.399228,0.000000,0.000000,-0.991715,0.128462,0,0,1,15354), + (@OGUID2+35,181530,533,3,1,2758.163000,-3667.129000,274.351800,3.138830,0.000000,0.000000,0.999999,0.001381,0,0,1,15354), + (@OGUID2+36,181514,533,3,1,2757.844000,-3659.562000,274.316700,1.971156,0.000000,0.000000,0.833592,0.552381,0,0,1,15354), + (@OGUID2+37,181515,533,3,1,2755.239000,-3649.898000,274.316700,3.396841,0.000000,0.000000,-0.991867,0.127278,0,0,1,15354), + (@OGUID2+38,181531,533,3,1,2749.335000,-3662.211000,274.351800,3.636871,0.000000,0.000000,-0.969494,0.245116,0,0,1,15354), + (@OGUID2+39,181512,533,3,1,2778.426000,-3651.314000,274.316700,3.540596,0.000000,0.000000,-0.980166,0.198181,0,0,1,15354), + + (@OGUID2+40,181532,533,3,1,2743.089000,-3671.320000,274.316700,2.464252,0.000000,0.000000,0.943197,0.332233,0,0,1,15354), + (@OGUID2+41,181534,533,3,1,2737.166000,-3675.165000,274.316700,4.369651,0.000000,0.000000,-0.817333,0.576165,0,0,1,15354), + (@OGUID2+42,181532,533,3,1,2743.089000,-3671.320000,274.316700,2.464252,0.000000,0.000000,0.943197,0.332233,0,0,1,15354), + (@OGUID2+43,181533,533,3,1,2754.007000,-3673.770000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+44,181534,533,3,1,2737.166000,-3675.165000,274.316700,4.369651,0.000000,0.000000,-0.817333,0.576165,0,0,1,15354), + (@OGUID2+45,181536,533,3,1,2740.491000,-3692.128000,274.387000,0.792787,0.000000,0.000000,0.386094,0.922459,0,0,1,15354), + (@OGUID2+46,181533,533,3,1,2754.007000,-3673.770000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+47,181535,533,3,1,2747.132000,-3684.353000,274.351800,3.132432,0.000000,0.000000,0.999989,0.004580,0,0,1,15354), + (@OGUID2+48,181541,533,3,1,2760.581000,-3688.306000,274.387000,0.412781,0.000000,0.000000,0.204928,0.978777,0,0,1,15354), + (@OGUID2+49,181533,533,3,1,2754.007000,-3673.770000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+50,181532,533,3,1,2743.089000,-3671.320000,274.316700,2.464252,0.000000,0.000000,0.943197,0.332233,0,0,1,15354), + (@OGUID2+51,181544,533,3,1,2774.958000,-3701.132000,274.316700,0.523547,0.000000,0.000000,0.258794,0.965933,0,0,1,15354), + (@OGUID2+52,181543,533,3,1,2772.080000,-3692.152000,274.351800,5.018846,0.000000,0.000000,-0.590897,0.806747,0,0,1,15354), + (@OGUID2+53,181543,533,3,1,2772.080000,-3692.152000,274.351800,5.018846,0.000000,0.000000,-0.590897,0.806747,0,0,1,15354), + (@OGUID2+54,181544,533,3,1,2774.958000,-3701.132000,274.316700,0.523547,0.000000,0.000000,0.258794,0.965933,0,0,1,15354), + (@OGUID2+55,181542,533,3,1,2764.288000,-3698.094000,274.422200,5.490798,0.000000,0.000000,-0.385910,0.922536,0,0,1,15354), + (@OGUID2+56,181541,533,3,1,2760.581000,-3688.306000,274.387000,0.412781,0.000000,0.000000,0.204928,0.978777,0,0,1,15354), + (@OGUID2+57,181533,533,3,1,2754.007000,-3673.770000,274.387000,0.791340,0.000000,0.000000,0.385427,0.922739,0,0,1,15354), + (@OGUID2+58,181540,533,3,1,2752.924000,-3693.020000,274.316700,4.099892,0.000000,0.000000,-0.887387,0.461025,0,0,1,15354), + (@OGUID2+59,181536,533,3,1,2740.491000,-3692.128000,274.387000,0.792787,0.000000,0.000000,0.386094,0.922459,0,0,1,15354), + (@OGUID2+60,181532,533,3,1,2743.089000,-3671.320000,274.316700,2.464252,0.000000,0.000000,0.943197,0.332233,0,0,1,15354), + (@OGUID2+61,181534,533,3,1,2737.166000,-3675.165000,274.316700,4.369651,0.000000,0.000000,-0.817333,0.576165,0,0,1,15354), + (@OGUID2+62,181535,533,3,1,2747.132000,-3684.353000,274.351800,3.132432,0.000000,0.000000,0.999989,0.004580,0,0,1,15354), + + (@OGUID2+63,181552,533,3,1,2784.168000,-3724.730000,274.385100,1.050844,0.000000,0.000000,0.501578,0.865112,0,0,1,15354), + (@OGUID2+64,181552,533,3,1,2784.168000,-3724.730000,274.385100,1.050844,0.000000,0.000000,0.501578,0.865112,0,0,1,15354), + (@OGUID2+65,181545,533,3,1,2772.289000,-3711.435000,274.422200,6.022432,0.000000,0.000000,-0.130008,0.991513,0,0,1,15354), + (@OGUID2+66,181549,533,3,1,2776.160000,-3721.793000,274.387000,3.937367,0.000000,0.000000,-0.921882,0.387472,0,0,1,15354), + (@OGUID2+67,181551,533,3,1,2774.989000,-3731.793000,274.387000,3.927917,0.000000,0.000000,-0.923702,0.383111,0,0,1,15354), + (@OGUID2+68,181548,533,3,1,2765.765000,-3718.734000,274.316700,4.807982,0.000000,0.000000,-0.672515,0.740084,0,0,1,15354), + (@OGUID2+69,181546,533,3,1,2761.821000,-3711.915000,274.314800,3.961473,0.000000,0.000000,-0.917145,0.398554,0,0,1,15354), + (@OGUID2+70,181550,533,3,1,2765.327000,-3728.606000,274.314800,6.217947,0.000000,0.000000,-0.032614,0.999468,0,0,1,15354), + (@OGUID2+71,181547,533,3,1,2754.189000,-3718.121000,274.316700,5.370356,0.000000,0.000000,-0.440733,0.897638,0,0,1,15354), + (@OGUID2+72,181538,533,3,1,2752.927000,-3706.516000,274.316700,1.047839,0.000000,0.000000,0.500278,0.865865,0,0,1,15354), + (@OGUID2+73,181537,533,3,1,2738.396000,-3703.130000,274.385100,5.746106,0.000000,0.000000,-0.265324,0.964159,0,0,1,15354), + (@OGUID2+74,181539,533,3,1,2746.133000,-3700.192000,274.316700,5.493282,0.000000,0.000000,-0.384764,0.923015,0,0,1,15354), + (@OGUID2+75,181549,533,3,1,2776.160000,-3721.793000,274.387000,3.937367,0.000000,0.000000,-0.921882,0.387472,0,0,1,15354); + + + +-- ======= -- +-- Loatheb -- +-- ======= -- +-- Get rid of the superfluous aurascript for a dummy that's just there to make him talk +DELETE FROM `spell_script_names` WHERE `scriptname`="spell_loatheb_necrotic_aura_warning"; + +-- ======== -- +-- Thaddius -- +-- ======== -- +-- Move UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE (thaddius' base state) to creature_template instead of applying it in script +UPDATE `creature_template` SET `unit_flags`=33554688 WHERE `entry`=15928; +-- Move inactive aura to creature_template_addon +DELETE FROM `creature_template_addon` WHERE entry = 15928; +INSERT INTO `creature_template_addon` (`entry`,`auras`) VALUES (15928,"28160"); + +-- ==================== -- +-- Instructor Razuvious -- +-- ==================== -- +-- Razuvious has been informed that Rubik's Cubes become noticably easier to solve if you buy six-colored ones. +-- Thus, he will no longer take out his frustration on raid groups by throwing unsolved two-colored variants at them. +UPDATE `creature_equip_template` SET `itemid3`=29010 WHERE `creatureid`=16061; +DELETE FROM `spell_custom_attr` WHERE `entry`=55550; +INSERT INTO `spell_custom_attr` (`entry`,`attributes`) VALUES +(55550,524288); +-- Also, give his understudies actual weaponry. The poor sods. +DELETE FROM `creature_equip_template` WHERE `CreatureID`=16803; +INSERT INTO `creature_equip_template` (`CreatureID`,`ItemID1`,`ItemID2`,`VerifiedBuild`) VALUES +(16803,2180,23356,"15354"); + +-- ================= -- +-- The Four Horsemen -- +-- ================= -- +-- Wanna hear something fun? +-- On the current core, you can shackle, daze, stun (and some others) Baron Rivendare. +-- Yes, only Baron Rivendare. The other Horsemen are fine. Why? I have no idea. +-- Fixing that. +UPDATE `creature_template` SET `mechanic_immune_mask`=617299803 WHERE `entry` in (16063,16064,16065,30549); + +-- ========= -- +-- Sapphiron -- +-- ========= -- +DELETE FROM `spell_script_names` WHERE `spell_id` in (24780,28522,28524,28560); +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES +(24780,"spell_sapphiron_change_blizzard_target"), -- Periodic aura on the Blizzard npc that handles target switches +(28522,"spell_sapphiron_icebolt"), -- AuraScript for spawning ice block GO once the player has stopped moving +(28524,"spell_sapphiron_frost_breath"), -- We can't get rid of the LoS emulation "hack" on frost breath targeting (yet!), but at least moving it to a spellscript... +(28560,"spell_sapphiron_summon_blizzard"); -- Blizzard is now properly summoned! Yay! +-- DB target position for the anti-cheese frost explosion +DELETE FROM `spell_target_position` WHERE `ID`=29318; +INSERT INTO `spell_target_position` (`ID`,`EffectIndex`,`MapID`,`PositionX`,`PositionY`,`PositionZ`,`VerifiedBuild`) VALUES +(29318,0,533,3493.45,-5375.38,138.168,"15595"); +-- Wing Buffet trigger NPC +UPDATE `creature_template` SET `unit_flags`=33554944,`unit_flags2`=2048,`flags_extra`=128,`spell1`=29328,`BaseAttackTime`=1000,`ScriptName`="trigger_periodic" WHERE `entry`=17025; +-- Blizzard bunny NPC +UPDATE `creature_template` SET `speed_run`=0.42857142,`BaseAttackTime`=3000,`InhabitType`=1 WHERE `entry`=16474; +DELETE FROM `creature_template_addon` WHERE `entry`=16474; +INSERT INTO `creature_template_addon` (`entry`,`auras`) VALUES (16474,"24780"); +-- Spawn GO is now spawned by DB +DELETE FROM `gameobject` WHERE `guid` between @OGUID+0 and @OGUID+0; +INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`VerifiedBuild`) VALUES +(@OGUID+0, 181356, 533, 3, 1, 3522.565, -5236.76, 137.6257, 4.485497, 0, 0, -0.782608, 0.6225148,0,0); +UPDATE `gameobject_template` SET `ScriptName`="go_sapphiron_birth" WHERE `entry`=181356; +-- Turn off interactivity on ice blocks +UPDATE `gameobject_template` SET `type`=5,`data2`=0 WHERE `entry`=181247; +-- Text cleanup +UPDATE `creature_text` SET `language`=0, `emote`=0, `textrange`=3 WHERE `entry`=15989; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 06e416d77f2..b2fb2766fb0 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3811,7 +3811,7 @@ void Spell::SendSpellStart() if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_PENDING; - if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO)) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) castFlags |= CAST_FLAG_AMMO; if ((m_caster->GetTypeId() == TYPEID_PLAYER || (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) @@ -3864,7 +3864,7 @@ void Spell::SendSpellGo() if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_PENDING; - if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO)) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index ba658c885fa..34208f268bd 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -187,6 +187,7 @@ enum SpellCustomAttributes SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER = 0x00010000, SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET = 0x00020000, SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET = 0x00040000, + SPELL_ATTR0_CU_NEEDS_AMMO_DATA = 0x00080000, SPELL_ATTR0_CU_NEGATIVE = SPELL_ATTR0_CU_NEGATIVE_EFF0 | SPELL_ATTR0_CU_NEGATIVE_EFF1 | SPELL_ATTR0_CU_NEGATIVE_EFF2 }; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 8bb0e401a6c..635a545bd5b 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3056,6 +3056,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 48246: // Ball of Flame case 36327: // Shoot Arcane Explosion Arrow case 55479: // Force Obedience + case 28560: // Summon Blizzard (Sapphiron) spellInfo->MaxAffectedTargets = 1; break; case 36384: // Skartax Purple Beam diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index e8c4216b00e..60c60640c04 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -162,13 +162,13 @@ public: summons.DoZoneInCombat(); events.SetPhase(PHASE_NORMAL); - events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); - events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL); - events.ScheduleEvent(EVENT_LOCUST, urand(80,120) * IN_MILLISECONDS, 0, PHASE_NORMAL); - events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_LOCUST, Minutes(1)+randtime(Seconds(40), Seconds(60)), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); if (!Is25ManRaid()) - events.ScheduleEvent(EVENT_SPAWN_GUARD, urand(15, 20) * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SPAWN_GUARD, randtime(Seconds(15), Seconds(20))); } void UpdateAI(uint32 diff) override @@ -189,11 +189,9 @@ public: else EnterEvadeMode(); - events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); + events.Repeat(randtime(Seconds(10), Seconds(20))); break; case EVENT_SCARABS: - events.ScheduleEvent(EVENT_SCARABS, urand(40 * IN_MILLISECONDS, 60 * IN_MILLISECONDS), 0, PHASE_NORMAL); - if (!guardCorpses.empty()) { if (ObjectGuid target = Trinity::Containers::SelectRandomContainerElement(guardCorpses)) @@ -204,27 +202,28 @@ public: creatureTarget->DespawnOrUnsummon(); } } + events.Repeat(randtime(Seconds(40), Seconds(60))); break; case EVENT_LOCUST: Talk(EMOTE_LOCUST); + events.SetPhase(PHASE_SWARM); DoCast(me, SPELL_LOCUST_SWARM); - events.ScheduleEvent(EVENT_SPAWN_GUARD, 3 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(19, 23) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_LOCUST, 90000); - events.SetPhase(PHASE_SWARM); + events.ScheduleEvent(EVENT_SPAWN_GUARD, Seconds(3)); + events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(Seconds(19), Seconds(23))); + events.Repeat(Minutes(1)+Seconds(30)); break; case EVENT_LOCUST_ENDS: - events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); - events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL); events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL); break; case EVENT_SPAWN_GUARD: me->SummonCreatureGroup(GROUP_SINGLE_SPAWN); break; case EVENT_BERSERK: DoCast(me, SPELL_BERSERK, true); - events.ScheduleEvent(EVENT_BERSERK, 600000); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); break; } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp index 39c41c935bf..8a7bdd293ba 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp @@ -100,9 +100,9 @@ class boss_faerlina : public CreatureScript _EnterCombat(); Talk(SAY_AGGRO); summons.DoZoneInCombat(); - events.ScheduleEvent(EVENT_POISON, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS)); - events.ScheduleEvent(EVENT_FIRE, urand(6 * IN_MILLISECONDS, 18 * IN_MILLISECONDS)); - events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15))); + events.ScheduleEvent(EVENT_FIRE, randtime(Seconds(6), Seconds(18))); + events.ScheduleEvent(EVENT_FRENZY, Minutes(1)+randtime(Seconds(0), Seconds(20))); } void Reset() override @@ -111,9 +111,9 @@ class boss_faerlina : public CreatureScript _frenzyDispels = 0; } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override { - if (!urand(0, 2)) + if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } @@ -158,21 +158,21 @@ class boss_faerlina : public CreatureScript case EVENT_POISON: if (!me->HasAura(SPELL_WIDOWS_EMBRACE_HELPER)) DoCastAOE(SPELL_POISON_BOLT_VOLLEY); - events.ScheduleEvent(EVENT_POISON, urand(8000, 15000)); + events.Repeat(randtime(Seconds(8), Seconds(15))); break; case EVENT_FIRE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) DoCast(target, SPELL_RAIN_OF_FIRE); - events.ScheduleEvent(EVENT_FIRE, urand(6000, 18000)); + events.Repeat(randtime(Seconds(6), Seconds(18))); break; case EVENT_FRENZY: if (Aura* widowsEmbrace = me->GetAura(SPELL_WIDOWS_EMBRACE_HELPER)) - events.ScheduleEvent(EVENT_FRENZY, widowsEmbrace->GetDuration()+1 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FRENZY, Milliseconds(widowsEmbrace->GetDuration()+1)); else { DoCast(SPELL_FRENZY); Talk(EMOTE_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS)); + events.Repeat(Minutes(1) + randtime(Seconds(0), Seconds(20))); } break; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp index a7a89f44d81..0de1c4785b8 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp @@ -285,7 +285,7 @@ struct boss_four_horsemen_baseAI : public BossAI void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_HORSEMEN) != NOT_STARTED) // another horseman already did it + if (instance->GetBossState(BOSS_HORSEMEN) == IN_PROGRESS || instance->GetBossState(BOSS_HORSEMEN) == DONE) // another horseman already did it return; Talk(SAY_AGGRO); BeginEncounter(); @@ -411,9 +411,9 @@ class boss_four_horsemen_baron : public CreatureScript else AttackStart(threat.getHostilTarget()); - events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(3,7)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_MARK, Seconds(24)); + events.ScheduleEvent(EVENT_UNHOLYSHADOW, randtime(Seconds(3), Seconds(7))); } void _UpdateAI(uint32 diff) override @@ -431,11 +431,11 @@ class boss_four_horsemen_baron : public CreatureScript break; case EVENT_MARK: DoCastAOE(SPELL_BARON_MARK, true); - events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS); + events.Repeat(Seconds(12)); break; case EVENT_UNHOLYSHADOW: DoCastVictim(SPELL_UNHOLY_SHADOW); - events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(10,30)); + events.Repeat(randtime(Seconds(10), Seconds(30))); break; } } @@ -484,9 +484,9 @@ class boss_four_horsemen_thane : public CreatureScript else AttackStart(threat.getHostilTarget()); - events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_METEOR, urandms(10,15)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_MARK, Seconds(24)); + events.ScheduleEvent(EVENT_METEOR, randtime(Seconds(10), Seconds(25))); } void _UpdateAI(uint32 diff) override { @@ -503,7 +503,7 @@ class boss_four_horsemen_thane : public CreatureScript break; case EVENT_MARK: DoCastAOE(SPELL_THANE_MARK, true); - events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS); + events.Repeat(Seconds(12)); break; case EVENT_METEOR: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true)) @@ -511,7 +511,7 @@ class boss_four_horsemen_thane : public CreatureScript DoCast(target, SPELL_METEOR); _shouldSay = true; } - events.ScheduleEvent(EVENT_METEOR, urandms(13,17)); + events.Repeat(randtime(Seconds(13), Seconds(17))); break; } } @@ -550,9 +550,9 @@ class boss_four_horsemen_lady : public CreatureScript boss_four_horsemen_ladyAI(Creature* creature) : boss_four_horsemen_baseAI(creature, LADY, ladyPath) { } void BeginFighting() override { - events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_VOIDZONE, urandms(5,10)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_MARK, Seconds(24)); + events.ScheduleEvent(EVENT_VOIDZONE, randtime(Seconds(5), Seconds(10))); } void _UpdateAI(uint32 diff) override @@ -578,7 +578,7 @@ class boss_four_horsemen_lady : public CreatureScript break; case EVENT_MARK: DoCastAOE(SPELL_LADY_MARK, true); - events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS); + events.Repeat(Seconds(15)); break; case EVENT_VOIDZONE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) @@ -586,7 +586,7 @@ class boss_four_horsemen_lady : public CreatureScript DoCast(target, SPELL_VOID_ZONE, true); Talk(SAY_SPECIAL); } - events.ScheduleEvent(EVENT_VOIDZONE, urandms(12, 18)); + events.Repeat(randtime(Seconds(12), Seconds(18))); break; } } @@ -620,9 +620,9 @@ class boss_four_horsemen_sir : public CreatureScript boss_four_horsemen_sirAI(Creature* creature) : boss_four_horsemen_baseAI(creature, SIR, sirPath), _shouldSay(true) { } void BeginFighting() override { - events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_HOLYWRATH, urandms(13,18)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_MARK, Seconds(24)); + events.ScheduleEvent(EVENT_HOLYWRATH, randtime(Seconds(13), Seconds(18))); } void _UpdateAI(uint32 diff) override @@ -648,7 +648,7 @@ class boss_four_horsemen_sir : public CreatureScript break; case EVENT_MARK: DoCastAOE(SPELL_SIR_MARK, true); - events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS); + events.Repeat(Seconds(15)); break; case EVENT_HOLYWRATH: if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true)) @@ -656,7 +656,7 @@ class boss_four_horsemen_sir : public CreatureScript DoCast(target, SPELL_HOLY_WRATH, true); _shouldSay = true; } - events.ScheduleEvent(EVENT_HOLYWRATH, urandms(10,18)); + events.Repeat(randtime(Seconds(10), Seconds(18))); break; } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index cf083900e8b..30c05c9dca9 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -324,13 +324,13 @@ class boss_gothik : public CreatureScript { _EnterCombat(); events.SetPhase(PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON, 25 * IN_MILLISECONDS, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_DOORS_UNLOCK, 205 * IN_MILLISECONDS, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_PHASE_TWO, 270 * IN_MILLISECONDS, 0, PHASE_ONE); + events.ScheduleEvent(EVENT_SUMMON, Seconds(25), 0, PHASE_ONE); + events.ScheduleEvent(EVENT_DOORS_UNLOCK, Minutes(3) + Seconds(25), 0, PHASE_ONE); + events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(4) + Seconds(30), 0, PHASE_ONE); Talk(SAY_INTRO_1); - events.ScheduleEvent(EVENT_INTRO_2, 4 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_INTRO_3, 9 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_INTRO_4, 14 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_INTRO_2, Seconds(4)); + events.ScheduleEvent(EVENT_INTRO_3, Seconds(9)); + events.ScheduleEvent(EVENT_INTRO_4, Seconds(14)); instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY); _gateIsOpen = false; } @@ -480,7 +480,7 @@ class boss_gothik : public CreatureScript } if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second) - events.ScheduleEvent(EVENT_SUMMON, timeToNext * IN_MILLISECONDS, 0, PHASE_ONE); + events.Repeat(Seconds(timeToNext)); ++_waveCount; break; @@ -497,9 +497,9 @@ class boss_gothik : public CreatureScript break; case EVENT_PHASE_TWO: events.SetPhase(PHASE_TWO); - events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_TELEPORT, Seconds(20), 0, PHASE_TWO); + events.ScheduleEvent(EVENT_HARVEST, Seconds(15), 0, PHASE_TWO); + events.ScheduleEvent(EVENT_RESUME_ATTACK, Seconds(2), 0, PHASE_TWO); Talk(SAY_PHASE_TWO); Talk(EMOTE_PHASE_TWO); me->SetReactState(REACT_PASSIVE); @@ -518,23 +518,23 @@ class boss_gothik : public CreatureScript _lastTeleportDead = !_lastTeleportDead; events.CancelEvent(EVENT_BOLT); - events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); + events.Repeat(Seconds(20)); } break; case EVENT_HARVEST: DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt - events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); + events.Repeat(Seconds(15)); break; case EVENT_RESUME_ATTACK: me->SetReactState(REACT_AGGRESSIVE); - events.ScheduleEvent(EVENT_BOLT, 0, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_BOLT, Seconds(0), 0, PHASE_TWO); // return to the start of this method so victim side etc is re-evaluated return UpdateAI(0u); // tail recursion for efficiency case EVENT_BOLT: DoCastVictim(SPELL_SHADOW_BOLT); - events.ScheduleEvent(EVENT_BOLT, 1 * IN_MILLISECONDS, 0, PHASE_TWO); + events.Repeat(Seconds(2)); break; case EVENT_INTRO_2: Talk(SAY_INTRO_2); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp index a1d8f6e5e02..cf38d659d40 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp @@ -57,10 +57,10 @@ class boss_grobbulus : public CreatureScript void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_CLOUD, 15000); - events.ScheduleEvent(EVENT_INJECT, 20000); - events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000)); // not sure - events.ScheduleEvent(EVENT_BERSERK, 12 * 60000); + events.ScheduleEvent(EVENT_CLOUD, Seconds(15)); + events.ScheduleEvent(EVENT_INJECT, Seconds(20)); + events.ScheduleEvent(EVENT_SPRAY, randtime(Seconds(15), Seconds(30))); // not sure + events.ScheduleEvent(EVENT_BERSERK, Minutes(12)); } void SpellHitTarget(Unit* target, SpellInfo const* spell) override @@ -82,19 +82,19 @@ class boss_grobbulus : public CreatureScript { case EVENT_CLOUD: DoCastAOE(SPELL_POISON_CLOUD); - events.ScheduleEvent(EVENT_CLOUD, 15000); + events.Repeat(Seconds(15)); return; case EVENT_BERSERK: DoCastAOE(SPELL_BERSERK, true); return; case EVENT_SPRAY: DoCastAOE(SPELL_SLIME_SPRAY); - events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000)); + events.Repeat(randtime(Seconds(15), Seconds(30))); return; case EVENT_INJECT: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_MUTATING_INJECTION)) DoCast(target, SPELL_MUTATING_INJECTION); - events.ScheduleEvent(EVENT_INJECT, 8000 + uint32(120 * me->GetHealthPct())); + events.Repeat(Seconds(8) + Milliseconds(uint32(std::round(120 * me->GetHealthPct())))); return; default: break; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp index 9b9619fe5b9..41d718d188d 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp @@ -27,6 +27,7 @@ enum Spells SPELL_SPELL_DISRUPTION = 29310, SPELL_PLAGUE_CLOUD = 29350, SPELL_TELEPORT_SELF = 30211, + SPELL_ERUPTION = 29371 }; enum Yells @@ -60,6 +61,15 @@ enum Misc DATA_SAFETY_DANCE = 19962139 }; +static const uint32 firstEruptionDBGUID = 84980; +static const uint8 numSections = 4; +static const uint8 numEruptions[numSections] = { // count of sequential GO DBGUIDs in the respective section of the room + 15, + 25, + 23, + 13 +}; + class boss_heigan : public CreatureScript { public: @@ -72,7 +82,7 @@ public: struct boss_heiganAI : public BossAI { - boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), eruptSection(0), eruptDirection(false), safetyDance(false) { } + boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), _safeSection(0), _danceDirection(false), _safetyDance(false) { } void Reset() override { @@ -82,15 +92,16 @@ public: void KilledUnit(Unit* who) override { - Talk(SAY_SLAY); - if (who->GetTypeId() == TYPEID_PLAYER) - safetyDance = false; + { + Talk(SAY_SLAY); + _safetyDance = false; + } } uint32 GetData(uint32 type) const override { - return (type == DATA_SAFETY_DANCE && safetyDance) ? 1u : 0u; + return (type == DATA_SAFETY_DANCE && _safetyDance) ? 1u : 0u; } void JustDied(Unit* /*killer*/) override @@ -104,13 +115,27 @@ public: _EnterCombat(); Talk(SAY_AGGRO); - eruptSection = 3; - events.ScheduleEvent(EVENT_DISRUPT, urand(15 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_FEVER, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT); + _safeSection = 0; + events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(10), Seconds(20)), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT); + + _safetyDance = true; - safetyDance = true; + // figure out the current GUIDs of our eruption tiles and which segment they belong in + std::unordered_multimap<uint32, GameObject*> const& mapGOs = me->GetMap()->GetGameObjectBySpawnIdStore(); + uint32 spawnId = firstEruptionDBGUID; + for (uint8 section = 0; section < numSections; ++section) + { + _eruptTiles[section].clear(); + for (uint8 i = 0; i < numEruptions[section]; ++i) + { + std::pair<std::unordered_multimap<uint32, GameObject*>::const_iterator, std::unordered_multimap<uint32, GameObject*>::const_iterator> tileIt = mapGOs.equal_range(spawnId++); + for (std::unordered_multimap<uint32, GameObject*>::const_iterator it = tileIt.first; it != tileIt.second; ++it) + _eruptTiles[section].push_back(it->second->GetGUID()); + } + } } void UpdateAI(uint32 diff) override @@ -126,52 +151,56 @@ public: { case EVENT_DISRUPT: DoCastAOE(SPELL_SPELL_DISRUPTION); - events.ScheduleEvent(EVENT_DISRUPT, 11 * IN_MILLISECONDS); + events.Repeat(Seconds(11)); break; case EVENT_FEVER: DoCastAOE(SPELL_DECREPIT_FEVER); - events.ScheduleEvent(EVENT_FEVER, urand(20 * IN_MILLISECONDS, 25 * IN_MILLISECONDS)); + events.Repeat(randtime(Seconds(20), Seconds(25))); break; case EVENT_DANCE: events.SetPhase(PHASE_DANCE); Talk(SAY_TAUNT); Talk(EMOTE_DANCE); - eruptSection = 3; + _safeSection = 0; me->SetReactState(REACT_PASSIVE); me->AttackStop(); me->StopMoving(); DoCast(SPELL_TELEPORT_SELF); DoCastAOE(SPELL_PLAGUE_CLOUD); - events.ScheduleEvent(EVENT_DANCE_END, 45 * IN_MILLISECONDS, 0, PHASE_DANCE); - events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_DANCE_END, Seconds(45), 0, PHASE_DANCE); + events.ScheduleEvent(EVENT_ERUPT, Seconds(10)); break; case EVENT_DANCE_END: events.SetPhase(PHASE_FIGHT); Talk(EMOTE_DANCE_END); - eruptSection = 3; - events.ScheduleEvent(EVENT_DISRUPT, urand(10, 25) * IN_MILLISECONDS, 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_FEVER, urand(15, 20) * IN_MILLISECONDS, 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT); + _safeSection = 0; + events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(10), Seconds(25)), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT); me->CastStop(); me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); break; case EVENT_ERUPT: - instance->SetData(DATA_HEIGAN_ERUPT, eruptSection); TeleportCheaters(); - - if (eruptSection == 0) - eruptDirection = true; - else if (eruptSection == 3) - eruptDirection = false; - - eruptDirection ? ++eruptSection : --eruptSection; - - if (events.IsInPhase(PHASE_DANCE)) - events.ScheduleEvent(EVENT_ERUPT, 3 * IN_MILLISECONDS, 0, PHASE_DANCE); - else - events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS, 0, PHASE_FIGHT); + for (uint8 section = 0; section < numSections; ++section) + if (section != _safeSection) + for (ObjectGuid tileGUID : _eruptTiles[section]) + if (GameObject* tile = ObjectAccessor::GetGameObject(*me, tileGUID)) + { + tile->SendCustomAnim(0); + tile->CastSpell(nullptr, SPELL_ERUPTION); + } + + if (_safeSection == 0) + _danceDirection = true; + else if (_safeSection == numSections-1) + _danceDirection = false; + + _danceDirection ? ++_safeSection : --_safeSection; + + events.Repeat(events.IsInPhase(PHASE_DANCE) ? Seconds(3) : Seconds(10)); break; } } @@ -180,10 +209,11 @@ public: } private: - uint32 eruptSection; - bool eruptDirection; + std::vector<ObjectGuid> _eruptTiles[numSections]; // populated on encounter start - bool safetyDance; // is achievement still possible? (= no player deaths yet) + uint32 _safeSection; // 0 is next to the entrance + bool _danceDirection; // true is counter-clockwise, false is clock-wise + bool _safetyDance; // is achievement still possible? (= no player deaths yet) }; }; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp index 086d21120e8..c6c9c76bed4 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp @@ -24,7 +24,6 @@ enum Spells { SPELL_NECROTIC_AURA = 55593, - SPELL_WARN_NECROTIC_AURA = 59481, SPELL_SUMMON_SPORE = 29234, SPELL_DEATHBLOOM = 29865, SPELL_INEVITABLE_DOOM = 29204, @@ -42,11 +41,12 @@ enum Texts enum Events { - EVENT_NECROTIC_AURA = 1, - EVENT_DEATHBLOOM = 2, - EVENT_INEVITABLE_DOOM = 3, - EVENT_SPORE = 4, - EVENT_NECROTIC_AURA_FADING = 5, + EVENT_NECROTIC_AURA = 1, + EVENT_DEATHBLOOM, + EVENT_INEVITABLE_DOOM, + EVENT_SPORE, + EVENT_NECROTIC_AURA_FADING, + EVENT_NECROTIC_AURA_FADED }; enum Achievement @@ -82,10 +82,10 @@ class boss_loatheb : public CreatureScript void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_NECROTIC_AURA, 17 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_DEATHBLOOM, 5 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_NECROTIC_AURA, Seconds(17)); + events.ScheduleEvent(EVENT_DEATHBLOOM, Seconds(5)); + events.ScheduleEvent(EVENT_SPORE, Seconds(18)); + events.ScheduleEvent(EVENT_INEVITABLE_DOOM, Minutes(2)); } void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override @@ -94,13 +94,6 @@ class boss_loatheb : public CreatureScript summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true); } - void SummonedCreatureDespawn(Creature* summon) override - { - summons.Despawn(summon); - if (summon->IsAlive()) - summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true); - } - uint32 GetData(uint32 id) const override { return (_sporeLoserData && id == DATA_ACHIEVEMENT_SPORE_LOSER) ? 1u : 0u; @@ -119,34 +112,33 @@ class boss_loatheb : public CreatureScript { case EVENT_NECROTIC_AURA: DoCastAOE(SPELL_NECROTIC_AURA); - DoCast(me, SPELL_WARN_NECROTIC_AURA); - events.ScheduleEvent(EVENT_NECROTIC_AURA, 20 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14 * IN_MILLISECONDS); + Talk(SAY_NECROTIC_AURA_APPLIED); + events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, Seconds(14)); + events.ScheduleEvent(EVENT_NECROTIC_AURA_FADED, Seconds(17)); + events.Repeat(Seconds(20)); break; case EVENT_DEATHBLOOM: DoCastAOE(SPELL_DEATHBLOOM); - events.ScheduleEvent(EVENT_DEATHBLOOM, 30 * IN_MILLISECONDS); + events.Repeat(Seconds(30)); break; case EVENT_INEVITABLE_DOOM: _doomCounter++; DoCastAOE(SPELL_INEVITABLE_DOOM); if (_doomCounter > 6) - { - if (_doomCounter & 1) // odd - events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 14 * IN_MILLISECONDS); - else - events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 17 * IN_MILLISECONDS); - } + events.Repeat((_doomCounter & 1) ? Seconds(14) : Seconds(17)); else - events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 30 * IN_MILLISECONDS); + events.Repeat(30); break; case EVENT_SPORE: DoCast(me, SPELL_SUMMON_SPORE, false); - events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS); + events.Repeat(RAID_MODE(Seconds(36), Seconds(15))); break; case EVENT_NECROTIC_AURA_FADING: Talk(SAY_NECROTIC_AURA_FADING); break; + case EVENT_NECROTIC_AURA_FADED: + Talk(SAY_NECROTIC_AURA_REMOVED); + break; default: break; } @@ -177,49 +169,6 @@ class achievement_spore_loser : public AchievementCriteriaScript } }; -typedef boss_loatheb::boss_loathebAI LoathebAI; - -class spell_loatheb_necrotic_aura_warning : public SpellScriptLoader -{ - public: - spell_loatheb_necrotic_aura_warning() : SpellScriptLoader("spell_loatheb_necrotic_aura_warning") { } - - class spell_loatheb_necrotic_aura_warning_AuraScript : public AuraScript - { - PrepareAuraScript(spell_loatheb_necrotic_aura_warning_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellStore.LookupEntry(SPELL_WARN_NECROTIC_AURA)) - return false; - return true; - } - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (GetTarget()->IsAIEnabled) - ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_APPLIED); - } - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (GetTarget()->IsAIEnabled) - ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_REMOVED); - } - - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_loatheb_necrotic_aura_warning_AuraScript(); - } -}; - class spell_loatheb_deathbloom : public SpellScriptLoader { public: @@ -260,6 +209,5 @@ void AddSC_boss_loatheb() { new boss_loatheb(); new achievement_spore_loser(); - new spell_loatheb_necrotic_aura_warning(); new spell_loatheb_deathbloom(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp index 9d8f1365afb..9502193884a 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp @@ -73,6 +73,8 @@ struct WebTargetSelector : public std::unary_function<Unit*, bool> WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {} bool operator()(Unit const* target) const { + if (target->GetTypeId() != TYPEID_PLAYER) // never web nonplayers (pets, guardians, etc.) + return false; if (_maexxna->GetVictim() == target) // never target tank return false; if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed @@ -101,11 +103,11 @@ public: void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10)); - events.ScheduleEvent(EVENT_POISON, urandms(10, 15)); - events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_WRAP, Seconds(20)); + events.ScheduleEvent(EVENT_SPRAY, Seconds(40)); + events.ScheduleEvent(EVENT_SHOCK, randtime(Seconds(5), Seconds(10))); + events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15))); + events.ScheduleEvent(EVENT_SUMMON, Seconds(30)); } void Reset() override @@ -153,28 +155,28 @@ public: } } } - events.ScheduleEvent(EVENT_WRAP, 40000); + events.Repeat(Seconds(40)); break; } case EVENT_SPRAY: Talk(EMOTE_WEB_SPRAY); DoCastAOE(SPELL_WEB_SPRAY); - events.ScheduleEvent(EVENT_SPRAY, 40000); + events.Repeat(Seconds(40)); break; case EVENT_SHOCK: DoCastAOE(SPELL_POISON_SHOCK); - events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20)); + events.Repeat(randtime(Seconds(10), Seconds(20))); break; case EVENT_POISON: DoCastVictim(SPELL_NECROTIC_POISON); - events.ScheduleEvent(EVENT_POISON, urandms(10, 20)); + events.Repeat(randtime(Seconds(10), Seconds(20))); break; case EVENT_SUMMON: Talk(EMOTE_SPIDERS); uint8 amount = urand(8, 10); for (uint8 i = 0; i < amount; ++i) DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); - events.ScheduleEvent(EVENT_SUMMON, 40000); + events.Repeat(Seconds(40)); break; } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index 8ee3936dee3..2108b4ce102 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -131,25 +131,25 @@ public: Reset(); else { - uint8 secondsGround; + uint8 timeGround; switch (balconyCount) { case 0: - secondsGround = 90; + timeGround = 90; break; case 1: - secondsGround = 110; + timeGround = 110; break; case 2: default: - secondsGround = 180; + timeGround = 180; } - events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, 2 * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_BALCONY, secondsGround * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_CURSE, urand(10,25) * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_WARRIOR, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - events.ScheduleEvent(EVENT_BLINK, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); } } @@ -220,7 +220,7 @@ public: case EVENT_CURSE: { DoCastAOE(SPELL_CURSE); - events.ScheduleEvent(EVENT_CURSE, urand(50, 70) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.Repeat(randtime(Seconds(50), Seconds(70))); break; } case EVENT_WARRIOR: @@ -229,7 +229,7 @@ public: CastSummon(RAID_MODE(2, 3), 0, 0); - events.ScheduleEvent(EVENT_WARRIOR, 40 * IN_MILLISECONDS, 0, PHASE_GROUND); + events.Repeat(Seconds(40)); break; case EVENT_BLINK: DoCastAOE(SPELL_CRIPPLE, true); @@ -237,7 +237,7 @@ public: DoResetThreat(); justBlinked = true; - events.ScheduleEvent(EVENT_BLINK, 40000, 0, PHASE_GROUND); + events.Repeat(Seconds(40)); break; case EVENT_BALCONY: events.SetPhase(PHASE_BALCONY); @@ -247,24 +247,24 @@ public: me->StopMoving(); me->RemoveAllAuras(); - events.ScheduleEvent(EVENT_BALCONY_TELEPORT, 3 * IN_MILLISECONDS, 0, PHASE_BALCONY); - events.ScheduleEvent(EVENT_WAVE, urand(5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS), 0, PHASE_BALCONY); + events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY); + events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY); - uint8 secondsBalcony; + uint8 timeBalcony; switch (balconyCount) { case 0: - secondsBalcony = 70; + timeBalcony = 70; break; case 1: - secondsBalcony = 97; + timeBalcony = 97; break; case 2: default: - secondsBalcony = 120; + timeBalcony = 120; break; } - events.ScheduleEvent(EVENT_GROUND, secondsBalcony * IN_MILLISECONDS, 0, PHASE_BALCONY); + events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY); break; case EVENT_BALCONY_TELEPORT: Talk(EMOTE_TELEPORT_1); @@ -287,7 +287,7 @@ public: CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10)); break; } - events.ScheduleEvent(EVENT_WAVE, urand(30, 45) * IN_MILLISECONDS, 0, PHASE_BALCONY); + events.Repeat(randtime(Seconds(30), Seconds(45))); break; case EVENT_GROUND: ++balconyCount; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index b8ce402a939..cfa6e2e9f30 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -97,8 +97,8 @@ public: _EnterCombat(); Enraged = false; Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_HATEFUL, Seconds(1)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(6)); instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT); } @@ -167,17 +167,17 @@ public: if (thirdThreatTarget) me->getThreatManager().addThreat(thirdThreatTarget, HATEFUL_THREAT_AMT); // this will only ever be used in 25m - events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS); + events.Repeat(Seconds(1)); break; } case EVENT_BERSERK: DoCast(me, SPELL_BERSERK, true); Talk(EMOTE_BERSERK); - events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SLIME, Seconds(2)); break; case EVENT_SLIME: - DoCastVictim(SPELL_SLIME_BOLT, true); - events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS); + DoCastAOE(SPELL_SLIME_BOLT, true); + events.Repeat(Seconds(2)); break; } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp index 1d12f64a949..548f8086eb8 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp @@ -104,6 +104,9 @@ public: void JustDied(Unit* /*killer*/) override { + for (ObjectGuid summonGuid : summons) + if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid)) + summon->RemoveCharmAuras(); Talk(SAY_DEATH); DoCastAOE(SPELL_HOPELESS, true); @@ -117,10 +120,10 @@ public: me->StopMoving(); summons.DoZoneInCombat(); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_ATTACK, 7 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_STRIKE, 21 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_KNIFE, 10 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_ATTACK, Seconds(7)); + events.ScheduleEvent(EVENT_STRIKE, Seconds(21)); + events.ScheduleEvent(EVENT_SHOUT, Seconds(16)); + events.ScheduleEvent(EVENT_KNIFE, Seconds(10)); } void UpdateAI(uint32 diff) override @@ -141,16 +144,16 @@ public: break; case EVENT_STRIKE: DoCastVictim(SPELL_UNBALANCING_STRIKE); - events.ScheduleEvent(EVENT_STRIKE, 6 * IN_MILLISECONDS); + events.Repeat(Seconds(6)); return; case EVENT_SHOUT: DoCastAOE(SPELL_DISRUPTING_SHOUT); - events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS); + events.Repeat(Seconds(16)); return; case EVENT_KNIFE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f)) DoCast(target, SPELL_JAGGED_KNIFE); - events.ScheduleEvent(EVENT_KNIFE, urandms(10,15)); + events.Repeat(randtime(Seconds(10), Seconds(15))); return; } } @@ -174,7 +177,10 @@ class npc_dk_understudy : public CreatureScript struct npc_dk_understudyAI : public ScriptedAI { - npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) { } + npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) + { + creature->LoadEquipment(1); + } void EnterCombat(Unit* /*who*/) override { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp index 68b9e252150..4596b17bc23 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp @@ -16,7 +16,9 @@ */ #include "ScriptMgr.h" +#include "GameObjectAI.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "Player.h" #include "SpellInfo.h" #include "naxxramas.h" @@ -31,25 +33,24 @@ enum Yells enum Spells { - SPELL_FROST_AURA = 28531, - SPELL_CLEAVE = 19983, - SPELL_TAIL_SWEEP = 55697, - SPELL_SUMMON_BLIZZARD = 28560, - SPELL_LIFE_DRAIN = 28542, - SPELL_ICEBOLT = 28522, - SPELL_FROST_BREATH = 29318, - SPELL_FROST_EXPLOSION = 28524, - SPELL_FROST_MISSILE = 30101, - SPELL_BERSERK = 26662, - SPELL_DIES = 29357, - SPELL_CHILL = 28547, - SPELL_CHECK_RESISTS = 60539, + SPELL_FROST_AURA = 28531, + SPELL_CLEAVE = 19983, + SPELL_TAIL_SWEEP = 55697, + SPELL_SUMMON_BLIZZARD = 28560, + SPELL_LIFE_DRAIN = 28542, + SPELL_ICEBOLT = 28522, + SPELL_FROST_BREATH_ANTICHEAT = 29318, // damage effect ignoring LoS on the entrance platform to prevent cheese + SPELL_FROST_BREATH = 28524, // damage effect below sapphiron + SPELL_FROST_MISSILE = 30101, // visual only + SPELL_BERSERK = 26662, + SPELL_DIES = 29357, + SPELL_CHECK_RESISTS = 60539, + SPELL_SAPPHIRON_WING_BUFFET = 29328 }; enum Phases { - PHASE_NULL = 0, - PHASE_BIRTH, + PHASE_BIRTH = 1, PHASE_GROUND, PHASE_FLIGHT }; @@ -72,9 +73,15 @@ enum Events EVENT_CHECK_RESISTS }; +enum Actions +{ + ACTION_BIRTH = 1 +}; + enum Misc { NPC_BLIZZARD = 16474, + NPC_WING_BUFFET = 17025, GO_ICEBLOCK = 181247, // The Hundred Club @@ -92,77 +99,74 @@ class boss_sapphiron : public CreatureScript struct boss_sapphironAI : public BossAI { boss_sapphironAI(Creature* creature) : - BossAI(creature, BOSS_SAPPHIRON), _iceboltCount(0), _map(me->GetMap()) + BossAI(creature, BOSS_SAPPHIRON) { Initialize(); } void Initialize() { - _phase = PHASE_NULL; - + _delayedDrain = false; _canTheHundredClub = true; } void InitializeAI() override { + if (instance->GetBossState(BOSS_SAPPHIRON) == DONE) + return; + _canTheHundredClub = true; - float x, y, z; - me->GetPosition(x, y, z); - me->SummonGameObject(GO_BIRTH, x, y, z, 0, 0, 0, 0, 0, 0); - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_PASSIVE); + if (!instance->GetData(DATA_HAD_SAPPHIRON_BIRTH)) + { + me->SetVisible(false); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_PASSIVE); + } BossAI::InitializeAI(); } void Reset() override { - _Reset(); - - if (_phase == PHASE_FLIGHT) + if (events.IsInPhase(PHASE_FLIGHT)) { - ClearIceBlock(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ICEBOLT); me->SetReactState(REACT_AGGRESSIVE); if (me->IsHovering()) { me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); me->SetHover(false); } - me->SetDisableGravity(false); } + _Reset(); Initialize(); } + void DamageTaken(Unit* /*who*/, uint32& damage) override + { + if (damage < me->GetHealth() || !events.IsInPhase(PHASE_FLIGHT)) + return; + damage = me->GetHealth()-1; // don't die during air phase + } + void EnterCombat(Unit* /*who*/) override { _EnterCombat(); me->CastSpell(me, SPELL_FROST_AURA, true); - DoCast(me, SPELL_CHECK_RESISTS); - events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_BERSERK, 15 * MINUTE * IN_MILLISECONDS); - EnterPhaseGround(); + events.SetPhase(PHASE_GROUND); + events.ScheduleEvent(EVENT_CHECK_RESISTS, Seconds(0)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); + EnterPhaseGround(true); } void SpellHitTarget(Unit* target, SpellInfo const* spell) override { switch(spell->Id) { - case SPELL_ICEBOLT: - { - IceBlockMap::iterator itr = _iceblocks.find(target->GetGUID()); - if (itr != _iceblocks.end() && !itr->second) - { - if (GameObject* iceblock = me->SummonGameObject(GO_ICEBLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, 0, 0, 0, 0, 25)) - itr->second = iceblock->GetGUID(); - } - break; - } case SPELL_CHECK_RESISTS: if (target && target->GetResistance(SPELL_SCHOOL_FROST) > MAX_FROST_RESISTANCE) _canTheHundredClub = false; @@ -179,41 +183,37 @@ class boss_sapphiron : public CreatureScript void MovementInform(uint32 /*type*/, uint32 id) override { if (id == 1) - events.ScheduleEvent(EVENT_LIFTOFF, 0); + events.ScheduleEvent(EVENT_LIFTOFF, Seconds(0), 0, PHASE_FLIGHT); } void DoAction(int32 param) override { - if (param == DATA_SAPPHIRON_BIRTH) + if (param == ACTION_BIRTH) { - _phase = PHASE_BIRTH; - events.ScheduleEvent(EVENT_BIRTH, 23 * IN_MILLISECONDS); + events.SetPhase(PHASE_BIRTH); + events.ScheduleEvent(EVENT_BIRTH, Seconds(23)); } } - void EnterPhaseGround() + void EnterPhaseGround(bool initial) { - _phase = PHASE_GROUND; me->SetReactState(REACT_AGGRESSIVE); - events.SetPhase(PHASE_GROUND); - events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_BLIZZARD, urand(5, 10) * IN_MILLISECONDS, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_FLIGHT, 45 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_BLIZZARD, randtime(Seconds(5), Seconds(10)), 0, PHASE_GROUND); + if (initial) + { + events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28))); + events.ScheduleEvent(EVENT_FLIGHT, Seconds(48) + Milliseconds(500), 0, PHASE_GROUND); + } + else + events.ScheduleEvent(EVENT_FLIGHT, Minutes(1), 0, PHASE_GROUND); } - void ClearIceBlock() + inline void CastDrain() { - for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr) - { - if (Player* player = ObjectAccessor::GetPlayer(*me, itr->first)) - player->RemoveAura(SPELL_ICEBOLT); - - if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second)) - go->Delete(); - } - _iceblocks.clear(); + DoCastAOE(SPELL_LIFE_DRAIN); + events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28))); } uint32 GetData(uint32 data) const override @@ -226,15 +226,12 @@ class boss_sapphiron : public CreatureScript void UpdateAI(uint32 diff) override { - if (!_phase) - return; - events.Update(diff); - if (_phase != PHASE_BIRTH && !UpdateVictim()) + if (!events.IsInPhase(PHASE_BIRTH) && !UpdateVictim()) return; - if (_phase == PHASE_GROUND) + if (events.IsInPhase(PHASE_GROUND)) { while (uint32 eventId = events.ExecuteEvent()) { @@ -242,7 +239,10 @@ class boss_sapphiron : public CreatureScript { case EVENT_CHECK_RESISTS: DoCast(me, SPELL_CHECK_RESISTS); - events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS); + events.Repeat(Seconds(30)); + return; + case EVENT_GROUND: + EnterPhaseGround(false); return; case EVENT_BERSERK: Talk(EMOTE_ENRAGE); @@ -250,27 +250,26 @@ class boss_sapphiron : public CreatureScript return; case EVENT_CLEAVE: DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND); return; case EVENT_TAIL: DoCastAOE(SPELL_TAIL_SWEEP); - events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND); return; case EVENT_DRAIN: - DoCastAOE(SPELL_LIFE_DRAIN); - events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND); + if (events.IsInPhase(PHASE_FLIGHT)) + _delayedDrain = true; + else + CastDrain(); return; case EVENT_BLIZZARD: - { - if (Creature* summon = DoSummon(NPC_BLIZZARD, me, 0.0f, urand(25, 30) * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN)) - summon->GetMotionMaster()->MoveRandom(40); - events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(20, 7) * IN_MILLISECONDS, 0, PHASE_GROUND); + DoCastAOE(SPELL_SUMMON_BLIZZARD); + events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(Seconds(20), Seconds(7)), 0, PHASE_GROUND); break; - } case EVENT_FLIGHT: if (HealthAbovePct(10)) { - _phase = PHASE_FLIGHT; + _delayedDrain = false; events.SetPhase(PHASE_FLIGHT); me->SetReactState(REACT_PASSIVE); me->AttackStop(); @@ -293,61 +292,69 @@ class boss_sapphiron : public CreatureScript { case EVENT_CHECK_RESISTS: DoCast(me, SPELL_CHECK_RESISTS); - events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS); + events.Repeat(Seconds(30)); return; case EVENT_LIFTOFF: + { Talk(EMOTE_AIR_PHASE); - me->SetDisableGravity(true); + if (Creature* buffet = DoSummon(NPC_WING_BUFFET, me, 0.0f, 0, TEMPSUMMON_MANUAL_DESPAWN)) + _buffet = buffet->GetGUID(); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); me->SetHover(true); - events.ScheduleEvent(EVENT_ICEBOLT, 1500); - _iceboltCount = RAID_MODE(2, 3); + events.ScheduleEvent(EVENT_ICEBOLT, Seconds(7), 0, PHASE_FLIGHT); + + _iceboltTargets.clear(); + std::list<Unit*> targets; + SelectTargetList(targets, RAID_MODE(2, 3), SELECT_TARGET_RANDOM, 200.0f, true); + for (Unit* target : targets) + if (target) + _iceboltTargets.push_back(target->GetGUID()); return; + } case EVENT_ICEBOLT: { - std::vector<Unit*> targets; - std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin(); - for (; i != me->getThreatManager().getThreatList().end(); ++i) - if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER && !(*i)->getTarget()->HasAura(SPELL_ICEBOLT)) - targets.push_back((*i)->getTarget()); - - if (targets.empty()) - _iceboltCount = 0; - else + if (_iceboltTargets.empty()) { - std::vector<Unit*>::const_iterator itr = targets.begin(); - advance(itr, rand32() % targets.size()); - _iceblocks.insert(std::make_pair((*itr)->GetGUID(), ObjectGuid::Empty)); - DoCast(*itr, SPELL_ICEBOLT); - --_iceboltCount; + events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT); + return; } - - if (_iceboltCount) - events.ScheduleEvent(EVENT_ICEBOLT, 1 * IN_MILLISECONDS); + ObjectGuid target = _iceboltTargets.back(); + if (Player* pTarget = ObjectAccessor::GetPlayer(*me, target)) + if (pTarget->IsAlive()) + DoCast(pTarget, SPELL_ICEBOLT); + _iceboltTargets.pop_back(); + + if (_iceboltTargets.empty()) + events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT); else - events.ScheduleEvent(EVENT_BREATH, 1 * IN_MILLISECONDS); + events.Repeat(Seconds(3)); return; } case EVENT_BREATH: { Talk(EMOTE_BREATH); DoCastAOE(SPELL_FROST_MISSILE); - events.ScheduleEvent(EVENT_EXPLOSION, 8 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_EXPLOSION, Seconds(8), 0, PHASE_FLIGHT); return; } case EVENT_EXPLOSION: - CastExplosion(); - ClearIceBlock(); - events.ScheduleEvent(EVENT_LAND, 3 * IN_MILLISECONDS); + DoCastAOE(SPELL_FROST_BREATH); + DoCastAOE(SPELL_FROST_BREATH_ANTICHEAT); + events.ScheduleEvent(EVENT_LAND, Seconds(3) + Milliseconds(500), 0, PHASE_FLIGHT); return; case EVENT_LAND: + if (_delayedDrain) + CastDrain(); + if (Creature* cBuffet = ObjectAccessor::GetCreature(*me, _buffet)) + { + cBuffet->DespawnOrUnsummon(1 * IN_MILLISECONDS); + _buffet.Clear(); + } me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); Talk(EMOTE_GROUND_PHASE); me->SetHover(false); - me->SetDisableGravity(false); - events.ScheduleEvent(EVENT_GROUND, 1500); - return; - case EVENT_GROUND: - EnterPhaseGround(); + events.SetPhase(PHASE_GROUND); + events.ScheduleEvent(EVENT_GROUND, Seconds(3) + Milliseconds(500), 0, PHASE_GROUND); return; case EVENT_BIRTH: me->SetVisible(true); @@ -359,56 +366,261 @@ class boss_sapphiron : public CreatureScript } } - void CastExplosion() + private: + std::vector<ObjectGuid> _iceboltTargets; + ObjectGuid _buffet; + bool _delayedDrain; + bool _canTheHundredClub; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new boss_sapphironAI(creature); + } +}; + +class go_sapphiron_birth : public GameObjectScript +{ + public: + go_sapphiron_birth() : GameObjectScript("go_sapphiron_birth") { } + + struct go_sapphiron_birthAI : public GameObjectAI + { + go_sapphiron_birthAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } + + void OnStateChanged(uint32 state, Unit* who) override + { + if (state == GO_ACTIVATED) + { + if (who) + { + if (Creature* sapphiron = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_SAPPHIRON))) + sapphiron->AI()->DoAction(ACTION_BIRTH); + instance->SetData(DATA_HAD_SAPPHIRON_BIRTH, 1u); + } + } + else if (state == GO_JUST_DEACTIVATED) + { // prevent ourselves from going back to _READY and resetting the client anim + go->SetRespawnTime(0); + go->Delete(); + } + } + + InstanceScript* instance; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return GetInstanceAI<go_sapphiron_birthAI>(go); + } +}; + + +class spell_sapphiron_change_blizzard_target : public SpellScriptLoader +{ + public: + spell_sapphiron_change_blizzard_target() : SpellScriptLoader("spell_sapphiron_change_blizzard_target") { } + + class spell_sapphiron_change_blizzard_target_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sapphiron_change_blizzard_target_AuraScript); + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + TempSummon* me = GetTarget()->ToTempSummon(); + if (Creature* owner = me ? me->GetSummonerCreatureBase() : nullptr) + { + Unit* newTarget = owner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true); + if (!newTarget) + newTarget = owner->getAttackerForHelper(); + if (newTarget) + me->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f); + else + { + me->StopMoving(); + me->GetMotionMaster()->Clear(); + } + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_change_blizzard_target_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sapphiron_change_blizzard_target_AuraScript(); + } +}; + +class spell_sapphiron_icebolt : public SpellScriptLoader +{ + public: + spell_sapphiron_icebolt() : SpellScriptLoader("spell_sapphiron_icebolt") { } + + class spell_sapphiron_icebolt_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sapphiron_icebolt_AuraScript); + + void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, true); + } + + void HandleRemove(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/) + { + if (_block) + if (GameObject* oBlock = ObjectAccessor::GetGameObject(*GetTarget(), _block)) + oBlock->Delete(); + GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, false); + } + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + if (_block) + return; + if (GetTarget()->isMoving()) + return; + float x, y, z; + GetTarget()->GetPosition(x, y, z); + if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0, 0, 0, 0, 0, 25)) + _block = block->GetGUID(); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_sapphiron_icebolt_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_sapphiron_icebolt_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_icebolt_AuraScript::HandlePeriodic, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + + ObjectGuid _block; + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sapphiron_icebolt_AuraScript(); + } +}; + +// @hack Hello, developer from the future! How has your day been? +// Anyway, this is, as you can undoubtedly see, a hack to emulate line of sight checks on a spell that abides line of sight anyway. +// In the current core, line of sight is not properly checked for people standing behind an ice block. This is not a good thing and kills people. +// Thus, we have this hack to check for ice block LoS in a "safe" way. Kind of. It's inaccurate, but in a good way (tends to save people when it shouldn't in edge cases). +// If LoS handling is better in whatever the current revision is when you read this, please get rid of the hack. Thanks! +class spell_sapphiron_frost_breath : public SpellScriptLoader +{ + public: + spell_sapphiron_frost_breath() : SpellScriptLoader("spell_sapphiron_frost_breath") { } + + class spell_sapphiron_frost_breath_SpellScript : public SpellScript + { + PrepareSpellScript(spell_sapphiron_frost_breath_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return !!sSpellMgr->GetSpellInfo(SPELL_FROST_BREATH); + } + + void HandleTargets(std::list<WorldObject*>& targetList) { - DoZoneInCombat(); // make sure everyone is in threatlist - std::vector<Unit*> targets; - std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin(); - for (; i != me->getThreatManager().getThreatList().end(); ++i) + std::list<GameObject*> blocks; + if (GetCaster()) + GetCaster()->GetGameObjectListWithEntryInGrid(blocks, GO_ICEBLOCK, 200.0f); + + std::vector<Unit*> toRemove; + toRemove.reserve(3); + std::list<WorldObject*>::iterator it = targetList.begin(); + while (it != targetList.end()) { - Unit* target = (*i)->getTarget(); - if (target->GetTypeId() != TYPEID_PLAYER) + Unit* target = (*it)->ToUnit(); + if (!target) + { + it = targetList.erase(it); continue; + } if (target->HasAura(SPELL_ICEBOLT)) { - target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true); - targets.push_back(target); + it = targetList.erase(it); + toRemove.push_back(target); + continue; + } + + bool found = false; + for (GameObject* block : blocks) + if (block->IsInBetween(GetCaster(), target, 2.0f) && GetCaster()->GetExactDist2d(block) + 5 >= GetCaster()->GetExactDist2d(target)) + { + found = true; + break; + } + if (found) + { + it = targetList.erase(it); continue; } + ++it; + } + + for (Unit* block : toRemove) + block->RemoveAura(SPELL_ICEBOLT); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_breath_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + } + }; - for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr) + SpellScript* GetSpellScript() const override + { + return new spell_sapphiron_frost_breath_SpellScript(); + } +}; + +class spell_sapphiron_summon_blizzard : public SpellScriptLoader +{ + public: + spell_sapphiron_summon_blizzard() : SpellScriptLoader("spell_sapphiron_summon_blizzard") { } + + class spell_sapphiron_summon_blizzard_SpellScript : public SpellScript + { + PrepareSpellScript(spell_sapphiron_summon_blizzard_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return !!sSpellMgr->GetSpellInfo(SPELL_SUMMON_BLIZZARD); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + if (Creature* blizzard = GetCaster()->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, urandms(25, 30))) { - if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second)) + blizzard->CastSpell(nullptr, blizzard->m_spells[0], TRIGGERED_NONE); + if (Creature* creatureCaster = GetCaster()->ToCreature()) { - if (go->IsInBetween(me, target, 2.0f) - && me->GetExactDist2d(target->GetPositionX(), target->GetPositionY()) - me->GetExactDist2d(go->GetPositionX(), go->GetPositionY()) < 5.0f) + if (Unit* newTarget = creatureCaster->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true)) { - target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true); - targets.push_back(target); - break; + blizzard->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f); + return; } } + blizzard->GetMotionMaster()->MoveFollow(target, 0.1f, 0.0f); } - } - - me->CastSpell(me, SPELL_FROST_EXPLOSION, true); - - for (std::vector<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) - (*itr)->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, false); } - private: - Phases _phase; - uint32 _iceboltCount; - IceBlockMap _iceblocks; - bool _canTheHundredClub; - Map* _map; + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_sapphiron_summon_blizzard_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; - CreatureAI* GetAI(Creature* creature) const override + SpellScript* GetSpellScript() const override { - return new boss_sapphironAI(creature); + return new spell_sapphiron_summon_blizzard_SpellScript(); } }; @@ -426,5 +638,10 @@ class achievement_the_hundred_club : public AchievementCriteriaScript void AddSC_boss_sapphiron() { new boss_sapphiron(); + new go_sapphiron_birth(); + new spell_sapphiron_change_blizzard_target(); + new spell_sapphiron_icebolt(); + new spell_sapphiron_frost_breath(); + new spell_sapphiron_summon_blizzard(); new achievement_the_hundred_club(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index 66a00b2b634..2cf3a4664dd 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -109,6 +109,10 @@ enum PetSpells SPELL_MAGNETIC_PULL = 54517, SPELL_MAGNETIC_PULL_EFFECT = 28337, + // @hack feugen/stalagg use this in P1 after gripping tanks to prevent mmaps from pathing them off the platform once they approach the ramp + // developer from the future, if you read this and mmaps in the room has been fixed, then get rid of the hackfix, please + SPELL_ROOT_SELF = 75215, + SPELL_TESLA_SHOCK = 28099 }; @@ -166,7 +170,9 @@ public: struct boss_thaddiusAI : public BossAI { public: - boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningEnabled(false), shockingEligibility(true) + boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {} + + void InitializeAI() override { if (instance->GetBossState(BOSS_THADDIUS) != DONE) { @@ -184,12 +190,33 @@ public: Talk(SAY_SLAY); } - void Reset() override + void Reset() override { } + + void EnterEvadeMode(EvadeReason why) override { + if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES) + { + ballLightningEnabled = true; + return; // try again + } if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive())) BeginResetEncounter(); } + bool CanAIAttack(Unit const* who) const override + { + if (ballLightningEnabled || me->IsWithinMeleeRange(who)) + return BossAI::CanAIAttack(who); + else + return false; + } + + void JustRespawned() override + { + if (events.IsInPhase(PHASE_RESETTING)) + ResetEncounter(); + } + void JustDied(Unit* /*killer*/) override { _JustDied(); @@ -217,7 +244,10 @@ public: case ACTION_FEUGEN_AGGRO: case ACTION_STALAGG_AGGRO: if (events.IsInPhase(PHASE_RESETTING)) - return BeginResetEncounter(); + { + BeginResetEncounter(); + return; + } if (!events.IsInPhase(PHASE_NOT_ENGAGED)) return; events.SetPhase(PHASE_PETS); @@ -225,10 +255,14 @@ public: shockingEligibility = true; if (!instance->CheckRequiredBosses(BOSS_THADDIUS)) - return BeginResetEncounter(); + { + BeginResetEncounter(); + return; + } instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS); me->setActive(true); + DoZoneInCombat(); if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) stalagg->setActive(true); if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) @@ -239,7 +273,7 @@ public: feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX); feugenAlive = false; if (stalaggAlive) - events.ScheduleEvent(EVENT_REVIVE_FEUGEN, 5 * IN_MILLISECONDS, 0, PHASE_PETS); + events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS); else Transition(); @@ -249,7 +283,7 @@ public: stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX); stalaggAlive = false; if (feugenAlive) - events.ScheduleEvent(EVENT_REVIVE_STALAGG, 5 * IN_MILLISECONDS, 0, PHASE_PETS); + events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS); else Transition(); @@ -274,9 +308,9 @@ public: me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_TRANSITION_1, 10 * IN_MILLISECONDS, 0, PHASE_TRANSITION); - events.ScheduleEvent(EVENT_TRANSITION_2, 12 * IN_MILLISECONDS, 0, PHASE_TRANSITION); - events.ScheduleEvent(EVENT_TRANSITION_3, 14 * IN_MILLISECONDS, 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION); } void BeginResetEncounter(bool initial = false) @@ -286,16 +320,12 @@ public: if (events.IsInPhase(PHASE_RESETTING)) return; - if (initial) // signal shorter spawn timer to instance script - instance->SetBossState(BOSS_THADDIUS, SPECIAL); - instance->ProcessEvent(me, EVENT_THADDIUS_BEGIN_RESET); - instance->SetBossState(BOSS_THADDIUS, NOT_STARTED); - // remove polarity shift debuffs on reset instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY); instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY); me->DespawnOrUnsummon(); + me->SetRespawnTime(initial ? 5 : 30); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); events.SetPhase(PHASE_RESETTING); @@ -312,11 +342,9 @@ public: feugenAlive = true; stalaggAlive = true; - me->Respawn(true); _Reset(); events.SetPhase(PHASE_NOT_ENGAGED); - - me->CastSpell(me, SPELL_THADDIUS_INACTIVE_VISUAL, true); + me->SetReactState(REACT_PASSIVE); if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER); @@ -361,16 +389,20 @@ public: break; case EVENT_TRANSITION_3: // thaddius becomes active me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); - ballLightningEnabled = false; + ballLightningUnlocked = false; me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f)) AttackStart(closest); else // if there is no nearest target, then there is no target, meaning we should reset - return BeginResetEncounter(); + { + BeginResetEncounter(); + return; + } if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) feugen->AI()->DoAction(ACTION_TRANSITION_3); @@ -381,20 +413,20 @@ public: Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, 5 * IN_MILLISECONDS, 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_SHIFT, 10 * IN_MILLISECONDS, 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS, 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS); break; case EVENT_ENABLE_BALL_LIGHTNING: - ballLightningEnabled = true; + ballLightningUnlocked = true; break; case EVENT_SHIFT: me->CastStop(); // shift overrides all other spells DoCastAOE(SPELL_POLARITY_SHIFT); - events.ScheduleEvent(EVENT_SHIFT_TALK, 3 * IN_MILLISECONDS, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_SHIFT, 30 * IN_MILLISECONDS, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS); break; case EVENT_SHIFT_TALK: Talk(SAY_ELECT); @@ -402,12 +434,12 @@ public: break; case EVENT_CHAIN: if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over - events.ScheduleEvent(EVENT_CHAIN, 3 * IN_MILLISECONDS, 0, PHASE_THADDIUS); + events.Repeat(Seconds(3)); else { me->CastStop(); DoCastVictim(SPELL_CHAIN_LIGHTNING); - events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, PHASE_THADDIUS); + events.Repeat(randtime(Seconds(10), Seconds(20))); } break; case EVENT_BERSERK: @@ -418,22 +450,24 @@ public: break; } } - - if (events.IsInPhase(PHASE_THADDIUS)) + if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady()) { if (me->IsWithinMeleeRange(me->GetVictim())) + { + ballLightningEnabled = false; DoMeleeAttackIfReady(); - else - if (ballLightningEnabled) - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - DoCast(target, SPELL_BALL_LIGHTNING); + } + else if (ballLightningUnlocked) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_BALL_LIGHTNING); } } private: bool stalaggAlive; bool feugenAlive; - bool ballLightningEnabled; + bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice + bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range bool shockingEligibility; }; @@ -652,7 +686,7 @@ public: else { DoCast(me, SPELL_STALAGG_POWERSURGE); - powerSurgeTimer = urand(25, 30) * IN_MILLISECONDS; + powerSurgeTimer = urandms(25, 30); } } else @@ -836,7 +870,7 @@ public: Talk(SAY_FEUGEN_AGGRO); if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) - thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO); + thaddius->AI()->DoAction(ACTION_FEUGEN_AGGRO); if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) if (!stalagg->IsInCombat()) @@ -1094,7 +1128,7 @@ class spell_thaddius_polarity_charge : public SpellScriptLoader SpellScript* GetSpellScript() const override { - return new spell_thaddius_polarity_charge_SpellScript; + return new spell_thaddius_polarity_charge_SpellScript(); } }; @@ -1212,6 +1246,10 @@ class spell_thaddius_magnetic_pull : public SpellScriptLoader feugenTank->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true); stalaggTank->CastSpell(feugenTank, SPELL_MAGNETIC_PULL_EFFECT, true); + // @hack prevent mmaps clusterfucks from breaking tesla while tanks are midair + feugen->AddAura(SPELL_ROOT_SELF, feugen); + stalagg->AddAura(SPELL_ROOT_SELF, stalagg); + // and make both attack their respective new tanks if (feugen->GetAI()) feugen->GetAI()->AttackStart(stalaggTank); diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index 77657d71ffc..02b82d255cd 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -100,36 +100,6 @@ ObjectData const objectData[] = { 0, 0, } }; -// from P2 teleport spell stored target -float const HeiganPos[2] = { 2793.86f, -3707.38f }; -float const HeiganEruptionSlope[3] = -{ - (-3703.303223f - HeiganPos[1]) / (2777.494141f - HeiganPos[0]), // between right center and far right - (-3696.948242f - HeiganPos[1]) / (2785.624268f - HeiganPos[0]), // between left and right halves - (-3691.880615f - HeiganPos[1]) / (2790.280029f - HeiganPos[0]) // between far left and left center -}; - -// 0 H x -// 1 ^ -// 2 | -// 3 y<--o -inline uint32 GetEruptionSection(float x, float y) -{ - y -= HeiganPos[1]; - if (y < 1.0f) - return 0; - - x -= HeiganPos[0]; - if (x > -1.0f) - return 3; - - float slope = y/x; - for (uint32 i = 0; i < 3; ++i) - if (slope > HeiganEruptionSlope[i]) - return i; - return 3; -} - class instance_naxxramas : public InstanceMapScript { public: @@ -149,6 +119,7 @@ class instance_naxxramas : public InstanceMapScript hadAnubRekhanGreet = false; hadFaerlinaGreet = false; hadThaddiusGreet = false; + hadSapphironBirth = false; CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT; playerDied = 0; @@ -208,28 +179,8 @@ class instance_naxxramas : public InstanceMapScript } } - void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override - { - switch (eventId) - { - case EVENT_THADDIUS_BEGIN_RESET: - if (GetBossState(BOSS_THADDIUS) == SPECIAL) // this is the initial spawn, we want a shorter spawn time - events.ScheduleEvent(EVENT_THADDIUS_RESET, 5 * IN_MILLISECONDS); - else - events.ScheduleEvent(EVENT_THADDIUS_RESET, 30 * IN_MILLISECONDS); - break; - } - } - void OnGameObjectCreate(GameObject* go) override { - if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287) - { - uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY()); - HeiganEruptionGUID[section].insert(go->GetGUID()); - return; - } - switch (go->GetEntry()) { case GO_GOTHIK_GATE: @@ -273,37 +224,18 @@ class instance_naxxramas : public InstanceMapScript if (GetBossState(BOSS_HORSEMEN) == DONE) go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); break; - default: - break; - } - - InstanceScript::OnGameObjectCreate(go); - } - - void OnGameObjectRemove(GameObject* go) override - { - if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287) - { - uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY()); - HeiganEruptionGUID[section].erase(go->GetGUID()); - return; - } - - switch (go->GetEntry()) - { case GO_BIRTH: - if (SapphironGUID) + if (hadSapphironBirth || GetBossState(BOSS_SAPPHIRON) == DONE) { - if (Creature* sapphiron = instance->GetCreature(SapphironGUID)) - sapphiron->AI()->DoAction(DATA_SAPPHIRON_BIRTH); - return; + hadSapphironBirth = true; + go->Delete(); } break; default: break; } - InstanceScript::OnGameObjectRemove(go); + InstanceScript::OnGameObjectCreate(go); } void OnUnitDeath(Unit* unit) override @@ -328,9 +260,6 @@ class instance_naxxramas : public InstanceMapScript { switch (id) { - case DATA_HEIGAN_ERUPT: - HeiganErupt(value); - break; case DATA_GOTHIK_GATE: if (GameObject* gate = instance->GetGameObject(GothikGateGUID)) gate->SetGoState(GOState(value)); @@ -347,6 +276,9 @@ class instance_naxxramas : public InstanceMapScript case DATA_HAD_THADDIUS_GREET: hadThaddiusGreet = (value == 1u); break; + case DATA_HAD_SAPPHIRON_BIRTH: + hadSapphironBirth = (value == 1u); + break; default: break; } @@ -364,6 +296,8 @@ class instance_naxxramas : public InstanceMapScript return hadFaerlinaGreet ? 1u : 0u; case DATA_HAD_THADDIUS_GREET: return hadThaddiusGreet ? 1u : 0u; + case DATA_HAD_SAPPHIRON_BIRTH: + return hadSapphironBirth ? 1u : 0u; default: break; } @@ -399,6 +333,8 @@ class instance_naxxramas : public InstanceMapScript return StalaggGUID; case DATA_THADDIUS: return ThaddiusGUID; + case DATA_SAPPHIRON: + return SapphironGUID; case DATA_KELTHUZAD: return KelthuzadGUID; case DATA_KELTHUZAD_PORTAL01: @@ -431,7 +367,7 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_ARACHNID)) teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); + events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6)); } break; case BOSS_LOATHEB: @@ -440,7 +376,7 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_PLAGUE)) teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); + events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6)); } break; case BOSS_THADDIUS: @@ -449,12 +385,12 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_CONSTRUCT)) teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); + events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6)); } break; case BOSS_GOTHIK: if (state == DONE) - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, 10000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, Seconds(10)); break; case BOSS_HORSEMEN: if (state == DONE) @@ -468,12 +404,12 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_MILITARY)) teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); + events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6)); } break; case BOSS_SAPPHIRON: if (state == DONE) - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, Seconds(6)); break; default: break; @@ -493,37 +429,37 @@ class instance_naxxramas : public InstanceMapScript case EVENT_DIALOGUE_GOTHIK_KORTHAZZ: if (Creature* korthazz = instance->GetCreature(ThaneGUID)) korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, 5000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, Seconds(5)); break; case EVENT_DIALOGUE_GOTHIK_ZELIEK: if (Creature* zeliek = instance->GetCreature(SirGUID)) zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_BLAUMEUX: if (Creature* blaumeux = instance->GetCreature(LadyGUID)) blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_RIVENDARE: if (Creature* rivendare = instance->GetCreature(BaronGUID)) rivendare->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_BLAUMEUX2: if (Creature* blaumeux = instance->GetCreature(LadyGUID)) blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_ZELIEK2: if (Creature* zeliek = instance->GetCreature(SirGUID)) zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_KORTHAZZ2: if (Creature* korthazz = instance->GetCreature(ThaneGUID)) korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2); - events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, Seconds(6)); break; case EVENT_DIALOGUE_GOTHIK_RIVENDARE2: if (Creature* rivendare = instance->GetCreature(BaronGUID)) @@ -540,62 +476,39 @@ class instance_naxxramas : public InstanceMapScript if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID)) kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD); HandleGameObject(KelthuzadDoorGUID, false); - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, Seconds(6)); break; case EVENT_DIALOGUE_SAPPHIRON_LICHKING: if (Creature* lichKing = instance->GetCreature(LichKingGUID)) lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING); - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, 16000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, Seconds(16)); break; case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2: if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID)) kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD2); - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, 9000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, Seconds(9)); break; case EVENT_DIALOGUE_SAPPHIRON_LICHKING2: if (Creature* lichKing = instance->GetCreature(LichKingGUID)) lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING2); - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, 12000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, Seconds(12)); break; case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3: if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID)) kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD3); - events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, 6000); + events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, Seconds(6)); break; case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4: if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID)) kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD4); HandleGameObject(KelthuzadDoorGUID, true); break; - case EVENT_THADDIUS_RESET: - if (GetBossState(BOSS_THADDIUS) != DONE) - if (Creature* thaddius = instance->GetCreature(ThaddiusGUID)) - thaddius->AI()->DoAction(-1); - break; default: break; } } } - void HeiganErupt(uint32 section) - { - for (uint32 i = 0; i < 4; ++i) - { - if (i == section) - continue; - - for (ObjectGuid guid : HeiganEruptionGUID[i]) - { - if (GameObject* heiganEruption = instance->GetGameObject(guid)) - { - heiganEruption->SendCustomAnim(heiganEruption->GetGoAnimProgress()); - heiganEruption->CastSpell(NULL, SPELL_ERUPTION); - } - } - } - } - // This Function is called in CheckAchievementCriteriaMeet and CheckAchievementCriteriaMeet is called before SetBossState(bossId, DONE), // so to check if all bosses are done the checker must exclude 1 boss, the last done, if there is at most 1 encouter in progress when is // called this function then all bosses are done. The one boss that check is the boss that calls this function, so it is dead. @@ -653,7 +566,6 @@ class instance_naxxramas : public InstanceMapScript /* The Plague Quarter */ // Heigan the Unclean - GuidSet HeiganEruptionGUID[4]; ObjectGuid HeiganGUID; /* The Military Quarter */ @@ -688,6 +600,7 @@ class instance_naxxramas : public InstanceMapScript bool hadAnubRekhanGreet; bool hadFaerlinaGreet; bool hadThaddiusGreet; + bool hadSapphironBirth; uint8 CurrentWingTaunt; /* The Immortal / The Undying */ diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index c0caa86e93f..bece5a60bee 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -43,12 +43,11 @@ enum Encounter enum Data { - DATA_HEIGAN_ERUPT, DATA_GOTHIK_GATE, - DATA_SAPPHIRON_BIRTH, DATA_HAD_ANUBREKHAN_GREET, DATA_HAD_FAERLINA_GREET, DATA_HAD_THADDIUS_GREET, + DATA_HAD_SAPPHIRON_BIRTH, DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT, DATA_ABOMINATION_KILLED, @@ -73,6 +72,7 @@ enum Data64 DATA_HEIGAN, DATA_FEUGEN, DATA_STALAGG, + DATA_SAPPHIRON, DATA_KELTHUZAD, DATA_KELTHUZAD_PORTAL01, DATA_KELTHUZAD_PORTAL02, @@ -158,12 +158,6 @@ enum GameObjectsIds GO_NAXX_PORTAL_MILITARY = 181578 }; -enum SpellIds -{ - SPELL_ERUPTION = 29371, - SPELL_SLIME = 28801 -}; - enum InstanceEvents { // Dialogue that happens after Gothik's death. @@ -176,10 +170,6 @@ enum InstanceEvents EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, EVENT_DIALOGUE_GOTHIK_RIVENDARE2, - // Thaddius AI requesting timed encounter (re-)spawn - EVENT_THADDIUS_BEGIN_RESET, - EVENT_THADDIUS_RESET, - // Dialogue that happens after each wing. EVENT_KELTHUZAD_WING_TAUNT, |