feat(Core/Maps): port spawn system/dynamic spawns from TrinityCore (#25206)
Co-authored-by: r00ty-tc <r00ty-tc@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Pet.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Transport.h"
|
||||
#include "VMapFactory.h"
|
||||
@@ -457,6 +458,18 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
return;
|
||||
}
|
||||
|
||||
/// Process any due respawns (non-compatibility mode spawns)
|
||||
if (!sWorld->getBoolConfig(CONFIG_RESPAWN_FORCE_COMPATIBILITY_MODE))
|
||||
{
|
||||
if (_respawnCheckTimer <= t_diff)
|
||||
{
|
||||
ProcessRespawns();
|
||||
_respawnCheckTimer = 5000; // Check every 5 seconds
|
||||
}
|
||||
else
|
||||
_respawnCheckTimer -= t_diff;
|
||||
}
|
||||
|
||||
_updatableObjectListRecheckTimer.Update(t_diff);
|
||||
resetMarkedCells();
|
||||
|
||||
@@ -2382,7 +2395,13 @@ void Map::SaveCreatureRespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTi
|
||||
if (GetInstanceResetPeriod() > 0 && respawnTime - now + 5 >= GetInstanceResetPeriod())
|
||||
respawnTime = now + YEAR;
|
||||
|
||||
// Remove old queue entry if updating an existing respawn time
|
||||
auto itr = _creatureRespawnTimes.find(spawnId);
|
||||
if (itr != _creatureRespawnTimes.end())
|
||||
_respawnQueue.erase({itr->second, SPAWN_TYPE_CREATURE, spawnId});
|
||||
|
||||
_creatureRespawnTimes[spawnId] = respawnTime;
|
||||
_respawnQueue.insert({respawnTime, SPAWN_TYPE_CREATURE, spawnId});
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN);
|
||||
stmt->SetData(0, spawnId);
|
||||
@@ -2394,7 +2413,12 @@ void Map::SaveCreatureRespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTi
|
||||
|
||||
void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
_creatureRespawnTimes.erase(spawnId);
|
||||
auto itr = _creatureRespawnTimes.find(spawnId);
|
||||
if (itr != _creatureRespawnTimes.end())
|
||||
{
|
||||
_respawnQueue.erase({itr->second, SPAWN_TYPE_CREATURE, spawnId});
|
||||
_creatureRespawnTimes.erase(itr);
|
||||
}
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
|
||||
stmt->SetData(0, spawnId);
|
||||
@@ -2416,7 +2440,13 @@ void Map::SaveGORespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTime)
|
||||
if (GetInstanceResetPeriod() > 0 && respawnTime - now + 5 >= GetInstanceResetPeriod())
|
||||
respawnTime = now + YEAR;
|
||||
|
||||
// Remove old queue entry if updating an existing respawn time
|
||||
auto itr = _goRespawnTimes.find(spawnId);
|
||||
if (itr != _goRespawnTimes.end())
|
||||
_respawnQueue.erase({itr->second, SPAWN_TYPE_GAMEOBJECT, spawnId});
|
||||
|
||||
_goRespawnTimes[spawnId] = respawnTime;
|
||||
_respawnQueue.insert({respawnTime, SPAWN_TYPE_GAMEOBJECT, spawnId});
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN);
|
||||
stmt->SetData(0, spawnId);
|
||||
@@ -2428,7 +2458,12 @@ void Map::SaveGORespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTime)
|
||||
|
||||
void Map::RemoveGORespawnTime(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
_goRespawnTimes.erase(spawnId);
|
||||
auto itr = _goRespawnTimes.find(spawnId);
|
||||
if (itr != _goRespawnTimes.end())
|
||||
{
|
||||
_respawnQueue.erase({itr->second, SPAWN_TYPE_GAMEOBJECT, spawnId});
|
||||
_goRespawnTimes.erase(itr);
|
||||
}
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
|
||||
stmt->SetData(0, spawnId);
|
||||
@@ -2448,9 +2483,10 @@ void Map::LoadRespawnTimes()
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ObjectGuid::LowType lowguid = fields[0].Get<uint32>();
|
||||
uint32 respawnTime = fields[1].Get<uint32>();
|
||||
time_t respawnTime = time_t(fields[1].Get<uint32>());
|
||||
|
||||
_creatureRespawnTimes[lowguid] = time_t(respawnTime);
|
||||
_creatureRespawnTimes[lowguid] = respawnTime;
|
||||
_respawnQueue.insert({respawnTime, SPAWN_TYPE_CREATURE, lowguid});
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
@@ -2463,9 +2499,10 @@ void Map::LoadRespawnTimes()
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ObjectGuid::LowType lowguid = fields[0].Get<uint32>();
|
||||
uint32 respawnTime = fields[1].Get<uint32>();
|
||||
time_t respawnTime = time_t(fields[1].Get<uint32>());
|
||||
|
||||
_goRespawnTimes[lowguid] = time_t(respawnTime);
|
||||
_goRespawnTimes[lowguid] = respawnTime;
|
||||
_respawnQueue.insert({respawnTime, SPAWN_TYPE_GAMEOBJECT, lowguid});
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
@@ -2474,6 +2511,7 @@ void Map::DeleteRespawnTimes()
|
||||
{
|
||||
_creatureRespawnTimes.clear();
|
||||
_goRespawnTimes.clear();
|
||||
_respawnQueue.clear();
|
||||
|
||||
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
|
||||
}
|
||||
@@ -2491,6 +2529,309 @@ void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
bool Map::IsSpawnGroupActive(uint32 groupId) const
|
||||
{
|
||||
SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
// System groups are always active
|
||||
if (data->flags & SPAWNGROUP_FLAG_SYSTEM)
|
||||
return true;
|
||||
|
||||
// Per-map toggled state: XOR with default.
|
||||
// MANUAL_SPAWN groups default to inactive; toggling makes them active.
|
||||
// Non-MANUAL groups default to active; toggling makes them inactive.
|
||||
bool toggled = _toggledSpawnGroupIds.count(groupId) != 0;
|
||||
bool defaultActive = !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
|
||||
return toggled != defaultActive; // XOR: toggled flips the default
|
||||
}
|
||||
|
||||
bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn /*= false*/, bool force /*= false*/)
|
||||
{
|
||||
SpawnGroupTemplateData const* groupData = sObjectMgr->GetSpawnGroupData(groupId);
|
||||
if (!groupData || (groupData->flags & SPAWNGROUP_FLAG_SYSTEM))
|
||||
{
|
||||
LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group {}. Blocked.", groupId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (groupData->mapId != SPAWNGROUP_MAP_UNSET && groupData->mapId != GetId())
|
||||
{
|
||||
LOG_ERROR("maps", "Tried to spawn group {} on map {}, but group has map {}. Blocked.",
|
||||
groupId, GetId(), groupData->mapId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark group as active on this map (toggle to active state)
|
||||
if (groupData->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
|
||||
_toggledSpawnGroupIds.insert(groupId);
|
||||
else
|
||||
_toggledSpawnGroupIds.erase(groupId);
|
||||
|
||||
auto range = sObjectMgr->GetSpawnDataForGroup(groupId);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
SpawnData const* data = it->second;
|
||||
ObjectGuid::LowType spawnId = data->spawnId;
|
||||
|
||||
// Check if there's already an alive instance
|
||||
if (!force)
|
||||
{
|
||||
if (data->type == SPAWN_TYPE_CREATURE)
|
||||
{
|
||||
auto bounds = _creatureBySpawnIdStore.equal_range(spawnId);
|
||||
bool alive = false;
|
||||
for (auto itr = bounds.first; itr != bounds.second; ++itr)
|
||||
if (itr->second->IsAlive())
|
||||
alive = true;
|
||||
if (alive)
|
||||
continue;
|
||||
}
|
||||
else if (data->type == SPAWN_TYPE_GAMEOBJECT)
|
||||
{
|
||||
if (_gameobjectBySpawnIdStore.count(spawnId))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
time_t respawnTime = GetRespawnTime(data->type, spawnId);
|
||||
if (respawnTime && respawnTime > GameTime::GetGameTime().count())
|
||||
{
|
||||
if (!force && !ignoreRespawn)
|
||||
continue;
|
||||
RemoveRespawnTime(data->type, spawnId);
|
||||
}
|
||||
|
||||
// Don't spawn if grid isn't loaded (will be handled in grid loader)
|
||||
if (!IsGridLoaded(data->posX, data->posY))
|
||||
continue;
|
||||
|
||||
switch (data->type)
|
||||
{
|
||||
case SPAWN_TYPE_CREATURE:
|
||||
{
|
||||
Creature* creature = new Creature();
|
||||
if (!creature->LoadCreatureFromDB(spawnId, this, true, true))
|
||||
delete creature;
|
||||
break;
|
||||
}
|
||||
case SPAWN_TYPE_GAMEOBJECT:
|
||||
{
|
||||
GameObject* gameobject = new GameObject();
|
||||
if (!gameobject->LoadGameObjectFromDB(spawnId, this, true))
|
||||
delete gameobject;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes /*= false*/)
|
||||
{
|
||||
SpawnGroupTemplateData const* groupData = sObjectMgr->GetSpawnGroupData(groupId);
|
||||
if (!groupData || (groupData->flags & SPAWNGROUP_FLAG_SYSTEM))
|
||||
{
|
||||
LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group {}. Blocked.", groupId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (groupData->mapId != SPAWNGROUP_MAP_UNSET && groupData->mapId != GetId())
|
||||
{
|
||||
LOG_ERROR("maps", "Tried to despawn group {} on map {}, but group has map {}. Blocked.",
|
||||
groupId, GetId(), groupData->mapId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark group as inactive on this map (toggle to inactive state)
|
||||
if (groupData->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
|
||||
_toggledSpawnGroupIds.erase(groupId);
|
||||
else
|
||||
_toggledSpawnGroupIds.insert(groupId);
|
||||
|
||||
std::vector<WorldObject*> toUnload;
|
||||
auto range = sObjectMgr->GetSpawnDataForGroup(groupId);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
SpawnData const* data = it->second;
|
||||
ObjectGuid::LowType spawnId = data->spawnId;
|
||||
|
||||
if (deleteRespawnTimes)
|
||||
RemoveRespawnTime(data->type, spawnId);
|
||||
|
||||
switch (data->type)
|
||||
{
|
||||
case SPAWN_TYPE_CREATURE:
|
||||
{
|
||||
auto bounds = _creatureBySpawnIdStore.equal_range(spawnId);
|
||||
for (auto itr = bounds.first; itr != bounds.second; ++itr)
|
||||
toUnload.emplace_back(itr->second);
|
||||
break;
|
||||
}
|
||||
case SPAWN_TYPE_GAMEOBJECT:
|
||||
{
|
||||
auto bounds = _gameobjectBySpawnIdStore.equal_range(spawnId);
|
||||
for (auto itr = bounds.first; itr != bounds.second; ++itr)
|
||||
toUnload.emplace_back(itr->second);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (WorldObject* obj : toUnload)
|
||||
obj->AddObjectToRemoveList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Map::ProcessRespawns()
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
|
||||
// Process due respawns from the time-ordered queue.
|
||||
// Entries are sorted by respawnTime — once we hit a future time, we're done.
|
||||
while (!_respawnQueue.empty())
|
||||
{
|
||||
auto it = _respawnQueue.begin();
|
||||
if (it->respawnTime > now)
|
||||
break; // nothing else is due this tick
|
||||
|
||||
SpawnObjectType type = it->type;
|
||||
ObjectGuid::LowType spawnId = it->spawnId;
|
||||
|
||||
// Remove from queue first — handlers below call Remove*RespawnTime()
|
||||
// which also erases from queue, so we must pop before processing.
|
||||
_respawnQueue.erase(it);
|
||||
|
||||
if (type == SPAWN_TYPE_CREATURE)
|
||||
ProcessCreatureRespawn(spawnId);
|
||||
else if (type == SPAWN_TYPE_GAMEOBJECT)
|
||||
ProcessGameObjectRespawn(spawnId);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::ProcessCreatureRespawn(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
// Pool members are handled entirely by PoolMgr
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<Creature>(spawnId))
|
||||
{
|
||||
sPoolMgr->UpdatePool<Creature>(poolId, spawnId);
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
|
||||
if (!data)
|
||||
{
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compat-mode creatures handle their own respawn in-place — don't interfere.
|
||||
// Clean up the stale respawn time entry since the legacy system manages these.
|
||||
SpawnGroupTemplateData const* groupData = sObjectMgr->GetSpawnGroupData(data->spawnGroupId);
|
||||
if (!groupData || (groupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE))
|
||||
{
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't respawn if the spawn group is not active
|
||||
if (!IsSpawnGroupActive(data->spawnGroupId))
|
||||
{
|
||||
// Re-queue — will be checked again next ProcessRespawns() tick
|
||||
_respawnQueue.insert({GameTime::GetGameTime().count() + 5, SPAWN_TYPE_CREATURE, spawnId});
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if grid isn't loaded (will be handled in grid loader)
|
||||
if (!IsGridLoaded(data->posX, data->posY))
|
||||
{
|
||||
_respawnQueue.insert({GameTime::GetGameTime().count() + 5, SPAWN_TYPE_CREATURE, spawnId});
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if already alive
|
||||
auto bounds = _creatureBySpawnIdStore.equal_range(spawnId);
|
||||
for (auto itr = bounds.first; itr != bounds.second; ++itr)
|
||||
{
|
||||
if (itr->second->IsAlive())
|
||||
{
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove respawn time BEFORE LoadFromDB, otherwise the creature
|
||||
// reads it back and loads as DEAD instead of ALIVE
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
|
||||
Creature* creature = new Creature();
|
||||
if (!creature->LoadCreatureFromDB(spawnId, this, true, true))
|
||||
delete creature;
|
||||
}
|
||||
|
||||
void Map::ProcessGameObjectRespawn(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
// Pool members are handled entirely by PoolMgr
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<GameObject>(spawnId))
|
||||
{
|
||||
sPoolMgr->UpdatePool<GameObject>(poolId, spawnId);
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
|
||||
if (!data)
|
||||
{
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compat-mode gameobjects handle their own respawn — don't interfere.
|
||||
// Clean up the stale respawn time entry since the legacy system manages these.
|
||||
SpawnGroupTemplateData const* groupData = sObjectMgr->GetSpawnGroupData(data->spawnGroupId);
|
||||
if (!groupData || (groupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE))
|
||||
{
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't respawn if the spawn group is not active
|
||||
if (!IsSpawnGroupActive(data->spawnGroupId))
|
||||
{
|
||||
_respawnQueue.insert({GameTime::GetGameTime().count() + 5, SPAWN_TYPE_GAMEOBJECT, spawnId});
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if grid isn't loaded (will be handled in grid loader)
|
||||
if (!IsGridLoaded(data->posX, data->posY))
|
||||
{
|
||||
_respawnQueue.insert({GameTime::GetGameTime().count() + 5, SPAWN_TYPE_GAMEOBJECT, spawnId});
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameobjectBySpawnIdStore.count(spawnId))
|
||||
{
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove respawn time BEFORE LoadFromDB, otherwise the GO
|
||||
// reads it back and loads as despawned
|
||||
RemoveGORespawnTime(spawnId);
|
||||
|
||||
GameObject* gameobject = new GameObject();
|
||||
if (!gameobject->LoadGameObjectFromDB(spawnId, this, true))
|
||||
delete gameobject;
|
||||
}
|
||||
|
||||
void Map::UpdateEncounterState(EncounterCreditType type, uint32 creditEntry, Unit* source)
|
||||
{
|
||||
Difficulty difficulty_fixed = (IsSharedDifficultyMap(GetId()) ? Difficulty(GetDifficulty() % 2) : GetDifficulty());
|
||||
|
||||
@@ -35,11 +35,13 @@
|
||||
#include "PathGenerator.h"
|
||||
#include "Position.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpawnData.h"
|
||||
#include "Timer.h"
|
||||
#include "GridTerrainData.h"
|
||||
#include <bitset>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
|
||||
class Unit;
|
||||
@@ -424,6 +426,8 @@ public:
|
||||
void RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid);
|
||||
void SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t& respawnTime);
|
||||
void RemoveGORespawnTime(ObjectGuid::LowType dbGuid);
|
||||
[[nodiscard]] std::unordered_map<ObjectGuid::LowType, time_t> const& GetCreatureRespawnTimes() const { return _creatureRespawnTimes; }
|
||||
[[nodiscard]] std::unordered_map<ObjectGuid::LowType, time_t> const& GetGORespawnTimes() const { return _goRespawnTimes; }
|
||||
void LoadRespawnTimes();
|
||||
void DeleteRespawnTimes();
|
||||
[[nodiscard]] time_t GetInstanceResetPeriod() const { return _instanceResetPeriod; }
|
||||
@@ -431,6 +435,40 @@ public:
|
||||
void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone);
|
||||
[[nodiscard]] uint32 ApplyDynamicModeRespawnScaling(WorldObject const* obj, uint32 respawnDelay) const;
|
||||
|
||||
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false);
|
||||
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false);
|
||||
[[nodiscard]] bool IsSpawnGroupActive(uint32 groupId) const;
|
||||
void ProcessRespawns();
|
||||
void ProcessCreatureRespawn(ObjectGuid::LowType spawnId);
|
||||
void ProcessGameObjectRespawn(ObjectGuid::LowType spawnId);
|
||||
|
||||
[[nodiscard]] time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SPAWN_TYPE_CREATURE:
|
||||
return GetCreatureRespawnTime(spawnId);
|
||||
case SPAWN_TYPE_GAMEOBJECT:
|
||||
return GetGORespawnTime(spawnId);
|
||||
default:
|
||||
return time_t(0);
|
||||
}
|
||||
}
|
||||
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SPAWN_TYPE_CREATURE:
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
break;
|
||||
case SPAWN_TYPE_GAMEOBJECT:
|
||||
RemoveGORespawnTime(spawnId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EventProcessor Events;
|
||||
|
||||
void ScheduleCreatureRespawn(ObjectGuid /*creatureGuid*/, Milliseconds /*respawnTimer*/, Position pos = Position());
|
||||
@@ -602,6 +640,27 @@ private:
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
|
||||
|
||||
// Time-ordered index for ProcessRespawns() — avoids O(n) full scan.
|
||||
// Based on TrinityCore's priority queue approach (r00ty-tc, 59db2eee).
|
||||
struct RespawnEntry
|
||||
{
|
||||
time_t respawnTime;
|
||||
SpawnObjectType type;
|
||||
ObjectGuid::LowType spawnId;
|
||||
bool operator<(RespawnEntry const& other) const
|
||||
{
|
||||
if (respawnTime != other.respawnTime)
|
||||
return respawnTime < other.respawnTime;
|
||||
if (type != other.type)
|
||||
return type < other.type;
|
||||
return spawnId < other.spawnId;
|
||||
}
|
||||
};
|
||||
std::set<RespawnEntry> _respawnQueue;
|
||||
|
||||
std::unordered_set<uint32> _toggledSpawnGroupIds;
|
||||
uint32 _respawnCheckTimer{0};
|
||||
|
||||
std::unordered_map<uint32, uint32> _zonePlayerCountMap;
|
||||
|
||||
ZoneDynamicInfoMap _zoneDynamicInfo;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define AZEROTHCORE_SPAWNDATA_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include <string>
|
||||
|
||||
enum SpawnObjectType : uint8
|
||||
@@ -41,26 +42,30 @@ enum SpawnGroupFlags : uint32
|
||||
{
|
||||
SPAWNGROUP_FLAG_NONE = 0x00,
|
||||
SPAWNGROUP_FLAG_SYSTEM = 0x01,
|
||||
SPAWNGROUP_FLAG_COMPATIBILITY_MODE = 0x02,
|
||||
SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04,
|
||||
SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08,
|
||||
SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10,
|
||||
|
||||
SPAWNGROUP_FLAG_ALL = SPAWNGROUP_FLAG_SYSTEM |
|
||||
SPAWNGROUP_FLAG_ALL = SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE |
|
||||
SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE |
|
||||
SPAWNGROUP_FLAG_ESCORTQUESTNPC
|
||||
};
|
||||
|
||||
constexpr uint32 SPAWNGROUP_MAP_UNSET = 0xFFFFFFFF;
|
||||
|
||||
struct SpawnGroupTemplateData
|
||||
{
|
||||
uint32 groupId;
|
||||
std::string name;
|
||||
uint16 mapid;
|
||||
uint32 mapId;
|
||||
SpawnGroupFlags flags;
|
||||
};
|
||||
|
||||
struct SpawnData
|
||||
{
|
||||
SpawnObjectType const type;
|
||||
ObjectGuid::LowType spawnId{0};
|
||||
uint16 mapid{0};
|
||||
uint32 phaseMask{0};
|
||||
float posX{0.0f};
|
||||
|
||||
Reference in New Issue
Block a user