aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Maps/Map.cpp
diff options
context:
space:
mode:
authorr00ty-tc <r00ty-tc@users.noreply.github.com>2017-05-07 21:48:41 +0100
committerShauren <shauren.trinity@gmail.com>2020-08-22 12:59:57 +0200
commit03b125e6d1947258316c931499746696a95aded2 (patch)
tree34d7ebc57cd3669d6d1a118e1491d3ecf353470a /src/server/game/Maps/Map.cpp
parentbf5be2839652e038eeb87c9fa301fd9dd6de8982 (diff)
Dynamic Creature/Go spawning:
- True blizzlike creature spawn/respawn behavior - new creature = new object - Toggleable spawn groups (with C++/SAI/command options to use them) - Custom feature: dynamic spawn rate scaling. Accelerates respawn rate based on players in the zone. - Backward compatibility mode (set via group and for summons) to support creatures/gos that currently don't work well with this (this should be removed once the exceptions are fixed) Fixes and closes #2858 Tags #8661 as fixable. Fixes and closes #13787 Fixes #15222. (cherry picked from commit 59db2eeea0a35028779fd76372ae06cc98c8086f)
Diffstat (limited to 'src/server/game/Maps/Map.cpp')
-rw-r--r--src/server/game/Maps/Map.cpp495
1 files changed, 434 insertions, 61 deletions
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 8b2e2cab53e..984e67d2a5d 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -23,6 +23,7 @@
#include "DisableMgr.h"
#include "DynamicTree.h"
#include "GameObjectModel.h"
+#include "GameTime.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GridStates.h"
@@ -41,6 +42,7 @@
#include "ObjectMgr.h"
#include "Pet.h"
#include "PhasingHandler.h"
+#include "PoolMgr.h"
#include "ScriptMgr.h"
#include "Transport.h"
#include "Vehicle.h"
@@ -59,6 +61,7 @@ u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
#define DEFAULT_GRID_EXPIRY 300
#define MAX_GRID_LOAD_TIME 50
#define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO))
+#define MAP_INVALID_ZONE 0xFFFFFFFF
GridState* si_GridStates[MAX_GRID_STATE];
@@ -72,6 +75,10 @@ Map::~Map()
sScriptMgr->OnDestroyMap(this);
+ // Delete all waiting spawns, else there will be a memory leak
+ // This doesn't delete from database.
+ DeleteRespawnInfo();
+
while (!i_worldObjects.empty())
{
WorldObject* obj = *i_worldObjects.begin();
@@ -318,7 +325,7 @@ m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()),
i_gridExpiry(expiry),
-i_scriptLock(false), _defaultLight(DB2Manager::GetDefaultMapLight(id))
+i_scriptLock(false), _respawnCheckTimer(0), _defaultLight(DB2Manager::GetDefaultMapLight(id))
{
if (_parent)
{
@@ -344,6 +351,8 @@ i_scriptLock(false), _defaultLight(DB2Manager::GetDefaultMapLight(id))
}
}
+ _zonePlayerCountMap.clear();
+
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
@@ -635,6 +644,29 @@ void Map::LoadGridObjects(NGridType* grid, Cell const& cell)
loader.LoadN();
}
+void Map::GridMarkNoUnload(uint32 x, uint32 y)
+{
+ // First make sure this grid is loaded
+ float gX = ((float(x) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
+ float gY = ((float(y) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
+ Cell cell = Cell(gX, gY);
+ EnsureGridLoaded(cell);
+
+ // Mark as don't unload
+ NGridType* grid = getNGrid(x, y);
+ grid->setUnloadExplicitLock(true);
+}
+
+void Map::GridUnmarkNoUnload(uint32 x, uint32 y)
+{
+ // If grid is loaded, clear unload lock
+ if (IsGridLoaded(GridCoord(x, y)))
+ {
+ NGridType* grid = getNGrid(x, y);
+ grid->setUnloadExplicitLock(false);
+ }
+}
+
void Map::LoadGrid(float x, float y)
{
EnsureGridLoaded(Cell(x, y));
@@ -807,7 +839,22 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::Obj
}
}
-void Map::Update(const uint32 t_diff)
+void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
+{
+ // Nothing to do if no change
+ if (oldZone == newZone)
+ return;
+
+ if (oldZone != MAP_INVALID_ZONE)
+ {
+ uint32& oldZoneCount = _zonePlayerCountMap[oldZone];
+ ASSERT(oldZoneCount, "A player left zone %u (went to %u) - but there were no players in the zone!", oldZone, newZone);
+ --oldZoneCount;
+ }
+ ++_zonePlayerCountMap[newZone];
+}
+
+void Map::Update(uint32 t_diff)
{
_dynamicTree.update(t_diff);
/// update worldsessions for existing players
@@ -822,6 +869,16 @@ void Map::Update(const uint32 t_diff)
session->Update(t_diff, updater);
}
}
+
+ /// process any due respawns
+ if (_respawnCheckTimer <= t_diff)
+ {
+ ProcessRespawns();
+ _respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS);
+ }
+ else
+ _respawnCheckTimer -= t_diff;
+
/// update active cells around players and active objects
resetMarkedCells();
@@ -1013,6 +1070,8 @@ void Map::ProcessRelocationNotifies(const uint32 diff)
void Map::RemovePlayerFromMap(Player* player, bool remove)
{
+ // Before leaving map, update zone/area for stats
+ player->UpdateZone(MAP_INVALID_ZONE, 0);
sScriptMgr->OnPlayerLeaveMap(this, player);
player->getHostileRefManager().deleteReferences(); // multithreading crashfix
@@ -3067,11 +3126,6 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa
return result;
}
-float Map::GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/)
-{
- return std::max<float>(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
-}
-
bool Map::IsInWater(PhaseShift const& phaseShift, float x, float y, float pZ, LiquidData* data)
{
LiquidData liquid_status;
@@ -3227,7 +3281,329 @@ void Map::SendObjectUpdates()
}
}
-void Map::DelayedUpdate(const uint32 t_diff)
+bool Map::CheckRespawn(RespawnInfo* info)
+{
+ uint32 poolId = info->spawnId ? sPoolMgr->IsPartOfAPool(info->type, info->spawnId) : 0;
+ // First, check if there's already an instance of this object that would block the respawn
+ // Only do this for unpooled spawns
+ if (!poolId)
+ {
+ bool doDelete = false;
+ switch (info->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ // escort check for creatures only (if the world config boolean is set)
+ bool isEscort = false;
+ if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && info->type == SPAWN_TYPE_CREATURE)
+ if (CreatureData const* cdata = sObjectMgr->GetCreatureData(info->spawnId))
+ if (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)
+ isEscort = true;
+
+ auto range = _creatureBySpawnIdStore.equal_range(info->spawnId);
+ for (auto it = range.first; it != range.second; ++it)
+ {
+ Creature* creature = it->second;
+ if (!creature->IsAlive())
+ continue;
+ // escort NPCs are allowed to respawn as long as all other instances are already escorting
+ if (isEscort && creature->IsEscortNPC(true))
+ continue;
+ doDelete = true;
+ break;
+ }
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ // gameobject check is simpler - they cannot be dead or escorting
+ if (_gameobjectBySpawnIdStore.find(info->spawnId) != _gameobjectBySpawnIdStore.end())
+ doDelete = true;
+ break;
+ default:
+ ASSERT(false, "Invalid spawn type %u with spawnId " UI64FMTD " on map %u", uint32(info->type), info->spawnId, GetId());
+ return true;
+ }
+ if (doDelete)
+ {
+ info->respawnTime = 0;
+ return false;
+ }
+ }
+
+ // next, check linked respawn time
+ ObjectGuid thisGUID = info->type == SPAWN_TYPE_GAMEOBJECT
+ ? ObjectGuid::Create<HighGuid::GameObject>(GetId(), info->entry, info->spawnId)
+ : ObjectGuid::Create<HighGuid::Creature>(GetId(), info->entry, info->spawnId);
+ if (time_t linkedTime = GetLinkedRespawnTime(thisGUID))
+ {
+ time_t now = time(NULL);
+ time_t respawnTime;
+ if (sObjectMgr->GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB
+ respawnTime = now + WEEK;
+ else // set us to check again shortly after linked unit
+ respawnTime = std::max<time_t>(now, linkedTime) + urand(5, 15);
+ info->respawnTime = respawnTime;
+ return false;
+ }
+
+ // now, check if we're part of a pool
+ if (poolId)
+ {
+ // ok, part of a pool - hand off to pool logic to handle this, we're just going to remove the respawn and call it a day
+ if (info->type == SPAWN_TYPE_GAMEOBJECT)
+ sPoolMgr->UpdatePool<GameObject>(poolId, info->spawnId);
+ else if (info->type == SPAWN_TYPE_CREATURE)
+ sPoolMgr->UpdatePool<Creature>(poolId, info->spawnId);
+ else
+ ASSERT(false, "Invalid spawn type %u (spawnid " UI64FMTD ") on map %u", uint32(info->type), info->spawnId, GetId());
+ info->respawnTime = 0;
+ return false;
+ }
+
+ // if we're a creature, see if the script objects to us spawning
+ if (info->type == SPAWN_TYPE_CREATURE)
+ {
+ if (!sScriptMgr->CanSpawn(info->spawnId, info->entry, sObjectMgr->GetCreatureData(info->spawnId), this))
+ { // if a script blocks our respawn, schedule next check in a little bit
+ info->respawnTime = time(NULL) + urand(4, 7);
+ return false;
+ }
+ }
+ return true;
+}
+
+void Map::DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId)
+{
+ if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler
+ return;
+
+ switch (type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ Creature* obj = new Creature();
+ if (!obj->LoadFromDB(spawnId, this, true, true))
+ delete obj;
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ GameObject* obj = new GameObject();
+ if (!obj->LoadFromDB(spawnId, this, true))
+ delete obj;
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u (spawnid " UI64FMTD ") on map %u", uint32(type), spawnId, GetId());
+ }
+}
+
+void Map::Respawn(RespawnInfo* info, bool force, CharacterDatabaseTransaction dbTrans)
+{
+ if (!force && !CheckRespawn(info))
+ {
+ if (info->respawnTime)
+ SaveRespawnTime(info->type, info->spawnId, info->entry, info->respawnTime, info->zoneId, info->gridId, true, true, dbTrans);
+ else
+ RemoveRespawnTime(info);
+ return;
+ }
+
+ // remove the actual respawn record first - since this deletes it, we save what we need
+ SpawnObjectType const type = info->type;
+ uint32 const gridId = info->gridId;
+ ObjectGuid::LowType const spawnId = info->spawnId;
+ RemoveRespawnTime(info);
+ DoRespawn(type, spawnId, gridId);
+}
+
+void Map::Respawn(RespawnVector& respawnData, bool force, CharacterDatabaseTransaction dbTrans)
+{
+ CharacterDatabaseTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction();
+ for (RespawnInfo* info : respawnData)
+ Respawn(info, force, trans);
+ if (!dbTrans)
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+void Map::AddRespawnInfo(RespawnInfo& info, bool replace)
+{
+ if (!info.spawnId)
+ return;
+
+ RespawnInfoMap& bySpawnIdMap = GetRespawnMapForType(info.type);
+
+ auto it = bySpawnIdMap.find(info.spawnId);
+ if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled
+ {
+ RespawnInfo* const existing = it->second;
+ if (replace || info.respawnTime < existing->respawnTime) // delete existing in this case
+ DeleteRespawnInfo(existing);
+ else // don't delete existing, instead replace respawn time so caller saves the correct time
+ {
+ info.respawnTime = existing->respawnTime;
+ return;
+ }
+ }
+
+ // if we get to this point, we should insert the respawninfo (there either was no prior entry, or it was deleted already)
+ RespawnInfo * ri = new RespawnInfo(info);
+ ri->handle = _respawnTimes.push(ri);
+ bool success = bySpawnIdMap.emplace(ri->spawnId, ri).second;
+ ASSERT(success, "Insertion of respawn info with id (%u," UI64FMTD ") into spawn id map failed - state desync.", uint32(ri->type), ri->spawnId);
+}
+
+static void PushRespawnInfoFrom(RespawnVector& data, RespawnInfoMap const& map, uint32 zoneId)
+{
+ for (auto const& pair : map)
+ if (!zoneId || pair.second->zoneId == zoneId)
+ data.push_back(pair.second);
+}
+void Map::GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId) const
+{
+ if (types & SPAWN_TYPEMASK_CREATURE)
+ PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId, zoneId);
+ if (types & SPAWN_TYPEMASK_GAMEOBJECT)
+ PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId, zoneId);
+}
+
+RespawnInfo* Map::GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const
+{
+ RespawnInfoMap const& map = GetRespawnMapForType(type);
+ auto it = map.find(spawnId);
+ if (it == map.end())
+ return nullptr;
+ return it->second;
+}
+
+void Map::DeleteRespawnInfo() // delete everything
+{
+ for (RespawnInfo* info : _respawnTimes)
+ delete info;
+ _respawnTimes.clear();
+ _creatureRespawnTimesBySpawnId.clear();
+ _gameObjectRespawnTimesBySpawnId.clear();
+}
+
+void Map::DeleteRespawnInfo(RespawnInfo* info)
+{
+ // Delete from all relevant containers to ensure consistency
+ ASSERT(info);
+
+ // spawnid store
+ size_t const n = GetRespawnMapForType(info->type).erase(info->spawnId);
+ ASSERT(n == 1, "Respawn stores inconsistent for map %u, spawnid " UI64FMTD " (type %u)", GetId(), info->spawnId, uint32(info->type));
+
+ //respawn heap
+ _respawnTimes.erase(info->handle);
+
+ // then cleanup the object
+ delete info;
+}
+
+void Map::RemoveRespawnTime(RespawnInfo* info, bool doRespawn, CharacterDatabaseTransaction dbTrans)
+{
+ CharacterDatabasePreparedStatement* stmt;
+ switch (info->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
+ break;
+ case SPAWN_TYPE_GAMEOBJECT:
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
+ break;
+ default:
+ ASSERT(false, "Invalid respawninfo type %u for spawnid " UI64FMTD " map %u", uint32(info->type), info->spawnId, GetId());
+ return;
+ }
+ stmt->setUInt64(0, info->spawnId);
+ stmt->setUInt16(1, GetId());
+ stmt->setUInt32(2, GetInstanceId());
+ CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
+
+ if (doRespawn)
+ Respawn(info);
+ else
+ DeleteRespawnInfo(info);
+}
+
+void Map::RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn, CharacterDatabaseTransaction dbTrans)
+{
+ CharacterDatabaseTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction();
+ for (RespawnInfo* info : respawnData)
+ RemoveRespawnTime(info, doRespawn, trans);
+ if (!dbTrans)
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+void Map::ProcessRespawns()
+{
+ time_t now = time(NULL);
+ while (!_respawnTimes.empty())
+ {
+ RespawnInfo* next = _respawnTimes.top();
+ if (now < next->respawnTime) // done for this tick
+ break;
+ if (CheckRespawn(next)) // see if we're allowed to respawn
+ {
+ // ok, respawn
+ _respawnTimes.pop();
+ GetRespawnMapForType(next->type).erase(next->spawnId);
+ DoRespawn(next->type, next->spawnId, next->gridId);
+ delete next;
+ }
+ else if (!next->respawnTime) // just remove respawn entry without rescheduling
+ {
+ _respawnTimes.pop();
+ GetRespawnMapForType(next->type).erase(next->spawnId);
+ delete next;
+ }
+ else // value changed, update heap position
+ {
+ ASSERT(now < next->respawnTime); // infinite loop guard
+ _respawnTimes.decrease(next->handle);
+ }
+ }
+}
+
+void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const
+{
+ ASSERT(mode == 1);
+ ASSERT(obj->GetMap() == this);
+ SpawnObjectType type;
+ switch (obj->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ type = SPAWN_TYPE_CREATURE;
+ break;
+ case TYPEID_GAMEOBJECT:
+ type = SPAWN_TYPE_GAMEOBJECT;
+ break;
+ default:
+ return;
+ }
+
+ SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId);
+ if (!data || !(data->spawnGroupData->flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE))
+ return;
+
+ auto it = _zonePlayerCountMap.find(obj->GetZoneId());
+ if (it == _zonePlayerCountMap.end())
+ return;
+ uint32 const playerCount = it->second;
+ if (!playerCount)
+ return;
+ double const adjustFactor = sWorld->getFloatConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE) / playerCount;
+ if (adjustFactor >= 1.0) // nothing to do here
+ return;
+ uint32 const timeMinimum = sWorld->getIntConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE);
+ if (respawnDelay <= timeMinimum)
+ return;
+
+ respawnDelay = std::max<uint32>(ceil(respawnDelay * adjustFactor), timeMinimum);
+}
+
+void Map::DelayedUpdate(uint32 t_diff)
{
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
{
@@ -3681,7 +4057,7 @@ bool InstanceMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
return true;
}
-void InstanceMap::Update(const uint32 t_diff)
+void InstanceMap::Update(uint32 t_diff)
{
Map::Update(t_diff);
@@ -4123,70 +4499,71 @@ Transport* Map::GetTransport(ObjectGuid const& guid)
return go ? go->ToTransport() : nullptr;
}
-void Map::UpdateIteratorBack(Player* player)
+Creature* Map::GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const
{
- if (m_mapRefIter == player->GetMapRef())
- m_mapRefIter = m_mapRefIter->nocheck_prev();
+ auto const bounds = GetCreatureBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
+
+ std::unordered_multimap<ObjectGuid::LowType, Creature*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair)
+ {
+ return pair.second->IsAlive();
+ });
+
+ return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
}
-void Map::SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime)
+GameObject* Map::GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const
{
- if (!respawnTime)
- {
- // Delete only
- RemoveCreatureRespawnTime(dbGuid);
- return;
- }
+ auto const bounds = GetGameObjectBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
- _creatureRespawnTimes[dbGuid] = respawnTime;
+ std::unordered_multimap<ObjectGuid::LowType, GameObject*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::GameObjectBySpawnIdContainer::value_type const& pair)
+ {
+ return pair.second->isSpawned();
+ });
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN);
- stmt->setUInt64(0, dbGuid);
- stmt->setUInt64(1, uint64(respawnTime));
- stmt->setUInt16(2, GetId());
- stmt->setUInt32(3, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
}
-void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid)
+void Map::UpdateIteratorBack(Player* player)
{
- _creatureRespawnTimes.erase(dbGuid);
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
- stmt->setUInt64(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ if (m_mapRefIter == player->GetMapRef())
+ m_mapRefIter = m_mapRefIter->nocheck_prev();
}
-void Map::SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime)
+void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId, bool writeDB, bool replace, CharacterDatabaseTransaction dbTrans)
{
if (!respawnTime)
{
// Delete only
- RemoveGORespawnTime(dbGuid);
+ RemoveRespawnTime(type, spawnId, false, dbTrans);
return;
}
- _goRespawnTimes[dbGuid] = respawnTime;
+ RespawnInfo ri;
+ ri.type = type;
+ ri.spawnId = spawnId;
+ ri.entry = entry;
+ ri.respawnTime = respawnTime;
+ ri.gridId = gridId;
+ ri.zoneId = zoneId;
+ AddRespawnInfo(ri, replace);
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN);
- stmt->setUInt64(0, dbGuid);
- stmt->setUInt64(1, uint64(respawnTime));
- stmt->setUInt16(2, GetId());
- stmt->setUInt32(3, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ if (writeDB)
+ SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace
}
-void Map::RemoveGORespawnTime(ObjectGuid::LowType dbGuid)
+void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, CharacterDatabaseTransaction dbTrans)
{
- _goRespawnTimes.erase(dbGuid);
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
- stmt->setUInt64(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ // Just here for support of compatibility mode
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement((type == SPAWN_TYPE_GAMEOBJECT) ? CHAR_REP_GO_RESPAWN : CHAR_REP_CREATURE_RESPAWN);
+ stmt->setUInt64(0, spawnId);
+ stmt->setUInt64(1, uint64(respawnTime));
+ stmt->setUInt16(2, GetId());
+ stmt->setUInt32(3, GetInstanceId());
+ CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
}
void Map::LoadRespawnTimes()
@@ -4202,7 +4579,9 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt64();
uint64 respawnTime = fields[1].GetUInt64();
- _creatureRespawnTimes[loguid] = time_t(respawnTime);
+ if (CreatureData const* cdata = sObjectMgr->GetCreatureData(loguid))
+ SaveRespawnTime(SPAWN_TYPE_CREATURE, loguid, cdata->id, time_t(respawnTime), GetZoneId(PhasingHandler::GetEmptyPhaseShift(), cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
+
} while (result->NextRow());
}
@@ -4217,19 +4596,13 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt64();
uint64 respawnTime = fields[1].GetUInt64();
- _goRespawnTimes[loguid] = time_t(respawnTime);
+ if (GameObjectData const* godata = sObjectMgr->GetGameObjectData(loguid))
+ SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, loguid, godata->id, time_t(respawnTime), GetZoneId(PhasingHandler::GetEmptyPhaseShift(), godata->spawnPoint), Trinity::ComputeGridCoord(godata->spawnPoint.GetPositionX(), godata->spawnPoint.GetPositionY()).GetId(), false);
+
} while (result->NextRow());
}
}
-void Map::DeleteRespawnTimes()
-{
- _creatureRespawnTimes.clear();
- _goRespawnTimes.clear();
-
- DeleteRespawnTimesInDB(GetId(), GetInstanceId());
-}
-
void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE);