mirror of
https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk.git
synced 2026-10-10 07:06:38 +08:00
feat(Scripts/ICC): script the Deathbringer Saurfang outro and victory… (#27112)
Co-authored-by: Skjalf <[email protected]>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
-- Deathbringer Saurfang outro: the camp props only spawned on 10N/25N.
|
||||
UPDATE `gameobject` SET `spawnMask` = 15 WHERE `id` IN (1685, 191345, 187852) AND `guid` IN (12363, 55882, 81649);
|
||||
|
||||
-- Grip of Agony hangs Muradin/High Overlord and their guards in the air, and the script owns the
|
||||
-- disable-gravity flag for that. Without 512 (CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE) the core
|
||||
-- clears the flag again on the next terrain update and they drop back to the floor mid-intro.
|
||||
UPDATE `creature_template` SET `flags_extra` = `flags_extra`|512 WHERE `entry` IN (37187, 37200, 37830, 37920);
|
||||
|
||||
-- Two emote beats of the Alliance outro that have no creature_text row.
|
||||
DELETE FROM `creature_text` WHERE (`CreatureID` = 37187 AND `GroupID` = 19) OR (`CreatureID` = 37188 AND `GroupID` = 2);
|
||||
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
|
||||
(37187, 19, 0, '%s nods.', 16, 0, 0, 273, 0, 0, 37531, 0, 'High Overlord Saurfang - SAY_OUTRO_ALLIANCE_SAURFANG_NOD'),
|
||||
(37188, 2, 0, '%s wipes her tears away and smiles at King Varian Wrynn.', 16, 0, 0, 0, 0, 0, 37544, 0, 'Lady Jaina Proudmoore - SAY_OUTRO_ALLIANCE_JAINA_SMILE');
|
||||
@@ -61,12 +61,13 @@ enum ScriptTexts
|
||||
SAY_OUTRO_HORDE_2 = 12,
|
||||
SAY_OUTRO_HORDE_3 = 13,
|
||||
SAY_OUTRO_HORDE_4 = 14,
|
||||
SAY_OUTRO_ALLIANCE_SAURFANG_NOD = 19, // emote beat, outside the numbered 21
|
||||
|
||||
// Muradin Bronzebeard
|
||||
SAY_INTRO_ALLIANCE_1 = 0,
|
||||
SAY_INTRO_ALLIANCE_4 = 1,
|
||||
SAY_INTRO_ALLIANCE_5 = 2,
|
||||
SAY_OUTRO_ALLIANCE_1 = 3, /// @todo ALLIANCE OUTRO
|
||||
SAY_OUTRO_ALLIANCE_1 = 3,
|
||||
SAY_OUTRO_ALLIANCE_2 = 4,
|
||||
SAY_OUTRO_ALLIANCE_3 = 5,
|
||||
SAY_OUTRO_ALLIANCE_4 = 6,
|
||||
@@ -80,6 +81,7 @@ enum ScriptTexts
|
||||
// Lady Jaina Proudmoore
|
||||
SAY_OUTRO_ALLIANCE_17 = 0,
|
||||
SAY_OUTRO_ALLIANCE_19 = 1,
|
||||
SAY_OUTRO_ALLIANCE_JAINA_SMILE = 2, // emote beat, outside the numbered 21
|
||||
|
||||
// King Varian Wrynn
|
||||
SAY_OUTRO_ALLIANCE_11 = 0,
|
||||
@@ -167,13 +169,22 @@ enum EventTypes
|
||||
EVENT_OUTRO_ALLIANCE_18 = 40,
|
||||
EVENT_OUTRO_ALLIANCE_19 = 41,
|
||||
EVENT_OUTRO_ALLIANCE_20 = 42,
|
||||
EVENT_OUTRO_ALLIANCE_21 = 43,
|
||||
|
||||
EVENT_OUTRO_HORDE_1 = 44,
|
||||
EVENT_OUTRO_HORDE_2 = 45,
|
||||
EVENT_OUTRO_HORDE_3 = 46,
|
||||
EVENT_OUTRO_HORDE_4 = 47,
|
||||
EVENT_OUTRO_HORDE_5 = 48,
|
||||
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,
|
||||
};
|
||||
|
||||
enum Phases
|
||||
@@ -191,11 +202,16 @@ enum Actions
|
||||
ACTION_CONTINUE_INTRO = -3781301,
|
||||
ACTION_CHARGE = -3781302,
|
||||
ACTION_START_OUTRO = -3781303,
|
||||
ACTION_DESPAWN = -3781304,
|
||||
ACTION_INTRO_DONE = -3781305,
|
||||
ACTION_EVADE = -3781306,
|
||||
ACTION_GAIN_SCENT_OF_BLOOD = -3781307,
|
||||
ACTION_MARK_OF_THE_FALLEN_CHAMPION = -72293,
|
||||
ACTION_READY_WEAPONS = -3781308,
|
||||
ACTION_OUTRO_DESCEND = -3781309,
|
||||
ACTION_OUTRO_KNEEL = -3781310,
|
||||
ACTION_OUTRO_RETREAT = -3781311,
|
||||
ACTION_OUTRO_FALL_IN = -3781312,
|
||||
ACTION_OUTRO_STAND_DOWN = -3781313,
|
||||
};
|
||||
|
||||
#define DATA_MADE_A_MESS 45374613 // 4537, 4613 are achievement IDs
|
||||
@@ -209,6 +225,15 @@ enum MovePoints
|
||||
POINT_CHOKE = 3781303,
|
||||
POINT_CORPSE = 3781304,
|
||||
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_STAND_DOWN = 3781313,
|
||||
POINT_A_MURADIN_HOME = 3781314,
|
||||
};
|
||||
|
||||
Position const deathbringerPos = {-496.3542f, 2211.33f, 541.1138f, 0.0f};
|
||||
@@ -224,17 +249,35 @@ 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.
|
||||
Position const chokePos[6] =
|
||||
{
|
||||
{-514.4834f, 2211.334f, 549.2887f, 0.0f}, // High Overlord Saurfang/Muradin Bronzebeard
|
||||
{-510.1081f, 2211.592f, 546.3773f, 0.0f}, // front left
|
||||
{-514.4834f, 2211.334f, 551.2882f, 0.0f}, // High Overlord Saurfang/Muradin Bronzebeard
|
||||
{-510.1081f, 2211.592f, 551.2882f, 0.0f}, // front left
|
||||
{-513.3210f, 2211.396f, 551.2882f, 0.0f}, // front 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
|
||||
{-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
|
||||
};
|
||||
|
||||
//Position const finalPos = {-563.7552f, 2211.328f, 538.7848f, 0.0f};
|
||||
Position const finalPos = {-563.7552f, 2211.328f, 538.7848f, 0.0f};
|
||||
Position const transporterPos = {-549.0735f, 2211.289f, 539.2917f, 3.1415927f};
|
||||
|
||||
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] =
|
||||
{
|
||||
{ 0.0f, 0.0f},
|
||||
{ 2.6f, -1.4f},
|
||||
{ 3.4f, 1.2f},
|
||||
{ 0.8f, -3.2f},
|
||||
{-1.6f, -2.4f},
|
||||
{-2.2f, 1.0f}
|
||||
};
|
||||
|
||||
class boss_deathbringer_saurfang : public CreatureScript
|
||||
{
|
||||
@@ -255,6 +298,7 @@ public:
|
||||
me->SetReactState(REACT_DEFENSIVE);
|
||||
events.Reset();
|
||||
_introDone = false;
|
||||
_outroStarted = false;
|
||||
_frenzied = false;
|
||||
_fallenChampionCastCount = 0;
|
||||
_transportCheckTimer = 1000;
|
||||
@@ -308,6 +352,7 @@ public:
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
_outroStarted = true;
|
||||
DoCast(me, SPELL_ACHIEVEMENT, true);
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
@@ -497,6 +542,10 @@ public:
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
// The outro revives the corpse to carry it, so evading here would roll DONE back to FAIL.
|
||||
if (_outroStarted)
|
||||
return;
|
||||
|
||||
BossAI::EnterEvadeMode(why);
|
||||
if (Creature* creature = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SAURFANG_EVENT_NPC)))
|
||||
creature->AI()->DoAction(ACTION_EVADE);
|
||||
@@ -505,6 +554,7 @@ public:
|
||||
private:
|
||||
uint32 _fallenChampionCastCount;
|
||||
bool _introDone;
|
||||
bool _outroStarted;
|
||||
bool _frenzied; // faster than iterating all auras to find Frenzy
|
||||
uint16 _transportCheckTimer;
|
||||
};
|
||||
@@ -549,14 +599,17 @@ public:
|
||||
if (!deathbringer || deathbringer->IsInEvadeMode())
|
||||
return;
|
||||
|
||||
if (_guardList.empty())
|
||||
{
|
||||
GetCreatureListWithEntryInGrid(_guardList, me, NPC_SE_KOR_KRON_REAVER, 20.0f);
|
||||
_guardList.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
}
|
||||
// Rebuilt each run: the guards despawn during the outro.
|
||||
_guardList.clear();
|
||||
std::list<Creature*> guards;
|
||||
GetCreatureListWithEntryInGrid(guards, me, NPC_SE_KOR_KRON_REAVER, 20.0f);
|
||||
guards.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
uint32 x = 1;
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->SetData(0, x++);
|
||||
for (Creature* guard : guards)
|
||||
{
|
||||
_guardList.push_back(guard->GetGUID());
|
||||
guard->AI()->SetData(0, x++);
|
||||
}
|
||||
|
||||
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
|
||||
Talk(SAY_INTRO_HORDE_1);
|
||||
@@ -581,13 +634,15 @@ public:
|
||||
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 (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->DoAction(ACTION_DESPAWN);
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_OUTRO_DESCEND);
|
||||
|
||||
/*Talk(SAY_OUTRO_HORDE_1);
|
||||
_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_2, 18s);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_3, 24s);*/
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_3, 18s);
|
||||
}
|
||||
break;
|
||||
case ACTION_EVADE:
|
||||
@@ -599,14 +654,18 @@ public:
|
||||
me->StopMovingOnCurrentPos();
|
||||
me->SetDisableGravity(false);
|
||||
EnterEvadeMode();
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
{
|
||||
(*itr)->GetMotionMaster()->Clear();
|
||||
(*itr)->GetHomePosition(x, y, z, o);
|
||||
(*itr)->SetPosition(x, y, z, o);
|
||||
(*itr)->StopMovingOnCurrentPos();
|
||||
(*itr)->SetDisableGravity(false);
|
||||
(*itr)->AI()->EnterEvadeMode();
|
||||
Creature* guard = ObjectAccessor::GetCreature(*me, guid);
|
||||
if (!guard)
|
||||
continue;
|
||||
|
||||
guard->GetMotionMaster()->Clear();
|
||||
guard->GetHomePosition(x, y, z, o);
|
||||
guard->SetPosition(x, y, z, o);
|
||||
guard->StopMovingOnCurrentPos();
|
||||
guard->SetDisableGravity(false);
|
||||
guard->AI()->EnterEvadeMode();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -633,29 +692,38 @@ public:
|
||||
case POINT_FIRST_STEP:
|
||||
me->SetWalk(false);
|
||||
Talk(SAY_INTRO_HORDE_3);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_4, 6500ms, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_5, 15s + 500ms, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_6, 29s + 500ms, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_7, 43s + 800ms, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_8, 47s, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_HORDE_9, 48s + 200ms, 0, PHASE_INTRO_H);
|
||||
_events.ScheduleEvent(EVENT_INTRO_FINISH, 56s + 700ms, 0, PHASE_INTRO_H);
|
||||
_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);
|
||||
break;
|
||||
/*case POINT_CORPSE:
|
||||
case POINT_CORPSE:
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
{
|
||||
deathbringer->CastSpell(me, SPELL_RIDE_VEHICLE, true);
|
||||
deathbringer->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
deathbringer->setDeathState(DeathState::Alive);
|
||||
}
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_4, 1s);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_5, 4s);
|
||||
me->SetFacingToObject(deathbringer);
|
||||
me->SetStandState(UNIT_STAND_STATE_KNEEL);
|
||||
Talk(SAY_OUTRO_HORDE_3);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_PICKUP, 6s);
|
||||
break;
|
||||
case POINT_TRANSPORTER:
|
||||
me->SetFacingTo(0.0f);
|
||||
Talk(SAY_OUTRO_HORDE_4);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_HORDE_LEAVE, 8s);
|
||||
break;
|
||||
case POINT_FINAL:
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
deathbringer->DespawnOrUnsummon();
|
||||
me->DespawnOrUnsummon();
|
||||
break;*/
|
||||
{
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
deathbringer->DespawnOrUnsummon();
|
||||
|
||||
float x, y, z, o;
|
||||
me->GetHomePosition(x, y, z, o);
|
||||
me->SetVisible(false);
|
||||
me->NearTeleportTo(x, y, z, o);
|
||||
_instance->SetData(DATA_SAURFANG_CAMP, DONE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -674,6 +742,9 @@ public:
|
||||
deathbringer->AI()->Talk(SAY_INTRO_HORDE_2);
|
||||
break;
|
||||
case EVENT_INTRO_HORDE_3:
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_READY_WEAPONS);
|
||||
me->SetWalk(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_FIRST_STEP, firstStepPos.GetPositionX(), firstStepPos.GetPositionY(), firstStepPos.GetPositionZ());
|
||||
break;
|
||||
@@ -692,8 +763,9 @@ public:
|
||||
break;
|
||||
case EVENT_INTRO_HORDE_8:
|
||||
Talk(SAY_INTRO_HORDE_8);
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->DoAction(ACTION_CHARGE);
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_CHARGE);
|
||||
me->GetMotionMaster()->MoveCharge(chargePos[0].GetPositionX(), chargePos[0].GetPositionY(), chargePos[0].GetPositionZ(), 8.5f, POINT_CHARGE);
|
||||
break;
|
||||
case EVENT_INTRO_HORDE_9:
|
||||
@@ -712,37 +784,55 @@ 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);
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_2:
|
||||
Talk(SAY_OUTRO_HORDE_3);
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_3:
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
{
|
||||
float x, y, z;
|
||||
deathbringer->GetClosePoint(x, y, z, deathbringer->GetObjectSize());
|
||||
me->SetWalk(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_CORPSE, x, y, z);
|
||||
}
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_4:
|
||||
me->GetMotionMaster()->MovePoint(POINT_FINAL, finalPos);
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_5:
|
||||
Talk(SAY_OUTRO_HORDE_4);
|
||||
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);
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_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:
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
{
|
||||
float x, y, z;
|
||||
deathbringer->GetClosePoint(x, y, z, deathbringer->GetObjectSize());
|
||||
me->SetWalk(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_CORPSE, x, y, z);
|
||||
}
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_PICKUP:
|
||||
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(me, SPELL_RIDE_VEHICLE, true);
|
||||
}
|
||||
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);
|
||||
break;
|
||||
case EVENT_OUTRO_HORDE_LEAVE:
|
||||
me->SetWalk(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_FINAL, finalPos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventMap _events;
|
||||
InstanceScript* _instance;
|
||||
std::list<Creature*> _guardList;
|
||||
GuidList _guardList;
|
||||
};
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature) override
|
||||
@@ -787,11 +877,13 @@ public:
|
||||
npc_muradin_bronzebeard_iccAI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
_instance = me->GetInstanceScript();
|
||||
_outroZeppelinWait = 0;
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_events.Reset();
|
||||
_outroZeppelinWait = 0;
|
||||
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
@@ -810,17 +902,21 @@ public:
|
||||
if (!deathbringer || deathbringer->IsInEvadeMode())
|
||||
return;
|
||||
|
||||
if (_guardList.empty())
|
||||
{
|
||||
GetCreatureListWithEntryInGrid(_guardList, me, NPC_SE_SKYBREAKER_MARINE, 20.0f);
|
||||
_guardList.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
}
|
||||
// Rebuilt each run: the guards despawn during the outro.
|
||||
_guardList.clear();
|
||||
std::list<Creature*> guards;
|
||||
GetCreatureListWithEntryInGrid(guards, me, NPC_SE_SKYBREAKER_MARINE, 20.0f);
|
||||
guards.sort(Acore::ObjectDistanceOrderPred(me));
|
||||
uint32 x = 1;
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->SetData(0, x++);
|
||||
for (Creature* guard : guards)
|
||||
{
|
||||
_guardList.push_back(guard->GetGUID());
|
||||
guard->AI()->SetData(0, x++);
|
||||
}
|
||||
|
||||
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
|
||||
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);
|
||||
@@ -843,10 +939,18 @@ public:
|
||||
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 (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->DoAction(ACTION_DESPAWN);
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_OUTRO_DESCEND);
|
||||
|
||||
//Talk(SAY_OUTRO_ALLIANCE_1);
|
||||
_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;
|
||||
}
|
||||
case ACTION_EVADE:
|
||||
@@ -858,14 +962,18 @@ public:
|
||||
me->StopMovingOnCurrentPos();
|
||||
me->SetDisableGravity(false);
|
||||
EnterEvadeMode();
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
{
|
||||
(*itr)->GetMotionMaster()->Clear();
|
||||
(*itr)->GetHomePosition(x, y, z, o);
|
||||
(*itr)->SetPosition(x, y, z, o);
|
||||
(*itr)->StopMovingOnCurrentPos();
|
||||
(*itr)->SetDisableGravity(false);
|
||||
(*itr)->AI()->EnterEvadeMode();
|
||||
Creature* guard = ObjectAccessor::GetCreature(*me, guid);
|
||||
if (!guard)
|
||||
continue;
|
||||
|
||||
guard->GetMotionMaster()->Clear();
|
||||
guard->GetHomePosition(x, y, z, o);
|
||||
guard->SetPosition(x, y, z, o);
|
||||
guard->StopMovingOnCurrentPos();
|
||||
guard->SetDisableGravity(false);
|
||||
guard->AI()->EnterEvadeMode();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -897,6 +1005,15 @@ public:
|
||||
_events.ScheduleEvent(EVENT_INTRO_ALLIANCE_7, 9s, 0, PHASE_INTRO_A);
|
||||
_events.ScheduleEvent(EVENT_INTRO_FINISH, 14s, 0, PHASE_INTRO_A);
|
||||
break;
|
||||
case POINT_A_MURADIN_HOME:
|
||||
{
|
||||
float x, y, z, o;
|
||||
me->GetHomePosition(x, y, z, o);
|
||||
me->SetFacingTo(o);
|
||||
me->SetSheath(SHEATH_STATE_UNARMED);
|
||||
_instance->SetData(DATA_SAURFANG_CAMP, DONE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -919,13 +1036,17 @@ public:
|
||||
deathbringer->AI()->Talk(SAY_INTRO_ALLIANCE_3);
|
||||
break;
|
||||
case EVENT_INTRO_ALLIANCE_4:
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_READY_WEAPONS);
|
||||
me->SetWalk(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_FIRST_STEP, firstStepPos.GetPositionX(), firstStepPos.GetPositionY(), firstStepPos.GetPositionZ());
|
||||
break;
|
||||
case EVENT_INTRO_ALLIANCE_5:
|
||||
Talk(SAY_INTRO_ALLIANCE_5);
|
||||
for (std::list<Creature*>::iterator itr = _guardList.begin(); itr != _guardList.end(); ++itr)
|
||||
(*itr)->AI()->DoAction(ACTION_CHARGE);
|
||||
for (ObjectGuid const& guid : _guardList)
|
||||
if (Creature* guard = ObjectAccessor::GetCreature(*me, guid))
|
||||
guard->AI()->DoAction(ACTION_CHARGE);
|
||||
me->GetMotionMaster()->MoveCharge(chargePos[0].GetPositionX(), chargePos[0].GetPositionY(), chargePos[0].GetPositionZ(), 8.5f, POINT_CHARGE);
|
||||
break;
|
||||
case EVENT_INTRO_ALLIANCE_6:
|
||||
@@ -950,13 +1071,246 @@ public:
|
||||
deathbringer->AI()->AttackStart(target);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_OUTRO_A_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);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_2:
|
||||
Talk(SAY_OUTRO_ALLIANCE_3);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_3:
|
||||
Talk(SAY_OUTRO_ALLIANCE_4);
|
||||
_instance->SetData(DATA_SAURFANG_OUTRO_ZEPPELIN, IN_PROGRESS);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_4:
|
||||
Talk(SAY_OUTRO_ALLIANCE_5);
|
||||
me->SetWalk(false);
|
||||
me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_STAND, allianceMuradinPos);
|
||||
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:
|
||||
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);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_6:
|
||||
Talk(SAY_OUTRO_ALLIANCE_7);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_7, 5s);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_7:
|
||||
if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID))
|
||||
saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_8);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_8, 7s);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_8:
|
||||
Talk(SAY_OUTRO_ALLIANCE_9);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_9, 15s);
|
||||
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);
|
||||
break;
|
||||
case EVENT_OUTRO_A_ROYALS:
|
||||
if (Creature* varian = me->SummonCreature(NPC_SE_KING_VARIAN_WRYNN, allianceVarianPos))
|
||||
{
|
||||
_outroVarianGUID = varian->GetGUID();
|
||||
varian->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
if (Creature* jaina = me->SummonCreature(NPC_SE_JAINA_PROUDMOORE, allianceJainaPos))
|
||||
{
|
||||
_outroJainaGUID = jaina->GetGUID();
|
||||
jaina->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_10, 5s);
|
||||
break;
|
||||
case EVENT_OUTRO_A_PORTAL_CLOSE:
|
||||
_instance->SetData(DATA_SAURFANG_OUTRO_PORTAL, DONE);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_10:
|
||||
if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID))
|
||||
varian->AI()->Talk(SAY_OUTRO_ALLIANCE_11);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_11, 6s);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_11:
|
||||
{
|
||||
// He carries the rest of the scene; without him, skip to the cleanup.
|
||||
Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID);
|
||||
if (!saurfang)
|
||||
{
|
||||
_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);
|
||||
break;
|
||||
}
|
||||
case EVENT_OUTRO_ALLIANCE_12:
|
||||
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);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_13:
|
||||
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);
|
||||
}
|
||||
saurfang->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
saurfang->SetWalk(true);
|
||||
saurfang->GetMotionMaster()->MovePoint(POINT_A_VARIAN, allianceVarianMeetPos);
|
||||
}
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_14, 18s);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_14:
|
||||
if (Creature* saurfang = ObjectAccessor::GetCreature(*me, _outroSaurfangGUID))
|
||||
saurfang->AI()->Talk(SAY_OUTRO_ALLIANCE_15);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_A_ZEPPELIN_RELEASE, 6s);
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_15, 9s);
|
||||
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);
|
||||
}
|
||||
_events.ScheduleEvent(EVENT_OUTRO_ALLIANCE_16, 6s);
|
||||
break;
|
||||
case EVENT_OUTRO_ALLIANCE_19:
|
||||
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:
|
||||
Talk(SAY_OUTRO_ALLIANCE_21);
|
||||
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:
|
||||
{
|
||||
if (Creature* deathbringer = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_DEATHBRINGER_SAURFANG)))
|
||||
deathbringer->DespawnOrUnsummon();
|
||||
if (Creature* varian = ObjectAccessor::GetCreature(*me, _outroVarianGUID))
|
||||
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();
|
||||
_outroJainaGUID.Clear();
|
||||
|
||||
float x, y, z, o;
|
||||
me->GetHomePosition(x, y, z, o);
|
||||
me->SetWalk(false);
|
||||
me->GetMotionMaster()->MovePoint(POINT_A_MURADIN_HOME, x, y, z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventMap _events;
|
||||
InstanceScript* _instance;
|
||||
std::list<Creature*> _guardList;
|
||||
GuidList _guardList;
|
||||
ObjectGuid _outroSaurfangGUID;
|
||||
ObjectGuid _outroVarianGUID;
|
||||
ObjectGuid _outroJainaGUID;
|
||||
uint8 _outroZeppelinWait;
|
||||
};
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature) override
|
||||
@@ -1004,6 +1358,14 @@ public:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
|
||||
// The intro draws their weapons, so every reset path has to put them away again.
|
||||
// _index is left alone: it is their identity, not encounter state.
|
||||
void Reset() override
|
||||
{
|
||||
me->SetSheath(SHEATH_STATE_UNARMED);
|
||||
me->SetEmoteState(EMOTE_ONESHOT_NONE);
|
||||
}
|
||||
|
||||
void SetData(uint32 type, uint32 data) override
|
||||
{
|
||||
ASSERT(!type && data && data < 6);
|
||||
@@ -1019,15 +1381,70 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
if (type != POINT_MOTION_TYPE)
|
||||
return;
|
||||
|
||||
if (id == POINT_RETREAT)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == ACTION_CHARGE && _index)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else if (action == ACTION_DESPAWN)
|
||||
me->DespawnOrUnsummon(1ms);
|
||||
else if (action == ACTION_READY_WEAPONS)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -134,6 +134,9 @@ enum DataTypes
|
||||
DATA_ENEMY_GUNSHIP = 40,
|
||||
DATA_THE_SKYBREAKER = 41,
|
||||
DATA_ORGRIMS_HAMMER = 42,
|
||||
DATA_SAURFANG_CAMP = 43,
|
||||
DATA_SAURFANG_OUTRO_ZEPPELIN = 44,
|
||||
DATA_SAURFANG_OUTRO_PORTAL = 45,
|
||||
|
||||
// pussywizard:
|
||||
DATA_BUFF_AVAILABLE = 251,
|
||||
@@ -250,6 +253,13 @@ enum CreaturesIds
|
||||
NPC_SE_HIGH_OVERLORD_SAURFANG = 37187,
|
||||
NPC_SE_KOR_KRON_REAVER = 37920,
|
||||
NPC_SE_SKYBREAKER_MARINE = 37830,
|
||||
// Victory camp raised on Deathbringer's Rise once Saurfang is defeated
|
||||
NPC_CAMP_ALLIANCE_MASON = 37902,
|
||||
NPC_CAMP_SHELY_STEELBOWELS = 37903, // Alliance blacksmith
|
||||
NPC_CAMP_BRAZIE_GETZ = 37904, // Alliance general goods
|
||||
NPC_CAMP_WARSONG_PEON = 37930,
|
||||
NPC_CAMP_APOTHECARY_CANDITH_TOMAS = 37935, // Horde general goods
|
||||
NPC_CAMP_MORGAN_DAYBLAZE = 37936, // Horde blacksmith
|
||||
NPC_FROST_FREEZE_TRAP = 37744,
|
||||
|
||||
// Festergut
|
||||
@@ -425,6 +435,22 @@ enum GameObjectsIds
|
||||
GO_DEATHBRINGER_S_CACHE_10H = 202238,
|
||||
GO_DEATHBRINGER_S_CACHE_25H = 202241,
|
||||
GO_SCOURGE_TRANSPORTER_SAURFANG = 202244,
|
||||
// Victory camp. Forge, bonfire and anvil are the Horde camp's and have spawn rows - the script
|
||||
// only raises them; everything else, the Alliance camp included, it summons.
|
||||
GO_SAURFANG_CAMP_FORGE = 1685,
|
||||
GO_SAURFANG_CAMP_BONFIRE = 187852,
|
||||
GO_SAURFANG_CAMP_ANVIL = 191345,
|
||||
GO_SAURFANG_CAMP_TENT_A = 201868,
|
||||
GO_SAURFANG_CAMP_TENT_H1 = 201886,
|
||||
GO_SAURFANG_CAMP_TENT_H2 = 201887,
|
||||
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
|
||||
// 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,
|
||||
|
||||
@@ -52,7 +52,20 @@ enum TimedEvents
|
||||
EVENT_QUAKE_SHATTER = 2,
|
||||
EVENT_REBUILD_PLATFORM = 3,
|
||||
EVENT_RESPAWN_GUNSHIP = 4,
|
||||
EVENT_RESPAWN_SINDRAGOSA = 5
|
||||
EVENT_RESPAWN_SINDRAGOSA = 5,
|
||||
EVENT_SPAWN_SAURFANG_EVENT = 6,
|
||||
EVENT_SAURFANG_ZEPPELIN_DOCK = 7,
|
||||
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_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
|
||||
};
|
||||
|
||||
enum Spells
|
||||
@@ -161,6 +174,46 @@ 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.
|
||||
Position const SaurfangCampTentPosH[2] =
|
||||
{
|
||||
{ -532.86456f, 2229.0088f, 539.2921f, 2.530723f },
|
||||
{ -524.55730f, 2238.0920f, 539.2920f, 0.13962449f }
|
||||
};
|
||||
|
||||
Position const SaurfangCampTentPosA[2] =
|
||||
{
|
||||
{ -531.84283f, 2230.6853f, 539.2918f, 5.625422f },
|
||||
{ -528.76000f, 2234.7815f, 539.2918f, 5.609714f }
|
||||
};
|
||||
|
||||
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 SaurfangCampTeleporterPos[2] =
|
||||
{
|
||||
{ -560.41840f, 2202.7500f, 539.28534f, 0.0f },
|
||||
{ -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 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;
|
||||
|
||||
// Set position traps Spirit Alarm
|
||||
std::vector<Position> 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 } };
|
||||
std::vector<Position> GoSpiritAlarm_2 = { { -178.41f, 2225.11f, 35.24f, 0.0f }, { -195.23f, 2221.55f, 35.24f, 0.0f}, { -209.94f, 2250.34f, 37.99f, 0.0f } };
|
||||
@@ -216,6 +269,10 @@ public:
|
||||
LichKingHeroicAvailable = true;
|
||||
LichKingRandomWhisperTimer = 120 * IN_MILLISECONDS;
|
||||
DarkwhisperElevatorTimer = DARKWHISPER_ELEVATOR_DWELL_TIME;
|
||||
_saurfangCampSpawned = false;
|
||||
_saurfangOutroRunning = false;
|
||||
_saurfangZeppelinDocked = false;
|
||||
_saurfangZeppelinLeaving = false;
|
||||
|
||||
SetHeaders(DataHeader);
|
||||
SetBossNumber(MAX_ENCOUNTERS);
|
||||
@@ -271,6 +328,14 @@ public:
|
||||
if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE)
|
||||
SpawnGunship();
|
||||
|
||||
// Also covers an instance whose Gunship Battle state was set directly, which would
|
||||
// otherwise keep them hidden for good.
|
||||
if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE && !_saurfangOutroRunning)
|
||||
RestoreHiddenSaurfangEventNpcs();
|
||||
|
||||
if (GetBossState(DATA_DEATHBRINGER_SAURFANG) == DONE && !_saurfangOutroRunning)
|
||||
SpawnSaurfangCamp(false);
|
||||
|
||||
if (GetBossState(DATA_SINDRAGOSA) != DONE && IsSindragosaIntroDone && !GetCreature(DATA_SINDRAGOSA) && !Events.HasTimeUntilEvent(EVENT_RESPAWN_SINDRAGOSA))
|
||||
{
|
||||
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
|
||||
@@ -391,6 +456,11 @@ public:
|
||||
DeathbringerSaurfangGUID = creature->GetGUID();
|
||||
break;
|
||||
case NPC_SE_HIGH_OVERLORD_SAURFANG:
|
||||
// Only the static spawn is the event NPC - the Alliance outro summons another
|
||||
// High Overlord Saurfang, which would otherwise clobber the guid.
|
||||
if (creature->IsSummon())
|
||||
break;
|
||||
|
||||
if (GetTeamIdInInstance() == TEAM_ALLIANCE)
|
||||
{
|
||||
creature->UpdateEntry(NPC_SE_MURADIN_BRONZEBEARD, true);
|
||||
@@ -398,9 +468,14 @@ public:
|
||||
}
|
||||
DeathbringerSaurfangEventGUID = creature->GetGUID();
|
||||
creature->LastUsedScriptID = creature->GetScriptId();
|
||||
HideSaurfangEventNpc(creature);
|
||||
break;
|
||||
case NPC_SE_MURADIN_BRONZEBEARD:
|
||||
if (creature->IsSummon())
|
||||
break;
|
||||
|
||||
DeathbringerSaurfangEventGUID = creature->GetGUID();
|
||||
HideSaurfangEventNpc(creature);
|
||||
break;
|
||||
case NPC_HIGH_OVERLORD_SAURFANG_DUMMY:
|
||||
if (GetTeamIdInInstance() == TEAM_ALLIANCE)
|
||||
@@ -410,8 +485,13 @@ public:
|
||||
}
|
||||
break;
|
||||
case NPC_SE_KOR_KRON_REAVER:
|
||||
if (creature->IsSummon())
|
||||
break;
|
||||
|
||||
if (GetTeamIdInInstance() == TEAM_ALLIANCE)
|
||||
creature->UpdateEntry(NPC_SE_SKYBREAKER_MARINE);
|
||||
SaurfangEventGuardGUIDs.push_back(creature->GetGUID());
|
||||
HideSaurfangEventNpc(creature);
|
||||
break;
|
||||
case NPC_FESTERGUT:
|
||||
FestergutGUID = creature->GetGUID();
|
||||
@@ -747,6 +827,18 @@ public:
|
||||
case GO_SCOURGE_TRANSPORTER_SAURFANG:
|
||||
SaurfangTeleportGUID = go->GetGUID();
|
||||
break;
|
||||
case GO_SAURFANG_CAMP_FORGE:
|
||||
case GO_SAURFANG_CAMP_BONFIRE:
|
||||
case GO_SAURFANG_CAMP_ANVIL:
|
||||
// Camp props belong to the aftermath; their spawn rows are unconditional, hence the despawn.
|
||||
SaurfangCampGUIDs.push_back(go->GetGUID());
|
||||
if (GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE)
|
||||
go->DespawnOrUnsummon(0ms, Seconds(WEEK));
|
||||
// The despawn is persisted, so an instance restarted after the outro comes back
|
||||
// with them hidden. Horde only - the Alliance camp summons its own props.
|
||||
else if (GetTeamIdInInstance() == TEAM_HORDE)
|
||||
go->Respawn();
|
||||
break;
|
||||
case GO_PLAGUE_SIGIL:
|
||||
PlagueSigilGUID = go->GetGUID();
|
||||
if (GetBossState(DATA_PROFESSOR_PUTRICIDE) == DONE)
|
||||
@@ -904,6 +996,9 @@ public:
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_SAURFANG_OUTRO_ZEPPELIN:
|
||||
// DONE once it has come to rest, so the outro can wait for the ship instead of guessing.
|
||||
return _saurfangZeppelinDocked ? DONE : IN_PROGRESS;
|
||||
case DATA_BUFF_AVAILABLE:
|
||||
return (IsBuffAvailable ? 1 : 0);
|
||||
case DATA_WEEKLY_QUEST_ID:
|
||||
@@ -1096,6 +1191,9 @@ public:
|
||||
loot->SetLootRecipient(deathbringer);
|
||||
loot->RemoveGameObjectFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
|
||||
}
|
||||
|
||||
// Not raised here: DONE fires the moment the boss dies. The outro asks
|
||||
// via SetData(DATA_SAURFANG_CAMP); OnPlayerEnter covers saved instances.
|
||||
[[fallthrough]];
|
||||
case NOT_STARTED:
|
||||
if (GameObject* teleporter = instance->GetGameObject(SaurfangTeleportGUID))
|
||||
@@ -1220,10 +1318,286 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden rather than despawned: a despawned creature leaves the map and its guid would no
|
||||
// longer resolve.
|
||||
void HideSaurfangEventNpc(Creature* creature)
|
||||
{
|
||||
if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE)
|
||||
return;
|
||||
|
||||
creature->SetVisible(false);
|
||||
}
|
||||
|
||||
void RestoreSaurfangEventNpc(Creature* creature)
|
||||
{
|
||||
float x, y, z, o;
|
||||
creature->GetHomePosition(x, y, z, o);
|
||||
creature->NearTeleportTo(x, y, z, o);
|
||||
creature->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
creature->SetEmoteState(EMOTE_ONESHOT_NONE);
|
||||
creature->SetSheath(SHEATH_STATE_UNARMED);
|
||||
creature->SetVisible(true);
|
||||
}
|
||||
|
||||
void SpawnSaurfangEventNpcs()
|
||||
{
|
||||
if (Creature* captain = instance->GetCreature(DeathbringerSaurfangEventGUID))
|
||||
RestoreSaurfangEventNpc(captain);
|
||||
|
||||
for (ObjectGuid const& guid : SaurfangEventGuardGUIDs)
|
||||
if (Creature* guard = instance->GetCreature(guid))
|
||||
RestoreSaurfangEventNpc(guard);
|
||||
}
|
||||
|
||||
// Only the ones left hidden: a scene in progress has them visible and placed, and
|
||||
// teleporting those to their spawn points would break it mid-run.
|
||||
void RestoreHiddenSaurfangEventNpcs()
|
||||
{
|
||||
if (Creature* captain = instance->GetCreature(DeathbringerSaurfangEventGUID))
|
||||
if (!captain->IsVisible())
|
||||
RestoreSaurfangEventNpc(captain);
|
||||
|
||||
for (ObjectGuid const& guid : SaurfangEventGuardGUIDs)
|
||||
if (Creature* guard = instance->GetCreature(guid))
|
||||
if (!guard->IsVisible())
|
||||
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 SpawnSaurfangCamp(bool staged)
|
||||
{
|
||||
if (_saurfangCampSpawned || !instance->HavePlayers())
|
||||
return;
|
||||
|
||||
_saurfangCampSpawned = true;
|
||||
if (staged)
|
||||
{
|
||||
Events.ScheduleEvent(EVENT_SAURFANG_CAMP_TELEPORTERS, 3s);
|
||||
return;
|
||||
}
|
||||
|
||||
SummonSaurfangCampTeleporters();
|
||||
SpawnSaurfangEventNpcs();
|
||||
SpawnSaurfangCampTents();
|
||||
SummonSaurfangCampVendor(true, false);
|
||||
SummonSaurfangCampVendor(false, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnSaurfangCampTeleporters()
|
||||
{
|
||||
SummonSaurfangCampTeleporters();
|
||||
SpawnSaurfangEventNpcs();
|
||||
Events.ScheduleEvent(EVENT_SAURFANG_CAMP_WORKERS, 5s);
|
||||
}
|
||||
|
||||
void SpawnSaurfangCampWorkers()
|
||||
{
|
||||
bool const horde = GetTeamIdInInstance() == TEAM_HORDE;
|
||||
uint32 const worker = 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());
|
||||
builder->SetReactState(REACT_PASSIVE);
|
||||
builder->SetWalk(false);
|
||||
builder->GetMotionMaster()->MovePoint(0, (horde ? SaurfangCampTentPosH[i] : SaurfangCampTentPosA[i]));
|
||||
builder->SetEmoteState(EMOTE_STATE_WORK_MINING);
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
void SpawnSaurfangCampTents()
|
||||
{
|
||||
bool const horde = GetTeamIdInInstance() == TEAM_HORDE;
|
||||
uint32 const tents[2] =
|
||||
{
|
||||
static_cast<uint32>(horde ? GO_SAURFANG_CAMP_TENT_H1 : GO_SAURFANG_CAMP_TENT_A),
|
||||
static_cast<uint32>(horde ? GO_SAURFANG_CAMP_TENT_H2 : GO_SAURFANG_CAMP_TENT_A)
|
||||
};
|
||||
|
||||
Position const* tentPos = horde ? SaurfangCampTentPosH : SaurfangCampTentPosA;
|
||||
for (uint8 i = 0; i < 2; ++i)
|
||||
instance->SummonGameObject(tents[i], tentPos[i], 0.0f, 0.0f, 0.0f, 0.0f, WEEK);
|
||||
|
||||
if (horde)
|
||||
{
|
||||
for (ObjectGuid const& guid : SaurfangCampGUIDs)
|
||||
if (GameObject* camp = instance->GetGameObject(guid))
|
||||
camp->Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
instance->SummonGameObject(GO_SAURFANG_CAMP_FORGE, SaurfangCampForgePosA, 0.0f, 0.0f, 0.0f, 0.0f, WEEK);
|
||||
instance->SummonGameObject(GO_SAURFANG_CAMP_ANVIL_A, SaurfangCampAnvilPosA, 0.0f, 0.0f, 0.0f, 0.0f, WEEK);
|
||||
instance->SummonGameObject(GO_SAURFANG_CAMP_BANNER_A, SaurfangCampBannerPosA, 0.0f, 0.0f, 0.0f, 0.0f, WEEK);
|
||||
}
|
||||
}
|
||||
|
||||
// They clear the site before the tent drops, or it lands on top of them.
|
||||
void SendSaurfangCampWorkersBack()
|
||||
{
|
||||
uint8 i = 0;
|
||||
for (ObjectGuid const& guid : SaurfangCampWorkerGUIDs)
|
||||
{
|
||||
if (Creature* builder = instance->GetCreature(guid))
|
||||
{
|
||||
builder->SetEmoteState(EMOTE_ONESHOT_NONE);
|
||||
builder->SetWalk(false);
|
||||
builder->GetMotionMaster()->MovePoint(0, SaurfangCampTeleporterPos[i % 2]);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
Creature* vendor = instance->SummonCreature(entry, SaurfangCampTeleporterPos[smith ? 0 : 1]);
|
||||
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);
|
||||
}
|
||||
|
||||
void SetData(uint32 type, uint32 data) override
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_SAURFANG_CAMP:
|
||||
// The boss is DONE for the whole outro, so the encounter state alone cannot tell
|
||||
// "outro playing" from "outro over".
|
||||
if (data == IN_PROGRESS)
|
||||
{
|
||||
_saurfangOutroRunning = true;
|
||||
// Safety net: an instance emptying mid-outro would leave the flag set and
|
||||
// the camp could never be raised.
|
||||
Events.ScheduleEvent(EVENT_SAURFANG_OUTRO_TIMEOUT, 8min);
|
||||
}
|
||||
else if (data == DONE)
|
||||
{
|
||||
Events.CancelEvent(EVENT_SAURFANG_OUTRO_TIMEOUT);
|
||||
_saurfangOutroRunning = false;
|
||||
SpawnSaurfangCamp(true);
|
||||
}
|
||||
break;
|
||||
case DATA_SAURFANG_OUTRO_ZEPPELIN:
|
||||
if (data == IN_PROGRESS)
|
||||
{
|
||||
if (!SaurfangZeppelinGUID)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Asked for twice (departure, then cleanup); without the guard the second call
|
||||
// would restart the removal timer.
|
||||
else if (SaurfangZeppelinGUID && !_saurfangZeppelinLeaving)
|
||||
{
|
||||
Events.CancelEvent(EVENT_SAURFANG_ZEPPELIN_DOCK);
|
||||
_saurfangZeppelinDocked = false;
|
||||
_saurfangZeppelinLeaving = true;
|
||||
if (GameObject* go = instance->GetGameObject(SaurfangZeppelinGUID))
|
||||
if (MotionTransport* zeppelin = go->ToMotionTransport())
|
||||
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();
|
||||
}
|
||||
break;
|
||||
case DATA_BUFF_AVAILABLE:
|
||||
IsBuffAvailable = !!data;
|
||||
if (!IsBuffAvailable)
|
||||
@@ -1761,6 +2135,74 @@ public:
|
||||
// Could happen more than once if more than one player enters before she respawns.
|
||||
Events.CancelEvent(EVENT_RESPAWN_SINDRAGOSA);
|
||||
break;
|
||||
case EVENT_SPAWN_SAURFANG_EVENT:
|
||||
SpawnSaurfangEventNpcs();
|
||||
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)
|
||||
{
|
||||
SpawnSaurfangCamp(false);
|
||||
SpawnSaurfangEventNpcs();
|
||||
}
|
||||
break;
|
||||
case EVENT_SAURFANG_CAMP_TELEPORTERS:
|
||||
SpawnSaurfangCampTeleporters();
|
||||
break;
|
||||
case EVENT_SAURFANG_CAMP_WORKERS:
|
||||
SpawnSaurfangCampWorkers();
|
||||
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();
|
||||
break;
|
||||
case EVENT_SAURFANG_ZEPPELIN_REMOVE:
|
||||
if (GameObject* go = instance->GetGameObject(SaurfangZeppelinGUID))
|
||||
{
|
||||
// A MO_TRANSPORT is not despawned like an ordinary gameobject.
|
||||
if (MotionTransport* zeppelin = go->ToMotionTransport())
|
||||
{
|
||||
zeppelin->EnableMovement(false);
|
||||
zeppelin->UnloadStaticPassengers();
|
||||
}
|
||||
go->AddObjectToRemoveList();
|
||||
}
|
||||
_saurfangZeppelinLeaving = false;
|
||||
SaurfangZeppelinGUID.Clear();
|
||||
break;
|
||||
case EVENT_SAURFANG_ZEPPELIN_DOCK:
|
||||
{
|
||||
// Reschedule unconditionally: giving up on a failed lookup would leave
|
||||
// it looping its path forever.
|
||||
GameObject* go = instance->GetGameObject(SaurfangZeppelinGUID);
|
||||
MotionTransport* zeppelin = go ? go->ToMotionTransport() : nullptr;
|
||||
if (zeppelin && zeppelin->GetExactDist2d(&SaurfangOutroZeppelinPos) <= SaurfangOutroZeppelinDockRange)
|
||||
{
|
||||
zeppelin->EnableMovement(false);
|
||||
_saurfangZeppelinDocked = true;
|
||||
}
|
||||
else if (SaurfangZeppelinGUID)
|
||||
Events.ScheduleEvent(EVENT_SAURFANG_ZEPPELIN_DOCK, 500ms);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1803,6 +2245,8 @@ public:
|
||||
}
|
||||
if (Creature* captain = source->FindNearestCreature(GetTeamIdInInstance() == TEAM_HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 200.0f))
|
||||
captain->AI()->DoAction(ACTION_EXIT_SHIP);
|
||||
// The captain despawns 18s into his walk off the ship; the event party appears with him.
|
||||
Events.ScheduleEvent(EVENT_SPAWN_SAURFANG_EVENT, 18s);
|
||||
break;
|
||||
|
||||
case EVENT_QUAKE:
|
||||
@@ -1930,6 +2374,17 @@ public:
|
||||
ObjectGuid DeathbringerSaurfangEventGUID; // Muradin Bronzebeard or High Overlord Saurfang
|
||||
ObjectGuid DeathbringersCacheGUID;
|
||||
ObjectGuid SaurfangTeleportGUID;
|
||||
GuidList SaurfangCampGUIDs;
|
||||
GuidList SaurfangEventGuardGUIDs;
|
||||
GuidList SaurfangCampWorkerGUIDs;
|
||||
ObjectGuid SaurfangCampSmithGUID;
|
||||
ObjectGuid SaurfangCampGoodsGUID;
|
||||
ObjectGuid SaurfangZeppelinGUID;
|
||||
ObjectGuid SaurfangPortalGUID;
|
||||
bool _saurfangCampSpawned;
|
||||
bool _saurfangOutroRunning;
|
||||
bool _saurfangZeppelinDocked;
|
||||
bool _saurfangZeppelinLeaving;
|
||||
ObjectGuid PlagueSigilGUID;
|
||||
ObjectGuid BloodwingSigilGUID;
|
||||
ObjectGuid FrostwingSigilGUID;
|
||||
|
||||
Reference in New Issue
Block a user