fix(Scripts/Ulduar): Salvaged Vehicles at the Expedition Base Camp must respawn when destroyed before pulling Flame Leviathan. (#27364)
This commit is contained in:
parent
e9f3e0acc3
commit
73a301904a
2 changed files with 102 additions and 43 deletions
|
|
@ -197,6 +197,17 @@ BossBoundaryData const boundaries =
|
||||||
{ BOSS_LEVIATHAN, new RectangleBoundary(130.0f, 450.0f, -170.0f, 110.0f) },
|
{ BOSS_LEVIATHAN, new RectangleBoundary(130.0f, 450.0f, -170.0f, 110.0f) },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Delay between a salvaged vehicle's wreck decaying and its replacement rolling out of the Expedition Base Camp
|
||||||
|
constexpr Seconds LEVIATHAN_VEHICLE_RESPAWN_DELAY = 40s;
|
||||||
|
|
||||||
|
// A salvaged vehicle summoned for the Flame Leviathan encounter, kept together with the slot it was summoned into
|
||||||
|
struct LeviathanVehicle
|
||||||
|
{
|
||||||
|
ObjectGuid guid;
|
||||||
|
uint32 entry;
|
||||||
|
uint32 index;
|
||||||
|
};
|
||||||
|
|
||||||
class instance_ulduar : public InstanceMapScript
|
class instance_ulduar : public InstanceMapScript
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -225,7 +236,9 @@ public:
|
||||||
ObjectGuid _leviathanVisualTowers[4][2];
|
ObjectGuid _leviathanVisualTowers[4][2];
|
||||||
ObjectGuid _repairSGUID[2];
|
ObjectGuid _repairSGUID[2];
|
||||||
bool _leviathanTowers[4];
|
bool _leviathanTowers[4];
|
||||||
GuidList _leviathanVehicles;
|
std::vector<LeviathanVehicle> _leviathanVehicles;
|
||||||
|
uint8 _leviathanVehicleMode;
|
||||||
|
bool _leviathanVehiclesUsable;
|
||||||
GuidUnorderedSet _leviathanGauntletGUIDs;
|
GuidUnorderedSet _leviathanGauntletGUIDs;
|
||||||
GuidUnorderedSet _leviathanBeaconGUIDs;
|
GuidUnorderedSet _leviathanBeaconGUIDs;
|
||||||
GuidList _leviathanCrewGUIDs;
|
GuidList _leviathanCrewGUIDs;
|
||||||
|
|
@ -252,6 +265,8 @@ public:
|
||||||
_leviathanTowers[i] = true;
|
_leviathanTowers[i] = true;
|
||||||
|
|
||||||
_leviathanVehicles.clear();
|
_leviathanVehicles.clear();
|
||||||
|
_leviathanVehicleMode = VEHICLE_POS_NONE;
|
||||||
|
_leviathanVehiclesUsable = false;
|
||||||
_leviathanGauntletGUIDs.clear();
|
_leviathanGauntletGUIDs.clear();
|
||||||
_leviathanBeaconGUIDs.clear();
|
_leviathanBeaconGUIDs.clear();
|
||||||
_leviathanCrewGUIDs.clear();
|
_leviathanCrewGUIDs.clear();
|
||||||
|
|
@ -295,16 +310,16 @@ public:
|
||||||
_leviathanBeaconGUIDs.clear();
|
_leviathanBeaconGUIDs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetLeviathanVehiclesUsable(bool usable)
|
// Brann only ever unlocks the vehicles, and they must stay unlocked across wipes, respawns and instance
|
||||||
|
// reloads. Locking one back is the job of SummonLeviathanVehicle, which withholds the flag on spawn.
|
||||||
|
void UnlockLeviathanVehicles()
|
||||||
{
|
{
|
||||||
for (ObjectGuid const& guid : _leviathanVehicles)
|
_leviathanVehiclesUsable = true;
|
||||||
if (Creature* vehicle = instance->GetCreature(guid))
|
StorePersistentData(PERSISTENT_DATA_LEVIATHAN_VEHICLES_USABLE, 1);
|
||||||
{
|
|
||||||
if (usable)
|
for (LeviathanVehicle const& summoned : _leviathanVehicles)
|
||||||
vehicle->SetNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
|
if (Creature* vehicle = instance->GetCreature(summoned.guid))
|
||||||
else
|
vehicle->SetNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
|
||||||
vehicle->RemoveNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnLeviathanOutro(bool justKilled)
|
void SpawnLeviathanOutro(bool justKilled)
|
||||||
|
|
@ -468,8 +483,10 @@ public:
|
||||||
{
|
{
|
||||||
if (IsBossDone(BOSS_LEVIATHAN))
|
if (IsBossDone(BOSS_LEVIATHAN))
|
||||||
SpawnLeviathanOutro(false);
|
SpawnLeviathanOutro(false);
|
||||||
// The salvaged vehicles wait for the raid at the Expedition Base Camp, they are not tied to Brann's intro
|
// The salvaged vehicles wait for the raid at the Expedition Base Camp, they are not tied to Brann's intro.
|
||||||
else if (GetBossState(BOSS_LEVIATHAN) != SPECIAL && _leviathanVehicles.empty())
|
// Keyed on the mode rather than on the pool being empty: a pool whose wrecks are all pending replacement
|
||||||
|
// is empty too, and respawning over it would leave the camp with a double set once the timers fire.
|
||||||
|
else if (GetBossState(BOSS_LEVIATHAN) != SPECIAL && _leviathanVehicleMode == VEHICLE_POS_NONE)
|
||||||
SpawnLeviathanEncounterVehicles(VEHICLE_POS_START);
|
SpawnLeviathanEncounterVehicles(VEHICLE_POS_START);
|
||||||
|
|
||||||
// mimiron tram:
|
// mimiron tram:
|
||||||
|
|
@ -922,7 +939,7 @@ public:
|
||||||
// Archmage Pentarus has just answered Brann, the crews get a moment to reach the vehicles
|
// Archmage Pentarus has just answered Brann, the crews get a moment to reach the vehicles
|
||||||
scheduler.Schedule(3s, [this](TaskContext /*context*/)
|
scheduler.Schedule(3s, [this](TaskContext /*context*/)
|
||||||
{
|
{
|
||||||
SetLeviathanVehiclesUsable(true);
|
UnlockLeviathanVehicles();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
case DATA_DESPAWN_ALGALON:
|
case DATA_DESPAWN_ALGALON:
|
||||||
|
|
@ -1053,6 +1070,9 @@ public:
|
||||||
|
|
||||||
void OnUnitDeath(Unit* unit) override
|
void OnUnitDeath(Unit* unit) override
|
||||||
{
|
{
|
||||||
|
if (Creature* creature = unit->ToCreature())
|
||||||
|
ScheduleLeviathanVehicleRespawn(creature);
|
||||||
|
|
||||||
// Feeds on Tears achievement
|
// Feeds on Tears achievement
|
||||||
if (unit->IsPlayer())
|
if (unit->IsPlayer())
|
||||||
{
|
{
|
||||||
|
|
@ -1170,6 +1190,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpawnLeviathanEncounterVehicles(uint8 mode);
|
void SpawnLeviathanEncounterVehicles(uint8 mode);
|
||||||
|
void SummonLeviathanVehicle(uint32 entry, uint32 index);
|
||||||
|
void ScheduleLeviathanVehicleRespawn(Creature* vehicle);
|
||||||
|
|
||||||
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override
|
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override
|
||||||
{
|
{
|
||||||
|
|
@ -1283,40 +1305,75 @@ const Position vehiclePositions[30] =
|
||||||
|
|
||||||
void instance_ulduar::instance_ulduar_InstanceMapScript::SpawnLeviathanEncounterVehicles(uint8 mode)
|
void instance_ulduar::instance_ulduar_InstanceMapScript::SpawnLeviathanEncounterVehicles(uint8 mode)
|
||||||
{
|
{
|
||||||
if (!_leviathanVehicles.empty())
|
// Retire the old set first: despawning it must not queue replacements for a set that is going away
|
||||||
{
|
std::vector<LeviathanVehicle> retired;
|
||||||
for (ObjectGuid const& guid : _leviathanVehicles)
|
retired.swap(_leviathanVehicles);
|
||||||
{
|
_leviathanVehicleMode = mode;
|
||||||
if (Creature* cr = instance->GetCreature(guid))
|
|
||||||
{
|
|
||||||
cr->DespawnOrUnsummon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_leviathanVehicles.clear();
|
for (LeviathanVehicle const& summoned : retired)
|
||||||
|
{
|
||||||
|
if (Creature* cr = instance->GetCreature(summoned.guid))
|
||||||
|
{
|
||||||
|
cr->DespawnOrUnsummon();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode < VEHICLE_POS_NONE)
|
if (mode >= VEHICLE_POS_NONE)
|
||||||
{
|
return;
|
||||||
for (uint8 i = 0; i < (instance->Is25ManRaid() ? 5 : 2); ++i)
|
|
||||||
{
|
|
||||||
if (TempSummon* veh = instance->SummonCreature(NPC_SALVAGED_SIEGE_ENGINE, vehiclePositions[15 * mode + i]))
|
|
||||||
{
|
|
||||||
_leviathanVehicles.push_back(veh->GetGUID());
|
|
||||||
}
|
|
||||||
if (TempSummon* veh = instance->SummonCreature(NPC_VEHICLE_CHOPPER, vehiclePositions[15 * mode + i + 5]))
|
|
||||||
{
|
|
||||||
_leviathanVehicles.push_back(veh->GetGUID());
|
|
||||||
}
|
|
||||||
if (TempSummon* veh = instance->SummonCreature(NPC_SALVAGED_DEMOLISHER, vehiclePositions[15 * mode + i + 10]))
|
|
||||||
{
|
|
||||||
_leviathanVehicles.push_back(veh->GetGUID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The raid may look the vehicles over on arrival, but cannot board them until the Kirin Tor say so
|
// The raid may look the vehicles over on arrival, but cannot board them until the Kirin Tor say so.
|
||||||
if (mode == VEHICLE_POS_START && GetPersistentData(PERSISTENT_DATA_MAGE_BARRIER) != MAGE_BARRIER_LOWERED)
|
// MAGE_BARRIER_LOWERED covers lockouts saved before the unlock got its own flag.
|
||||||
SetLeviathanVehiclesUsable(false);
|
_leviathanVehiclesUsable = mode != VEHICLE_POS_START
|
||||||
|
|| GetPersistentData(PERSISTENT_DATA_LEVIATHAN_VEHICLES_USABLE) != 0
|
||||||
|
|| GetPersistentData(PERSISTENT_DATA_MAGE_BARRIER) == MAGE_BARRIER_LOWERED;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < (instance->Is25ManRaid() ? 5u : 2u); ++i)
|
||||||
|
{
|
||||||
|
SummonLeviathanVehicle(NPC_SALVAGED_SIEGE_ENGINE, 15 * mode + i);
|
||||||
|
SummonLeviathanVehicle(NPC_VEHICLE_CHOPPER, 15 * mode + i + 5);
|
||||||
|
SummonLeviathanVehicle(NPC_SALVAGED_DEMOLISHER, 15 * mode + i + 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_ulduar::instance_ulduar_InstanceMapScript::SummonLeviathanVehicle(uint32 entry, uint32 index)
|
||||||
|
{
|
||||||
|
if (TempSummon* veh = instance->SummonCreature(entry, vehiclePositions[index]))
|
||||||
|
{
|
||||||
|
// The vehicle kit hands out the spell click on install, take it back while the barrier is still up
|
||||||
|
if (!_leviathanVehiclesUsable)
|
||||||
|
veh->RemoveNpcFlag(UNIT_NPC_FLAG_SPELLCLICK);
|
||||||
|
|
||||||
|
_leviathanVehicles.push_back({ veh->GetGUID(), entry, index });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_ulduar::instance_ulduar_InstanceMapScript::ScheduleLeviathanVehicleRespawn(Creature* vehicle)
|
||||||
|
{
|
||||||
|
// The Expedition Base Camp keeps its motor pool stocked. The trigger that ends it is the pool itself being
|
||||||
|
// relocated to the Formation Grounds on the first Reset() after the boss has been engaged, not the raid
|
||||||
|
// driving up there: for the whole first attempt wrecks are still replaced back at the camp. From the
|
||||||
|
// relocation on, a wreck stays where it fell and only the next reset lays out a fresh set
|
||||||
|
if (_leviathanVehicleMode != VEHICLE_POS_START)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto itr = _leviathanVehicles.begin(); itr != _leviathanVehicles.end(); ++itr)
|
||||||
|
{
|
||||||
|
if (itr->guid != vehicle->GetGUID())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 entry = itr->entry;
|
||||||
|
uint32 index = itr->index;
|
||||||
|
_leviathanVehicles.erase(itr);
|
||||||
|
|
||||||
|
// A replacement rolls out of the camp 40 seconds after the wreck has decayed
|
||||||
|
Seconds respawnDelay = Seconds(vehicle->GetCorpseDelay()) + LEVIATHAN_VEHICLE_RESPAWN_DELAY;
|
||||||
|
scheduler.Schedule(respawnDelay, [this, entry, index](TaskContext /*context*/)
|
||||||
|
{
|
||||||
|
if (_leviathanVehicleMode == VEHICLE_POS_START)
|
||||||
|
SummonLeviathanVehicle(entry, index);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,8 @@ enum UlduarPersistentData
|
||||||
PERSISTENT_DATA_C_OF_ULDUAR_MASK,
|
PERSISTENT_DATA_C_OF_ULDUAR_MASK,
|
||||||
PERSISTENT_DATA_MAGE_BARRIER,
|
PERSISTENT_DATA_MAGE_BARRIER,
|
||||||
PERSISTENT_DATA_ALGALON_FIRST_PULL,
|
PERSISTENT_DATA_ALGALON_FIRST_PULL,
|
||||||
|
// Set when Brann's action list unlocks the salvaged vehicles, they stay boardable for the rest of the lockout
|
||||||
|
PERSISTENT_DATA_LEVIATHAN_VEHICLES_USABLE,
|
||||||
MAX_PERSISTENT_DATA
|
MAX_PERSISTENT_DATA
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue