mirror of
https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk.git
synced 2026-10-10 07:06:38 +08:00
fix(Scripts/Stratholme): recover events after a restart/persist in instance state (#27834)
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "GameObjectScript.h"
|
||||
#include "GameTime.h"
|
||||
#include "InstanceMapScript.h"
|
||||
#include "InstanceScript.h"
|
||||
#include "Player.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
|
||||
const Position BlackGuardPos[10] =
|
||||
{
|
||||
@@ -57,11 +59,28 @@ Position const MindlessUndeadPos = { 3941.75f, -3393.06f, 119.70f, 0.0f };
|
||||
Position const BarthilasPos = { 4068.74f, -3535.97f, 122.825f, 2.478367567062377929f };
|
||||
Position const SlaughterPos = { 4032.20f, -3378.06f, 119.75f, 4.67f };
|
||||
|
||||
enum SlaughterPhases
|
||||
{
|
||||
SLAUGHTER_ABOMINATIONS,
|
||||
SLAUGHTER_RAMSTEIN,
|
||||
SLAUGHTER_MINDLESS_UNDEAD,
|
||||
SLAUGHTER_BLACK_GUARD,
|
||||
SLAUGHTER_COMPLETE
|
||||
};
|
||||
|
||||
enum GateTrapIndexes
|
||||
{
|
||||
GATE_TRAP_SCARLET,
|
||||
GATE_TRAP_UNDEAD,
|
||||
MAX_GATE_TRAPS
|
||||
};
|
||||
|
||||
constexpr uint8 MAX_GATE_TRAP_GATES = 4;
|
||||
constexpr uint32 GATE_TRAP_COOLDOWN = 30 * MINUTE;
|
||||
|
||||
static constexpr uint8 ScarletThreadSpawnCount = 4;
|
||||
static constexpr uint8 AllScarletThreadLocations = (1 << ScarletThreadSpawnCount) - 1;
|
||||
|
||||
// uint32 m_uiGateTrapTimers[2][3] = { {0,0,0}, {0,0,0} };
|
||||
|
||||
class instance_stratholme : public InstanceMapScript
|
||||
{
|
||||
public:
|
||||
@@ -81,8 +100,9 @@ public:
|
||||
_zigguratState1 = 0;
|
||||
_zigguratState2 = 0;
|
||||
_zigguratState3 = 0;
|
||||
_slaughterProgress = 0;
|
||||
_slaughterProgress = SLAUGHTER_ABOMINATIONS;
|
||||
_slaughterNPCs = 0;
|
||||
_abominationSpawnIds.clear();
|
||||
_postboxesOpened = 0;
|
||||
|
||||
_scarletThreadUsedLocations = 0;
|
||||
@@ -91,6 +111,14 @@ public:
|
||||
|
||||
_gateTrapsCooldown[0] = false;
|
||||
_gateTrapsCooldown[1] = false;
|
||||
_gateTrapCooldownEnd[0] = 0;
|
||||
_gateTrapCooldownEnd[1] = 0;
|
||||
|
||||
for (bool& needsRecovery : _gateTrapNeedsRecovery)
|
||||
needsRecovery = true;
|
||||
|
||||
for (ObjectGuid& trappedPlayerGUID : _trappedPlayerGUIDs)
|
||||
trappedPlayerGUID.Clear();
|
||||
|
||||
events.Reset();
|
||||
}
|
||||
@@ -113,19 +141,19 @@ public:
|
||||
break;
|
||||
case NPC_VENOM_BELCHER:
|
||||
case NPC_BILE_SPEWER:
|
||||
if (_slaughterProgress == 0)
|
||||
++_slaughterNPCs;
|
||||
if (_slaughterProgress == SLAUGHTER_ABOMINATIONS && creature->GetSpawnId())
|
||||
_abominationSpawnIds.insert(creature->GetSpawnId());
|
||||
break;
|
||||
case NPC_RAMSTEIN_THE_GORGER:
|
||||
if (_slaughterProgress == 1)
|
||||
if (_slaughterProgress == SLAUGHTER_RAMSTEIN)
|
||||
++_slaughterNPCs;
|
||||
break;
|
||||
case NPC_MINDLESS_UNDEAD:
|
||||
if (_slaughterProgress == 2)
|
||||
if (_slaughterProgress == SLAUGHTER_MINDLESS_UNDEAD)
|
||||
++_slaughterNPCs;
|
||||
break;
|
||||
case NPC_BLACK_GUARD:
|
||||
if (_slaughterProgress == 3)
|
||||
if (_slaughterProgress == SLAUGHTER_BLACK_GUARD)
|
||||
++_slaughterNPCs;
|
||||
break;
|
||||
case NPC_BARTHILAS:
|
||||
@@ -138,14 +166,14 @@ public:
|
||||
|
||||
void ProcessSlaughterEvent()
|
||||
{
|
||||
if (_slaughterProgress == 1)
|
||||
if (_slaughterProgress == SLAUGHTER_RAMSTEIN)
|
||||
{
|
||||
if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
|
||||
baron->AI()->Talk(SAY_BRAON_SUMMON_RAMSTEIN);
|
||||
|
||||
instance->SummonCreature(NPC_RAMSTEIN_THE_GORGER, SlaughterPos);
|
||||
}
|
||||
if (_slaughterProgress == 2)
|
||||
if (_slaughterProgress == SLAUGHTER_MINDLESS_UNDEAD)
|
||||
{
|
||||
for (uint32 i = 0; i < 33; ++i)
|
||||
events.ScheduleEvent(EVENT_SPAWN_MINDLESS, Milliseconds(5000 + i * 210));
|
||||
@@ -153,11 +181,11 @@ public:
|
||||
if (GameObject* gate = baron->FindNearestGameObject(GO_SLAUGHTER_GATE_SIDE, 200.0f))
|
||||
gate->SetGoState(GO_STATE_ACTIVE);
|
||||
}
|
||||
if (_slaughterProgress == 3)
|
||||
if (_slaughterProgress == SLAUGHTER_BLACK_GUARD)
|
||||
{
|
||||
events.ScheduleEvent(EVENT_SPAWN_BLACK_GUARD, 20s);
|
||||
}
|
||||
if (_slaughterProgress == 4)
|
||||
if (_slaughterProgress == SLAUGHTER_COMPLETE)
|
||||
{
|
||||
if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
|
||||
baron->AI()->Talk(SAY_BARON_GUARD_DEAD);
|
||||
@@ -172,15 +200,22 @@ public:
|
||||
{
|
||||
case NPC_VENOM_BELCHER:
|
||||
case NPC_BILE_SPEWER:
|
||||
{
|
||||
Creature* creature = unit->ToCreature();
|
||||
if (_slaughterProgress != SLAUGHTER_ABOMINATIONS || !creature ||
|
||||
!_abominationSpawnIds.count(creature->GetSpawnId()) ||
|
||||
HasLivingAbominations(creature->GetSpawnId()))
|
||||
break;
|
||||
|
||||
_abominationSpawnIds.clear();
|
||||
AdvanceSlaughterEvent();
|
||||
break;
|
||||
}
|
||||
case NPC_RAMSTEIN_THE_GORGER:
|
||||
case NPC_MINDLESS_UNDEAD:
|
||||
case NPC_BLACK_GUARD:
|
||||
if (--_slaughterNPCs == 0)
|
||||
{
|
||||
++_slaughterProgress;
|
||||
ProcessSlaughterEvent();
|
||||
SaveToDB();
|
||||
}
|
||||
if (IsCurrentSlaughterTarget(unit->GetEntry()) && _slaughterNPCs && --_slaughterNPCs == 0)
|
||||
AdvanceSlaughterEvent();
|
||||
break;
|
||||
case NPC_BARON_RIVENDARE:
|
||||
events.CancelEvent(EVENT_BARON_TIME);
|
||||
@@ -233,35 +268,31 @@ public:
|
||||
case GO_ZIGGURAT_DOORS4:
|
||||
go->AllowSaveToDB(true);
|
||||
_zigguratDoorsGUID4 = go->GetGUID();
|
||||
if (_slaughterProgress == 4)
|
||||
if (_slaughterProgress == SLAUGHTER_COMPLETE)
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
break;
|
||||
case GO_ZIGGURAT_DOORS5:
|
||||
go->AllowSaveToDB(true);
|
||||
_zigguratDoorsGUID5 = go->GetGUID();
|
||||
if (_slaughterProgress == 4)
|
||||
if (_slaughterProgress == SLAUGHTER_COMPLETE)
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
break;
|
||||
case GO_SLAUGHTER_GATE_SIDE:
|
||||
go->AllowSaveToDB(true);
|
||||
if (_slaughterProgress >= 2)
|
||||
if (_slaughterProgress >= SLAUGHTER_MINDLESS_UNDEAD)
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
break;
|
||||
case GO_PORT_TRAP_GATE_1:
|
||||
go->AllowSaveToDB(true);
|
||||
_trapGatesGUIDs[0] = go->GetGUID();
|
||||
HandleGateTrapCreate(go, 0);
|
||||
break;
|
||||
case GO_PORT_TRAP_GATE_2:
|
||||
go->AllowSaveToDB(true);
|
||||
_trapGatesGUIDs[1] = go->GetGUID();
|
||||
HandleGateTrapCreate(go, 1);
|
||||
break;
|
||||
case GO_PORT_TRAP_GATE_3:
|
||||
go->AllowSaveToDB(true);
|
||||
_trapGatesGUIDs[2] = go->GetGUID();
|
||||
HandleGateTrapCreate(go, 2);
|
||||
break;
|
||||
case GO_PORT_TRAP_GATE_4:
|
||||
go->AllowSaveToDB(true);
|
||||
_trapGatesGUIDs[3] = go->GetGUID();
|
||||
HandleGateTrapCreate(go, 3);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -402,19 +433,41 @@ public:
|
||||
data >> _postboxesOpened;
|
||||
data >> _barthilasrunProgress;
|
||||
|
||||
uint32 scarletThreadUsedLocations;
|
||||
if (data >> scarletThreadUsedLocations && scarletThreadUsedLocations <= AllScarletThreadLocations)
|
||||
_scarletThreadUsedLocations = scarletThreadUsedLocations;
|
||||
// Older saves contain either the thread mask or the two trap cooldowns.
|
||||
std::array<time_t, 3> savedFields{};
|
||||
std::size_t savedFieldCount = 0;
|
||||
while (savedFieldCount < savedFields.size() && data >> savedFields[savedFieldCount])
|
||||
++savedFieldCount;
|
||||
|
||||
if (savedFieldCount == MAX_GATE_TRAPS)
|
||||
{
|
||||
_gateTrapCooldownEnd[GATE_TRAP_SCARLET] = savedFields[0];
|
||||
_gateTrapCooldownEnd[GATE_TRAP_UNDEAD] = savedFields[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (savedFieldCount && savedFields[0] >= 0 && savedFields[0] <= AllScarletThreadLocations)
|
||||
_scarletThreadUsedLocations = uint8(savedFields[0]);
|
||||
|
||||
if (savedFieldCount == savedFields.size())
|
||||
{
|
||||
_gateTrapCooldownEnd[GATE_TRAP_SCARLET] = savedFields[1];
|
||||
_gateTrapCooldownEnd[GATE_TRAP_UNDEAD] = savedFields[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (_baronRunTime)
|
||||
{
|
||||
events.ScheduleEvent(EVENT_BARON_TIME, 60s);
|
||||
}
|
||||
|
||||
if (_slaughterProgress > 0 && _slaughterProgress < 4)
|
||||
if (_slaughterProgress > SLAUGHTER_ABOMINATIONS && _slaughterProgress < SLAUGHTER_COMPLETE)
|
||||
{
|
||||
events.ScheduleEvent(EVENT_FORCE_SLAUGHTER_EVENT, 5s);
|
||||
}
|
||||
|
||||
RestoreGateTrapCooldown(GATE_TRAP_SCARLET);
|
||||
RestoreGateTrapCooldown(GATE_TRAP_UNDEAD);
|
||||
}
|
||||
|
||||
void WriteSaveDataMore(std::ostringstream& data) override
|
||||
@@ -427,7 +480,9 @@ public:
|
||||
<< _slaughterProgress << ' '
|
||||
<< _postboxesOpened << ' '
|
||||
<< _barthilasrunProgress << ' '
|
||||
<< uint32(_scarletThreadUsedLocations);
|
||||
<< uint32(_scarletThreadUsedLocations) << ' '
|
||||
<< _gateTrapCooldownEnd[GATE_TRAP_SCARLET] << ' '
|
||||
<< _gateTrapCooldownEnd[GATE_TRAP_UNDEAD];
|
||||
}
|
||||
|
||||
uint32 GetData(uint32 type) const override
|
||||
@@ -458,7 +513,7 @@ public:
|
||||
{
|
||||
// if the gate is in cooldown, skip the other checks
|
||||
if (_gateTrapsCooldown[i])
|
||||
break;
|
||||
continue;
|
||||
|
||||
// Check that the trap is not on cooldown, if so check if player/pet is in range
|
||||
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
||||
@@ -468,38 +523,8 @@ public:
|
||||
// should pet also trigger the trap? could not find any source for it
|
||||
if (!player->IsGameMaster() && player->IsWithinDist2d(aGateTrap[i].m_positionX, aGateTrap[i].m_positionY, 5.5f))
|
||||
{
|
||||
// Check if timer was not already set by another player/pet a few milliseconds before
|
||||
if (_gateTrapsCooldown[i])
|
||||
return;
|
||||
|
||||
_gateTrapsCooldown[i] = true;
|
||||
|
||||
// close the gates
|
||||
if (_trapGatesGUIDs[2 * i])
|
||||
DoUseDoorOrButton(_trapGatesGUIDs[2 * i]);
|
||||
if (_trapGatesGUIDs[2 * i + 1])
|
||||
DoUseDoorOrButton(_trapGatesGUIDs[2 * i + 1]);
|
||||
|
||||
_trappedPlayerGUID = player->GetGUID();
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// set timer to reset the trap
|
||||
events.ScheduleEvent(EVENT_GATE1_TRAP, 1800s);
|
||||
// set timer to reopen gates
|
||||
events.ScheduleEvent(EVENT_GATE1_DELAY, 20s);
|
||||
// set timer to spawn the plagued critters
|
||||
events.ScheduleEvent(EVENT_GATE1_CRITTER_DELAY, 2s);
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
// set timer to reset the trap
|
||||
events.ScheduleEvent(EVENT_GATE2_TRAP, 1800s);
|
||||
// set timer to reopen gates
|
||||
events.ScheduleEvent(EVENT_GATE2_DELAY, 20s);
|
||||
// set timer to spawn the plagued critters
|
||||
events.ScheduleEvent(EVENT_GATE2_CRITTER_DELAY, 2s);
|
||||
}
|
||||
StartGateTrap(i, player);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -512,9 +537,13 @@ public:
|
||||
{
|
||||
case EVENT_GATE1_TRAP:
|
||||
_gateTrapsCooldown[GATE1] = false;
|
||||
_gateTrapCooldownEnd[GATE1] = 0;
|
||||
SaveToDB();
|
||||
break;
|
||||
case EVENT_GATE2_TRAP:
|
||||
_gateTrapsCooldown[GATE2] = false;
|
||||
_gateTrapCooldownEnd[GATE2] = 0;
|
||||
SaveToDB();
|
||||
break;
|
||||
case EVENT_GATE1_DELAY:
|
||||
gate_delay(GATE1);
|
||||
@@ -624,6 +653,7 @@ public:
|
||||
uint32 _zigguratState3;
|
||||
uint32 _slaughterProgress;
|
||||
uint32 _slaughterNPCs;
|
||||
std::unordered_set<ObjectGuid::LowType> _abominationSpawnIds;
|
||||
uint32 _barthilasrunProgress{};
|
||||
uint32 _postboxesOpened;
|
||||
EventMap events;
|
||||
@@ -638,9 +668,110 @@ public:
|
||||
ObjectGuid _baronRivendareGUID;
|
||||
ObjectGuid _barthilasGUID;
|
||||
|
||||
bool _gateTrapsCooldown[2];
|
||||
ObjectGuid _trappedPlayerGUID;
|
||||
ObjectGuid _trapGatesGUIDs[4];
|
||||
bool _gateTrapsCooldown[MAX_GATE_TRAPS];
|
||||
bool _gateTrapNeedsRecovery[MAX_GATE_TRAP_GATES];
|
||||
time_t _gateTrapCooldownEnd[MAX_GATE_TRAPS];
|
||||
ObjectGuid _trappedPlayerGUIDs[MAX_GATE_TRAPS];
|
||||
ObjectGuid _trapGatesGUIDs[MAX_GATE_TRAP_GATES];
|
||||
|
||||
bool HasLivingAbominations(ObjectGuid::LowType dyingSpawnId) const
|
||||
{
|
||||
auto const& creatures = instance->GetCreatureBySpawnIdStore();
|
||||
for (ObjectGuid::LowType spawnId : _abominationSpawnIds)
|
||||
{
|
||||
if (spawnId == dyingSpawnId)
|
||||
continue;
|
||||
|
||||
auto const range = creatures.equal_range(spawnId);
|
||||
if (range.first == range.second)
|
||||
{
|
||||
// An unloaded spawn still blocks the event unless it is waiting to respawn.
|
||||
if (instance->GetCreatureRespawnTime(spawnId) <= GameTime::GetGameTime().count())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
for (auto itr = range.first; itr != range.second; ++itr)
|
||||
if (itr->second->IsAlive())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AdvanceSlaughterEvent()
|
||||
{
|
||||
++_slaughterProgress;
|
||||
SaveToDB();
|
||||
ProcessSlaughterEvent();
|
||||
}
|
||||
|
||||
bool IsCurrentSlaughterTarget(uint32 entry) const
|
||||
{
|
||||
switch (_slaughterProgress)
|
||||
{
|
||||
case SLAUGHTER_RAMSTEIN:
|
||||
return entry == NPC_RAMSTEIN_THE_GORGER;
|
||||
case SLAUGHTER_MINDLESS_UNDEAD:
|
||||
return entry == NPC_MINDLESS_UNDEAD;
|
||||
case SLAUGHTER_BLACK_GUARD:
|
||||
return entry == NPC_BLACK_GUARD;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleGateTrapCreate(GameObject* gate, uint8 index)
|
||||
{
|
||||
gate->AllowSaveToDB(true);
|
||||
_trapGatesGUIDs[index] = gate->GetGUID();
|
||||
|
||||
// EventMap timers do not survive an instance unload. Finish an interrupted
|
||||
// trap in its safe open state instead of restoring a permanently closed gate.
|
||||
if (_gateTrapNeedsRecovery[index])
|
||||
{
|
||||
_gateTrapNeedsRecovery[index] = false;
|
||||
gate->SetGoState(GO_STATE_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreGateTrapCooldown(uint8 gate)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
if (_gateTrapCooldownEnd[gate] <= now)
|
||||
{
|
||||
_gateTrapCooldownEnd[gate] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_gateTrapsCooldown[gate] = true;
|
||||
uint32 eventId = gate == GATE_TRAP_SCARLET ? EVENT_GATE1_TRAP : EVENT_GATE2_TRAP;
|
||||
events.ScheduleEvent(eventId, Seconds(_gateTrapCooldownEnd[gate] - now));
|
||||
}
|
||||
|
||||
void StartGateTrap(uint8 gate, Player* player)
|
||||
{
|
||||
_gateTrapsCooldown[gate] = true;
|
||||
_gateTrapCooldownEnd[gate] = GameTime::GetGameTime().count() + GATE_TRAP_COOLDOWN;
|
||||
_trappedPlayerGUIDs[gate] = player->GetGUID();
|
||||
|
||||
HandleGameObject(_trapGatesGUIDs[2 * gate], false);
|
||||
HandleGameObject(_trapGatesGUIDs[2 * gate + 1], false);
|
||||
|
||||
if (gate == GATE_TRAP_SCARLET)
|
||||
{
|
||||
events.ScheduleEvent(EVENT_GATE1_TRAP, Seconds(GATE_TRAP_COOLDOWN));
|
||||
events.ScheduleEvent(EVENT_GATE1_DELAY, 20s);
|
||||
events.ScheduleEvent(EVENT_GATE1_CRITTER_DELAY, 2s);
|
||||
}
|
||||
else
|
||||
{
|
||||
events.ScheduleEvent(EVENT_GATE2_TRAP, Seconds(GATE_TRAP_COOLDOWN));
|
||||
events.ScheduleEvent(EVENT_GATE2_DELAY, 20s);
|
||||
events.ScheduleEvent(EVENT_GATE2_CRITTER_DELAY, 2s);
|
||||
}
|
||||
|
||||
SaveToDB();
|
||||
}
|
||||
|
||||
uint8 _scarletThreadUsedLocations;
|
||||
uint8 _scarletThreadLocation;
|
||||
@@ -673,21 +804,15 @@ public:
|
||||
|
||||
void gate_delay(int gate)
|
||||
{
|
||||
if (_trapGatesGUIDs[2 * gate])
|
||||
{
|
||||
DoUseDoorOrButton(_trapGatesGUIDs[2 * gate]);
|
||||
}
|
||||
if (_trapGatesGUIDs[2 * gate + 1])
|
||||
{
|
||||
DoUseDoorOrButton(_trapGatesGUIDs[2 * gate + 1]);
|
||||
}
|
||||
HandleGameObject(_trapGatesGUIDs[2 * gate], true);
|
||||
HandleGameObject(_trapGatesGUIDs[2 * gate + 1], true);
|
||||
}
|
||||
|
||||
void gate_critter_delay(int gate)
|
||||
{
|
||||
if (_trappedPlayerGUID)
|
||||
if (_trappedPlayerGUIDs[gate])
|
||||
{
|
||||
if (Player* pPlayer = ObjectAccessor::GetPlayer(instance, _trappedPlayerGUID))
|
||||
if (Player* pPlayer = ObjectAccessor::GetPlayer(instance, _trappedPlayerGUIDs[gate]))
|
||||
{
|
||||
DoSpawnPlaguedCritters(gate, pPlayer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user