From 215579c828adcb6a3e35449eff4a2a780d104b34 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Mon, 21 Sep 2026 09:23:22 -0300 Subject: [PATCH] fix(Scripts/ICC): time the Deathbringer Saurfang intro, outro and camp to the sniffs (#27663) --- .../rev_1789542597283738000.sql | 63 ++ .../boss_deathbringer_saurfang.cpp | 803 +++++++++++------- .../IcecrownCitadel/icecrown_citadel.h | 6 +- .../instance_icecrown_citadel.cpp | 326 +++---- 4 files changed, 755 insertions(+), 443 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1789542597283738000.sql diff --git a/data/sql/updates/pending_db_world/rev_1789542597283738000.sql b/data/sql/updates/pending_db_world/rev_1789542597283738000.sql new file mode 100644 index 0000000000..76fec4d556 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1789542597283738000.sql @@ -0,0 +1,63 @@ +-- Deathbringer Saurfang intro and outro: the emotes played with each line, and the Orcish line spoken in Orcish. +UPDATE `creature_text` SET `Emote` = 22 WHERE `CreatureID` = 37200 AND `GroupID` = 0; +UPDATE `creature_text` SET `Emote` = 6 WHERE `CreatureID` = 37200 AND `GroupID` = 1; +UPDATE `creature_text` SET `Emote` = 15 WHERE `CreatureID` = 37200 AND `GroupID` = 2; +UPDATE `creature_text` SET `Emote` = 397 WHERE `CreatureID` = 37813 AND `GroupID` = 0; +UPDATE `creature_text` SET `Emote` = 153 WHERE `CreatureID` = 37813 AND `GroupID` = 2; +UPDATE `creature_text` SET `Emote` = 274 WHERE `CreatureID` = 37200 AND `GroupID` = 4; +UPDATE `creature_text` SET `Emote` = 25 WHERE `CreatureID` = 37200 AND `GroupID` = 5; +UPDATE `creature_text` SET `Emote` = 5 WHERE `CreatureID` = 37200 AND `GroupID` = 7; +UPDATE `creature_text` SET `Emote` = 397 WHERE `CreatureID` = 37187 AND `GroupID` = 6; +UPDATE `creature_text` SET `Text` = 'No\'ku kil zil\'nok ha tar.', `Language` = 1 WHERE `CreatureID` = 37187 AND `GroupID` = 8; +UPDATE `creature_text` SET `Emote` = 1 WHERE `CreatureID` = 37187 AND `GroupID` = 10; +UPDATE `creature_text` SET `Emote` = 25 WHERE `CreatureID` = 37879 AND `GroupID` = 0; +UPDATE `creature_text` SET `Emote` = 1 WHERE `CreatureID` = 37879 AND `GroupID` = 1; +UPDATE `creature_text` SET `Emote` = 6 WHERE `CreatureID` = 37879 AND `GroupID` = 2; + +-- The Stormwind portal is scenery, Varian and Jaina are not attackable. +UPDATE `creature_template` SET `unit_flags` = 33555200, `VerifiedBuild` = 69814 WHERE `entry` = 37880; +UPDATE `creature_template` SET `unit_flags` = 768, `VerifiedBuild` = 69814 WHERE `entry` = 37879; +UPDATE `creature_template` SET `unit_flags` = 33600, `VerifiedBuild` = 69814 WHERE `entry` = 37188; + +-- Victory camp vendors walking from their teleporter pads to their pitches, entry * 10. +-- Shely Steelbowels +DELETE FROM `waypoint_data` WHERE `id` = 379030; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(379030, 1, -548.3507, 2202.4739, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379030, 2, -544.9071, 2207.38, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379030, 3, -543.533, 2209.3838, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379030, 4, -530.8691, 2222.263, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379030, 5, -528.9062, 2224.5051, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379030, 6, -526.80206, 2231.3682, 539.28, 5.5152402, 0, 1, 0, 0, 0, 100, 0); + +-- Brazie Getz +DELETE FROM `waypoint_data` WHERE `id` = 379040; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(379040, 1, -546.2265, 2223.0754, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379040, 2, -540.0955, 2224.25, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379040, 3, -533.3658, 2223.3423, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379040, 4, -530.3941, 2222.8645, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379040, 5, -528.3299, 2225.2656, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379040, 6, -530.6007, 2227.6736, 539.28, 5.497787, 0, 1, 0, 0, 0, 100, 0); + +-- Apothecary Candith Tomas, who lingers on the pad before setting off +DELETE FROM `waypoint_data` WHERE `id` = 379350; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(379350, 1, -560.29517, 2220.2153, 539.2854, NULL, 0, 2400, 0, 0, 0, 100, 0), +(379350, 2, -548.1919, 2219.9202, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379350, 3, -544.9419, 2219.9202, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379350, 4, -535.7101, 2219.7864, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379350, 5, -533.0685, 2223.0264, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379350, 6, -530.17017, 2226.231, 539.28, 5.4628806, 0, 1, 0, 0, 0, 100, 0); + +-- Morgan Dayblaze +DELETE FROM `waypoint_data` WHERE `id` = 379360; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(379360, 1, -544.9707, 2206.6287, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 2, -533.3457, 2209.617, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 3, -527.9583, 2211.0017, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 4, -524.5902, 2216.5625, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 5, -521.3402, 2221.0625, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 6, -518.1354, 2225.9167, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 7, -520.3289, 2232.0615, 539.28, NULL, 0, 0, 0, 0, 0, 100, 0), +(379360, 8, -520.941, 2233.1077, 539.28, 5.3756142, 0, 1, 0, 0, 0, 100, 0); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 406ce838ff..b039ab0150 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -28,11 +28,10 @@ enum ScriptTexts { - // Deathbringer Saurfang + // Deathbringer Saurfang. Group 3 is a narration row that is never spoken. SAY_INTRO_ALLIANCE_2 = 0, SAY_INTRO_ALLIANCE_3 = 1, SAY_INTRO_ALLIANCE_6 = 2, - SAY_INTRO_ALLIANCE_7 = 3, SAY_INTRO_HORDE_2 = 4, SAY_INTRO_HORDE_4 = 5, SAY_INTRO_HORDE_9 = 6, @@ -53,9 +52,7 @@ enum ScriptTexts SAY_INTRO_HORDE_7 = 4, SAY_INTRO_HORDE_8 = 5, SAY_OUTRO_ALLIANCE_8 = 6, - SAY_OUTRO_ALLIANCE_12 = 7, // kneel after WP reached SAY_OUTRO_ALLIANCE_13 = 8, - SAY_OUTRO_ALLIANCE_14 = 9, SAY_OUTRO_ALLIANCE_15 = 10, SAY_OUTRO_HORDE_1 = 11, SAY_OUTRO_HORDE_2 = 12, @@ -63,19 +60,16 @@ enum ScriptTexts SAY_OUTRO_HORDE_4 = 14, SAY_OUTRO_ALLIANCE_SAURFANG_NOD = 19, // emote beat, outside the numbered 21 - // Muradin Bronzebeard + // Muradin Bronzebeard. Groups 6, 8 and 11 are narration rows that are never spoken. SAY_INTRO_ALLIANCE_1 = 0, SAY_INTRO_ALLIANCE_4 = 1, SAY_INTRO_ALLIANCE_5 = 2, SAY_OUTRO_ALLIANCE_1 = 3, SAY_OUTRO_ALLIANCE_2 = 4, SAY_OUTRO_ALLIANCE_3 = 5, - SAY_OUTRO_ALLIANCE_4 = 6, SAY_OUTRO_ALLIANCE_5 = 7, - SAY_OUTRO_ALLIANCE_6 = 8, SAY_OUTRO_ALLIANCE_7 = 9, SAY_OUTRO_ALLIANCE_9 = 10, - SAY_OUTRO_ALLIANCE_10 = 11, SAY_OUTRO_ALLIANCE_21 = 12, // Lady Jaina Proudmoore @@ -118,6 +112,7 @@ enum Spells SPELL_RIDE_VEHICLE = 70640, // Outro SPELL_ACHIEVEMENT = 72928, + SPELL_SIMPLE_TELEPORT = 12980, // Outro, Varian and Jaina stepping out of the portal }; enum EventTypes @@ -149,42 +144,70 @@ enum EventTypes EVENT_BLOOD_NOVA = 21, EVENT_RUNE_OF_BLOOD = 22, - EVENT_OUTRO_ALLIANCE_1 = 23, - EVENT_OUTRO_ALLIANCE_2 = 24, - EVENT_OUTRO_ALLIANCE_3 = 25, - EVENT_OUTRO_ALLIANCE_4 = 26, - EVENT_OUTRO_ALLIANCE_5 = 27, - EVENT_OUTRO_ALLIANCE_6 = 28, - EVENT_OUTRO_ALLIANCE_7 = 29, - EVENT_OUTRO_ALLIANCE_8 = 30, - EVENT_OUTRO_ALLIANCE_9 = 31, - EVENT_OUTRO_ALLIANCE_10 = 32, - EVENT_OUTRO_ALLIANCE_11 = 33, - EVENT_OUTRO_ALLIANCE_12 = 34, - EVENT_OUTRO_ALLIANCE_13 = 35, - EVENT_OUTRO_ALLIANCE_14 = 36, - EVENT_OUTRO_ALLIANCE_15 = 37, - EVENT_OUTRO_ALLIANCE_16 = 38, - EVENT_OUTRO_ALLIANCE_17 = 39, - EVENT_OUTRO_ALLIANCE_18 = 40, - EVENT_OUTRO_ALLIANCE_19 = 41, - EVENT_OUTRO_ALLIANCE_20 = 42, + // Outro beats shared by both captains + EVENT_OUTRO_DESCEND = 23, + EVENT_OUTRO_GUARDS_DESCEND = 24, + EVENT_OUTRO_MOURN = 25, + EVENT_OUTRO_KNEEL = 26, + EVENT_OUTRO_GUARDS_KNEEL = 27, + EVENT_OUTRO_WALK = 28, - EVENT_OUTRO_HORDE_1 = 44, - EVENT_OUTRO_HORDE_3 = 46, - EVENT_OUTRO_HORDE_4 = 47, - EVENT_OUTRO_HORDE_GUARDS_KNEEL = 49, - EVENT_OUTRO_HORDE_PICKUP = 50, - EVENT_OUTRO_HORDE_LEAVE = 51, - EVENT_OUTRO_A_GUARDS_KNEEL = 52, - EVENT_OUTRO_A_SAURFANG_NOD = 53, - EVENT_OUTRO_A_JAINA_SMILE = 54, - EVENT_OUTRO_A_SAURFANG_LEAVE = 55, - EVENT_OUTRO_A_DISMISS = 56, - EVENT_OUTRO_A_ROYALS = 57, - EVENT_OUTRO_A_PORTAL_CLOSE = 58, - EVENT_OUTRO_A_SAURFANG_ADVANCE = 59, - EVENT_OUTRO_A_ZEPPELIN_RELEASE = 60, + EVENT_OUTRO_HORDE_WEEP = 29, + EVENT_OUTRO_HORDE_CEREMONY = 30, + EVENT_OUTRO_HORDE_PICKUP = 31, + EVENT_OUTRO_HORDE_CARRY = 32, + EVENT_OUTRO_HORDE_HONOR = 33, + EVENT_OUTRO_HORDE_LEAVE = 34, + + EVENT_OUTRO_A_DISTANCE = 35, + EVENT_OUTRO_A_TAKE_POST = 36, + EVENT_OUTRO_A_FALL_IN = 37, + EVENT_OUTRO_A_READY = 38, + EVENT_OUTRO_A_FORM_LINE = 39, + EVENT_OUTRO_A_SAURFANG_ARRIVES = 40, + EVENT_OUTRO_A_MURADIN_HALT = 41, + EVENT_OUTRO_A_SAURFANG_DEMAND = 42, + EVENT_OUTRO_A_SAURFANG_READY = 43, + EVENT_OUTRO_A_MURADIN_YIELD = 44, + EVENT_OUTRO_A_PORTAL = 45, + EVENT_OUTRO_A_ROYALS = 46, + EVENT_OUTRO_A_VARIAN_STAND_DOWN = 47, + EVENT_OUTRO_A_STEP_ASIDE = 48, + EVENT_OUTRO_A_SAURFANG_LOWER = 49, + EVENT_OUTRO_A_SAURFANG_TO_CORPSE = 50, + EVENT_OUTRO_A_SAURFANG_KNEEL = 51, + EVENT_OUTRO_A_SAURFANG_ORCISH = 52, + EVENT_OUTRO_A_PICKUP = 53, + EVENT_OUTRO_A_SAURFANG_AT_VARIAN = 54, + EVENT_OUTRO_A_SAURFANG_THANKS = 55, + EVENT_OUTRO_A_VARIAN_EULOGY = 56, + EVENT_OUTRO_A_VARIAN_TALK = 57, + EVENT_OUTRO_A_VARIAN_SALUTE = 58, + EVENT_OUTRO_A_ZEPPELIN_RELEASE = 59, + EVENT_OUTRO_A_SAURFANG_NOD = 60, + EVENT_OUTRO_A_SAURFANG_LEAVE = 61, + EVENT_OUTRO_A_SAURFANG_GONE = 62, + EVENT_OUTRO_A_VARIAN_TURN = 63, + EVENT_OUTRO_A_VARIAN_ASK = 64, + EVENT_OUTRO_A_JAINA_SOB = 65, + EVENT_OUTRO_A_JAINA_TURN = 66, + EVENT_OUTRO_A_JAINA_SMILE = 67, + EVENT_OUTRO_A_JAINA_REPLY = 68, + EVENT_OUTRO_A_VARIAN_ORDERS = 69, + EVENT_OUTRO_A_VARIAN_FACE_MURADIN = 70, + EVENT_OUTRO_A_MURADIN_ACK = 71, + EVENT_OUTRO_A_DISMISS = 72, + + EVENT_OUTRO_A_ZEPPELIN = 73, + + // Guards + EVENT_OUTRO_GUARD_VANISH = 74, + + // One-shot emotes between the Horde intro lines + EVENT_INTRO_HORDE_10 = 75, + EVENT_INTRO_HORDE_TALK = 76, + EVENT_INTRO_HORDE_NO = 77, + EVENT_INTRO_HORDE_QUESTION = 78, }; enum Phases @@ -212,6 +235,8 @@ enum Actions ACTION_OUTRO_RETREAT = -3781311, ACTION_OUTRO_FALL_IN = -3781312, ACTION_OUTRO_STAND_DOWN = -3781313, + ACTION_OUTRO_STAND = -3781314, + ACTION_OUTRO_STEP_ASIDE = -3781315, }; #define DATA_MADE_A_MESS 45374613 // 4537, 4613 are achievement IDs @@ -227,13 +252,16 @@ enum MovePoints POINT_FINAL = 3781305, POINT_TRANSPORTER = 3781306, POINT_RETREAT = 3781307, - POINT_A_MURADIN_STAND = 3781308, - POINT_A_SAURFANG_MEET = 3781309, - POINT_A_CORPSE = 3781310, - POINT_A_VARIAN = 3781311, - POINT_A_EXIT = 3781312, + POINT_A_MURADIN_STEP = 3781308, + POINT_A_MURADIN_ASIDE = 3781309, + POINT_A_MURADIN_HOME = 3781310, + POINT_A_FORMATION = 3781311, + POINT_A_ASIDE = 3781312, POINT_A_STAND_DOWN = 3781313, - POINT_A_MURADIN_HOME = 3781314, + POINT_A_SAURFANG_MEET = 3781314, + POINT_A_CORPSE = 3781315, + POINT_A_VARIAN = 3781316, + POINT_A_EXIT = 3781317, }; Position const deathbringerPos = {-496.3542f, 2211.33f, 541.1138f, 0.0f}; @@ -249,35 +277,57 @@ Position const chargePos[6] = {-509.0040f, 2211.743f, 539.2870f, 0.0f} // back right }; -// Grip of Agony hangs the whole group at one height, 12 yards over the floor at 539.287. +// Grip of Agony hangs each of them at his own height, between 6 and 12 yards over the floor. Position const chokePos[6] = { - {-514.4834f, 2211.334f, 551.2882f, 0.0f}, // High Overlord Saurfang/Muradin Bronzebeard - {-510.1081f, 2211.592f, 551.2882f, 0.0f}, // front left + {-514.4834f, 2211.334f, 549.2887f, 0.0f}, // High Overlord Saurfang/Muradin Bronzebeard + {-510.1081f, 2211.592f, 546.3773f, 0.0f}, // front left {-513.3210f, 2211.396f, 551.2882f, 0.0f}, // front right - {-507.3684f, 2210.353f, 551.2882f, 0.0f}, // back middle - {-507.0486f, 2212.999f, 551.2882f, 0.0f}, // back left - {-510.7041f, 2211.069f, 551.2882f, 0.0f} // back right + {-507.3684f, 2210.353f, 545.7497f, 0.0f}, // back middle + {-507.0486f, 2212.999f, 545.5512f, 0.0f}, // back left + {-510.7041f, 2211.069f, 546.5298f, 0.0f} // back right }; -Position const finalPos = {-563.7552f, 2211.328f, 538.7848f, 0.0f}; -Position const transporterPos = {-549.0735f, 2211.289f, 539.2917f, 3.1415927f}; +Position const finalPos = {-561.5052f, 2211.5781f, 539.2754f, 0.0f}; +Position const transporterPos = {-548.8629f, 2211.3767f, 539.2780f, 0.0f}; -Position const allianceSaurfangPos = {-521.8657f, 2250.3455f, 539.29240f, 5.2911840f}; -Position const allianceMuradinPos = {-518.2308f, 2232.5356f, 539.29095f, 1.8000813f}; -Position const allianceVarianPos = {-521.0001f, 2238.3192f, 539.29095f, 5.6681833f}; -Position const allianceJainaPos = {-522.2058f, 2236.3171f, 539.29095f, 5.7388660f}; -Position const allianceSaurfangMeetPos = {-519.0500f, 2236.4000f, 539.29095f, 1.8000813f + 3.1415927f}; -Position const allianceVarianMeetPos = {-518.5498f, 2236.5883f, 539.29095f, 2.5265906f}; -float const allianceGuardOffset[6][2] = +// Alliance outro. Muradin mourns a few steps from where he lands, then takes post at the head of +// the rise while the marines form a line behind him, facing the arriving zeppelin. Once Varian has +// spoken they all step aside to the west, facing east, to let Saurfang through. +Position const allianceMuradinStepPos = {-528.6855f, 2212.4653f, 539.2772f, 1.6580628f}; +Position const allianceMuradinPostPos = {-522.4410f, 2227.0660f, 539.2767f, 1.1660000f}; +Position const allianceMuradinAsidePos = {-528.3924f, 2225.5903f, 539.2769f, 0.2094395f}; +Position const allianceFormationPos[6] = { - { 0.0f, 0.0f}, - { 2.6f, -1.4f}, - { 3.4f, 1.2f}, - { 0.8f, -3.2f}, - {-1.6f, -2.4f}, - {-2.2f, 1.0f} + {0.0f, 0.0f, 0.0f, 0.0f}, + {-527.3128f, 2224.8762f, 539.2769f, 1.5166270f}, + {-524.3242f, 2225.1377f, 539.2767f, 1.8044972f}, + {-521.3356f, 2225.3992f, 539.2805f, 1.6580628f}, + {-518.6803f, 2225.0977f, 539.2797f, 1.6309766f}, + {-515.7000f, 2225.0000f, 539.2790f, 1.6000000f} }; +Position const allianceAsidePos[6] = +{ + {0.0f, 0.0f, 0.0f, 0.0f}, + {-534.7246f, 2229.2915f, 539.2773f, 0.1323780f}, + {-534.8294f, 2226.2935f, 539.2773f, 6.0318213f}, + {-534.9340f, 2223.2952f, 539.2773f, 6.2482786f}, + {-535.0387f, 2220.2969f, 539.2773f, 6.2558422f}, + {-535.1500f, 2217.3000f, 539.2773f, 6.2600000f} +}; +Position const allianceSaurfangPos = {-521.6962f, 2248.8108f, 539.3757f, 4.7298422f}; +Position const allianceSaurfangMeetPos = {-522.2356f, 2233.0625f, 539.2769f, 4.7298422f}; +Position const allianceSaurfangExitPos = {-526.4601f, 2246.0347f, 539.2776f, 1.7061962f}; +Position const alliancePortalPos = {-529.5122f, 2229.8923f, 539.3734f, 0.0f}; +Position const allianceVarianPos = {-527.3386f, 2230.5730f, 539.3734f, 5.9515729f}; +Position const allianceJainaPos = {-527.9063f, 2228.6372f, 539.3730f, 6.2657318f}; +Position const allianceVarianMeetPos = {-524.2362f, 2229.7102f, 539.2769f, 2.8703434f}; + +// Walking time to a point at walk speed, plus a margin for the spline to settle. +static Milliseconds WalkTimeTo(Unit const* who, Position const& to) +{ + return Milliseconds(uint32(who->GetExactDist2d(&to) / who->GetSpeed(MOVE_WALK) * 1000.0f)) + 300ms; +} class boss_deathbringer_saurfang : public CreatureScript { @@ -615,7 +665,7 @@ public: Talk(SAY_INTRO_HORDE_1); _events.SetPhase(PHASE_INTRO_H); _events.ScheduleEvent(EVENT_INTRO_HORDE_2, 5s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_3, 18s + 500ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_3, 21s + 400ms, 0, PHASE_INTRO_H); _instance->HandleGameObject(_instance->GetGuidData(GO_SAURFANG_S_DOOR), true); if (GameObject* teleporter = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(GO_SCOURGE_TRANSPORTER_SAURFANG))) @@ -630,20 +680,14 @@ public: } break; case ACTION_START_OUTRO: - { - me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); - me->SetDisableGravity(false); - me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); - for (ObjectGuid const& guid : _guardList) - if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) - guard->AI()->DoAction(ACTION_OUTRO_DESCEND); - - _instance->SetData(DATA_SAURFANG_CAMP, IN_PROGRESS); - Talk(SAY_OUTRO_HORDE_1); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_GUARDS_KNEEL, 2s + 500ms); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_1, 10s); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_3, 18s); - } + // The party hangs in the grip for a while after the kill before it drops them. + _instance->SetData(DATA_SAURFANG_CAMP, IN_PROGRESS); + _events.ScheduleEvent(EVENT_OUTRO_DESCEND, 6s + 100ms); + _events.ScheduleEvent(EVENT_OUTRO_GUARDS_DESCEND, 8s + 100ms); + _events.ScheduleEvent(EVENT_OUTRO_MOURN, 9s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_KNEEL, 9s + 700ms); + _events.ScheduleEvent(EVENT_OUTRO_GUARDS_KNEEL, 14s + 500ms); + _events.ScheduleEvent(EVENT_OUTRO_WALK, 15s + 300ms); break; case ACTION_EVADE: { @@ -692,25 +736,25 @@ public: case POINT_FIRST_STEP: me->SetWalk(false); Talk(SAY_INTRO_HORDE_3); - _events.ScheduleEvent(EVENT_INTRO_HORDE_4, 10s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_5, 22s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_6, 36s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_7, 53s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_8, 57s + 800ms, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_HORDE_9, 59s, 0, PHASE_INTRO_H); - _events.ScheduleEvent(EVENT_INTRO_FINISH, 68s, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_4, 8s + 500ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_5, 18s + 600ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_6, 35s + 400ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_7, 52s + 400ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_8, 56s, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_9, 57s + 200ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_10, 59s + 800ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_FINISH, 66s + 200ms, 0, PHASE_INTRO_H); break; case POINT_CORPSE: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) me->SetFacingToObject(deathbringer); me->SetStandState(UNIT_STAND_STATE_KNEEL); - Talk(SAY_OUTRO_HORDE_3); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_PICKUP, 6s); + _events.ScheduleEvent(EVENT_OUTRO_HORDE_WEEP, 2s + 700ms); + _events.ScheduleEvent(EVENT_OUTRO_HORDE_CEREMONY, 11s + 200ms); break; case POINT_TRANSPORTER: me->SetFacingTo(0.0f); - Talk(SAY_OUTRO_HORDE_4); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_LEAVE, 8s); + _events.ScheduleEvent(EVENT_OUTRO_HORDE_HONOR, 2s + 300ms); break; case POINT_FINAL: { @@ -751,12 +795,17 @@ public: case EVENT_INTRO_HORDE_4: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) deathbringer->AI()->Talk(SAY_INTRO_HORDE_4); + _events.ScheduleEvent(EVENT_INTRO_HORDE_QUESTION, 3s + 800ms, 0, PHASE_INTRO_H); break; case EVENT_INTRO_HORDE_5: Talk(SAY_INTRO_HORDE_5); + _events.ScheduleEvent(EVENT_INTRO_HORDE_TALK, 3s, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_TALK, 6s + 700ms, 0, PHASE_INTRO_H); break; case EVENT_INTRO_HORDE_6: Talk(SAY_INTRO_HORDE_6); + _events.ScheduleEvent(EVENT_INTRO_HORDE_TALK, 3s + 400ms, 0, PHASE_INTRO_H); + _events.ScheduleEvent(EVENT_INTRO_HORDE_NO, 7s + 100ms, 0, PHASE_INTRO_H); break; case EVENT_INTRO_HORDE_7: Talk(SAY_INTRO_HORDE_7); @@ -770,10 +819,21 @@ public: break; case EVENT_INTRO_HORDE_9: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) - { deathbringer->AI()->DoCast(me, SPELL_GRIP_OF_AGONY); + break; + case EVENT_INTRO_HORDE_10: + if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) deathbringer->AI()->Talk(SAY_INTRO_HORDE_9); - } + break; + case EVENT_INTRO_HORDE_TALK: + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + break; + case EVENT_INTRO_HORDE_NO: + me->HandleEmoteCommand(EMOTE_ONESHOT_NO); + break; + case EVENT_INTRO_HORDE_QUESTION: + if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) + deathbringer->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); break; case EVENT_INTRO_FINISH: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) @@ -784,17 +844,29 @@ public: deathbringer->AI()->AttackStart(target); } break; - case EVENT_OUTRO_HORDE_1: - if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) - me->SetFacingToObject(deathbringer); - Talk(SAY_OUTRO_HORDE_2); + case EVENT_OUTRO_DESCEND: + me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); + me->SetDisableGravity(false); + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); break; - case EVENT_OUTRO_HORDE_GUARDS_KNEEL: + case EVENT_OUTRO_GUARDS_DESCEND: + for (ObjectGuid const& guid : _guardList) + if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) + guard->AI()->DoAction(ACTION_OUTRO_DESCEND); + break; + case EVENT_OUTRO_MOURN: + Talk(SAY_OUTRO_HORDE_1); + break; + case EVENT_OUTRO_KNEEL: + me->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case EVENT_OUTRO_GUARDS_KNEEL: for (ObjectGuid const& guid : _guardList) if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) guard->AI()->DoAction(ACTION_OUTRO_KNEEL); break; - case EVENT_OUTRO_HORDE_3: + case EVENT_OUTRO_WALK: + me->SetStandState(UNIT_STAND_STATE_STAND); if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) { float x, y, z; @@ -803,6 +875,13 @@ public: me->GetMotionMaster()->MovePoint(POINT_CORPSE, x, y, z); } break; + case EVENT_OUTRO_HORDE_WEEP: + Talk(SAY_OUTRO_HORDE_2); + break; + case EVENT_OUTRO_HORDE_CEREMONY: + Talk(SAY_OUTRO_HORDE_3); + _events.ScheduleEvent(EVENT_OUTRO_HORDE_PICKUP, 6s); + break; case EVENT_OUTRO_HORDE_PICKUP: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) { @@ -810,17 +889,23 @@ public: deathbringer->setDeathState(DeathState::Alive); deathbringer->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); deathbringer->CastSpell(me, SPELL_RIDE_VEHICLE, true); + // The revived body would otherwise stand upright on his shoulder. + deathbringer->SetEmoteState(EMOTE_STATE_DROWNED); } + _events.ScheduleEvent(EVENT_OUTRO_HORDE_CARRY, 4s + 800ms); + break; + case EVENT_OUTRO_HORDE_CARRY: + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(POINT_TRANSPORTER, transporterPos); for (ObjectGuid const& guid : _guardList) if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) guard->AI()->DoAction(ACTION_OUTRO_RETREAT); _guardList.clear(); - _events.ScheduleEvent(EVENT_OUTRO_HORDE_4, 3s); break; - case EVENT_OUTRO_HORDE_4: - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(POINT_TRANSPORTER, transporterPos); + case EVENT_OUTRO_HORDE_HONOR: + Talk(SAY_OUTRO_HORDE_4); + _events.ScheduleEvent(EVENT_OUTRO_HORDE_LEAVE, 7s + 300ms); break; case EVENT_OUTRO_HORDE_LEAVE: me->SetWalk(true); @@ -918,9 +1003,9 @@ public: Talk(SAY_INTRO_ALLIANCE_1); _outroZeppelinWait = 0; _events.SetPhase(PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_2, 2500ms, 0, PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_3, 20s, 0, PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_4, 29s + 500ms, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_2, 1s, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_3, 18s + 400ms, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_4, 24s + 500ms, 0, PHASE_INTRO_A); _instance->HandleGameObject(_instance->GetGuidData(GO_SAURFANG_S_DOOR), true); if (GameObject* teleporter = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(GO_SCOURGE_TRANSPORTER_SAURFANG))) @@ -935,24 +1020,21 @@ public: } break; case ACTION_START_OUTRO: - { - me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); - me->SetDisableGravity(false); - me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); - for (ObjectGuid const& guid : _guardList) - if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) - guard->AI()->DoAction(ACTION_OUTRO_DESCEND); - - _instance->SetData(DATA_SAURFANG_CAMP, IN_PROGRESS); - Talk(SAY_OUTRO_ALLIANCE_1); - _events.ScheduleEvent(EVENT_OUTRO_A_GUARDS_KNEEL, 2s + 500ms); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_1, 3s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_2, 18s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_3, 24s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_4, 30s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_5, 49s); - break; - } + // The party hangs in the grip for a while after the kill before it drops them. + _instance->SetData(DATA_SAURFANG_CAMP, IN_PROGRESS); + _events.ScheduleEvent(EVENT_OUTRO_DESCEND, 6s + 100ms); + _events.ScheduleEvent(EVENT_OUTRO_MOURN, 9s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_KNEEL, 9s + 700ms); + _events.ScheduleEvent(EVENT_OUTRO_A_ZEPPELIN, 7s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_GUARDS_KNEEL, 13s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_WALK, 15s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_A_DISTANCE, 27s + 500ms); + _events.ScheduleEvent(EVENT_OUTRO_A_TAKE_POST, 31s + 100ms); + _events.ScheduleEvent(EVENT_OUTRO_A_FALL_IN, 40s + 800ms); + _events.ScheduleEvent(EVENT_OUTRO_A_READY, 43s + 600ms); + _events.ScheduleEvent(EVENT_OUTRO_A_FORM_LINE, 45s + 600ms); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_ARRIVES, 57s + 700ms); + break; case ACTION_EVADE: { float x, y, z, o; @@ -1000,10 +1082,17 @@ public: case POINT_FIRST_STEP: me->SetWalk(false); Talk(SAY_INTRO_ALLIANCE_4); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_5, 5s, 0, PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_6, 7s, 0, PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_7, 9s, 0, PHASE_INTRO_A); - _events.ScheduleEvent(EVENT_INTRO_FINISH, 14s, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_5, 6s, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_6, 8s + 600ms, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_ALLIANCE_7, 11s + 300ms, 0, PHASE_INTRO_A); + _events.ScheduleEvent(EVENT_INTRO_FINISH, 15s + 600ms, 0, PHASE_INTRO_A); + break; + case POINT_A_MURADIN_STEP: + Talk(SAY_OUTRO_ALLIANCE_2); + break; + case POINT_A_MURADIN_ASIDE: + me->SetFacingTo(allianceMuradinAsidePos.GetOrientation()); + me->SetEmoteState(EMOTE_STATE_READY1H); break; case POINT_A_MURADIN_HOME: { @@ -1051,10 +1140,7 @@ public: break; case EVENT_INTRO_ALLIANCE_6: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) - { - deathbringer->AI()->Talk(SAY_INTRO_ALLIANCE_7); deathbringer->AI()->DoCast(me, SPELL_GRIP_OF_AGONY); - } break; case EVENT_INTRO_ALLIANCE_7: if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) @@ -1072,213 +1158,321 @@ public: } break; - case EVENT_OUTRO_A_GUARDS_KNEEL: + case EVENT_OUTRO_DESCEND: + me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); + me->SetDisableGravity(false); + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); + for (ObjectGuid const& guid : _guardList) + if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) + guard->AI()->DoAction(ACTION_OUTRO_DESCEND); + break; + case EVENT_OUTRO_MOURN: + Talk(SAY_OUTRO_ALLIANCE_1); + break; + case EVENT_OUTRO_KNEEL: + me->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case EVENT_OUTRO_GUARDS_KNEEL: for (ObjectGuid const& guid : _guardList) if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) guard->AI()->DoAction(ACTION_OUTRO_KNEEL); break; - case EVENT_OUTRO_ALLIANCE_1: - Talk(SAY_OUTRO_ALLIANCE_2); + case EVENT_OUTRO_WALK: + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_STEP, allianceMuradinStepPos); break; - case EVENT_OUTRO_ALLIANCE_2: - Talk(SAY_OUTRO_ALLIANCE_3); - break; - case EVENT_OUTRO_ALLIANCE_3: - Talk(SAY_OUTRO_ALLIANCE_4); + case EVENT_OUTRO_A_ZEPPELIN: + // Launched well before Muradin spots it, so it is in the distance by then. _instance->SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, IN_PROGRESS); break; - case EVENT_OUTRO_ALLIANCE_4: - Talk(SAY_OUTRO_ALLIANCE_5); + case EVENT_OUTRO_A_DISTANCE: + Talk(SAY_OUTRO_ALLIANCE_3); + me->SetFacingTo(allianceMuradinStepPos.GetOrientation()); + break; + case EVENT_OUTRO_A_TAKE_POST: me->SetWalk(false); - me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_STAND, allianceMuradinPos); + me->GetMotionMaster()->MovePoint(0, allianceMuradinPostPos); + break; + case EVENT_OUTRO_A_FALL_IN: + Talk(SAY_OUTRO_ALLIANCE_5); + for (ObjectGuid const& guid : _guardList) + if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) + guard->AI()->DoAction(ACTION_OUTRO_STAND); + break; + case EVENT_OUTRO_A_READY: + me->SetSheath(SHEATH_STATE_MELEE); + me->SetEmoteState(EMOTE_STATE_READY1H); + break; + case EVENT_OUTRO_A_FORM_LINE: for (ObjectGuid const& guid : _guardList) if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) guard->AI()->DoAction(ACTION_OUTRO_FALL_IN); break; - case EVENT_OUTRO_ALLIANCE_5: + case EVENT_OUTRO_A_SAURFANG_ARRIVES: if (_instance->GetData(DATA_SAURFANG_OUTRO_ZEPPELIN) != DONE) { // Bounded wait for the ship to dock, so a taxi path that never arrives cannot hang the scene. if (++_outroZeppelinWait <= 60) { - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_5, 1s); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_ARRIVES, 1s); break; } } - Talk(SAY_OUTRO_ALLIANCE_6); if (Creature* saurfang = me->SummonCreature(NPC_SE_HIGH_OVERLORD_SAURFANG, allianceSaurfangPos)) { _outroSaurfangGUID = saurfang->GetGUID(); saurfang->SetReactState(REACT_PASSIVE); saurfang->SetSheath(SHEATH_STATE_MELEE); - saurfang->SetEmoteState(EMOTE_STATE_READY1H); // He shares npc_high_overlord_saurfangAI, whose Reset() flags him for gossip. saurfang->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - } - _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_ADVANCE, 3s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_6, 31s); - break; - case EVENT_OUTRO_A_SAURFANG_ADVANCE: - if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) - { saurfang->SetWalk(true); saurfang->GetMotionMaster()->MovePoint(POINT_A_SAURFANG_MEET, allianceSaurfangMeetPos); } + // The one riding the zeppelin has stepped off. + _instance->SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, SPECIAL); + _events.ScheduleEvent(EVENT_OUTRO_A_MURADIN_HALT, 6s + 400ms); break; - case EVENT_OUTRO_ALLIANCE_6: + case EVENT_OUTRO_A_MURADIN_HALT: Talk(SAY_OUTRO_ALLIANCE_7); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_7, 5s); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_DEMAND, 8s + 500ms); break; - case EVENT_OUTRO_ALLIANCE_7: + case EVENT_OUTRO_A_SAURFANG_DEMAND: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_8); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_8, 7s); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_READY, 3s + 700ms); + _events.ScheduleEvent(EVENT_OUTRO_A_MURADIN_YIELD, 9s + 600ms); break; - case EVENT_OUTRO_ALLIANCE_8: + case EVENT_OUTRO_A_SAURFANG_READY: + if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) + saurfang->SetEmoteState(EMOTE_STATE_READY2H); + break; + case EVENT_OUTRO_A_MURADIN_YIELD: Talk(SAY_OUTRO_ALLIANCE_9); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_9, 15s); + _events.ScheduleEvent(EVENT_OUTRO_A_PORTAL, 7s + 800ms); break; - case EVENT_OUTRO_ALLIANCE_9: - Talk(SAY_OUTRO_ALLIANCE_10); - _instance->SetData(DATA_SAURFANG_OUTRO_PORTAL, IN_PROGRESS); - _events.ScheduleEvent(EVENT_OUTRO_A_ROYALS, 3s); - _events.ScheduleEvent(EVENT_OUTRO_A_PORTAL_CLOSE, 9s); + case EVENT_OUTRO_A_PORTAL: + me->SummonCreature(NPC_SE_STORMWIND_PORTAL, alliancePortalPos, TEMPSUMMON_TIMED_DESPAWN, 9800); + _events.ScheduleEvent(EVENT_OUTRO_A_ROYALS, 3s + 700ms); break; case EVENT_OUTRO_A_ROYALS: if (Creature* varian = me->SummonCreature(NPC_SE_KING_VARIAN_WRYNN, allianceVarianPos)) { _outroVarianGUID = varian->GetGUID(); varian->SetReactState(REACT_PASSIVE); + varian->CastSpell(varian, SPELL_SIMPLE_TELEPORT, true); } if (Creature* jaina = me->SummonCreature(NPC_SE_JAINA_PROUDMOORE, allianceJainaPos)) { _outroJainaGUID = jaina->GetGUID(); jaina->SetReactState(REACT_PASSIVE); + jaina->CastSpell(jaina, SPELL_SIMPLE_TELEPORT, true); } - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_10, 5s); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_STAND_DOWN, 3s + 600ms); break; - case EVENT_OUTRO_A_PORTAL_CLOSE: - _instance->SetData(DATA_SAURFANG_OUTRO_PORTAL, DONE); - break; - case EVENT_OUTRO_ALLIANCE_10: + case EVENT_OUTRO_A_VARIAN_STAND_DOWN: if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) varian->AI()->Talk(SAY_OUTRO_ALLIANCE_11); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_11, 6s); + _events.ScheduleEvent(EVENT_OUTRO_A_STEP_ASIDE, 6s + 100ms); break; - case EVENT_OUTRO_ALLIANCE_11: + case EVENT_OUTRO_A_STEP_ASIDE: { - // He carries the rest of the scene; without him, skip to the cleanup. Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID); - if (!saurfang) + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + if (saurfang) + varian->SetFacingToObject(saurfang); + + // The looping ready emote would override the run animation; it is put back on arrival. + me->SetEmoteState(EMOTE_ONESHOT_NONE); + me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_ASIDE, allianceMuradinAsidePos); + for (ObjectGuid const& guid : _guardList) + if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) + guard->AI()->DoAction(ACTION_OUTRO_STEP_ASIDE); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_LOWER, 4s + 100ms); + break; + } + case EVENT_OUTRO_A_SAURFANG_LOWER: + if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) + saurfang->SetEmoteState(EMOTE_ONESHOT_NONE); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_TO_CORPSE, 1s + 200ms); + break; + case EVENT_OUTRO_A_SAURFANG_TO_CORPSE: + { + // He carries the rest of the scene; without him or the body, skip to the cleanup. + Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID); + Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)); + if (!saurfang || !deathbringer) { _events.ScheduleEvent(EVENT_OUTRO_A_DISMISS, 1s); break; } - saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_12); - saurfang->SetEmoteState(EMOTE_ONESHOT_NONE); - saurfang->SetSheath(SHEATH_STATE_UNARMED); - if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) - { - float x, y, z; - deathbringer->GetClosePoint(x, y, z, deathbringer->GetObjectSize()); - saurfang->SetWalk(true); - saurfang->GetMotionMaster()->MovePoint(POINT_A_CORPSE, x, y, z); - } - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_12, 14s); + float x, y, z; + deathbringer->GetClosePoint(x, y, z, deathbringer->GetObjectSize()); + Position const corpse(x, y, z); + saurfang->SetWalk(true); + saurfang->GetMotionMaster()->MovePoint(POINT_A_CORPSE, corpse); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_KNEEL, WalkTimeTo(saurfang, corpse)); break; } - case EVENT_OUTRO_ALLIANCE_12: + case EVENT_OUTRO_A_SAURFANG_KNEEL: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) { if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) saurfang->SetFacingToObject(deathbringer); saurfang->SetStandState(UNIT_STAND_STATE_KNEEL); - saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_13); } - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_13, 8s); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_ORCISH, 1s + 800ms); break; - case EVENT_OUTRO_ALLIANCE_13: + case EVENT_OUTRO_A_SAURFANG_ORCISH: + if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) + saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_13); + _events.ScheduleEvent(EVENT_OUTRO_A_PICKUP, 6s); + break; + case EVENT_OUTRO_A_PICKUP: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) { - saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_14); if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) { // Revive before the cast - Unit::_EnterVehicle refuses a dead passenger. deathbringer->setDeathState(DeathState::Alive); deathbringer->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); deathbringer->CastSpell(saurfang, SPELL_RIDE_VEHICLE, true); + // The revived body would otherwise stand upright on his shoulder. + deathbringer->SetEmoteState(EMOTE_STATE_DROWNED); } saurfang->SetStandState(UNIT_STAND_STATE_STAND); saurfang->SetWalk(true); saurfang->GetMotionMaster()->MovePoint(POINT_A_VARIAN, allianceVarianMeetPos); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_AT_VARIAN, WalkTimeTo(saurfang, allianceVarianMeetPos)); } - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_14, 18s); + else + _events.ScheduleEvent(EVENT_OUTRO_A_DISMISS, 1s); break; - case EVENT_OUTRO_ALLIANCE_14: + case EVENT_OUTRO_A_SAURFANG_AT_VARIAN: + { + Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID); + Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID); + if (saurfang && varian) + { + saurfang->SetFacingToObject(varian); + varian->SetFacingToObject(saurfang); + } + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_THANKS, 1s + 800ms); + break; + } + case EVENT_OUTRO_A_SAURFANG_THANKS: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_15); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_EULOGY, 9s + 800ms); + break; + case EVENT_OUTRO_A_VARIAN_EULOGY: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + varian->AI()->Talk(SAY_OUTRO_ALLIANCE_16); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_TALK, 3s); _events.ScheduleEvent(EVENT_OUTRO_A_ZEPPELIN_RELEASE, 6s); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_15, 9s); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_TALK, 6s + 600ms); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_SALUTE, 10s + 500ms); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_NOD, 19s); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_LEAVE, 23s + 100ms); + break; + case EVENT_OUTRO_A_VARIAN_TALK: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + varian->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + break; + case EVENT_OUTRO_A_VARIAN_SALUTE: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + varian->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); break; case EVENT_OUTRO_A_ZEPPELIN_RELEASE: // The transport only pulls away ~24s after release, timed to lift off as he boards. _instance->SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, DONE); break; - case EVENT_OUTRO_ALLIANCE_15: - if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) - varian->AI()->Talk(SAY_OUTRO_ALLIANCE_16); - _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_NOD, 6s); - _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_LEAVE, 18s); - break; case EVENT_OUTRO_A_SAURFANG_NOD: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_SAURFANG_NOD); break; - case EVENT_OUTRO_ALLIANCE_16: - if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) - jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_17); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_17, 4s); - break; - case EVENT_OUTRO_ALLIANCE_17: - if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) - varian->AI()->Talk(SAY_OUTRO_ALLIANCE_18); - _events.ScheduleEvent(EVENT_OUTRO_A_JAINA_SMILE, 4s); - break; - case EVENT_OUTRO_A_JAINA_SMILE: - if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) - jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_JAINA_SMILE); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_18, 4s); - break; - case EVENT_OUTRO_ALLIANCE_18: - if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) - jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_19); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_19, 6s); - break; case EVENT_OUTRO_A_SAURFANG_LEAVE: if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) { - saurfang->SetWalk(false); - saurfang->GetMotionMaster()->MovePoint(POINT_A_EXIT, allianceSaurfangPos); - saurfang->DespawnOrUnsummon(5s); - // The body goes with him: despawned later it would drop a revived Deathbringer on the rise. - if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) - deathbringer->DespawnOrUnsummon(5s); + saurfang->SetWalk(true); + saurfang->GetMotionMaster()->MovePoint(POINT_A_EXIT, allianceSaurfangExitPos); + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_GONE, WalkTimeTo(saurfang, allianceSaurfangExitPos)); } - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_16, 6s); + else + _events.ScheduleEvent(EVENT_OUTRO_A_SAURFANG_GONE, 1s); break; - case EVENT_OUTRO_ALLIANCE_19: + case EVENT_OUTRO_A_SAURFANG_GONE: + if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID)) + saurfang->DespawnOrUnsummon(); + // The body goes with him: despawned later it would drop a revived Deathbringer on the rise. + if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG))) + deathbringer->DespawnOrUnsummon(); + _outroSaurfangGUID.Clear(); + if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) + jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_17); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_TURN, 2s + 400ms); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_ASK, 3s + 600ms); + _events.ScheduleEvent(EVENT_OUTRO_A_JAINA_SOB, 6s + 500ms); + _events.ScheduleEvent(EVENT_OUTRO_A_JAINA_TURN, 8s + 900ms); + _events.ScheduleEvent(EVENT_OUTRO_A_JAINA_SMILE, 10s + 300ms); + _events.ScheduleEvent(EVENT_OUTRO_A_JAINA_REPLY, 12s + 700ms); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_ORDERS, 22s + 400ms); + _events.ScheduleEvent(EVENT_OUTRO_A_VARIAN_FACE_MURADIN, 25s + 800ms); + _events.ScheduleEvent(EVENT_OUTRO_A_MURADIN_ACK, 35s + 900ms); + _events.ScheduleEvent(EVENT_OUTRO_A_DISMISS, 42s + 400ms); + break; + case EVENT_OUTRO_A_VARIAN_TURN: + { + Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID); + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + if (jaina) + varian->SetFacingToObject(jaina); + if (jaina) + jaina->HandleEmoteCommand(EMOTE_ONESHOT_CRY); + break; + } + case EVENT_OUTRO_A_VARIAN_ASK: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + varian->AI()->Talk(SAY_OUTRO_ALLIANCE_18); + break; + case EVENT_OUTRO_A_JAINA_SOB: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) + jaina->HandleEmoteCommand(EMOTE_ONESHOT_CRY); + break; + case EVENT_OUTRO_A_JAINA_TURN: + { + Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID); + if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) + if (varian) + jaina->SetFacingToObject(varian); + break; + } + case EVENT_OUTRO_A_JAINA_SMILE: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) + jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_JAINA_SMILE); + break; + case EVENT_OUTRO_A_JAINA_REPLY: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) + jaina->AI()->Talk(SAY_OUTRO_ALLIANCE_19); + break; + case EVENT_OUTRO_A_VARIAN_ORDERS: if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) varian->AI()->Talk(SAY_OUTRO_ALLIANCE_20); - _events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_20, 6s); break; - case EVENT_OUTRO_ALLIANCE_20: + case EVENT_OUTRO_A_VARIAN_FACE_MURADIN: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + varian->SetFacingToObject(me); + break; + case EVENT_OUTRO_A_MURADIN_ACK: + if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID)) + me->SetFacingToObject(varian); Talk(SAY_OUTRO_ALLIANCE_21); + me->SetEmoteState(EMOTE_ONESHOT_NONE); for (ObjectGuid const& guid : _guardList) if (Creature* guard = ObjectAccessor::GetCreature(*me, guid)) guard->AI()->DoAction(ACTION_OUTRO_STAND_DOWN); - _events.ScheduleEvent(EVENT_OUTRO_A_DISMISS, 8s); break; case EVENT_OUTRO_A_DISMISS: { @@ -1288,7 +1482,6 @@ public: varian->DespawnOrUnsummon(); if (Creature* jaina = ObjectAccessor::GetCreature(*me, _outroJainaGUID)) jaina->DespawnOrUnsummon(); - _instance->SetData(DATA_SAURFANG_OUTRO_PORTAL, DONE); _instance->SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, DONE); _outroSaurfangGUID.Clear(); _outroVarianGUID.Clear(); @@ -1362,6 +1555,7 @@ public: // _index is left alone: it is their identity, not encounter state. void Reset() override { + _events.Reset(); me->SetSheath(SHEATH_STATE_UNARMED); me->SetEmoteState(EMOTE_ONESHOT_NONE); } @@ -1386,68 +1580,107 @@ public: if (type != POINT_MOTION_TYPE) return; - if (id == POINT_RETREAT) + switch (id) { - me->SetSheath(SHEATH_STATE_UNARMED); - me->SetVisible(false); - } - else if (id == POINT_A_STAND_DOWN) - { - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->SetFacingTo(o); - me->SetSheath(SHEATH_STATE_UNARMED); + case POINT_A_FORMATION: + me->SetFacingTo(allianceFormationPos[_index].GetOrientation()); + me->SetSheath(SHEATH_STATE_MELEE); + me->SetEmoteState(EMOTE_STATE_READY1H); + break; + case POINT_A_ASIDE: + me->SetFacingTo(allianceAsidePos[_index].GetOrientation()); + me->SetEmoteState(EMOTE_STATE_READY1H); + break; + case POINT_A_STAND_DOWN: + { + float x, y, z, o; + me->GetHomePosition(x, y, z, o); + me->SetFacingTo(o); + me->SetSheath(SHEATH_STATE_UNARMED); + } + break; + default: + break; } } void DoAction(int32 action) override { - if (action == ACTION_CHARGE && _index) + switch (action) { - // Drop the guard stance, otherwise the looping emote overrides the run animation. - me->SetEmoteState(EMOTE_ONESHOT_NONE); - me->SetWalk(false); - me->GetMotionMaster()->MoveCharge(chargePos[_index].GetPositionX(), chargePos[_index].GetPositionY(), chargePos[_index].GetPositionZ(), 13.0f, POINT_CHARGE); + case ACTION_CHARGE: + if (!_index) + break; + // Drop the guard stance, otherwise the looping emote overrides the run animation. + me->SetEmoteState(EMOTE_ONESHOT_NONE); + me->SetWalk(false); + me->GetMotionMaster()->MoveCharge(chargePos[_index].GetPositionX(), chargePos[_index].GetPositionY(), chargePos[_index].GetPositionZ(), 13.0f, POINT_CHARGE); + break; + case ACTION_READY_WEAPONS: + me->SetSheath(SHEATH_STATE_MELEE); + me->SetEmoteState(EMOTE_STATE_READY1H); + break; + case ACTION_OUTRO_DESCEND: + me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); + me->SetDisableGravity(false); + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); + me->SetEmoteState(EMOTE_STATE_READY1H); + break; + case ACTION_OUTRO_KNEEL: + // Kneeling is a stand state; the combat-ready emote would override it. + me->SetEmoteState(EMOTE_ONESHOT_NONE); + me->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case ACTION_OUTRO_STAND: + me->SetStandState(UNIT_STAND_STATE_STAND); + break; + case ACTION_OUTRO_FALL_IN: + if (!_index) + break; + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(POINT_A_FORMATION, allianceFormationPos[_index]); + break; + case ACTION_OUTRO_STEP_ASIDE: + if (!_index) + break; + // The ready stance comes back on arrival, or it would override the run animation. + me->SetEmoteState(EMOTE_ONESHOT_NONE); + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(POINT_A_ASIDE, allianceAsidePos[_index]); + break; + case ACTION_OUTRO_RETREAT: + case ACTION_OUTRO_STAND_DOWN: + { + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetEmoteState(EMOTE_ONESHOT_NONE); + me->SetWalk(false); + float x, y, z, o; + me->GetHomePosition(x, y, z, o); + me->GetMotionMaster()->MovePoint(action == ACTION_OUTRO_RETREAT ? POINT_RETREAT : POINT_A_STAND_DOWN, x, y, z); + // The Horde guards never reach the transporter: a few strides and they are gone. + if (action == ACTION_OUTRO_RETREAT) + _events.ScheduleEvent(EVENT_OUTRO_GUARD_VANISH, 2s + 400ms); + } + break; + default: + break; } - else if (action == ACTION_READY_WEAPONS) + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + if (_events.ExecuteEvent() == EVENT_OUTRO_GUARD_VANISH) { - me->SetSheath(SHEATH_STATE_MELEE); - me->SetEmoteState(EMOTE_STATE_READY1H); - } - else if (action == ACTION_OUTRO_DESCEND) - { - me->RemoveAurasDueToSpell(SPELL_GRIP_OF_AGONY); - me->SetDisableGravity(false); - me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 539.2917f, FORCED_MOVEMENT_NONE, 10.0f); - me->SetEmoteState(EMOTE_STATE_READY1H); - } - else if (action == ACTION_OUTRO_FALL_IN) - { - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetSheath(SHEATH_STATE_MELEE); - me->SetEmoteState(EMOTE_STATE_READY1H); - me->SetWalk(false); - me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_STAND, allianceMuradinPos.GetPositionX() + allianceGuardOffset[_index][0], - allianceMuradinPos.GetPositionY() + allianceGuardOffset[_index][1], allianceMuradinPos.GetPositionZ()); - } - else if (action == ACTION_OUTRO_KNEEL) - { - // Kneeling is a stand state; the combat-ready emote would override it. - me->SetEmoteState(EMOTE_ONESHOT_NONE); - me->SetStandState(UNIT_STAND_STATE_KNEEL); - } - else if (action == ACTION_OUTRO_RETREAT || action == ACTION_OUTRO_STAND_DOWN) - { - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetEmoteState(EMOTE_ONESHOT_NONE); - me->SetWalk(false); - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->GetMotionMaster()->MovePoint(action == ACTION_OUTRO_RETREAT ? POINT_RETREAT : POINT_A_STAND_DOWN, x, y, z); + me->GetMotionMaster()->Clear(); + me->StopMoving(); + me->SetSheath(SHEATH_STATE_UNARMED); + me->SetVisible(false); } } private: + EventMap _events; uint32 _index; }; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 2ed868cd19..40b5175216 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -136,7 +136,6 @@ enum DataTypes DATA_ORGRIMS_HAMMER = 42, DATA_SAURFANG_CAMP = 43, DATA_SAURFANG_OUTRO_ZEPPELIN = 44, - DATA_SAURFANG_OUTRO_PORTAL = 45, // pussywizard: DATA_BUFF_AVAILABLE = 251, @@ -253,6 +252,7 @@ enum CreaturesIds NPC_SE_HIGH_OVERLORD_SAURFANG = 37187, NPC_SE_KOR_KRON_REAVER = 37920, NPC_SE_SKYBREAKER_MARINE = 37830, + NPC_SE_STORMWIND_PORTAL = 37880, // Victory camp raised on Deathbringer's Rise once Saurfang is defeated NPC_CAMP_ALLIANCE_MASON = 37902, NPC_CAMP_SHELY_STEELBOWELS = 37903, // Alliance blacksmith @@ -446,11 +446,9 @@ enum GameObjectsIds GO_SAURFANG_CAMP_TELEPORTER_A = 201858, GO_SAURFANG_CAMP_TELEPORTER_H = 201880, GO_SAURFANG_CAMP_BANNER_A = 201869, - GO_SAURFANG_CAMP_ANVIL_A = 23303, // the Alliance camp uses its own anvil + GO_SAURFANG_CAMP_ANVIL_A = 1684, // the Alliance camp uses its own anvil // Zeppelin is a MO_TRANSPORT (taxi path 1834) created through TransportMgr, like the gunships. - // The portal is a SPELLCASTER, spawned non-selectable so players cannot click its spell. GO_SAURFANG_OUTRO_ZEPPELIN = 201834, - GO_SAURFANG_OUTRO_PORTAL = 193207, // Professor Putricide GO_ORANGE_PLAGUE_MONSTER_ENTRANCE = 201371, diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index a520451db0..6c30742e8c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -30,6 +30,7 @@ #include "WorldSession.h" #include "WorldStateDefines.h" #include "icecrown_citadel.h" +#include enum EventIds { @@ -58,14 +59,23 @@ enum TimedEvents EVENT_SAURFANG_OUTRO_TIMEOUT = 8, EVENT_SAURFANG_ZEPPELIN_REMOVE = 9, // The camp is built on screen, stage by stage. - EVENT_SAURFANG_CAMP_TELEPORTERS = 10, + EVENT_SAURFANG_CAMP_ACTIVATE = 10, EVENT_SAURFANG_CAMP_WORKERS = 11, - EVENT_SAURFANG_CAMP_TENTS = 12, - EVENT_SAURFANG_CAMP_WORKERS_OUT = 13, - EVENT_SAURFANG_CAMP_VENDORS = 14, - EVENT_SAURFANG_CAMP_SMITH_ARRIVE = 15, - EVENT_SAURFANG_CAMP_WORKERS_BACK = 16, - EVENT_SAURFANG_CAMP_VENDOR_ARRIVE = 17 + EVENT_SAURFANG_CAMP_WORKER_0_RUN = 12, + EVENT_SAURFANG_CAMP_WORKER_1_RUN = 13, + EVENT_SAURFANG_CAMP_WORKER_0_WORK = 14, + EVENT_SAURFANG_CAMP_WORKER_1_WORK = 15, + EVENT_SAURFANG_CAMP_TENTS = 16, + EVENT_SAURFANG_CAMP_VENDORS = 17 +}; + +// waypoint_data paths walking the camp vendors from their teleporter pads to their pitches. +enum SaurfangCampPaths +{ + PATH_CAMP_SHELY_STEELBOWELS = NPC_CAMP_SHELY_STEELBOWELS * 10, + PATH_CAMP_BRAZIE_GETZ = NPC_CAMP_BRAZIE_GETZ * 10, + PATH_CAMP_APOTHECARY_CANDITH_TOMAS = NPC_CAMP_APOTHECARY_CANDITH_TOMAS * 10, + PATH_CAMP_MORGAN_DAYBLAZE = NPC_CAMP_MORGAN_DAYBLAZE * 10 }; enum Spells @@ -76,7 +86,8 @@ enum Spells BLOOD_BEAM_VISUAL_LHAND = 72303, BLOOD_BEAM_VISUAL_LLEG = 72302, BLOOD_BEAM_VISUAL_RLEG = 72301, - VOID_ZONE_VISUAL = 69422 + VOID_ZONE_VISUAL = 69422, + SPELL_SIMPLE_TELEPORT = 12980 // the camp crew stepping off the teleporter pads }; enum Say @@ -174,8 +185,8 @@ Position const UtherSpawnPos = { -26.58507f, 2211.524f, 30.19898f, 3.124139f Position const SylvanasSpawnPos = { -41.45833f, 2222.891f, 27.98586f, 3.647738f }; Position const SindragosaSpawnPos = { 4818.6997f, 2483.7102f, 287.06497f, 3.286661f }; -// The camps differ: Horde tents from the retail sniff, Alliance ones measured in game and much -// closer together, with their own forge, anvil and banner by the second tent, and no bonfire. +// The camps differ: the Horde raises two tents by its bonfire, forge and anvil, the Alliance a +// tighter cluster with a banner and its own anvil and forge, and no bonfire. Position const SaurfangCampTentPosH[2] = { { -532.86456f, 2229.0088f, 539.2921f, 2.530723f }, @@ -184,18 +195,13 @@ Position const SaurfangCampTentPosH[2] = Position const SaurfangCampTentPosA[2] = { - { -531.84283f, 2230.6853f, 539.2918f, 5.625422f }, - { -528.76000f, 2234.7815f, 539.2918f, 5.609714f } + { -532.52606f, 2229.6511f, 539.29193f, 5.480516f }, + { -528.74830f, 2233.4705f, 539.29193f, 5.463047f } }; -Position const SaurfangCampBannerPosA = { -533.05540f, 2234.6326f, 539.2918f, 5.621495f }; -Position const SaurfangCampBlacksmithPosA = { -526.41502f, 2232.9104f, 539.2918f, 5.609714f }; -Position const SaurfangCampGeneralGoodsPosA = { -529.46875f, 2228.8513f, 539.2918f, 5.625422f }; -Position const SaurfangCampAnvilPosA = { -525.71640f, 2236.1909f, 539.2918f, 5.609714f }; -Position const SaurfangCampForgePosA = { -528.06140f, 2238.0620f, 539.2918f, 5.609714f }; -// Detour: both Alliance vendors round the south side, on separate corners. -Position const SaurfangCampSmithDetourPosA = { -525.50000f, 2227.5000f, 539.2918f, 0.0f }; -Position const SaurfangCampGoodsDetourPosA = { -528.50000f, 2225.0000f, 539.2918f, 0.0f }; +Position const SaurfangCampBannerPosA = { -533.14410f, 2233.9670f, 539.29224f, 5.463047f }; +Position const SaurfangCampAnvilPosA = { -524.68580f, 2235.8855f, 539.29190f, 0.7853982f }; +Position const SaurfangCampForgePosA = { -526.50867f, 2237.5417f, 539.29205f, 0.0f }; Position const SaurfangCampTeleporterPos[2] = { @@ -203,16 +209,36 @@ Position const SaurfangCampTeleporterPos[2] = { -560.29517f, 2220.2153f, 539.28540f, 0.0f } }; -Position const SaurfangCampBlacksmithPos = { -520.94100f, 2233.1077f, 539.3463f, 5.3756142f }; -Position const SaurfangCampGeneralGoodsPos = { -530.17017f, 2226.2310f, 539.3463f, 5.4628806f }; -// Travel time for the ~41y between a teleporter pad and its tent site. -Seconds const SaurfangCampWorkerTravel = 7s; -// The Horde smith would otherwise walk straight through the bonfire. -Position const SaurfangCampSmithDetourPos = { -529.00000f, 2233.0000f, 539.2920f, 0.0f }; -Position const SaurfangOutroPortalPos = { -523.55963f, 2238.8900f, 539.29070f, 6.1102815f }; +// Where the workers stand hammering, one per teleporter pad, short of the tent they raise. +Position const SaurfangCampWorkPosH[2] = +{ + { -521.00696f, 2235.4844f, 539.29175f, 2.3f }, + { -529.47220f, 2225.7360f, 539.29193f, 2.3f } +}; + +Position const SaurfangCampWorkPosA[2] = +{ + { -524.22050f, 2232.9392f, 539.27704f, 2.2165682f }, + { -530.03820f, 2227.0989f, 539.27704f, 2.2863812f } +}; + +// The vendors' pitches, the end of their waypoint paths, for a camp not built on screen. +Position const SaurfangCampBlacksmithPos = { -520.94100f, 2233.1077f, 539.2769f, 5.3756142f }; +Position const SaurfangCampGeneralGoodsPos = { -530.17017f, 2226.2310f, 539.2770f, 5.4628806f }; +Position const SaurfangCampBlacksmithPosA = { -526.80206f, 2231.3682f, 539.2771f, 5.5152402f }; +Position const SaurfangCampGeneralGoodsPosA = { -530.60070f, 2227.6736f, 539.2771f, 5.4977870f }; + // Where the zeppelin comes to rest; it is frozen on arrival. Position const SaurfangOutroZeppelinPos = { -527.66110f, 2254.6910f, 538.53300f, 0.6848107f }; float const SaurfangOutroZeppelinDockRange = 12.0f; +// Saurfang's spot on deck while it flies in, relative to the transport. +Position const SaurfangZeppelinPassengerOffset = { -1.122207f, -2.488975f, -17.782246f, 1.553343f }; + +// Travel time of a mover at its run speed to a point, plus a margin for the spline to settle. +static Milliseconds RunTimeTo(Unit const* who, Position const& to) +{ + return Milliseconds(uint32(who->GetExactDist2d(&to) / who->GetSpeed(MOVE_RUN) * 1000.0f)) + 300ms; +} // Set position traps Spirit Alarm std::vector GoSpiritAlarm_1 = { { -160.96f, 2210.46f, 35.24f, 0.0f }, { -176.27f, 2201.93f, 35.24f, 0.0f}, { -207.83f, 2207.38f, 35.24f, 0.0f } }; @@ -1363,63 +1389,94 @@ public: RestoreSaurfangEventNpc(guard); } - // staged runs the on-screen build: teleporters, then workers raising the tents, then the - // vendors. Unstaged drops the finished camp at once, for an instance already DONE. + void DespawnSaurfangZeppelinPassenger() + { + if (Creature* passenger = instance->GetCreature(SaurfangZeppelinPassengerGUID)) + passenger->DespawnOrUnsummon(); + SaurfangZeppelinPassengerGUID.Clear(); + } + + // staged runs the on-screen build: teleporters and workers first, the tents once the workers + // have hammered a while, then the vendors walking in. Unstaged drops the finished camp at + // once, for an instance already DONE. The Horde crew is quicker off the mark than the Alliance one. void SpawnSaurfangCamp(bool staged) { if (_saurfangCampSpawned || !instance->HavePlayers()) return; _saurfangCampSpawned = true; - if (staged) + SummonSaurfangCampTeleporters(); + SpawnSaurfangEventNpcs(); + if (!staged) { - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_TELEPORTERS, 3s); + ActivateSaurfangCampTeleporters(); + SpawnSaurfangCampTents(); + SummonSaurfangCampVendor(true, false); + SummonSaurfangCampVendor(false, false); return; } - SummonSaurfangCampTeleporters(); - SpawnSaurfangEventNpcs(); - SpawnSaurfangCampTents(); - SummonSaurfangCampVendor(true, false); - SummonSaurfangCampVendor(false, false); + bool const horde = GetTeamIdInInstance() == TEAM_HORDE; + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_ACTIVATE, horde ? 2s : 1s + 700ms); + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKERS, horde ? 0s : 5s + 800ms); + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKER_0_RUN, horde ? 2s : 11s); + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKER_1_RUN, horde ? 2s : 8s + 200ms); + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_TENTS, horde ? 26s + 800ms : 34s + 500ms); + Events.ScheduleEvent(EVENT_SAURFANG_CAMP_VENDORS, horde ? 30s + 400ms : 41s + 300ms); } + // Summoned ready is the inert model; activating it is what turns the beam on. void SummonSaurfangCampTeleporters() { uint32 const teleporter = GetTeamIdInInstance() == TEAM_HORDE ? GO_SAURFANG_CAMP_TELEPORTER_H : GO_SAURFANG_CAMP_TELEPORTER_A; for (uint8 i = 0; i < 2; ++i) - { - // Summoned ready is the inert model; activating it is what turns the beam on. if (GameObject* go = instance->SummonGameObject(teleporter, SaurfangCampTeleporterPos[i], 0.0f, 0.0f, 0.0f, 0.0f, WEEK)) - go->SetGoState(GO_STATE_ACTIVE); - } + SaurfangCampTeleporterGUIDs[i] = go->GetGUID(); } - void SpawnSaurfangCampTeleporters() + void ActivateSaurfangCampTeleporters() { - SummonSaurfangCampTeleporters(); - SpawnSaurfangEventNpcs(); - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKERS, 5s); + for (ObjectGuid const& guid : SaurfangCampTeleporterGUIDs) + if (GameObject* go = instance->GetGameObject(guid)) + go->SetGoState(GO_STATE_ACTIVE); } void SpawnSaurfangCampWorkers() { - bool const horde = GetTeamIdInInstance() == TEAM_HORDE; - uint32 const worker = horde ? NPC_CAMP_WARSONG_PEON : NPC_CAMP_ALLIANCE_MASON; + uint32 const worker = GetTeamIdInInstance() == TEAM_HORDE ? NPC_CAMP_WARSONG_PEON : NPC_CAMP_ALLIANCE_MASON; for (uint8 i = 0; i < 2; ++i) { if (Creature* builder = instance->SummonCreature(worker, SaurfangCampTeleporterPos[i])) { - SaurfangCampWorkerGUIDs.push_back(builder->GetGUID()); + SaurfangCampWorkerGUIDs[i] = builder->GetGUID(); builder->SetReactState(REACT_PASSIVE); - builder->SetWalk(false); - builder->GetMotionMaster()->MovePoint(0, (horde ? SaurfangCampTentPosH[i] : SaurfangCampTentPosA[i])); - builder->SetEmoteState(EMOTE_STATE_WORK_MINING); + builder->CastSpell(builder, SPELL_SIMPLE_TELEPORT, true); } } + } - // Timed from the summon, so the run out has to be paid for before the hammering starts. - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKERS_BACK, SaurfangCampWorkerTravel + (horde ? 3s : 15s)); + // The hammering only starts once he is standing at the site. + void SendSaurfangCampWorkerOut(uint8 i) + { + Creature* builder = instance->GetCreature(SaurfangCampWorkerGUIDs[i]); + if (!builder) + return; + + Position const& site = GetTeamIdInInstance() == TEAM_HORDE ? SaurfangCampWorkPosH[i] : SaurfangCampWorkPosA[i]; + builder->SetWalk(false); + builder->GetMotionMaster()->MovePoint(0, site); + Events.ScheduleEvent(i ? EVENT_SAURFANG_CAMP_WORKER_1_WORK : EVENT_SAURFANG_CAMP_WORKER_0_WORK, RunTimeTo(builder, site)); + } + + void SetSaurfangCampWorkerWorking(uint8 i) + { + Creature* builder = instance->GetCreature(SaurfangCampWorkerGUIDs[i]); + if (!builder) + return; + + Position const& site = GetTeamIdInInstance() == TEAM_HORDE ? SaurfangCampWorkPosH[i] : SaurfangCampWorkPosA[i]; + builder->SetFacingTo(site.GetOrientation()); + builder->SetEmoteState(EMOTE_STATE_WORK_MINING); } void SpawnSaurfangCampTents() @@ -1449,50 +1506,32 @@ public: } } - // They clear the site before the tent drops, or it lands on top of them. + // They down tools the moment the tents drop, run back to their pads and vanish there. void SendSaurfangCampWorkersBack() { - uint8 i = 0; - for (ObjectGuid const& guid : SaurfangCampWorkerGUIDs) + for (uint8 i = 0; i < 2; ++i) { - if (Creature* builder = instance->GetCreature(guid)) + if (Creature* builder = instance->GetCreature(SaurfangCampWorkerGUIDs[i])) { builder->SetEmoteState(EMOTE_ONESHOT_NONE); builder->SetWalk(false); - builder->GetMotionMaster()->MovePoint(0, SaurfangCampTeleporterPos[i % 2]); + builder->GetMotionMaster()->MovePoint(0, SaurfangCampTeleporterPos[i]); + builder->DespawnOrUnsummon(RunTimeTo(builder, SaurfangCampTeleporterPos[i])); } - ++i; + SaurfangCampWorkerGUIDs[i].Clear(); } - - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_TENTS, 2s); - // They only vanish once they are back standing on the pad. - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKERS_OUT, - SaurfangCampWorkerTravel + (GetTeamIdInInstance() == TEAM_HORDE ? 2s : 5s)); - } - - void DespawnSaurfangCampWorkers() - { - for (ObjectGuid const& guid : SaurfangCampWorkerGUIDs) - if (Creature* builder = instance->GetCreature(guid)) - builder->DespawnOrUnsummon(); - - SaurfangCampWorkerGUIDs.clear(); - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_VENDORS, 1s); } // walkIn false drops the vendor straight on his pitch, for a camp not built on screen. - // The detours exist because the direct line clips the Horde bonfire / the first Alliance tent. void SummonSaurfangCampVendor(bool smith, bool walkIn) { bool const horde = GetTeamIdInInstance() == TEAM_HORDE; uint32 const entry = smith ? (horde ? NPC_CAMP_MORGAN_DAYBLAZE : NPC_CAMP_SHELY_STEELBOWELS) : (horde ? NPC_CAMP_APOTHECARY_CANDITH_TOMAS : NPC_CAMP_BRAZIE_GETZ); - Position const& pitch = smith ? (horde ? SaurfangCampBlacksmithPos : SaurfangCampBlacksmithPosA) - : (horde ? SaurfangCampGeneralGoodsPos : SaurfangCampGeneralGoodsPosA); - if (!walkIn) { - instance->SummonCreature(entry, pitch); + instance->SummonCreature(entry, smith ? (horde ? SaurfangCampBlacksmithPos : SaurfangCampBlacksmithPosA) + : (horde ? SaurfangCampGeneralGoodsPos : SaurfangCampGeneralGoodsPosA)); return; } @@ -1500,33 +1539,10 @@ public: if (!vendor) return; - vendor->SetWalk(true); - if (horde && !smith) - { - vendor->GetMotionMaster()->MovePoint(0, pitch); - return; - } - - Position const& detour = horde ? SaurfangCampSmithDetourPos - : (smith ? SaurfangCampSmithDetourPosA : SaurfangCampGoodsDetourPosA); - vendor->GetMotionMaster()->MovePoint(0, detour); - - if (smith) - { - SaurfangCampSmithGUID = vendor->GetGUID(); - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_SMITH_ARRIVE, horde ? 19s : 17s); - } - else - { - SaurfangCampGoodsGUID = vendor->GetGUID(); - Events.ScheduleEvent(EVENT_SAURFANG_CAMP_VENDOR_ARRIVE, 15s); - } - } - - void SpawnSaurfangCampVendors() - { - SummonSaurfangCampVendor(true, true); - SummonSaurfangCampVendor(false, true); + vendor->CastSpell(vendor, SPELL_SIMPLE_TELEPORT, true); + // The path ends on the pitch, facing the customers. + vendor->GetMotionMaster()->MoveWaypoint(smith ? (horde ? PATH_CAMP_MORGAN_DAYBLAZE : PATH_CAMP_SHELY_STEELBOWELS) + : (horde ? PATH_CAMP_APOTHECARY_CANDITH_TOMAS : PATH_CAMP_BRAZIE_GETZ), false); } void SetData(uint32 type, uint32 data) override @@ -1553,18 +1569,37 @@ public: case DATA_SAURFANG_OUTRO_ZEPPELIN: if (data == IN_PROGRESS) { - if (!SaurfangZeppelinGUID) + if (SaurfangZeppelinGUID) + break; + + _saurfangZeppelinDocked = false; + _saurfangZeppelinLeaving = false; + MotionTransport* zeppelin = sTransportMgr->CreateTransport(GO_SAURFANG_OUTRO_ZEPPELIN, 0, instance); + if (!zeppelin) + break; + + SaurfangZeppelinGUID = zeppelin->GetGUID(); + zeppelin->setActive(true); + Events.ScheduleEvent(EVENT_SAURFANG_ZEPPELIN_DOCK, 1s); + + // Saurfang rides in on deck; the one stepping off at the edge replaces him. + float x, y, z, o; + SaurfangZeppelinPassengerOffset.GetPosition(x, y, z, o); + zeppelin->CalculatePassengerPosition(x, y, z, &o); + if (Creature* passenger = instance->SummonCreature(NPC_SE_HIGH_OVERLORD_SAURFANG, Position(x, y, z, o))) { - _saurfangZeppelinDocked = false; - _saurfangZeppelinLeaving = false; - if (MotionTransport* zeppelin = sTransportMgr->CreateTransport(GO_SAURFANG_OUTRO_ZEPPELIN, 0, instance)) - { - SaurfangZeppelinGUID = zeppelin->GetGUID(); - zeppelin->setActive(true); - Events.ScheduleEvent(EVENT_SAURFANG_ZEPPELIN_DOCK, 1s); - } + zeppelin->AddPassenger(passenger, true); + // Clients already got his create packet without the transport; the stop + // spline carries the transport guid and puts him on deck for them. + passenger->StopMovingOnCurrentPos(); + passenger->SetReactState(REACT_PASSIVE); + passenger->SetSheath(SHEATH_STATE_MELEE); + passenger->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + SaurfangZeppelinPassengerGUID = passenger->GetGUID(); } } + else if (data == SPECIAL) + DespawnSaurfangZeppelinPassenger(); // Asked for twice (departure, then cleanup); without the guard the second call // would restart the removal timer. else if (SaurfangZeppelinGUID && !_saurfangZeppelinLeaving) @@ -1577,25 +1612,9 @@ public: zeppelin->EnableMovement(true); // Releasing _pendingStop does not launch it: it leaves when the stop frame - // DepartureTime comes round, ~24s. Removal is well past that. - Events.ScheduleEvent(EVENT_SAURFANG_ZEPPELIN_REMOVE, 60s); - } - break; - case DATA_SAURFANG_OUTRO_PORTAL: - if (data == IN_PROGRESS) - { - if (GameObject* portal = instance->SummonGameObject(GO_SAURFANG_OUTRO_PORTAL, SaurfangOutroPortalPos, 0.0f, 0.0f, 0.0f, 0.0f, HOUR)) - { - // SPELLCASTER object carrying spell 59065; scenery here, so it must not be clickable. - portal->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - SaurfangPortalGUID = portal->GetGUID(); - } - } - else - { - if (GameObject* portal = instance->GetGameObject(SaurfangPortalGUID)) - portal->DespawnOrUnsummon(); - SaurfangPortalGUID.Clear(); + // DepartureTime comes round, ~24s, and takes about 50s more to fly back to + // its berth above the rise. Removal is once it is back there. + Events.ScheduleEvent(EVENT_SAURFANG_ZEPPELIN_REMOVE, 80s); } break; case DATA_BUFF_AVAILABLE: @@ -2140,7 +2159,6 @@ public: break; case EVENT_SAURFANG_OUTRO_TIMEOUT: _saurfangOutroRunning = false; - SetData(DATA_SAURFANG_OUTRO_PORTAL, DONE); SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, DONE); if (GetBossState(DATA_DEATHBRINGER_SAURFANG) == DONE) { @@ -2148,33 +2166,34 @@ public: SpawnSaurfangEventNpcs(); } break; - case EVENT_SAURFANG_CAMP_TELEPORTERS: - SpawnSaurfangCampTeleporters(); + case EVENT_SAURFANG_CAMP_ACTIVATE: + ActivateSaurfangCampTeleporters(); break; case EVENT_SAURFANG_CAMP_WORKERS: SpawnSaurfangCampWorkers(); break; + case EVENT_SAURFANG_CAMP_WORKER_0_RUN: + SendSaurfangCampWorkerOut(0); + break; + case EVENT_SAURFANG_CAMP_WORKER_1_RUN: + SendSaurfangCampWorkerOut(1); + break; + case EVENT_SAURFANG_CAMP_WORKER_0_WORK: + SetSaurfangCampWorkerWorking(0); + break; + case EVENT_SAURFANG_CAMP_WORKER_1_WORK: + SetSaurfangCampWorkerWorking(1); + break; case EVENT_SAURFANG_CAMP_TENTS: SpawnSaurfangCampTents(); - break; - case EVENT_SAURFANG_CAMP_WORKERS_OUT: - DespawnSaurfangCampWorkers(); - break; - case EVENT_SAURFANG_CAMP_WORKERS_BACK: SendSaurfangCampWorkersBack(); break; - case EVENT_SAURFANG_CAMP_SMITH_ARRIVE: - if (Creature* smith = instance->GetCreature(SaurfangCampSmithGUID)) - smith->GetMotionMaster()->MovePoint(0, GetTeamIdInInstance() == TEAM_HORDE ? SaurfangCampBlacksmithPos : SaurfangCampBlacksmithPosA); - break; - case EVENT_SAURFANG_CAMP_VENDOR_ARRIVE: - if (Creature* goods = instance->GetCreature(SaurfangCampGoodsGUID)) - goods->GetMotionMaster()->MovePoint(0, SaurfangCampGeneralGoodsPosA); - break; case EVENT_SAURFANG_CAMP_VENDORS: - SpawnSaurfangCampVendors(); + SummonSaurfangCampVendor(true, true); + SummonSaurfangCampVendor(false, true); break; case EVENT_SAURFANG_ZEPPELIN_REMOVE: + DespawnSaurfangZeppelinPassenger(); if (GameObject* go = instance->GetGameObject(SaurfangZeppelinGUID)) { // A MO_TRANSPORT is not despawned like an ordinary gameobject. @@ -2376,11 +2395,10 @@ public: ObjectGuid SaurfangTeleportGUID; GuidList SaurfangCampGUIDs; GuidList SaurfangEventGuardGUIDs; - GuidList SaurfangCampWorkerGUIDs; - ObjectGuid SaurfangCampSmithGUID; - ObjectGuid SaurfangCampGoodsGUID; + std::array SaurfangCampTeleporterGUIDs; + std::array SaurfangCampWorkerGUIDs; ObjectGuid SaurfangZeppelinGUID; - ObjectGuid SaurfangPortalGUID; + ObjectGuid SaurfangZeppelinPassengerGUID; bool _saurfangCampSpawned; bool _saurfangOutroRunning; bool _saurfangZeppelinDocked;