aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
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
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')
-rw-r--r--src/server/game/AI/CreatureAI.h3
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp10
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp42
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h1
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp86
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp6
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h19
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp391
-rw-r--r--src/server/game/Entities/Creature/Creature.h17
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h46
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp262
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h16
-rw-r--r--src/server/game/Entities/GameObject/GameObjectData.h30
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Object/Object.h2
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.cpp8
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h9
-rw-r--r--src/server/game/Entities/Object/Position.h21
-rw-r--r--src/server/game/Entities/Player/Player.cpp2
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp18
-rw-r--r--src/server/game/Events/GameEventMgr.cpp22
-rw-r--r--src/server/game/Garrison/Garrison.cpp2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp495
-rw-r--r--src/server/game/Globals/ObjectMgr.h59
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h21
-rw-r--r--src/server/game/Grids/ObjectGridLoader.cpp64
-rw-r--r--src/server/game/Maps/Map.cpp495
-rw-r--r--src/server/game/Maps/Map.h165
-rw-r--r--src/server/game/Maps/MapManager.h6
-rw-r--r--src/server/game/Maps/SpawnData.h82
-rw-r--r--src/server/game/Miscellaneous/Language.h26
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp11
-rw-r--r--src/server/game/Pools/PoolMgr.cpp40
-rw-r--r--src/server/game/Pools/PoolMgr.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp38
-rw-r--r--src/server/game/Scripting/ScriptMgr.h2
-rw-r--r--src/server/game/World/World.cpp124
-rw-r--r--src/server/game/World/World.h23
40 files changed, 2008 insertions, 666 deletions
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index 76eec1909d8..09f4744171e 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -180,6 +180,9 @@ class TC_GAME_API CreatureAI : public UnitAI
// If a PlayerAI* is returned, that AI is placed on the player instead of the default charm AI
// Object destruction is handled by Unit::RemoveCharmedBy
virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; }
+ // Should return true if the NPC is target of an escort quest
+ // If onlyIfActive is set, should return true only if the escort quest is currently active
+ virtual bool IsEscortNPC(bool /*onlyIfActive*/) const { return false; }
// intended for encounter design/debugging. do not use for other purposes. expensive.
int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const;
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index ff1e5591fb8..ee6d0770604 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -462,7 +462,7 @@ void BossAI::_Reset()
events.Reset();
summons.DespawnAll();
scheduler.CancelAll();
- if (instance)
+ if (instance && instance->GetBossState(_bossId) != DONE)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -548,12 +548,12 @@ bool BossAI::CanAIAttack(Unit const* target) const
return CheckBoundary(target);
}
-void BossAI::_DespawnAtEvade(uint32 delayToRespawn /*= 30*/, Creature* who /*= nullptr*/)
+void BossAI::_DespawnAtEvade(Seconds delayToRespawn, Creature* who)
{
- if (delayToRespawn < 2)
+ if (delayToRespawn < Seconds(2))
{
- TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn);
- delayToRespawn = 2;
+ TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of " SI64FMTD " seconds, defaulting to 2.", delayToRespawn.count());
+ delayToRespawn = Seconds(2);
}
if (!who)
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 084f05552c4..c255f97ae24 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -364,8 +364,8 @@ class TC_GAME_API BossAI : public ScriptedAI
void _EnterCombat();
void _JustDied();
void _JustReachedHome();
- void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr);
- void _DespawnAtEvade(Seconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); }
+ void _DespawnAtEvade(Seconds delayToRespawn, Creature* who = nullptr);
+ void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr) { _DespawnAtEvade(Seconds(delayToRespawn), who); }
void TeleportCheaters();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index fc0ed3fa9e0..02e41275b0c 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -30,6 +30,7 @@ EndScriptData */
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
+#include "World.h"
enum Points
{
@@ -238,13 +239,17 @@ void npc_escortAI::UpdateAI(uint32 diff)
return;
}
- if (m_bCanInstantRespawn)
+ if (m_bCanInstantRespawn && !sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC))
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
else
+ {
+ if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC))
+ me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true);
me->DespawnOrUnsummon();
+ }
return;
}
@@ -279,11 +284,17 @@ void npc_escortAI::UpdateAI(uint32 diff)
{
TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found");
- if (m_bCanInstantRespawn)
+ bool isEscort = false;
+ if (CreatureData const* cdata = me->GetCreatureData())
+ isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC));
+
+ if (m_bCanInstantRespawn && !isEscort)
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
+ else if (m_bCanInstantRespawn && isEscort)
+ me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true);
else
me->DespawnOrUnsummon();
@@ -423,6 +434,22 @@ void npc_escortAI::SetRun(bool on)
/// @todo get rid of this many variables passed in function.
void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
{
+ // Queue respawn from the point it starts
+ if (Map* map = me->GetMap())
+ {
+ if (CreatureData const* cdata = me->GetCreatureData())
+ {
+ if (SpawnGroupTemplateData const* groupdata = cdata->spawnGroupData)
+ {
+ if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (groupdata->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC) && !map->GetCreatureRespawnTime(me->GetSpawnId()))
+ {
+ me->SetRespawnTime(me->GetRespawnDelay());
+ me->SaveRespawnTime();
+ }
+ }
+ }
+ }
+
if (me->GetVictim())
{
TC_LOG_ERROR("scripts.escortai", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
@@ -567,3 +594,14 @@ bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float
return false;
}
+
+bool npc_escortAI::IsEscortNPC(bool onlyIfActive) const
+{
+ if (!onlyIfActive)
+ return true;
+
+ if (!GetEventStarterGUID().IsEmpty())
+ return true;
+
+ return false;
+}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index c72421ddc4b..81126b704e8 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -106,6 +106,7 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; }
+ virtual bool IsEscortNPC(bool isEscorting) const override;
protected:
Player* GetPlayerForEscort();
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 28c10607072..8b6616a2ee3 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2107,6 +2107,92 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsCreature(target))
target->ToCreature()->SetCorpseDelay(e.action.corpseDelay.timer);
}
+
+ break;
+ }
+ case SMART_ACTION_SPAWN_SPAWNGROUP:
+ {
+ if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
+ {
+ bool const ignoreRespawn = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN) != 0);
+ bool const force = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_FORCE_SPAWN) != 0);
+
+ // Instant spawn
+ sObjectMgr->SpawnGroupSpawn(e.action.groupSpawn.groupId, GetBaseObject()->GetMap(), ignoreRespawn, force);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = SmartEvent();
+ ne.type = (SMART_EVENT)SMART_EVENT_UPDATE;
+ ne.event_chance = 100;
+
+ ne.minMaxRepeat.min = e.action.groupSpawn.minDelay;
+ ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay;
+ ne.minMaxRepeat.repeatMin = 0;
+ ne.minMaxRepeat.repeatMax = 0;
+
+ ne.event_flags = 0;
+ ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+
+ SmartAction ac = SmartAction();
+ ac.type = (SMART_ACTION)SMART_ACTION_SPAWN_SPAWNGROUP;
+ ac.groupSpawn.groupId = e.action.groupSpawn.groupId;
+ ac.groupSpawn.minDelay = 0;
+ ac.groupSpawn.maxDelay = 0;
+ ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags;
+ ac.timeEvent.id = e.action.timeEvent.id;
+
+ SmartScriptHolder ev = SmartScriptHolder();
+ ev.event = ne;
+ ev.event_id = e.event_id;
+ ev.target = e.target;
+ ev.action = ac;
+ InitTimer(ev);
+ mStoredEvents.push_back(ev);
+ }
+ break;
+ }
+ case SMART_ACTION_DESPAWN_SPAWNGROUP:
+ {
+ if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
+ {
+ bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0);
+
+ // Instant spawn
+ sObjectMgr->SpawnGroupDespawn(e.action.groupSpawn.groupId, GetBaseObject()->GetMap(), deleteRespawnTimes);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = SmartEvent();
+ ne.type = (SMART_EVENT)SMART_EVENT_UPDATE;
+ ne.event_chance = 100;
+
+ ne.minMaxRepeat.min = e.action.groupSpawn.minDelay;
+ ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay;
+ ne.minMaxRepeat.repeatMin = 0;
+ ne.minMaxRepeat.repeatMax = 0;
+
+ ne.event_flags = 0;
+ ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+
+ SmartAction ac = SmartAction();
+ ac.type = (SMART_ACTION)SMART_ACTION_DESPAWN_SPAWNGROUP;
+ ac.groupSpawn.groupId = e.action.groupSpawn.groupId;
+ ac.groupSpawn.minDelay = 0;
+ ac.groupSpawn.maxDelay = 0;
+ ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags;
+ ac.timeEvent.id = e.action.timeEvent.id;
+
+ SmartScriptHolder ev = SmartScriptHolder();
+ ev.event = ne;
+ ev.event_id = e.event_id;
+ ev.target = e.target;
+ ev.action = ac;
+ InitTimer(ev);
+ mStoredEvents.push_back(ev);
+ }
break;
}
case SMART_ACTION_DISABLE_EVADE:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 40a9cdbd9ff..f1240950f1e 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -237,7 +237,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
}
case SMART_SCRIPT_TYPE_GAMEOBJECT:
{
- GameObjectData const* gameObject = sObjectMgr->GetGOData(uint64(-temp.entryOrGuid));
+ GameObjectData const* gameObject = sObjectMgr->GetGameObjectData(uint64(-temp.entryOrGuid));
if (!gameObject)
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid (" SI64FMTD ") does not exist, skipped loading.", -temp.entryOrGuid);
@@ -964,7 +964,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
}
- if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid))
+ if (e.event.distance.guid != 0 && !sObjectMgr->GetGameObjectData(e.event.distance.guid))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid);
return false;
@@ -1629,6 +1629,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
case SMART_ACTION_SET_COUNTER:
case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
+ case SMART_ACTION_SPAWN_SPAWNGROUP:
+ case SMART_ACTION_DESPAWN_SPAWNGROUP:
break;
default:
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry " SI64FMTD " SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 1bb6c6b7fdb..6285726bb33 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -603,7 +603,9 @@ enum SMART_ACTION
SMART_ACTION_PLAY_ANIMKIT = 128, // id, type (0 = oneShot, 1 = aiAnim, 2 = meleeAnim, 3 = movementAnim)
SMART_ACTION_SCENE_PLAY = 129, // sceneId
SMART_ACTION_SCENE_CANCEL = 130, // sceneId
- // 131 - 134 : 3.3.5 reserved
+ SMART_ACTION_SPAWN_SPAWNGROUP = 131, // Group ID, min secs, max secs, spawnflags
+ SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // Group ID, min secs, max secs, spawnflags
+ // 134 : 3.3.5 reserved
SMART_ACTION_PLAY_CINEMATIC = 135, // reserved for future uses
SMART_ACTION_SET_MOVEMENT_SPEED = 136, // movementType, speedInteger, speedFraction
SMART_ACTION_PLAY_SPELL_VISUAL_KIT = 137, // spellVisualKitId, kitType (unknown values, copypaste from packet dumps), duration
@@ -1119,6 +1121,13 @@ struct SmartAction
{
uint32 disable;
} disableEvade;
+ struct
+ {
+ uint32 groupId;
+ uint32 minDelay;
+ uint32 maxDelay;
+ uint32 spawnflags;
+ } groupSpawn;
struct
{
@@ -1188,6 +1197,14 @@ struct SmartAction
};
};
+enum SMARTAI_SPAWN_FLAGS
+{
+ SMARTAI_SPAWN_FLAG_NONE = 0x00,
+ SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN = 0x01,
+ SMARTAI_SPAWN_FLAG_FORCE_SPAWN = 0x02,
+ SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN = 0x04,
+};
+
enum SMARTAI_TEMPLATE
{
SMARTAI_TEMPLATE_BASIC = 0, //nothing is preset
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index cbc704b38fa..1e411d9d56c 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -2135,7 +2135,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
}
if (cond->ConditionValue3)
{
- if (GameObjectData const* goData = sObjectMgr->GetGOData(cond->ConditionValue3))
+ if (GameObjectData const* goData = sObjectMgr->GetGameObjectData(cond->ConditionValue3))
{
if (cond->ConditionValue2 && goData->id != cond->ConditionValue2)
{
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 17d3be260ee..93489d5516b 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -287,7 +287,7 @@ _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0),
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
-m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f),
+m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_respawnCompatibilityMode(false), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f),
_lastDamagedTime(0)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
@@ -376,44 +376,60 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers)
if (getDeathState() != CORPSE)
return;
- m_corpseRemoveTime = time(nullptr);
- setDeathState(DEAD);
- RemoveAllAuras();
- DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
- loot.clear();
- uint32 respawnDelay = m_respawnDelay;
- if (IsAIEnabled)
- AI()->CorpseRemoved(respawnDelay);
+ if (m_respawnCompatibilityMode)
+ {
+ m_corpseRemoveTime = time(nullptr);
+ setDeathState(DEAD);
+ RemoveAllAuras();
+ loot.clear();
+ uint32 respawnDelay = m_respawnDelay;
+ if (IsAIEnabled)
+ AI()->CorpseRemoved(respawnDelay);
- if (destroyForNearbyPlayers)
- DestroyForNearbyPlayers();
+ if (destroyForNearbyPlayers)
+ DestroyForNearbyPlayers();
- // Should get removed later, just keep "compatibility" with scripts
- if (setSpawnTime)
- m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime);
+ // Should get removed later, just keep "compatibility" with scripts
+ if (setSpawnTime)
+ m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime);
- // if corpse was removed during falling, the falling will continue and override relocation to respawn position
- if (IsFalling())
- StopMoving();
+ // if corpse was removed during falling, the falling will continue and override relocation to respawn position
+ if (IsFalling())
+ StopMoving();
- float x, y, z, o;
- GetRespawnPosition(x, y, z, &o);
+ float x, y, z, o;
+ GetRespawnPosition(x, y, z, &o);
- // We were spawned on transport, calculate real position
- if (IsSpawnedOnTransport())
- {
- Position& pos = m_movementInfo.transport.pos;
- pos.m_positionX = x;
- pos.m_positionY = y;
- pos.m_positionZ = z;
- pos.SetOrientation(o);
+ // We were spawned on transport, calculate real position
+ if (IsSpawnedOnTransport())
+ {
+ Position& pos = m_movementInfo.transport.pos;
+ pos.m_positionX = x;
+ pos.m_positionY = y;
+ pos.m_positionZ = z;
+ pos.SetOrientation(o);
+
+ if (TransportBase* transport = GetDirectTransport())
+ transport->CalculatePassengerPosition(x, y, z, &o);
+ }
- if (TransportBase* transport = GetDirectTransport())
- transport->CalculatePassengerPosition(x, y, z, &o);
+ SetHomePosition(x, y, z, o);
+ GetMap()->CreatureRelocation(this, x, y, z, o);
}
+ else
+ {
+ // In case this is called directly and normal respawn timer not set
+ // Since this timer will be longer than the already present time it
+ // will be ignored if the correct place added a respawn timer
+ if (setSpawnTime)
+ {
+ uint32 respawnDelay = m_respawnDelay;
+ m_respawnTime = std::max<time_t>(time(NULL) + respawnDelay, m_respawnTime);
- SetHomePosition(x, y, z, o);
- GetMap()->CreatureRelocation(this, x, y, z, o);
+ SaveRespawnTime(0, false);
+ }
+ AddObjectToRemoveList();
+ }
}
/**
@@ -635,7 +651,7 @@ void Creature::Update(uint32 diff)
break;
case JUST_DIED:
// Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
- TC_LOG_ERROR("entities.unit", "Creature (%s) in wrong state: JUST_DEAD (1)", GetGUID().ToString().c_str());
+ TC_LOG_ERROR("entities.unit", "Creature (%s) in wrong state: JUST_DIED (1)", GetGUID().ToString().c_str());
break;
case DEAD:
{
@@ -643,22 +659,25 @@ void Creature::Update(uint32 diff)
if (m_respawnTime <= now)
{
// First check if there are any scripts that object to us respawning
- if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap()))
- break; // Will be rechecked on next Update call
+ if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
+ {
+ m_respawnTime = now + urand(4,7);
+ break; // Will be rechecked on next Update call after delay expires
+ }
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Creature>(GetMapId(), GetEntry(), m_spawnId);
- time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
- if (!linkedRespawntime) // Can respawn
+ time_t linkedRespawnTime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
+ if (!linkedRespawnTime) // Can respawn
Respawn();
else // the master is dead
{
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
- if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
- SetRespawnTime(DAY);
+ if (targetGuid == dbtableHighGuid) // if linking self, never respawn
+ SetRespawnTime(WEEK);
else
{
// else copy time from master and add a little
- time_t baseRespawnTime = std::max(linkedRespawntime, now);
+ time_t baseRespawnTime = std::max(linkedRespawnTime, now);
time_t const offset = urand(5, MINUTE);
// linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance
@@ -1008,7 +1027,7 @@ void Creature::Motion_Initialize()
GetMotionMaster()->Initialize();
}
-bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float x, float y, float z, float ang, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/)
+bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Position const& pos, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/, bool dynamic)
{
ASSERT(map);
SetMap(map);
@@ -1019,6 +1038,10 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
PhasingHandler::InitDbVisibleMapId(GetPhaseShift(), data->terrainSwapMap);
}
+ // Set if this creature can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
@@ -1028,13 +1051,13 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
//! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
- Relocate(x, y, z, ang);
+ Relocate(pos);
// Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
// invalid position, triggering a crash about Auras not removed in the destructor
if (!IsPositionValid())
{
- TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow " UI64FMTD ", entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, x, y, z, ang);
+ TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow " UI64FMTD ", entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
return false;
}
UpdatePositionData();
@@ -1074,10 +1097,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
if (HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
{
- z += m_unitData->HoverHeight;
-
//! Relocate again with updated Z coord
- Relocate(x, y, z, ang);
+ m_positionZ += m_unitData->HoverHeight;
}
LastUsedScriptID = GetScriptId();
@@ -1113,7 +1134,7 @@ Creature* Creature::CreateCreature(uint32 entry, Map* map, Position const& pos,
lowGuid = map->GenerateLowGuid<HighGuid::Creature>();
Creature* creature = new Creature();
- if (!creature->Create(lowGuid, map, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId))
+ if (!creature->Create(lowGuid, map, entry, pos, nullptr, vehId))
{
delete creature;
return nullptr;
@@ -1125,7 +1146,7 @@ Creature* Creature::CreateCreature(uint32 entry, Map* map, Position const& pos,
Creature* Creature::CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap /*= true*/, bool allowDuplicate /*= false*/)
{
Creature* creature = new Creature();
- if (!creature->LoadCreatureFromDB(spawnId, map, addToMap, allowDuplicate))
+ if (!creature->LoadFromDB(spawnId, map, addToMap, allowDuplicate))
{
delete creature;
return nullptr;
@@ -1300,26 +1321,16 @@ void Creature::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiffic
dynamicflags = 0;
}
- // data->guid = guid must not be updated at save
+ if (!data.spawnId)
+ data.spawnId = m_spawnId;
+ ASSERT(data.spawnId == m_spawnId);
data.id = GetEntry();
- data.mapid = mapid;
data.displayid = displayId;
data.equipmentId = GetCurrentEquipmentId();
if (!GetTransport())
- {
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZMinusOffset();
- data.orientation = GetOrientation();
- }
+ data.spawnPoint.WorldRelocate(this);
else
- {
- data.posX = GetTransOffsetX();
- data.posY = GetTransOffsetY();
- data.posZ = GetTransOffsetZ();
- data.orientation = GetTransOffsetO();
- }
-
+ data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO());
data.spawntimesecs = m_respawnDelay;
// prevent add data integrity problems
data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius;
@@ -1335,6 +1346,8 @@ void Creature::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiffic
data.unit_flags2 = unitFlags2;
data.unit_flags3 = unitFlags3;
data.dynamicflags = dynamicflags;
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? -GetDBPhase() : data.phaseGroup;
@@ -1580,7 +1593,7 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu
return true;
}
-bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
+bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
{
if (!allowDuplicate)
{
@@ -1620,31 +1633,41 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
}
m_spawnId = spawnId;
+
+ m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0);
m_creatureData = data;
m_respawnradius = data->spawndist;
m_respawnDelay = data->spawntimesecs;
- if (!Create(map->GenerateLowGuid<HighGuid::Creature>(), map, data->id, data->posX, data->posY, data->posZ, data->orientation, data, 0))
+
+ // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
+ if (!m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, data->id, data, map))
+ {
+ SaveRespawnTime(urand(4,7));
+ return false;
+ }
+
+ if (!Create(map->GenerateLowGuid<HighGuid::Creature>(), map, data->id, data->spawnPoint, data, 0U , !m_respawnCompatibilityMode))
return false;
//We should set first home position, because then AI calls home movement
- SetHomePosition(data->posX, data->posY, data->posZ, data->orientation);
+ SetHomePosition(data->spawnPoint);
m_deathState = ALIVE;
m_respawnTime = GetMap()->GetCreatureRespawnTime(m_spawnId);
- // Is the creature script objecting to us spawning? If yes, delay by one second (then re-check in ::Update)
- if (!m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map))
- m_respawnTime = time(nullptr)+1;
+ // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
+ if (m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
+ m_respawnTime = time(nullptr)+urand(4,7);
if (m_respawnTime) // respawn on Update
{
m_deathState = DEAD;
if (CanFly())
{
- float tz = map->GetHeight(GetPhaseShift(), data->posX, data->posY, data->posZ, true, MAX_FALL_DISTANCE);
- if (data->posZ - tz > 0.1f && Trinity::IsValidMapCoord(tz))
- Relocate(data->posX, data->posY, tz);
+ float tz = map->GetHeight(GetPhaseShift(), data->spawnPoint, true, MAX_FALL_DISTANCE);
+ if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz))
+ Relocate(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY(), tz);
}
}
@@ -1653,7 +1676,7 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
// checked at creature_template loading
m_defaultMovementType = MovementGeneratorType(data->movementType);
- loot.SetGUID(ObjectGuid::Create<HighGuid::LootObject>(data->mapid, data->id, GetMap()->GenerateLowGuid<HighGuid::LootObject>()));
+ loot.SetGUID(ObjectGuid::Create<HighGuid::LootObject>(GetMapId(), data->id, GetMap()->GenerateLowGuid<HighGuid::LootObject>()));
if (addToMap && !GetMap()->AddToMap(this))
return false;
@@ -1742,7 +1765,7 @@ void Creature::DeleteFromDB()
return;
}
- GetMap()->RemoveCreatureRespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
sObjectMgr->DeleteCreatureData(m_spawnId);
WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
@@ -1751,6 +1774,11 @@ void Creature::DeleteFromDB()
stmt->setUInt64(0, m_spawnId);
trans->Append(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_CREATURE));
+ stmt->setUInt64(1, m_spawnId);
+ trans->Append(stmt);
+
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON);
stmt->setUInt64(0, m_spawnId);
trans->Append(stmt);
@@ -1897,14 +1925,31 @@ void Creature::setDeathState(DeathState s)
if (s == JUST_DIED)
{
m_corpseRemoveTime = time(nullptr) + m_corpseDelay;
- if (IsDungeonBoss() && !m_respawnDelay)
- m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+
+ uint32 respawnDelay = m_respawnDelay;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
+ // @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances
+ if (m_respawnCompatibilityMode)
+ {
+ if (IsDungeonBoss() && !m_respawnDelay)
+ m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+ else
+ m_respawnTime = time(nullptr) + respawnDelay + m_corpseDelay;
+ }
else
- m_respawnTime = time(nullptr) + m_respawnDelay + m_corpseDelay;
+ {
+ if (IsDungeonBoss() && !m_respawnDelay)
+ m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+ else
+ m_respawnTime = time(nullptr) + respawnDelay;
+ }
// always save boss respawn time at death to prevent crash cheating
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss())
SaveRespawnTime();
+ else if (!m_respawnCompatibilityMode)
+ SaveRespawnTime(0, false);
ReleaseFocus(nullptr, false); // remove spellcast focus
DoNotReacquireTarget(); // cancel delayed re-target
@@ -1980,8 +2025,6 @@ void Creature::setDeathState(DeathState s)
void Creature::Respawn(bool force)
{
- DestroyForNearbyPlayers();
-
if (force)
{
if (IsAlive())
@@ -1990,51 +2033,62 @@ void Creature::Respawn(bool force)
setDeathState(CORPSE);
}
- RemoveCorpse(false, false);
-
- if (getDeathState() == DEAD)
+ if (m_respawnCompatibilityMode)
{
- if (m_spawnId)
- GetMap()->RemoveCreatureRespawnTime(m_spawnId);
+ DestroyForNearbyPlayers();
+ RemoveCorpse(false, false);
- TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
- GetName().c_str(), GetGUID().ToString().c_str());
- m_respawnTime = 0;
- ResetPickPocketRefillTimer();
- loot.clear();
+ if (getDeathState() == DEAD)
+ {
+ if (m_spawnId)
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
+
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str());
+ m_respawnTime = 0;
+ ResetPickPocketRefillTimer();
+ loot.clear();
+
+ if (m_originalEntry != GetEntry())
+ UpdateEntry(m_originalEntry);
- if (m_originalEntry != GetEntry())
- UpdateEntry(m_originalEntry);
- else
SelectLevel();
- setDeathState(JUST_RESPAWNED);
+ setDeathState(JUST_RESPAWNED);
- CreatureModel display(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
- if (sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate()))
- {
- SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
- SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
- }
+ CreatureModel display(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
+ if (sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate()))
+ {
+ SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
+ SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
+ }
- GetMotionMaster()->InitDefault();
- //Re-initialize reactstate that could be altered by movementgenerators
- InitializeReactState();
+ GetMotionMaster()->InitDefault();
+ //Re-initialize reactstate that could be altered by movementgenerators
+ InitializeReactState();
- //Call AI respawn virtual function
- if (IsAIEnabled)
- {
- //reset the AI to be sure no dirty or uninitialized values will be used till next tick
- AI()->Reset();
- m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
- }
+ //Call AI respawn virtual function//Call AI respawn virtual function
+ if (IsAIEnabled)
+ {
+ //reset the AI to be sure no dirty or uninitialized values will be used till next tick
+ AI()->Reset();
+ m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
+ }
- uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
- if (poolid)
- sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
+ uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
+ if (poolid)
+ sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
+ }
+ UpdateObjectVisibility();
+ }
+ else
+ {
+ if (m_spawnId)
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, true);
}
- UpdateObjectVisibility();
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
+ GetName().c_str(), GetGUID().ToString().c_str());
+
}
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawnTimer)
@@ -2045,30 +2099,48 @@ void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawn
return;
}
- uint32 corpseDelay = GetCorpseDelay();
- uint32 respawnDelay = GetRespawnDelay();
+ if (m_respawnCompatibilityMode)
+ {
+ uint32 corpseDelay = GetCorpseDelay();
+ uint32 respawnDelay = GetRespawnDelay();
+
+ // do it before killing creature
+ DestroyForNearbyPlayers();
+
+ bool overrideRespawnTime = false;
+ if (IsAlive())
+ {
+ if (forceRespawnTimer > Seconds::zero())
+ {
+ SetCorpseDelay(0);
+ SetRespawnDelay(forceRespawnTimer.count());
+ overrideRespawnTime = true;
+ }
+
+ setDeathState(JUST_DIED);
+ }
- // do it before killing creature
- DestroyForNearbyPlayers();
+ // Skip corpse decay time
+ RemoveCorpse(!overrideRespawnTime, false);
- bool overrideRespawnTime = false;
- if (IsAlive())
+ SetCorpseDelay(corpseDelay);
+ SetRespawnDelay(respawnDelay);
+ }
+ else
{
if (forceRespawnTimer > Seconds::zero())
+ SaveRespawnTime(forceRespawnTimer.count());
+ else
{
- SetCorpseDelay(0);
- SetRespawnDelay(forceRespawnTimer.count());
- overrideRespawnTime = false;
+ uint32 respawnDelay = m_respawnDelay;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
+ m_respawnTime = time(NULL) + respawnDelay;
+ SaveRespawnTime();
}
- setDeathState(JUST_DIED);
+ AddObjectToRemoveList();
}
-
- // Skip corpse decay time
- RemoveCorpse(overrideRespawnTime, false);
-
- SetCorpseDelay(corpseDelay);
- SetRespawnDelay(respawnDelay);
}
void Creature::DespawnOrUnsummon(uint32 msTimeToDespawn /*= 0*/, Seconds const& forceRespawnTimer /*= 0*/)
@@ -2429,12 +2501,19 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const
return false;
}
-void Creature::SaveRespawnTime()
+void Creature::SaveRespawnTime(uint32 forceDelay, bool savetodb)
{
if (IsSummon() || !m_spawnId || (m_creatureData && !m_creatureData->dbData))
return;
- GetMap()->SaveCreatureRespawnTime(m_spawnId, m_respawnTime);
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_CREATURE, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ uint32 thisRespawnTime = forceDelay ? time(NULL) + forceDelay : m_respawnTime;
+ GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, GetMap()->GetZoneId(GetPhaseShift(), GetHomePosition()), Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), m_creatureData->dbData && savetodb);
}
// this should not be called by petAI or
@@ -2644,32 +2723,26 @@ time_t Creature::GetRespawnTimeEx() const
void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, float* dist) const
{
- // for npcs on transport, this will return transport offset
- if (m_spawnId)
+ if (m_creatureData)
{
- if (CreatureData const* data = sObjectMgr->GetCreatureData(GetSpawnId()))
- {
- x = data->posX;
- y = data->posY;
- z = data->posZ;
- if (ori)
- *ori = data->orientation;
- if (dist)
- *dist = data->spawndist;
-
- return;
- }
- }
+ if (ori)
+ m_creatureData->spawnPoint.GetPosition(x, y, z, *ori);
+ else
+ m_creatureData->spawnPoint.GetPosition(x, y, z);
- // changed this from current position to home position, fixes world summons with infinite duration (wg npcs for example)
- Position homePos = GetHomePosition();
- x = homePos.GetPositionX();
- y = homePos.GetPositionY();
- z = homePos.GetPositionZ();
- if (ori)
- *ori = homePos.GetOrientation();
- if (dist)
- *dist = 0;
+ if (dist)
+ *dist = m_creatureData->spawndist;
+ }
+ else
+ {
+ Position const& homePos = GetHomePosition();
+ if (ori)
+ homePos.GetPosition(x, y, z, *ori);
+ else
+ homePos.GetPosition(x, y, z);
+ if (dist)
+ *dist = 0;
+ }
}
void Creature::AllLootRemovedFromCorpse()
@@ -2812,7 +2885,7 @@ std::string Creature::GetScriptName() const
uint32 Creature::GetScriptId() const
{
if (CreatureData const* creatureData = GetCreatureData())
- if (uint32 scriptId = creatureData->ScriptId)
+ if (uint32 scriptId = creatureData->scriptId)
return scriptId;
return sObjectMgr->GetCreatureTemplate(GetEntry())->ScriptID;
@@ -3245,3 +3318,11 @@ bool Creature::CanGiveExperience() const
&& !IsTotem()
&& !(GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL);
}
+
+bool Creature::IsEscortNPC(bool onlyIfActive)
+{
+ if (!IsAIEnabled)
+ return false;
+
+ return AI()->IsEscortNPC(onlyIfActive);
+}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index b897454e6ad..a693b00d026 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -73,7 +73,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void DisappearAndDie();
- bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, float x, float y, float z, float ang, CreatureData const* data, uint32 vehId);
+ bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Position const& pos, CreatureData const* data, uint32 vehId, bool dynamic = false);
static Creature* CreateCreature(uint32 entry, Map* map, Position const& pos, uint32 vehId = 0);
static Creature* CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false);
@@ -88,7 +88,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void Update(uint32 time) override; // overwrited Unit::Update
void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr, float* dist = nullptr) const;
- bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->mapid != GetMapId(); }
+ bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->spawnPoint.GetMapId() != GetMapId(); }
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
uint32 GetCorpseDelay() const { return m_corpseDelay; }
@@ -181,10 +181,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void setDeathState(DeathState s) override; // override virtual Unit::setDeathState
- bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadCreatureFromDB(spawnId, map, false, false); }
- private:
- bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate);
- public:
+ bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate);
void SaveToDB();
// overriden in Pet
virtual void SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties);
@@ -246,7 +243,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
time_t GetRespawnTimeEx() const;
void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(nullptr) + respawn : 0; }
void Respawn(bool force = false);
- void SaveRespawnTime() override;
+ void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override;
uint32 GetRespawnDelay() const { return m_respawnDelay; }
void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
@@ -314,6 +311,10 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
uint32 GetOriginalEntry() const { return m_originalEntry; }
void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; }
+ // There's many places not ready for dynamic spawns. This allows them to live on for now.
+ void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
+ bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
+
static float _GetDamageMod(int32 Rank);
float m_SightDistance, m_CombatDistance;
@@ -337,6 +338,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
CreatureTextRepeatIds GetTextRepeatGroup(uint8 textGroup);
void SetTextRepeatId(uint8 textGroup, uint8 id);
void ClearTextRepeatGroup(uint8 textGroup);
+ bool IsEscortNPC(bool onlyIfActive = true);
bool CanGiveExperience() const;
@@ -405,6 +407,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
//Formation var
CreatureGroup* m_formation;
bool m_TriggerJustRespawned;
+ bool m_respawnCompatibilityMode;
/* Spell focus system */
Spell const* m_focusSpell; // Locks the target during spell cast for proper facing
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 3f86b49f0aa..0fc310f1fce 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -21,11 +21,11 @@
#include "DBCEnums.h"
#include "Optional.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "UnitDefines.h"
#include "WorldPacket.h"
#include <string>
#include <unordered_map>
-#include <vector>
#include <cmath>
struct ItemTemplate;
@@ -519,39 +519,21 @@ struct EquipmentInfo
};
// from `creature` table
-struct CreatureData
+struct CreatureData : public SpawnData
{
- CreatureData() : id(0), mapid(0), displayid(0), equipmentId(0),
- posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
- spawndist(0.0f), currentwaypoint(0), curhealth(0), curmana(0), movementType(0),
- spawnDifficulties(), npcflag(0), unit_flags(0), unit_flags2(0), unit_flags3(0), dynamicflags(0),
- phaseUseFlags(0), phaseId(0), phaseGroup(0), terrainSwapMap(-1), ScriptId(0), dbData(true) { }
- uint32 id; // entry in creature_template
- uint16 mapid;
- uint32 displayid;
- int8 equipmentId;
- float posX;
- float posY;
- float posZ;
- float orientation;
- uint32 spawntimesecs;
- float spawndist;
- uint32 currentwaypoint;
- uint32 curhealth;
- uint32 curmana;
- uint8 movementType;
- std::vector<Difficulty> spawnDifficulties;
+ CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) { }
+ uint32 displayid = 0;
+ int8 equipmentId = 0;
+ float spawndist = 0.0f;
+ uint32 currentwaypoint = 0;
+ uint32 curhealth = 0;
+ uint32 curmana = 0;
+ uint8 movementType = 0;
uint64 npcflag;
- uint32 unit_flags; // enum UnitFlags mask values
- uint32 unit_flags2; // enum UnitFlags2 mask values
- uint32 unit_flags3; // enum UnitFlags3 mask values
- uint32 dynamicflags;
- uint8 phaseUseFlags;
- uint32 phaseId;
- uint32 phaseGroup;
- int32 terrainSwapMap;
- uint32 ScriptId;
- bool dbData;
+ uint32 unit_flags = 0; // enum UnitFlags mask values
+ uint32 unit_flags2 = 0; // enum UnitFlags2 mask values
+ uint32 unit_flags3 = 0; // enum UnitFlags3 mask values
+ uint32 dynamicflags = 0;
};
struct CreatureModelInfo
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 921fc20f5e7..3fc26262ede 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -107,7 +107,7 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X)
}
GameObject::GameObject() : WorldObject(false), MapObject(),
- m_model(nullptr), m_goValue(), m_AI(nullptr), _animKitId(0), _worldEffectID(0)
+ m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false), _animKitId(0), _worldEffectID(0)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
@@ -245,7 +245,7 @@ void GameObject::RemoveFromWorld()
}
}
-bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit)
+bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid)
{
ASSERT(map);
SetMap(map);
@@ -258,6 +258,10 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD
return false;
}
+ // Set if this object can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
UpdatePositionData();
SetZoneScript();
@@ -442,6 +446,9 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD
if (map->Is25ManRaid())
loot.maxDuplicates = 3;
+ if (spawnid)
+ m_spawnId = spawnid;
+
if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry())
{
if (GameObject* linkedGo = GameObject::CreateGameObject(linkedEntry, map, pos, rotation, 255, GO_STATE_READY))
@@ -474,7 +481,7 @@ GameObject* GameObject::CreateGameObject(uint32 entry, Map* map, Position const&
return nullptr;
GameObject* go = new GameObject();
- if (!go->Create(entry, map, pos, rotation, animProgress, goState, artKit))
+ if (!go->Create(entry, map, pos, rotation, animProgress, goState, artKit, false, 0))
{
delete go;
return nullptr;
@@ -486,7 +493,7 @@ GameObject* GameObject::CreateGameObject(uint32 entry, Map* map, Position const&
GameObject* GameObject::CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap /*= true*/)
{
GameObject* go = new GameObject();
- if (!go->LoadGameObjectFromDB(spawnId, map, addToMap))
+ if (!go->LoadFromDB(spawnId, map, addToMap))
{
delete go;
return nullptr;
@@ -603,81 +610,89 @@ void GameObject::Update(uint32 diff)
/* fallthrough */
case GO_READY:
{
- if (m_respawnTime > 0) // timer on
+ if (m_respawnCompatibilityMode)
{
- time_t now = time(nullptr);
- if (m_respawnTime <= now) // timer expired
+ if (m_respawnTime > 0) // timer on
{
- ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::GameObject>(GetMapId(), GetEntry(), m_spawnId);
- time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
- if (linkedRespawntime) // Can't respawn, the master is dead
+ time_t now = time(nullptr);
+ if (m_respawnTime <= now) // timer expired
{
- ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
- if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
- SetRespawnTime(DAY);
- else
- m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
- SaveRespawnTime(); // also save to DB immediately
- return;
- }
+ ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::GameObject>(GetMapId(), GetEntry(), m_spawnId);
+ time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
+ if (linkedRespawntime) // Can't respawn, the master is dead
+ {
+ ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
+ if (targetGuid == dbtableHighGuid) // if linking self, never respawn
+ SetRespawnTime(WEEK);
+ else
+ m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
+ SaveRespawnTime(); // also save to DB immediately
+ return;
+ }
- m_respawnTime = 0;
- m_SkillupList.clear();
- m_usetimes = 0;
+ m_respawnTime = 0;
+ m_SkillupList.clear();
+ m_usetimes = 0;
- // If nearby linked trap exists, respawn it
- if (GameObject* linkedTrap = GetLinkedTrap())
- linkedTrap->SetLootState(GO_READY);
+ // If nearby linked trap exists, respawn it
+ if (GameObject* linkedTrap = GetLinkedTrap())
+ linkedTrap->SetLootState(GO_READY);
- switch (GetGoType())
- {
- case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
+ switch (GetGoType())
{
- Unit* caster = GetOwner();
- if (caster && caster->GetTypeId() == TYPEID_PLAYER)
+ case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
{
- caster->ToPlayer()->RemoveGameObject(this, false);
- caster->ToPlayer()->SendDirectMessage(WorldPackets::GameObject::FishEscaped().Write());
+ Unit* caster = GetOwner();
+ if (caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ caster->ToPlayer()->RemoveGameObject(this, false);
+
+ caster->ToPlayer()->SendDirectMessage(WorldPackets::GameObject::FishEscaped().Write());
+ }
+ // can be delete
+ m_lootState = GO_JUST_DEACTIVATED;
+ return;
}
- // can be delete
- m_lootState = GO_JUST_DEACTIVATED;
- return;
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
+ if (GetGoState() != GO_STATE_READY)
+ ResetDoorOrButton();
+ break;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ // Initialize a new max fish count on respawn
+ m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock);
+ break;
+ default:
+ break;
}
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
- if (GetGoState() != GO_STATE_READY)
- ResetDoorOrButton();
- break;
- case GAMEOBJECT_TYPE_FISHINGHOLE:
- // Initialize a new max fish count on respawn
- m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock);
- break;
- default:
- break;
- }
- // Despawn timer
- if (!m_spawnedByDefault)
- {
- // Can be despawned or destroyed
- SetLootState(GO_JUST_DEACTIVATED);
- return;
- }
+ // Despawn timer
+ if (!m_spawnedByDefault)
+ {
+ // Can be despawned or destroyed
+ SetLootState(GO_JUST_DEACTIVATED);
+ return;
+ }
- // Call AI Reset (required for example in SmartAI to clear one time events)
- if (AI())
- AI()->Reset();
+ // Call AI Reset (required for example in SmartAI to clear one time events)
+ if (AI())
+ AI()->Reset();
- // Respawn timer
- uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
- if (poolid)
- sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId());
- else
- GetMap()->AddToMap(this);
+ // Respawn timer
+ uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
+ if (poolid)
+ sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId());
+ else
+ GetMap()->AddToMap(this);
+ }
}
}
+ // Set respawn timer
+ if (!m_respawnCompatibilityMode && m_respawnTime > 0)
+ SaveRespawnTime(0, false);
+
if (isSpawned())
{
GameObjectTemplate const* goInfo = GetGOInfo();
@@ -868,6 +883,7 @@ void GameObject::Update(uint32 diff)
if (!m_respawnDelayTime)
return;
+ // ToDo: Decide if we should properly despawn these. Maybe they expect to be able to manually respawn from script?
if (!m_spawnedByDefault)
{
m_respawnTime = 0;
@@ -875,12 +891,28 @@ void GameObject::Update(uint32 diff)
return;
}
- m_respawnTime = time(nullptr) + m_respawnDelayTime;
+ uint32 respawnDelay = m_respawnDelayTime;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, this->m_spawnId, respawnDelay, scalingMode);
+ m_respawnTime = time(nullptr) + respawnDelay;
// if option not set then object will be saved at grid unload
+ // Otherwise just save respawn time to map object memory
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
SaveRespawnTime();
+ if (!m_respawnCompatibilityMode)
+ {
+ // Respawn time was just saved if set to save to DB
+ // If not, we save only to map memory
+ if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
+ SaveRespawnTime(0, false);
+
+ // Then despawn
+ AddObjectToRemoveList();
+ return;
+ }
+
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
break;
@@ -974,7 +1006,7 @@ void GameObject::SaveToDB()
{
// this should only be used when the gameobject has already been loaded
// preferably after adding to map, because mapid may not be valid otherwise
- GameObjectData const* data = sObjectMgr->GetGOData(m_spawnId);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(m_spawnId);
if (!data)
{
TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
@@ -995,21 +1027,21 @@ void GameObject::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiff
m_spawnId = sObjectMgr->GenerateGameObjectSpawnId();
// update in loaded data (changing data only in this place)
- GameObjectData& data = sObjectMgr->NewGOData(m_spawnId);
+ GameObjectData& data = sObjectMgr->NewOrExistGameObjectData(m_spawnId);
- // data->guid = guid must not be updated at save
+ if (!data.spawnId)
+ data.spawnId = m_spawnId;
+ ASSERT(data.spawnId == m_spawnId);
data.id = GetEntry();
- data.mapid = mapid;
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZ();
- data.orientation = GetOrientation();
+ data.spawnPoint.WorldRelocate(this);
data.rotation = m_worldRotation;
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
- data.go_state = GetGoState();
+ data.goState = GetGoState();
data.spawnDifficulties = spawnDifficulties;
data.artKit = GetGoArtKit();
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? -GetDBPhase() : data.phaseGroup;
@@ -1059,9 +1091,9 @@ void GameObject::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDiff
WorldDatabase.CommitTransaction(trans);
}
-bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap)
+bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool)
{
- GameObjectData const* data = sObjectMgr->GetGOData(spawnId);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
if (!data)
{
TC_LOG_ERROR("sql.sql", "Gameobject (GUID: " UI64FMTD ") not found in table `gameobject`, can't load. ", spawnId);
@@ -1070,14 +1102,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
- Position pos(data->posX, data->posY, data->posZ, data->orientation);
uint32 animprogress = data->animprogress;
- GOState go_state = data->go_state;
+ GOState go_state = data->goState;
uint32 artKit = data->artKit;
m_spawnId = spawnId;
- if (!Create(entry, map, pos, data->rotation, animprogress, go_state, artKit))
+ m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0);
+ if (!Create(entry, map, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId))
return false;
PhasingHandler::InitDbPhaseShift(GetPhaseShift(), data->phaseUseFlags, data->phaseId, data->phaseGroup);
@@ -1102,7 +1134,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
if (m_respawnTime && m_respawnTime <= time(nullptr))
{
m_respawnTime = 0;
- GetMap()->RemoveGORespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId);
}
}
}
@@ -1123,20 +1155,25 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
void GameObject::DeleteFromDB()
{
- GetMap()->RemoveGORespawnTime(m_spawnId);
- sObjectMgr->DeleteGOData(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId);
+ sObjectMgr->DeleteGameObjectData(m_spawnId);
- WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
+ WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
+ WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt64(0, m_spawnId);
+ trans->Append(stmt);
- WorldDatabase.Execute(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_GAMEOBJECT));
+ stmt->setUInt64(1, m_spawnId);
+ trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
-
stmt->setUInt64(0, m_spawnId);
+ trans->Append(stmt);
- WorldDatabase.Execute(stmt);
+ WorldDatabase.CommitTransaction(trans);
}
/*********************************************************/
@@ -1199,10 +1236,19 @@ Unit* GameObject::GetOwner() const
return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
}
-void GameObject::SaveRespawnTime()
+void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb)
{
- if (m_goData && m_goData->dbData && m_respawnTime > time(nullptr) && m_spawnedByDefault)
- GetMap()->SaveGORespawnTime(m_spawnId, m_respawnTime);
+ if (m_goData && m_respawnTime > time(nullptr) && m_spawnedByDefault)
+ {
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_GAMEOBJECT, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ uint32 thisRespawnTime = forceDelay ? time(nullptr) + forceDelay : m_respawnTime;
+ GetMap()->SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, GetEntry(), thisRespawnTime, GetZoneId(), Trinity::ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData->dbData ? savetodb : false);
+ }
}
bool GameObject::IsNeverVisibleFor(WorldObject const* seer) const
@@ -1270,7 +1316,7 @@ void GameObject::Respawn()
if (m_spawnedByDefault && m_respawnTime > 0)
{
m_respawnTime = time(nullptr);
- GetMap()->RemoveGORespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, true);
}
}
@@ -1374,7 +1420,7 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f
void GameObject::SetGoArtKit(uint8 kit)
{
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::ArtKit), kit);
- GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGOData(m_spawnId));
+ GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId));
if (data)
data->artKit = kit;
}
@@ -1385,10 +1431,10 @@ void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType l
if (go)
{
go->SetGoArtKit(artkit);
- data = go->GetGOData();
+ data = go->GetGameObjectData();
}
else if (lowguid)
- data = sObjectMgr->GetGOData(lowguid);
+ data = sObjectMgr->GetGameObjectData(lowguid);
if (data)
const_cast<GameObjectData*>(data)->artKit = artkit;
@@ -2177,8 +2223,8 @@ void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/)
uint32 GameObject::GetScriptId() const
{
- if (GameObjectData const* gameObjectData = GetGOData())
- if (uint32 scriptId = gameObjectData->ScriptId)
+ if (GameObjectData const* gameObjectData = GetGameObjectData())
+ if (uint32 scriptId = gameObjectData->scriptId)
return scriptId;
return GetGOInfo()->ScriptId;
@@ -2636,24 +2682,20 @@ void GameObject::ClearUpdateMask(bool remove)
void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const
{
- if (m_spawnId)
+ if (m_goData)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(GetSpawnId()))
- {
- x = data->posX;
- y = data->posY;
- z = data->posZ;
- if (ori)
- *ori = data->orientation;
- return;
- }
+ if (ori)
+ m_goData->spawnPoint.GetPosition(x, y, z, *ori);
+ else
+ m_goData->spawnPoint.GetPosition(x, y, z);
+ }
+ else
+ {
+ if (ori)
+ GetPosition(x, y, z, *ori);
+ else
+ GetPosition(x, y, z);
}
-
- x = GetPositionX();
- y = GetPositionY();
- z = GetPositionZ();
- if (ori)
- *ori = GetOrientation();
}
float GameObject::GetInteractionDistance() const
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 315210ce2e9..d49a8363e51 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -98,7 +98,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void CleanupsBeforeDelete(bool finalCleanup = true) override;
private:
- bool Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit);
+ bool Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid);
public:
static GameObject* CreateGameObject(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit = 0);
static GameObject* CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true);
@@ -106,7 +106,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void Update(uint32 p_time) override;
GameObjectTemplate const* GetGOInfo() const { return m_goInfo; }
GameObjectTemplateAddon const* GetTemplateAddon() const { return m_goTemplateAddon; }
- GameObjectData const* GetGOData() const { return m_goData; }
+ GameObjectData const* GetGameObjectData() const { return m_goData; }
GameObjectValue const* GetGOValue() const { return &m_goValue; }
bool IsTransport() const;
@@ -126,10 +126,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void SaveToDB();
void SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties);
- bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadGameObjectFromDB(spawnId, map, false); }
- private:
- bool LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap);
- public:
+ bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool = true); // arg4 is unused, only present to match the signature on Creature
void DeleteFromDB();
void SetOwnerGUID(ObjectGuid owner)
@@ -229,7 +226,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetUseCount() const { return m_usetimes; }
uint32 GetUniqueUseCount() const { return uint32(m_unique_users.size()); }
- void SaveRespawnTime() override;
+ void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override;
Loot loot;
@@ -280,6 +277,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void EventInform(uint32 eventId, WorldObject* invoker = nullptr);
+ // There's many places not ready for dynamic spawns. This allows them to live on for now.
+ void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
+ bool GetRespawnCompatibilityMode() {return m_respawnCompatibilityMode; }
+
uint32 GetScriptId() const;
GameObjectAI* AI() const { return m_AI; }
@@ -370,6 +371,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
}
GameObjectAI* m_AI;
+ bool m_respawnCompatibilityMode;
uint16 _animKitId;
uint32 _worldEffectID;
};
diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h
index 7477b9b5137..9b382bc84ab 100644
--- a/src/server/game/Entities/GameObject/GameObjectData.h
+++ b/src/server/game/Entities/GameObject/GameObjectData.h
@@ -22,9 +22,9 @@
#include "DBCEnums.h"
#include "QuaternionData.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "WorldPacket.h"
#include <string>
-#include <vector>
// from `gameobject_template`
struct GameObjectTemplate
@@ -978,30 +978,14 @@ struct GameObjectAddon
uint32 WorldEffectID;
};
-// from `gameobject`
-struct GameObjectData
+// `gameobject` table
+struct GameObjectData : public SpawnData
{
- explicit GameObjectData() : id(0), mapid(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
- animprogress(0), go_state(GO_STATE_ACTIVE), spawnDifficulties(), artKit(0),
- phaseUseFlags(0), phaseId(0), phaseGroup(0), terrainSwapMap(-1), ScriptId(0), dbData(true) { }
- uint32 id; // entry in gamobject_template
- uint16 mapid;
- float posX;
- float posY;
- float posZ;
- float orientation;
+ GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) { }
QuaternionData rotation;
- int32 spawntimesecs;
- uint32 animprogress;
- GOState go_state;
- std::vector<Difficulty> spawnDifficulties;
- uint8 artKit;
- uint8 phaseUseFlags;
- uint32 phaseId;
- uint32 phaseGroup;
- int32 terrainSwapMap;
- uint32 ScriptId;
- bool dbData;
+ uint32 animprogress = 0;
+ GOState goState = GO_STATE_ACTIVE;
+ uint8 artKit = 0;
};
#endif // GameObjectData_h__
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 2a7d63156fa..3efc014ffaf 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1742,7 +1742,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert
break;
}
- if (!summon->Create(GenerateLowGuid<HighGuid::Creature>(), this, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId))
+ if (!summon->Create(GenerateLowGuid<HighGuid::Creature>(), this, entry, pos, nullptr, vehId))
{
delete summon;
return nullptr;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 29a7e970da0..865d4095bff 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -474,7 +474,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void PlayDirectSound(uint32 soundId, Player* target = nullptr);
void PlayDirectMusic(uint32 musicId, Player* target = nullptr);
- virtual void SaveRespawnTime() { }
+ virtual void SaveRespawnTime(uint32 /*forceDelay*/ = 0, bool /*saveToDB*/ = true) { }
void AddObjectToRemoveList();
float GetGridActivationRange() const;
diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp
index 7806c7fb897..fd468f4e6ea 100644
--- a/src/server/game/Entities/Object/ObjectGuid.cpp
+++ b/src/server/game/Entities/Object/ObjectGuid.cpp
@@ -434,6 +434,14 @@ void ObjectGuidGeneratorBase::HandleCounterOverflow(HighGuid high)
World::StopNow(ERROR_EXIT_CODE);
}
+void ObjectGuidGeneratorBase::CheckGuidTrigger(ObjectGuid::LowType guidlow)
+{
+ if (!sWorld->IsGuidAlert() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDALERTLEVEL))
+ sWorld->TriggerGuidAlert();
+ else if (!sWorld->IsGuidWarning() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDWARNLEVEL))
+ sWorld->TriggerGuidWarning();
+}
+
template class TC_GAME_API ObjectGuidGenerator<HighGuid::Null>;
template class TC_GAME_API ObjectGuidGenerator<HighGuid::Uniq>;
template class TC_GAME_API ObjectGuidGenerator<HighGuid::Player>;
diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h
index c0ed13122e4..bd23084e19e 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -380,13 +380,14 @@ public:
ObjectGuidGeneratorBase(ObjectGuid::LowType start = UI64LIT(1)) : _nextGuid(start) { }
virtual ~ObjectGuidGeneratorBase() = default;
- virtual void Set(uint64 val) { _nextGuid = val; }
+ virtual void Set(ObjectGuid::LowType val) { _nextGuid = val; }
virtual ObjectGuid::LowType Generate() = 0;
ObjectGuid::LowType GetNextAfterMaxUsed() const { return _nextGuid; }
protected:
static void HandleCounterOverflow(HighGuid high);
- uint64 _nextGuid;
+ static void CheckGuidTrigger(ObjectGuid::LowType guid);
+ ObjectGuid::LowType _nextGuid;
};
template<HighGuid high>
@@ -399,6 +400,10 @@ public:
{
if (_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1)
HandleCounterOverflow(high);
+
+ if (high == HighGuid::Creature || high == HighGuid::Vehicle || high == HighGuid::GameObject || high == HighGuid::Transport)
+ CheckGuidTrigger(_nextGuid);
+
return _nextGuid++;
}
};
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index e2ee7f12384..84342433aa0 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -205,15 +205,9 @@ public:
return GetExactDist2dSq(pos) < dist * dist;
}
- bool IsInDist(float x, float y, float z, float dist) const
- {
- return GetExactDistSq(x, y, z) < dist * dist;
- }
-
- bool IsInDist(Position const* pos, float dist) const
- {
- return GetExactDistSq(pos) < dist * dist;
- }
+ bool IsInDist(float x, float y, float z, float dist) const { return GetExactDistSq(x, y, z) < dist * dist; }
+ bool IsInDist(Position const& pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
+ bool IsInDist(Position const* pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
bool IsWithinBox(const Position& center, float xradius, float yradius, float zradius) const;
@@ -240,12 +234,9 @@ public:
WorldLocation(uint32 mapId, Position const& position)
: Position(position), m_mapId(mapId) { }
- void WorldRelocate(WorldLocation const& loc)
- {
- m_mapId = loc.GetMapId();
- Relocate(loc);
- }
-
+ void WorldRelocate(WorldLocation const& loc) { m_mapId = loc.GetMapId(); Relocate(loc); }
+ void WorldRelocate(WorldLocation const* loc) { m_mapId = loc->GetMapId(); Relocate(loc); }
+ void WorldRelocate(uint32 mapId, Position const& pos) { m_mapId = mapId; Relocate(pos); }
void WorldRelocate(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
{
m_mapId = mapId;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index c4134b4f3b5..4a441af47b1 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -7164,6 +7164,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
}
+ GetMap()->UpdatePlayerZoneStats(m_zoneUpdateId, newZone);
+
// group update
if (GetGroup())
{
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index c5393ea21c9..f2667e84a37 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -300,16 +300,14 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c
{
Map* map = GetMap();
- Creature* creature = Creature::CreateCreatureFromDB(guid, map, false);
+ Creature* creature = Creature::CreateCreatureFromDB(guid, map, false, true);
if (!creature)
return nullptr;
ASSERT(data);
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float o = data->orientation;
+ float x, y, z, o;
+ data->spawnPoint.GetPosition(x, y, z, o);
creature->SetTransport(this);
creature->m_movementInfo.transport.guid = GetGUID();
@@ -355,10 +353,8 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
ASSERT(data);
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float o = data->orientation;
+ float x, y, z, o;
+ data->spawnPoint.GetPosition(x, y, z, o);
go->SetTransport(this);
go->m_movementInfo.transport.guid = GetGUID();
@@ -466,7 +462,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu
pos.GetPosition(x, y, z, o);
CalculatePassengerPosition(x, y, z, &o);
- if (!summon->Create(map->GenerateLowGuid<HighGuid::Creature>(), map, entry, x, y, z, o, nullptr, vehId))
+ if (!summon->Create(map->GenerateLowGuid<HighGuid::Creature>(), map, entry, { x, y, z, o }, nullptr, vehId))
{
delete summon;
return nullptr;
@@ -545,7 +541,7 @@ void Transport::LoadStaticPassengers()
// GameObjects on transport
guidEnd = cellItr->second.gameobjects.end();
for (CellGuidSet::const_iterator guidItr = cellItr->second.gameobjects.begin(); guidItr != guidEnd; ++guidItr)
- CreateGOPassenger(*guidItr, sObjectMgr->GetGOData(*guidItr));
+ CreateGOPassenger(*guidItr, sObjectMgr->GetGameObjectData(*guidItr));
}
}
}
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index 8958f62b5c7..64443d58e90 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -437,7 +437,7 @@ void GameEventMgr::LoadFromDB()
int32 internal_event_id = mGameEvent.size() + event_id - 1;
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "`game_event_gameobject` contains gameobject (GUID: " UI64FMTD ") not found in `gameobject` table.", guid);
@@ -1148,7 +1148,7 @@ void GameEventMgr::UpdateEventNPCFlags(uint16 event_id)
for (NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr)
// get the creature data from the low guid to get the entry, to be able to find out the whole guid
if (CreatureData const* data = sObjectMgr->GetCreatureData(itr->first))
- creaturesByMap[data->mapid].insert(itr->first);
+ creaturesByMap[data->spawnPoint.GetMapId()].insert(itr->first);
for (auto const& p : creaturesByMap)
{
@@ -1215,9 +1215,9 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
sObjectMgr->AddCreatureToGrid(*itr, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
// We use spawn coords to spawn
- if (map && !map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (map && !map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
Creature::CreateCreatureFromDB(*itr, map);
}
}
@@ -1232,14 +1232,14 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin(); itr != mGameEventGameobjectGuids[internal_event_id].end(); ++itr)
{
// Add to correct cell
- if (GameObjectData const* data = sObjectMgr->GetGOData(*itr))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
{
sObjectMgr->AddGameobjectToGrid(*itr, data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (map && !map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (map && !map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
if (GameObject* go = GameObject::CreateGameObjectFromDB(*itr, map, false))
{
@@ -1286,7 +1286,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
{
sObjectMgr->RemoveCreatureFromGrid(*itr, data);
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map)
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(*itr);
for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second;)
@@ -1312,11 +1312,11 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
if (event_id >0 && hasGameObjectActiveEventExcept(*itr, event_id))
continue;
// Remove the gameobject from grid
- if (GameObjectData const* data = sObjectMgr->GetGOData(*itr))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
{
sObjectMgr->RemoveGameobjectFromGrid(*itr, data);
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map)
{
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr);
for (auto itr2 = gameobjectBounds.first; itr2 != gameobjectBounds.second;)
@@ -1351,7 +1351,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
continue;
// Update if spawned
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr, activate](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr, activate](Map* map)
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first);
for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second; ++itr2)
diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp
index 83755d4877f..0a16ac42e36 100644
--- a/src/server/game/Garrison/Garrison.cpp
+++ b/src/server/game/Garrison/Garrison.cpp
@@ -683,7 +683,7 @@ template<class T, void(T::*SecondaryRelocate)(float,float,float,float)>
T* BuildingSpawnHelper(GameObject* building, ObjectGuid::LowType spawnId, Map* map)
{
T* spawn = new T();
- if (!spawn->LoadFromDB(spawnId, map))
+ if (!spawn->LoadFromDB(spawnId, map, false, false))
{
delete spawn;
return nullptr;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index eb6ef70b5f9..0728465d34c 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -260,9 +260,9 @@ ObjectMgr::ObjectMgr():
_equipmentSetGuid(1),
_mailId(1),
_hiPetNumber(1),
- _voidItemId(1),
_creatureSpawnId(1),
_gameObjectSpawnId(1),
+ _voidItemId(1),
DBCLocaleIndex(LOCALE_enUS)
{
for (uint8 i = 0; i < MAX_CLASSES; ++i)
@@ -1218,7 +1218,7 @@ void ObjectMgr::LoadGameObjectAddons()
ObjectGuid::LowType guid = fields[0].GetUInt64();
- GameObjectData const* goData = GetGOData(guid);
+ GameObjectData const* goData = GetGameObjectData(guid);
if (!goData)
{
TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") does not exist but has a record in `gameobject_addon`", guid);
@@ -1612,8 +1612,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '" UI64FMTD "' linking to Creature '" UI64FMTD "' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1628,8 +1628,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapid, slave->id, guidLow);
- linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapid, master->id, linkedGuidLow);
+ guid = ObjectGuid::Create<HighGuid::Creature>(slave->spawnPoint.GetMapId(), slave->id, guidLow);
+ linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->spawnPoint.GetMapId(), master->id, linkedGuidLow);
break;
}
case CREATURE_TO_GO:
@@ -1642,7 +1642,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const GameObjectData* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '" UI64FMTD "' not found in gameobject table", linkedGuidLow);
@@ -1650,8 +1650,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '" UI64FMTD "' linking to Gameobject '" UI64FMTD "' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1666,13 +1666,13 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapid, slave->id, guidLow);
- linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapid, master->id, linkedGuidLow);
+ guid = ObjectGuid::Create<HighGuid::Creature>(slave->spawnPoint.GetMapId(), slave->id, guidLow);
+ linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->spawnPoint.GetMapId(), master->id, linkedGuidLow);
break;
}
case GO_TO_GO:
{
- const GameObjectData* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '" UI64FMTD "' not found in gameobject table", guidLow);
@@ -1680,7 +1680,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const GameObjectData* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '" UI64FMTD "' not found in gameobject table", linkedGuidLow);
@@ -1688,8 +1688,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '" UI64FMTD "' linking to Gameobject '" UI64FMTD "' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1704,13 +1704,13 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapid, slave->id, guidLow);
- linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapid, master->id, linkedGuidLow);
+ guid = ObjectGuid::Create<HighGuid::GameObject>(slave->spawnPoint.GetMapId(), slave->id, guidLow);
+ linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->spawnPoint.GetMapId(), master->id, linkedGuidLow);
break;
}
case GO_TO_CREATURE:
{
- const GameObjectData* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '" UI64FMTD "' not found in gameobject table", guidLow);
@@ -1726,8 +1726,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '" UI64FMTD "' linking to Creature '" UI64FMTD "' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1742,8 +1742,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapid, slave->id, guidLow);
- linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapid, master->id, linkedGuidLow);
+ guid = ObjectGuid::Create<HighGuid::GameObject>(slave->spawnPoint.GetMapId(), slave->id, guidLow);
+ linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->spawnPoint.GetMapId(), master->id, linkedGuidLow);
break;
}
}
@@ -1763,7 +1763,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
CreatureData const* master = GetCreatureData(guidLow);
ASSERT(master);
- ObjectGuid guid = ObjectGuid::Create<HighGuid::Creature>(master->mapid, master->id, guidLow);
+ ObjectGuid guid = ObjectGuid::Create<HighGuid::Creature>(master->spawnPoint.GetMapId(), master->id, guidLow);
if (!linkedGuidLow) // we're removing the linking
{
@@ -1781,8 +1781,8 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false;
}
- MapEntry const* map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "Creature '" UI64FMTD "' linking to '" UI64FMTD "' on an unpermitted map.", guidLow, linkedGuidLow);
return false;
@@ -1795,7 +1795,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false;
}
- ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Creature>(slave->mapid, slave->id, linkedGuidLow);
+ ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Creature>(slave->spawnPoint.GetMapId(), slave->id, linkedGuidLow);
_linkedRespawnStore[guid] = linkedGuid;
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
@@ -1927,8 +1927,8 @@ void ObjectMgr::LoadCreatures()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4 5 6 7 8 9 10
- QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, "
+ // 0 1 2 3 4 5 6 7 8 9 10
+ QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, spawndist, "
// 11 12 13 14 15 16 17 18 19 20 21
"currentwaypoint, curhealth, curmana, MovementType, spawnDifficulties, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, "
// 22 23 24 25 26 27
@@ -1968,21 +1968,18 @@ void ObjectMgr::LoadCreatures()
}
CreatureData& data = _creatureDataStore[guid];
+ data.spawnId = guid;
data.id = entry;
- data.mapid = fields[2].GetUInt16();
- data.displayid = fields[3].GetUInt32();
- data.equipmentId = fields[4].GetInt8();
- data.posX = fields[5].GetFloat();
- data.posY = fields[6].GetFloat();
- data.posZ = fields[7].GetFloat();
- data.orientation = fields[8].GetFloat();
+ data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
+ data.displayid = fields[7].GetUInt32();
+ data.equipmentId = fields[8].GetInt8();
data.spawntimesecs = fields[9].GetUInt32();
data.spawndist = fields[10].GetFloat();
data.currentwaypoint= fields[11].GetUInt32();
data.curhealth = fields[12].GetUInt32();
data.curmana = fields[13].GetUInt32();
data.movementType = fields[14].GetUInt8();
- data.spawnDifficulties = ParseSpawnDifficulties(fields[15].GetString(), "creature", guid, data.mapid, spawnMasks[data.mapid]);
+ data.spawnDifficulties = ParseSpawnDifficulties(fields[15].GetString(), "creature", guid, data.spawnPoint.GetMapId(), spawnMasks[data.spawnPoint.GetMapId()]);
int16 gameEvent = fields[16].GetInt8();
uint32 PoolId = fields[17].GetUInt32();
data.npcflag = fields[18].GetUInt64();
@@ -1994,12 +1991,13 @@ void ObjectMgr::LoadCreatures()
data.phaseId = fields[24].GetUInt32();
data.phaseGroup = fields[25].GetUInt32();
data.terrainSwapMap = fields[26].GetInt32();
- data.ScriptId = GetScriptId(fields[27].GetString());
+ data.scriptId = GetScriptId(fields[27].GetString());
+ data.spawnGroupData = &_spawnGroupDataStore[0];
- MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
+ MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
if (!mapEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD ") that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD ") that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId());
continue;
}
@@ -2007,16 +2005,16 @@ void ObjectMgr::LoadCreatures()
{
if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
{
- if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapid))
+ if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.spawnPoint.GetMapId()))
{
- GridCoord gridCoord = Trinity::ComputeGridCoord(data.posX, data.posY);
+ GridCoord gridCoord = Trinity::ComputeGridCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
- VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy);
+ VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.spawnPoint.GetMapId(), gx, gy);
if (result != VMAP::LoadResult::Success)
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (X: %f Y: %f Z: %f)",
- guid, data.id, data.mapid, data.posX, data.posY, data.posZ);
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (%s)",
+ guid, data.id, data.spawnPoint.GetMapId(), data.spawnPoint.ToString().c_str());
}
}
}
@@ -2084,12 +2082,6 @@ void ObjectMgr::LoadCreatures()
}
}
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD " Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position::NormalizeOrientation(data.orientation);
- }
-
if (data.phaseUseFlags & ~PHASE_USE_FLAGS_ALL)
{
TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: " UI64FMTD " Entry: %u) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
@@ -2135,7 +2127,7 @@ void ObjectMgr::LoadCreatures()
TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: " UI64FMTD " Entry: %u) with `terrainSwapMap` %u does not exist, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
- else if (terrainSwapEntry->ParentMapID != data.mapid)
+ else if (terrainSwapEntry->ParentMapID != int16(data.spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: " UI64FMTD " Entry: %u) with `terrainSwapMap` %u which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
@@ -2147,7 +2139,7 @@ void ObjectMgr::LoadCreatures()
uint32 zoneId = 0;
uint32 areaId = 0;
PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
- sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
@@ -2171,8 +2163,8 @@ void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const*
{
for (Difficulty difficulty : data->spawnDifficulties)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, difficulty)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), difficulty)][cellCoord.GetId()];
cell_guids.creatures.insert(guid);
}
}
@@ -2181,13 +2173,13 @@ void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData co
{
for (Difficulty difficulty : data->spawnDifficulties)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, difficulty)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), difficulty)][cellCoord.GetId()];
cell_guids.creatures.erase(guid);
}
}
-ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
+ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
{
GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
if (!goinfo)
@@ -2197,38 +2189,37 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position co
if (!map)
return UI64LIT(0);
- ObjectGuid::LowType guid = GenerateGameObjectSpawnId();
- GameObjectData& data = NewGOData(guid);
+ ObjectGuid::LowType spawnId = GenerateGameObjectSpawnId();
+ GameObjectData& data = NewOrExistGameObjectData(spawnId);
+ data.spawnId = spawnId;
data.id = entry;
- data.mapid = mapId;
-
- pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation);
-
+ data.spawnPoint.WorldRelocate(mapId,pos);
data.rotation = rot;
data.spawntimesecs = spawntimedelay;
data.animprogress = 100;
data.spawnDifficulties.push_back(DIFFICULTY_NONE);
- data.go_state = GO_STATE_READY;
+ data.goState = GO_STATE_READY;
data.artKit = goinfo->type == GAMEOBJECT_TYPE_CONTROL_ZONE ? 21 : 0;
- data.dbData = false;
+ data.dbData = false;
+ data.spawnGroupData = GetLegacySpawnGroup();
- AddGameobjectToGrid(guid, &data);
+ AddGameobjectToGrid(spawnId, &data);
// Spawn if necessary (loaded grids only)
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data.posX, data.posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint))
{
- GameObject* go = GameObject::CreateGameObjectFromDB(guid, map);
+ GameObject* go = GameObject::CreateGameObjectFromDB(spawnId, map);
if (!go)
{
- TC_LOG_ERROR("misc", "AddGOData: cannot add gameobject entry %u to map", entry);
+ TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry %u to map", entry);
return UI64LIT(0);
}
}
- TC_LOG_DEBUG("maps", "AddGOData: dbguid " UI64FMTD " entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, data.posX, data.posY, data.posZ, data.orientation);
+ TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid " UI64FMTD " entry %u map %u pos %s", spawnId, entry, mapId, data.spawnPoint.ToString().c_str());
- return guid;
+ return spawnId;
}
ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Position const& pos, uint32 spawntimedelay /*= 0*/)
@@ -2246,15 +2237,13 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(map->GetDifficultyID());
- ObjectGuid::LowType guid = GenerateCreatureSpawnId();
- CreatureData& data = NewOrExistCreatureData(guid);
+ ObjectGuid::LowType spawnId = GenerateCreatureSpawnId();
+ CreatureData& data = NewOrExistCreatureData(spawnId);
+ data.spawnId = spawnId;
data.id = entry;
- data.mapid = mapId;
+ data.spawnPoint.WorldRelocate(mapId, pos);
data.displayid = 0;
data.equipmentId = 0;
-
- pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation);
-
data.spawntimesecs = spawntimedelay;
data.spawndist = 0;
data.currentwaypoint = 0;
@@ -2266,13 +2255,14 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
data.npcflag = cInfo->npcflag;
data.unit_flags = cInfo->unit_flags;
data.dynamicflags = cInfo->dynamicflags;
+ data.spawnGroupData = GetLegacySpawnGroup();
- AddCreatureToGrid(guid, &data);
+ AddCreatureToGrid(spawnId, &data);
// We use spawn coords to spawn
- if (!map->Instanceable() && !map->IsRemovalGrid(data.posX, data.posY))
+ if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint))
{
- Creature* creature = Creature::CreateCreatureFromDB(guid, map);
+ Creature* creature = Creature::CreateCreatureFromDB(spawnId, map, true, true);
if (!creature)
{
TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry);
@@ -2280,10 +2270,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
}
}
- return guid;
+ return spawnId;
}
-void ObjectMgr::LoadGameobjects()
+void ObjectMgr::LoadGameObjects()
{
uint32 oldMSTime = getMSTime();
@@ -2347,22 +2337,20 @@ void ObjectMgr::LoadGameobjects()
GameObjectData& data = _gameObjectDataStore[guid];
+ data.spawnId = guid;
data.id = entry;
- data.mapid = fields[2].GetUInt16();
- data.posX = fields[3].GetFloat();
- data.posY = fields[4].GetFloat();
- data.posZ = fields[5].GetFloat();
- data.orientation = fields[6].GetFloat();
+ data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
data.rotation.x = fields[7].GetFloat();
data.rotation.y = fields[8].GetFloat();
data.rotation.z = fields[9].GetFloat();
data.rotation.w = fields[10].GetFloat();
data.spawntimesecs = fields[11].GetInt32();
+ data.spawnGroupData = &_spawnGroupDataStore[0];
- MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
+ MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
if (!mapEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.mapid);
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.spawnPoint.GetMapId());
continue;
}
@@ -2370,16 +2358,16 @@ void ObjectMgr::LoadGameobjects()
{
if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
{
- if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapid))
+ if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.spawnPoint.GetMapId()))
{
- GridCoord gridCoord = Trinity::ComputeGridCoord(data.posX, data.posY);
+ GridCoord gridCoord = Trinity::ComputeGridCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
- VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy);
+ VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.spawnPoint.GetMapId(), gx, gy);
if (result != VMAP::LoadResult::Success)
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (X: %f Y: %f Z: %f)",
- guid, data.id, data.mapid, data.posX, data.posY, data.posZ);
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (%s)",
+ guid, data.id, data.spawnPoint.GetMapId(), data.spawnPoint.ToString().c_str());
}
}
}
@@ -2401,9 +2389,9 @@ void ObjectMgr::LoadGameobjects()
continue;
}
}
- data.go_state = GOState(go_state);
+ data.goState = GOState(go_state);
- data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetString(), "gameobject", guid, data.mapid, spawnMasks[data.mapid]);
+ data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetString(), "gameobject", guid, data.spawnPoint.GetMapId(), spawnMasks[data.spawnPoint.GetMapId()]);
if (data.spawnDifficulties.empty())
{
TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD ") that is not spawned in any difficulty, skipped.", guid);
@@ -2462,20 +2450,14 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: " UI64FMTD " Entry: %u) with `terrainSwapMap` %u does not exist, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
- else if (terrainSwapEntry->ParentMapID != data.mapid)
+ else if (terrainSwapEntry->ParentMapID != int16(data.spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: " UI64FMTD " Entry: %u) with `terrainSwapMap` %u which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
}
- data.ScriptId = GetScriptId(fields[21].GetString());
-
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position::NormalizeOrientation(data.orientation);
- }
+ data.scriptId = GetScriptId(fields[21].GetString());
if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
{
@@ -2501,7 +2483,7 @@ void ObjectMgr::LoadGameobjects()
continue;
}
- if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
+ if (!MapManager::IsValidMapCoord(data.spawnPoint))
{
TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u) with invalid coordinates, skip", guid, data.id);
continue;
@@ -2512,7 +2494,7 @@ void ObjectMgr::LoadGameobjects()
uint32 zoneId = 0;
uint32 areaId = 0;
PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
- sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
@@ -2531,12 +2513,159 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " gameobjects in %u ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+void ObjectMgr::LoadSpawnGroupTemplates()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
+
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 groupId = fields[0].GetUInt32();
+ SpawnGroupTemplateData& group = _spawnGroupDataStore[groupId];
+ group.groupId = groupId;
+ group.name = fields[1].GetString();
+ group.mapId = SPAWNGROUP_MAP_UNSET;
+ uint32 flags = fields[2].GetUInt32();
+ if (flags & ~SPAWNGROUP_FLAGS_ALL)
+ {
+ flags &= SPAWNGROUP_FLAGS_ALL;
+ TC_LOG_ERROR("server.loading", "Invalid spawn group flag %u on group ID %u (%s), reduced to valid flag %u.", flags, groupId, group.name.c_str(), uint32(group.flags));
+ }
+ if (flags & SPAWNGROUP_FLAG_SYSTEM && flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
+ {
+ flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
+ TC_LOG_ERROR("server.loading", "System spawn group %u (%s) has invalid manual spawn flag. Ignored.", groupId, group.name.c_str());
+ }
+ group.flags = SpawnGroupFlags(flags);
+ group.isActive = !(group.flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
+ } while (result->NextRow());
+ }
+
+ if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("server.loading", "Default spawn group (index 0) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData& data = _spawnGroupDataStore[0];
+ data.groupId = 0;
+ data.name = "Default Group";
+ data.mapId = 0;
+ data.flags = SPAWNGROUP_FLAG_SYSTEM;
+ data.isActive = true;
+ }
+ if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("server.loading", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData&data = _spawnGroupDataStore[1];
+ data.groupId = 1;
+ data.name = "Legacy Group";
+ data.mapId = 0;
+ data.flags = SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE);
+ data.isActive = true;
+ }
+
+ if (result)
+ TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " spawn group templates in %u ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
+ else
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
+
+ return;
+}
+
+void ObjectMgr::LoadSpawnGroups()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
+
+ if (!result)
+ {
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
+ return;
+ }
+
+ uint32 numMembers = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 groupId = fields[0].GetUInt32();
+ SpawnObjectType spawnType;
+ {
+ uint32 type = fields[1].GetUInt8();
+ if (type >= SPAWN_TYPE_MAX)
+ {
+ TC_LOG_ERROR("server.loading", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", type, groupId);
+ continue;
+ }
+ spawnType = SpawnObjectType(type);
+ }
+ ObjectGuid::LowType spawnId = fields[2].GetUInt64();
+
+ SpawnData const* data = GetSpawnData(spawnType, spawnId);
+ if (!data)
+ {
+ TC_LOG_ERROR("server.loading", "Spawn data with ID (%u," UI64FMTD ") not found, but is listed as a member of spawn group %u!", uint32(spawnType), spawnId, groupId);
+ continue;
+ }
+ else if (data->spawnGroupData->groupId)
+ {
+ TC_LOG_ERROR("server.loading", "Spawn with ID (%u," UI64FMTD ") is listed as a member of spawn group %u, but is already a member of spawn group %u. Skipping.", uint32(spawnType), spawnId, groupId, data->spawnGroupData->groupId);
+ continue;
+ }
+ auto it = _spawnGroupDataStore.find(groupId);
+ if (it == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("server.loading", "Spawn group %u assigned to spawn ID (%u," UI64FMTD "), but group is found!", groupId, uint32(spawnType), spawnId);
+ continue;
+ }
+ else
+ {
+ SpawnGroupTemplateData& groupTemplate = it->second;
+ if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
+ groupTemplate.mapId = data->spawnPoint.GetMapId();
+ else if (groupTemplate.mapId != data->spawnPoint.GetMapId() && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
+ {
+ TC_LOG_ERROR("server.loading", "Spawn group %u has map ID %u, but spawn (%u," UI64FMTD ") has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->spawnPoint.GetMapId());
+ continue;
+ }
+ const_cast<SpawnData*>(data)->spawnGroupData = &groupTemplate;
+ if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
+ _spawnGroupMapStore.emplace(groupId, data);
+ ++numMembers;
+ }
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u spawn group members in %u ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ObjectMgr::OnDeleteSpawnData(SpawnData const* data)
+{
+ auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
+ ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u," UI64FMTD ") is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
+ if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
+ return;
+
+ auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
+ for (auto it = pair.first; it != pair.second; ++it)
+ {
+ if (it->second != data)
+ continue;
+ _spawnGroupMapStore.erase(it);
+ return;
+ }
+ ASSERT(false, "Spawn data (%u," UI64FMTD ") being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
+}
+
void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data)
{
for (Difficulty difficulty : data->spawnDifficulties)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, difficulty)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), difficulty)][cellCoord.GetId()];
cell_guids.gameobjects.insert(guid);
}
}
@@ -2545,8 +2674,8 @@ void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectDat
{
for (Difficulty difficulty : data->spawnDifficulties)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, difficulty)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), difficulty)][cellCoord.GetId()];
cell_guids.gameobjects.erase(guid);
}
}
@@ -5067,7 +5196,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
{
- GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
+ GameObjectData const* data = GetGameObjectData(tmp.RespawnGameobject.GOGuid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
@@ -5117,7 +5246,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
case SCRIPT_COMMAND_OPEN_DOOR:
case SCRIPT_COMMAND_CLOSE_DOOR:
{
- GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
+ GameObjectData const* data = GetGameObjectData(tmp.ToggleDoor.GOGuid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
@@ -6913,12 +7042,142 @@ uint64 ObjectMgr::GenerateGameObjectSpawnId()
{
if (_gameObjectSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
{
- TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
+ TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _gameObjectSpawnId++;
}
+bool ObjectMgr::SpawnGroupSpawn(uint32 groupId, Map* map, bool ignoreRespawn, bool force, std::vector<WorldObject*>* spawnedObjects)
+{
+ auto itr = _spawnGroupDataStore.find(groupId);
+ if (itr == _spawnGroupDataStore.end() || itr->second.flags & SPAWNGROUP_FLAG_SYSTEM)
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u. Blocked.", groupId);
+ return false;
+ }
+
+ if (!map)
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but no map was supplied. Blocked.", groupId);
+ return false;
+ }
+
+ if (itr->second.mapId != map->GetId())
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but supplied map is %u, creature group has map %u. Blocked.", groupId, map->GetId(), itr->second.mapId);
+ return false;
+ }
+
+ for (auto& pair : GetSpawnDataForGroup(groupId))
+ {
+ SpawnData const* data = pair.second;
+ ASSERT(itr->second.mapId == data->spawnPoint.GetMapId());
+ // Check if there's already an instance spawned
+ if (!force)
+ if (WorldObject* obj = map->GetWorldObjectBySpawnId(data->type, data->spawnId))
+ if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive())
+ continue;
+
+ time_t respawnTime = map->GetRespawnTime(data->type, data->spawnId);
+ if (respawnTime && respawnTime > time(NULL))
+ {
+ if (!force && !ignoreRespawn)
+ continue;
+
+ // we need to remove the respawn time, otherwise we'd end up double spawning
+ map->RemoveRespawnTime(data->type, data->spawnId, false);
+ }
+
+ // don't spawn if the grid isn't loaded (will be handled in grid loader)
+ if (!map->IsGridLoaded(data->spawnPoint))
+ continue;
+
+ // Everything OK, now do the actual (re)spawn
+ switch (data->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ Creature* creature = new Creature();
+ if (!creature->LoadFromDB(data->spawnId, map, true, force))
+ delete creature;
+ else if (spawnedObjects)
+ spawnedObjects->push_back(creature);
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ GameObject* gameobject = new GameObject();
+ if (!gameobject->LoadFromDB(data->spawnId, map, true))
+ delete gameobject;
+ else if (spawnedObjects)
+ spawnedObjects->push_back(gameobject);
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u with spawnId " UI64FMTD, uint32(data->type), data->spawnId);
+ return false;
+ }
+ }
+ itr->second.isActive = true; // start processing respawns for the group
+ return true;
+}
+
+bool ObjectMgr::SpawnGroupDespawn(uint32 groupId, Map* map, bool deleteRespawnTimes)
+{
+ auto itr = _spawnGroupDataStore.find(groupId);
+ if (itr == _spawnGroupDataStore.end() || itr->second.flags & SPAWNGROUP_FLAG_SYSTEM)
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u. Blocked.", groupId);
+ return false;
+ }
+
+ if (!map)
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but no map was supplied. Blocked.", groupId);
+ return false;
+ }
+
+ if (itr->second.mapId != map->GetId())
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but supplied map is %u, creature group has map %u. Blocked.", groupId, map->GetId(), itr->second.mapId);
+ return false;
+ }
+
+ std::vector<WorldObject*> toUnload; // unload after iterating, otherwise iterator invalidation
+ for (auto const& pair : GetSpawnDataForGroup(groupId))
+ {
+ SpawnData const* data = pair.second;
+ if (deleteRespawnTimes)
+ map->RemoveRespawnTime(data->type, data->spawnId);
+ switch (data->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ auto bounds = map->GetCreatureBySpawnIdStore().equal_range(data->spawnId);
+ for (auto it = bounds.first; it != bounds.second; ++it)
+ toUnload.emplace_back(it->second);
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(data->spawnId);
+ for (auto it = bounds.first; it != bounds.second; ++it)
+ toUnload.emplace_back(it->second);
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u in spawn data with spawnId " UI64FMTD ".", uint32(data->type), data->spawnId);
+ return false;
+ }
+ }
+ // now do the actual despawning
+ for (WorldObject* obj : toUnload)
+ obj->AddObjectToRemoveList();
+ itr->second.isActive = false; // stop processing respawns for the group, too
+ return true;
+}
+
void ObjectMgr::LoadGameObjectLocales()
{
uint32 oldMSTime = getMSTime();
@@ -7854,17 +8113,23 @@ void ObjectMgr::DeleteCreatureData(ObjectGuid::LowType guid)
// remove mapid*cellid -> guid_set map
CreatureData const* data = GetCreatureData(guid);
if (data)
+ {
RemoveCreatureFromGrid(guid, data);
+ OnDeleteSpawnData(data);
+ }
_creatureDataStore.erase(guid);
}
-void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid)
+void ObjectMgr::DeleteGameObjectData(ObjectGuid::LowType guid)
{
// remove mapid*cellid -> guid_set map
- GameObjectData const* data = GetGOData(guid);
+ GameObjectData const* data = GetGameObjectData(guid);
if (data)
+ {
RemoveGameobjectFromGrid(guid, data);
+ OnDeleteSpawnData(data);
+ }
_gameObjectDataStore.erase(guid);
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index e5b3ba78226..5f93284920e 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -22,6 +22,7 @@
#include "ConditionMgr.h"
#include "CreatureData.h"
#include "DatabaseEnvFwd.h"
+#include "Errors.h"
#include "GameObjectData.h"
#include "ItemTemplate.h"
#include "IteratorPair.h"
@@ -40,6 +41,7 @@
class Item;
class Unit;
class Vehicle;
+class Map;
struct AccessRequirement;
struct DeclinedName;
struct DungeonEncounterEntry;
@@ -492,6 +494,8 @@ typedef std::unordered_map<uint32, GameObjectTemplateAddon> GameObjectTemplateAd
typedef std::unordered_map<ObjectGuid::LowType, GameObjectData> GameObjectDataContainer;
typedef std::unordered_map<ObjectGuid::LowType, GameObjectAddon> GameObjectAddonContainer;
typedef std::unordered_map<uint32, std::vector<uint32>> GameObjectQuestItemMap;
+typedef std::unordered_map<uint32, SpawnGroupTemplateData> SpawnGroupDataContainer;
+typedef std::multimap<uint32, SpawnData const*> SpawnGroupLinkContainer;
typedef std::map<TempSummonGroupKey, std::vector<TempSummonData>> TempSummonDataContainer;
typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer;
typedef std::unordered_map<uint32, GameObjectLocale> GameObjectLocaleContainer;
@@ -874,6 +878,7 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_CHARTER_NAME 24 // max allowed by client name length
TC_GAME_API bool normalizePlayerName(std::string& name);
+#define SPAWNGROUP_MAP_UNSET 0xFFFFFFFF
struct ExtendedPlayerName
{
@@ -1241,7 +1246,9 @@ class TC_GAME_API ObjectMgr
void LoadCreatureModelInfo();
void LoadEquipmentTemplates();
void LoadGameObjectLocales();
- void LoadGameobjects();
+ void LoadGameObjects();
+ void LoadSpawnGroupTemplates();
+ void LoadSpawnGroups();
void LoadItemTemplates();
void LoadItemTemplateAddon();
void LoadItemScriptNames();
@@ -1347,10 +1354,18 @@ class TC_GAME_API ObjectMgr
uint32 GenerateMailID();
uint32 GeneratePetNumber();
uint64 GenerateVoidStorageItemId();
- uint64 GenerateCreatureSpawnId();
- uint64 GenerateGameObjectSpawnId();
+ ObjectGuid::LowType GenerateCreatureSpawnId();
+ ObjectGuid::LowType GenerateGameObjectSpawnId();
- MailLevelReward const* GetMailLevelReward(uint8 level, uint8 race)
+ bool SpawnGroupSpawn(uint32 groupId, Map* map, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr);
+ bool SpawnGroupDespawn(uint32 groupId, Map* map, bool deleteRespawnTimes = false);
+ void SetSpawnGroupActive(uint32 groupId, bool state) { auto it = _spawnGroupDataStore.find(groupId); if (it != _spawnGroupDataStore.end()) it->second.isActive = state; }
+ bool IsSpawnGroupActive(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return (it != _spawnGroupDataStore.end()) && it->second.isActive; }
+ SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); }
+ SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); }
+ Trinity::IteratorPair<SpawnGroupLinkContainer::const_iterator> GetSpawnDataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); }
+
+ MailLevelReward const* GetMailLevelReward(uint8 level, uint8 race) const
{
MailLevelRewardContainer::const_iterator map_itr = _mailLevelRewardStore.find(level);
if (map_itr == _mailLevelRewardStore.end())
@@ -1391,6 +1406,17 @@ class TC_GAME_API ObjectMgr
return nullptr;
}
+ SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType guid)
+ {
+ if (type == SPAWN_TYPE_CREATURE)
+ return GetCreatureData(guid);
+ else if (type == SPAWN_TYPE_GAMEOBJECT)
+ return GetGameObjectData(guid);
+ else
+ ASSERT(false, "Invalid spawn object type %u", uint32(type));
+ return nullptr;
+ }
+ void OnDeleteSpawnData(SpawnData const* data);
CreatureData const* GetCreatureData(ObjectGuid::LowType guid) const
{
CreatureDataContainer::const_iterator itr = _creatureDataStore.find(guid);
@@ -1411,6 +1437,14 @@ class TC_GAME_API ObjectMgr
if (itr == _creatureLocaleStore.end()) return nullptr;
return &itr->second;
}
+ GameObjectData const* GetGameObjectData(ObjectGuid::LowType guid) const
+ {
+ GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid);
+ if (itr == _gameObjectDataStore.end()) return nullptr;
+ return &itr->second;
+ }
+ GameObjectData& NewOrExistGameObjectData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
+ void DeleteGameObjectData(ObjectGuid::LowType guid);
GameObjectLocale const* GetGameObjectLocale(uint32 entry) const
{
GameObjectLocaleContainer::const_iterator itr = _gameObjectLocaleStore.find(entry);
@@ -1465,15 +1499,6 @@ class TC_GAME_API ObjectMgr
if (itr == _playerChoiceLocales.end()) return nullptr;
return &itr->second;
}
- GameObjectData const* GetGOData(ObjectGuid::LowType guid) const
- {
- GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid);
- if (itr == _gameObjectDataStore.end()) return nullptr;
- return &itr->second;
- }
- GameObjectData& NewGOData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
- void DeleteGOData(ObjectGuid::LowType guid);
-
TrinityString const* GetTrinityString(uint32 entry) const
{
TrinityStringContainer::const_iterator itr = _trinityStringStore.find(entry);
@@ -1491,7 +1516,7 @@ class TC_GAME_API ObjectMgr
void RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const* data);
void AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data);
void RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectData const* data);
- ObjectGuid::LowType AddGOData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0);
+ ObjectGuid::LowType AddGameObjectData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0);
ObjectGuid::LowType AddCreatureData(uint32 entry, uint32 map, Position const& pos, uint32 spawntimedelay = 0);
// reserved names
@@ -1628,9 +1653,9 @@ class TC_GAME_API ObjectMgr
uint64 _equipmentSetGuid;
std::atomic<uint32> _mailId;
std::atomic<uint32> _hiPetNumber;
+ ObjectGuid::LowType _creatureSpawnId;
+ ObjectGuid::LowType _gameObjectSpawnId;
uint64 _voidItemId;
- uint64 _creatureSpawnId;
- uint64 _gameObjectSpawnId;
// first free low guid for selected guid type
template<HighGuid high>
@@ -1763,6 +1788,8 @@ class TC_GAME_API ObjectMgr
GameObjectLocaleContainer _gameObjectLocaleStore;
GameObjectTemplateContainer _gameObjectTemplateStore;
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore;
+ SpawnGroupDataContainer _spawnGroupDataStore;
+ SpawnGroupLinkContainer _spawnGroupMapStore;
/// Stores temp summon data grouped by summoner's entry, summoner's type and group id
TempSummonDataContainer _tempSummonDataStore;
std::unordered_map<int32 /*choiceId*/, PlayerChoice> _playerChoices;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 266ebdd3efb..85c0fab72d0 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -1352,6 +1352,27 @@ namespace Trinity
bool _reqAlive;
};
+ class AnyPlayerInPositionRangeCheck
+ {
+ public:
+ AnyPlayerInPositionRangeCheck(Position const* pos, float range, bool reqAlive = true) : _pos(pos), _range(range), _reqAlive(reqAlive) { }
+ bool operator()(Player* u)
+ {
+ if (_reqAlive && !u->IsAlive())
+ return false;
+
+ if (!u->IsWithinDist3d(_pos, _range))
+ return false;
+
+ return true;
+ }
+
+ private:
+ Position const* _pos;
+ float _range;
+ bool _reqAlive;
+ };
+
class NearestPlayerInObjectRangeCheck
{
public:
diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp
index a64771876d9..7b2ead0e1cc 100644
--- a/src/server/game/Grids/ObjectGridLoader.cpp
+++ b/src/server/game/Grids/ObjectGridLoader.cpp
@@ -16,18 +16,20 @@
*/
#include "ObjectGridLoader.h"
-#include "ObjectAccessor.h"
-#include "ObjectMgr.h"
-#include "Creature.h"
-#include "GameObject.h"
-#include "DynamicObject.h"
-#include "Corpse.h"
#include "AreaTrigger.h"
-#include "Conversation.h"
-#include "World.h"
#include "CellImpl.h"
+#include "Conversation.h"
+#include "Corpse.h"
+#include "Creature.h"
#include "CreatureAI.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
#include "Log.h"
+#include "ObjectAccessor.h"
+#include "ObjectMgr.h"
+#include "PhasingHandler.h"
+#include "World.h"
+#include "ScriptMgr.h"
void ObjectGridEvacuator::Visit(CreatureMapType &m)
{
@@ -120,15 +122,47 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T>
for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid)
{
T* obj = new T;
- ObjectGuid::LowType guid = *i_guid;
- //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
- if (!obj->LoadFromDB(guid, map))
+
+ // Don't spawn at all if there's a respawn time
+ if ((obj->GetTypeId() == TYPEID_UNIT && !map->GetCreatureRespawnTime(*i_guid)) || (obj->GetTypeId() == TYPEID_GAMEOBJECT && !map->GetGORespawnTime(*i_guid)))
{
- delete obj;
- continue;
- }
+ ObjectGuid::LowType guid = *i_guid;
+ //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
+
+ if (obj->GetTypeId() == TYPEID_UNIT)
+ {
+ CreatureData const* cdata = sObjectMgr->GetCreatureData(guid);
+ ASSERT(cdata, "Tried to load creature with spawnId " UI64FMTD ", but no such creature exists.", guid);
+ SpawnGroupTemplateData const* const group = cdata->spawnGroupData;
+ // If creature in manual spawn group, don't spawn here, unless group is already active.
+ if ((group->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN) && !group->isActive)
+ continue;
+
+ // If script is blocking spawn, don't spawn but queue for a re-check in a little bit
+ if (!(group->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) && !sScriptMgr->CanSpawn(guid, cdata->id, cdata, map))
+ {
+ map->SaveRespawnTime(SPAWN_TYPE_CREATURE, guid, cdata->id, time(NULL) + urand(4,7), map->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
+ continue;
+ }
+ }
+ else if (obj->GetTypeId() == TYPEID_GAMEOBJECT)
+ {
+ // If gameobject in manual spawn group, don't spawn here, unless group is already active.
+ GameObjectData const* godata = sObjectMgr->GetGameObjectData(guid);
+ ASSERT(godata, "Tried to load gameobject with spawnId " UI64FMTD ", but no such object exists.", guid);
+ if ((godata->spawnGroupData->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN) && !godata->spawnGroupData->isActive)
+ continue;
+ }
- AddObjectHelper(cell, m, count, map, obj);
+ if (!obj->LoadFromDB(guid, map, false, false))
+ {
+ delete obj;
+ continue;
+ }
+ AddObjectHelper(cell, m, count, map, obj);
+ }
+ else
+ delete obj;
}
}
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);
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index d52a8976f20..7b3426c8d6e 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -20,16 +20,18 @@
#include "Define.h"
-#include "GridDefines.h"
#include "Cell.h"
-#include "Timer.h"
-#include "SharedDefines.h"
+#include "DatabaseEnvFwd.h"
+#include "DynamicTree.h"
+#include "GridDefines.h"
#include "GridRefManager.h"
#include "MapRefManager.h"
-#include "DynamicTree.h"
#include "ObjectGuid.h"
#include "Optional.h"
-
+#include "SharedDefines.h"
+#include "SpawnData.h"
+#include "Timer.h"
+#include <boost/heap/fibonacci_heap.hpp>
#include <bitset>
#include <list>
#include <memory>
@@ -281,7 +283,37 @@ struct ZoneDynamicInfo
typedef std::map<ObjectGuid::LowType/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
+struct RespawnInfo; // forward declaration
+struct CompareRespawnInfo
+{
+ bool operator()(RespawnInfo const* a, RespawnInfo const* b) const;
+};
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
+typedef boost::heap::fibonacci_heap<RespawnInfo*, boost::heap::compare<CompareRespawnInfo>> RespawnListContainer;
+typedef RespawnListContainer::handle_type RespawnListHandle;
+typedef std::unordered_map<ObjectGuid::LowType, RespawnInfo*> RespawnInfoMap;
+typedef std::vector<RespawnInfo*> RespawnVector;
+struct RespawnInfo
+{
+ SpawnObjectType type;
+ ObjectGuid::LowType spawnId;
+ uint32 entry;
+ time_t respawnTime;
+ uint32 gridId;
+ uint32 zoneId;
+ RespawnListHandle handle;
+};
+inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo const* b) const
+{
+ if (a == b)
+ return false;
+ if (a->respawnTime != b->respawnTime)
+ return (a->respawnTime > b->respawnTime);
+ if (a->spawnId != b->spawnId)
+ return a->spawnId < b->spawnId;
+ ASSERT(a->type != b->type, "Duplicate respawn entry for spawnId (%u," UI64FMTD ") found!", a->type, a->spawnId);
+ return a->type < b->type;
+}
typedef TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid> MapStoredObjectTypesContainer;
@@ -314,7 +346,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
template<class T> void RemoveFromMap(T *, bool);
void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::ObjectUpdater, GridTypeMapContainer> &gridVisitor, TypeContainerVisitor<Trinity::ObjectUpdater, WorldTypeMapContainer> &worldVisitor);
- virtual void Update(const uint32);
+ virtual void Update(uint32);
float GetVisibilityRange() const { return m_VisibleDistance; }
//function for setting up visibility distance for maps on per-type/per-Id basis
@@ -334,17 +366,19 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
GridCoord p = Trinity::ComputeGridCoord(x, y);
return !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL;
}
+ bool IsRemovalGrid(Position const& pos) const { return IsRemovalGrid(pos.GetPositionX(), pos.GetPositionY()); }
- bool IsGridLoaded(float x, float y) const
- {
- return IsGridLoaded(Trinity::ComputeGridCoord(x, y));
- }
+ bool IsGridLoaded(uint32 gridId) const { return IsGridLoaded(GridCoord(gridId % MAX_NUMBER_OF_GRIDS, gridId / MAX_NUMBER_OF_GRIDS)); }
+ bool IsGridLoaded(float x, float y) const { return IsGridLoaded(Trinity::ComputeGridCoord(x, y)); }
+ bool IsGridLoaded(Position const& pos) const { return IsGridLoaded(pos.GetPositionX(), pos.GetPositionY()); }
bool GetUnloadLock(const GridCoord &p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); }
void SetUnloadLock(const GridCoord &p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); }
void LoadGrid(float x, float y);
void LoadAllCells();
bool UnloadGrid(NGridType& ngrid, bool pForce);
+ void GridMarkNoUnload(uint32 x, uint32 y);
+ void GridUnmarkNoUnload(uint32 x, uint32 y);
virtual void UnloadAll();
void ResetGridExpiry(NGridType &grid, float factor = 1) const
@@ -367,18 +401,17 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
void AddChildTerrainMap(Map* map) { m_childTerrainMaps->push_back(map); map->m_parentTerrainMap = this; }
void UnlinkAllChildTerrainMaps() { m_childTerrainMaps->clear(); }
- // some calls like isInWater should not use vmaps due to processor power
- // can return INVALID_HEIGHT if under z+2 z coord not found height
- float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH);
- float GetMinHeight(PhaseShift const& phaseShift, float x, float y);
-
+ void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const;
void GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS);
ZLiquidStatus GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr);
bool GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId);
uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, bool *isOutdoors = nullptr);
+ uint32 GetAreaId(PhaseShift const& phaseShift, Position const& pos) { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetZoneId(PhaseShift const& phaseShift, float x, float y, float z);
+ uint32 GetZoneId(PhaseShift const& phaseShift, Position const& pos) { return GetZoneId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z);
+ void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, Position const& pos) { GetZoneAndAreaId(phaseShift, zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
bool IsOutdoors(PhaseShift const& phaseShift, float x, float y, float z);
@@ -488,14 +521,19 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
GameObject* GetGameObject(ObjectGuid const& guid);
Pet* GetPet(ObjectGuid const& guid);
Transport* GetTransport(ObjectGuid const& guid);
+ Creature* GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const;
+ GameObject* GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const;
+ WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? reinterpret_cast<WorldObject*>(GetGameObjectBySpawnId(spawnId)) : reinterpret_cast<WorldObject*>(GetCreatureBySpawnId(spawnId)); }
MapStoredObjectTypesContainer& GetObjectsStore() { return _objectsStore; }
typedef std::unordered_multimap<ObjectGuid::LowType, Creature*> CreatureBySpawnIdContainer;
CreatureBySpawnIdContainer& GetCreatureBySpawnIdStore() { return _creatureBySpawnIdStore; }
+ CreatureBySpawnIdContainer const& GetCreatureBySpawnIdStore() const { return _creatureBySpawnIdStore; }
typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer;
GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; }
+ GameObjectBySpawnIdContainer const& GetGameObjectBySpawnIdStore() const { return _gameobjectBySpawnIdStore; }
std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const
{
@@ -525,7 +563,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; }
float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false);
- float GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH);
+ float GetMinHeight(PhaseShift const& phaseShift, float x, float y);
+ float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH);
+ float GetStaticHeight(PhaseShift const& phaseShift, Position const& pos, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetStaticHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), checkVMap, maxSearchDist); }
+ float 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)); }
+ float GetHeight(PhaseShift const& phaseShift, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
@@ -544,28 +586,24 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
time_t GetLinkedRespawnTime(ObjectGuid guid) const;
time_t GetCreatureRespawnTime(ObjectGuid::LowType dbGuid) const
{
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _creatureRespawnTimes.find(dbGuid);
- if (itr != _creatureRespawnTimes.end())
- return itr->second;
-
- return time_t(0);
+ RespawnInfoMap::const_iterator itr = _creatureRespawnTimesBySpawnId.find(dbGuid);
+ return itr != _creatureRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0;
}
time_t GetGORespawnTime(ObjectGuid::LowType dbGuid) const
{
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _goRespawnTimes.find(dbGuid);
- if (itr != _goRespawnTimes.end())
- return itr->second;
-
- return time_t(0);
+ RespawnInfoMap::const_iterator itr = _gameObjectRespawnTimesBySpawnId.find(dbGuid);
+ return itr != _gameObjectRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0;
}
- void SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime);
- void RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid);
- void SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime);
- void RemoveGORespawnTime(ObjectGuid::LowType dbGuid);
+ time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); }
+
+ void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone);
+
+ void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId = 0, bool writeDB = true, bool replace = false, CharacterDatabaseTransaction dbTrans = nullptr);
+ void SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, CharacterDatabaseTransaction dbTrans = nullptr);
void LoadRespawnTimes();
- void DeleteRespawnTimes();
+ void DeleteRespawnTimes() { DeleteRespawnInfo(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); }
void LoadCorpseData();
void DeleteCorpseData();
@@ -733,6 +771,59 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
typedef std::multimap<time_t, ScriptAction> ScriptScheduleMap;
ScriptScheduleMap m_scriptSchedule;
+ public:
+ void ProcessRespawns();
+ void ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const;
+
+ private:
+ // if return value is true, we can respawn
+ // if return value is false, reschedule the respawn to new value of info->respawnTime iff nonzero, delete otherwise
+ // if return value is false and info->respawnTime is nonzero, it is guaranteed to be greater than time(NULL)
+ bool CheckRespawn(RespawnInfo* info);
+ void DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId);
+ void Respawn(RespawnInfo* info, bool force = false, CharacterDatabaseTransaction dbTrans = nullptr);
+ void Respawn(RespawnVector& respawnData, bool force = false, CharacterDatabaseTransaction dbTrans = nullptr);
+ void AddRespawnInfo(RespawnInfo& info, bool replace = false);
+ void DeleteRespawnInfo();
+ void DeleteRespawnInfo(RespawnInfo* info);
+ void DeleteRespawnInfo(RespawnVector& toDelete)
+ {
+ for (RespawnInfo* info : toDelete)
+ DeleteRespawnInfo(info);
+ toDelete.clear();
+ }
+ void DeleteRespawnInfo(SpawnObjectTypeMask types, uint32 zoneId = 0)
+ {
+ RespawnVector v;
+ GetRespawnInfo(v, types, zoneId);
+ if (!v.empty())
+ DeleteRespawnInfo(v);
+ }
+ void DeleteRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId)
+ {
+ if (RespawnInfo* info = GetRespawnInfo(type, spawnId))
+ DeleteRespawnInfo(info);
+ }
+
+ public:
+ void GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId = 0) const;
+ RespawnInfo* GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
+ void RemoveRespawnTime(RespawnInfo* info, bool doRespawn = false, CharacterDatabaseTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn = false, CharacterDatabaseTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, uint32 zoneId = 0, bool doRespawn = false, CharacterDatabaseTransaction dbTrans = nullptr)
+ {
+ RespawnVector v;
+ GetRespawnInfo(v, types, zoneId);
+ if (!v.empty())
+ RemoveRespawnTime(v, doRespawn, dbTrans);
+ }
+ void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, bool doRespawn = false, CharacterDatabaseTransaction dbTrans = nullptr)
+ {
+ if (RespawnInfo* info = GetRespawnInfo(type, spawnId))
+ RemoveRespawnTime(info, doRespawn, dbTrans);
+ }
+
+ private:
// Type specific code for add/remove to/from grid
template<class T>
void AddToGrid(T* object, Cell const& cell);
@@ -761,8 +852,14 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
m_activeNonPlayers.erase(obj);
}
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
+ RespawnListContainer _respawnTimes;
+ RespawnInfoMap _creatureRespawnTimesBySpawnId;
+ RespawnInfoMap _gameObjectRespawnTimesBySpawnId;
+ RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
+ RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
+
+ uint32 _respawnCheckTimer;
+ std::unordered_map<uint32, uint32> _zonePlayerCountMap;
ZoneDynamicInfoMap _zoneDynamicInfo;
IntervalTimer _weatherUpdateTimer;
@@ -806,7 +903,7 @@ class TC_GAME_API InstanceMap : public Map
~InstanceMap();
bool AddPlayerToMap(Player* player, bool initPlayer = true) override;
void RemovePlayerFromMap(Player*, bool) override;
- void Update(const uint32) override;
+ void Update(uint32) override;
void CreateInstanceData(bool load);
bool Reset(uint8 method);
uint32 GetScriptId() const { return i_script_id; }
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index 345cab478e1..9ff42ff557f 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -44,16 +44,22 @@ class TC_GAME_API MapManager
Map* m = CreateBaseMap(mapid);
return m->GetAreaId(phaseShift, x, y, z);
}
+ uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetAreaId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetAreaId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetAreaId(phaseShift, loc.GetMapId(), loc); }
uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z)
{
Map* m = CreateBaseMap(mapid);
return m->GetZoneId(phaseShift, x, y, z);
}
+ uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetZoneId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetZoneId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetZoneId(phaseShift, loc.GetMapId(), loc); }
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
{
Map* m = CreateBaseMap(mapid);
m->GetZoneAndAreaId(phaseShift, zoneid, areaid, x, y, z);
}
+ void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) { GetZoneAndAreaId(phaseShift, zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, WorldLocation const& loc) { GetZoneAndAreaId(phaseShift, zoneid, areaid, loc.GetMapId(), loc); }
void Initialize();
void InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData);
diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h
new file mode 100644
index 00000000000..5d0c8d17e9d
--- /dev/null
+++ b/src/server/game/Maps/SpawnData.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_SPAWNDATA_H
+#define TRINITY_SPAWNDATA_H
+
+#include "DBCEnums.h"
+#include "Position.h"
+#include <vector>
+
+enum SpawnObjectType
+{
+ SPAWN_TYPE_CREATURE = 0,
+ SPAWN_TYPE_GAMEOBJECT = 1,
+
+ SPAWN_TYPE_MAX
+};
+
+enum SpawnObjectTypeMask
+{
+ SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE),
+ SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT),
+
+ SPAWN_TYPEMASK_ALL = (1 << SPAWN_TYPE_MAX)-1
+};
+
+enum SpawnGroupFlags
+{
+ 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_FLAGS_ALL = (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE | SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | SPAWNGROUP_FLAG_ESCORTQUESTNPC)
+};
+
+struct SpawnGroupTemplateData
+{
+ uint32 groupId;
+ std::string name;
+ uint32 mapId;
+ SpawnGroupFlags flags;
+ bool isActive;
+};
+
+struct SpawnData
+{
+ SpawnObjectType const type;
+ uint64 spawnId = 0;
+ uint32 id = 0; // entry in respective _template table
+ WorldLocation spawnPoint;
+ uint8 phaseUseFlags = 0;
+ uint32 phaseId = 0;
+ uint32 phaseGroup = 0;
+ int32 terrainSwapMap = -1;
+ int32 spawntimesecs = 0;
+ std::vector<Difficulty> spawnDifficulties;
+ SpawnGroupTemplateData const* spawnGroupData = nullptr;
+ uint32 scriptId = 0;
+ bool dbData = true;
+
+ protected:
+ SpawnData(SpawnObjectType t) : type(t) {}
+};
+
+#endif
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index df3f8a55ab1..dc0d9fbdee4 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1054,12 +1054,26 @@ enum TrinityStrings
LANG_DEBUG_SCENE_OBJECT_LIST = 5068,
LANG_DEBUG_SCENE_OBJECT_DETAIL = 5069,
- LANG_NPCINFO_UNIT_FIELD_FLAGS_2 = 5070,
- LANG_NPCINFO_UNIT_FIELD_FLAGS_3 = 5071,
- LANG_NPCINFO_NPC_FLAGS = 5072,
-
- // Room for more Trinity strings 5073-9999
-
+ // Strings added for dynamic_spawning
+ LANG_SPAWNINFO_GROUP_ID = 5070,
+ LANG_SPAWNINFO_COMPATIBILITY_MODE = 5071,
+ LANG_SPAWNINFO_GUIDINFO = 5072,
+ LANG_SPAWNINFO_SPAWNID_LOCATION = 5073,
+ LANG_SPAWNINFO_DISTANCEFROMPLAYER = 5074,
+ LANG_SPAWNGROUP_BADGROUP = 5075,
+ LANG_SPAWNGROUP_SPAWNCOUNT = 5076,
+ LANG_LIST_RESPAWNS_RANGE = 5077,
+ LANG_LIST_RESPAWNS_ZONE = 5078,
+ LANG_LIST_RESPAWNS_LISTHEADER = 5079,
+ LANG_LIST_RESPAWNS_OVERDUE = 5080,
+ LANG_LIST_RESPAWNS_CREATURES = 5081,
+ LANG_LIST_RESPAWNS_GAMEOBJECTS = 5082,
+
+ LANG_NPCINFO_UNIT_FIELD_FLAGS_2 = 5084,
+ LANG_NPCINFO_UNIT_FIELD_FLAGS_3 = 5085,
+ LANG_NPCINFO_NPC_FLAGS = 5086,
+
+ // Room for more Trinity strings 5087-6603
// Level requirement notifications
LANG_SAY_REQ = 6604,
LANG_WHISPER_REQ = 6605,
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index e02383b54cc..379dc7e1d07 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -91,7 +91,7 @@ void OPvPCapturePoint::SendChangePhase()
void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid)
{
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
return;
@@ -111,7 +111,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid)
bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot)
{
- if (ObjectGuid::LowType guid = sObjectMgr->AddGOData(entry, map, pos, rot, 0))
+ if (ObjectGuid::LowType guid = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0))
{
AddGO(type, guid);
return true;
@@ -143,7 +143,7 @@ bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, Position co
return false;
}
- m_capturePointSpawnId = sObjectMgr->AddGOData(entry, map, pos, rot, 0);
+ m_capturePointSpawnId = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0);
if (!m_capturePointSpawnId)
return false;
@@ -210,8 +210,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
go->SetRespawnTime(0);
go->Delete();
}
-
- sObjectMgr->DeleteGOData(spawnId);
+ sObjectMgr->DeleteGameObjectData(spawnId);
m_ObjectTypes[m_Objects[type]] = 0;
m_Objects[type] = 0;
return true;
@@ -219,7 +218,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
bool OPvPCapturePoint::DelCapturePoint()
{
- sObjectMgr->DeleteGOData(m_capturePointSpawnId);
+ sObjectMgr->DeleteGameObjectData(m_capturePointSpawnId);
m_capturePointSpawnId = 0;
if (m_capturePoint)
diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp
index d075b7eeabb..f7f1facd90c 100644
--- a/src/server/game/Pools/PoolMgr.cpp
+++ b/src/server/game/Pools/PoolMgr.cpp
@@ -228,7 +228,7 @@ void PoolGroup<Creature>::Despawn1Object(uint64 guid)
{
sObjectMgr->RemoveCreatureFromGrid(guid, data);
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
if (map && !map->Instanceable())
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid);
@@ -236,6 +236,9 @@ void PoolGroup<Creature>::Despawn1Object(uint64 guid)
{
Creature* creature = itr->second;
++itr;
+ // For dynamic spawns, save respawn time here
+ if (!creature->GetRespawnCompatibilityMode())
+ creature->SaveRespawnTime(0, false);
creature->AddObjectToRemoveList();
}
}
@@ -246,11 +249,11 @@ void PoolGroup<Creature>::Despawn1Object(uint64 guid)
template<>
void PoolGroup<GameObject>::Despawn1Object(uint64 guid)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(guid))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid))
{
sObjectMgr->RemoveGameobjectFromGrid(guid, data);
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
if (map && !map->Instanceable())
{
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid);
@@ -258,6 +261,10 @@ void PoolGroup<GameObject>::Despawn1Object(uint64 guid)
{
GameObject* go = itr->second;
++itr;
+
+ // For dynamic spawns, save respawn time here
+ if (!go->GetRespawnCompatibilityMode())
+ go->SaveRespawnTime(0, false);
go->AddObjectToRemoveList();
}
}
@@ -385,9 +392,9 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj)
sObjectMgr->AddCreatureToGrid(obj->guid, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
// We use spawn coords to spawn
- if (map && !map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (map && !map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
Creature::CreateCreatureFromDB(obj->guid, map);
}
}
@@ -396,14 +403,14 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj)
template <>
void PoolGroup<GameObject>::Spawn1Object(PoolObject* obj)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(obj->guid))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(obj->guid))
{
sObjectMgr->AddGameobjectToGrid(obj->guid, data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
- Map* map = sMapMgr->FindMap(data->mapid, 0);
+ Map* map = sMapMgr->FindMap(data->spawnPoint.GetMapId(), 0);
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (map && !map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (map && !map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
if (GameObject* go = GameObject::CreateGameObjectFromDB(obj->guid, map, false))
{
@@ -670,7 +677,7 @@ void PoolMgr::LoadFromDB()
uint32 pool_id = fields[1].GetUInt32();
float chance = fields[2].GetFloat();
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: " UI64FMTD ") defined for pool id (%u), skipped.", guid, pool_id);
@@ -1080,6 +1087,21 @@ void PoolMgr::DespawnPool(uint32 pool_id)
}
}
+// Selects proper template overload to call based on passed type
+uint32 PoolMgr::IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const
+{
+ switch (type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ return IsPartOfAPool<Creature>(spawnId);
+ case SPAWN_TYPE_GAMEOBJECT:
+ return IsPartOfAPool<GameObject>(spawnId);
+ default:
+ ASSERT(false, "Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId " UI64FMTD ")", uint32(type), spawnId);
+ return 0;
+ }
+}
+
// Method that check chance integrity of the creatures and gameobjects in this pool
bool PoolMgr::CheckPool(uint32 pool_id) const
{
diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h
index e89fb8b565b..4961164536b 100644
--- a/src/server/game/Pools/PoolMgr.h
+++ b/src/server/game/Pools/PoolMgr.h
@@ -19,6 +19,7 @@
#define TRINITY_POOLHANDLER_H
#include "Define.h"
+#include "SpawnData.h"
#include <map>
#include <set>
#include <unordered_map>
@@ -122,6 +123,7 @@ class TC_GAME_API PoolMgr
template<typename T>
uint32 IsPartOfAPool(uint64 db_guid_or_pool_id) const;
+ uint32 IsPartOfAPool(SpawnObjectType type, uint64 spawnId) const;
template<typename T>
bool IsSpawnedObject(uint64 db_guid_or_pool_id) const { return mSpawnedData.IsActiveObject<T>(db_guid_or_pool_id); }
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 3ca9702128d..531900b8141 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -1600,16 +1600,40 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co
return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item);
}
-bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map)
+bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map)
{
- ASSERT(actTemplate);
-
+ ASSERT(map);
CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry);
- if (!baseTemplate)
- baseTemplate = actTemplate;
+ ASSERT(baseTemplate);
+
+ // find out which template we'd be using
+ CreatureTemplate const* actTemplate = nullptr;
+ DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(map->GetDifficultyID());
+ while (!actTemplate && difficultyEntry)
+ {
+ int32 idx = CreatureTemplate::DifficultyIDToDifficultyEntryIndex(difficultyEntry->ID);
+ if (idx == -1)
+ break;
+
+ if (baseTemplate->DifficultyEntry[idx])
+ {
+ actTemplate = sObjectMgr->GetCreatureTemplate(baseTemplate->DifficultyEntry[idx]);
+ break;
+ }
+
+ if (!difficultyEntry->FallbackDifficultyID)
+ break;
+
+ difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID);
+ }
+
+ if (!actTemplate)
+ actTemplate = baseTemplate;
+
uint32 scriptId = baseTemplate->ScriptID;
- if (cData && cData->ScriptId)
- scriptId = cData->ScriptId;
+ if (cData && cData->scriptId)
+ scriptId = cData->scriptId;
+
GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, true);
return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map);
}
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index c5e5d1456c5..79925f338ce 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -973,7 +973,7 @@ class TC_GAME_API ScriptMgr
public: /* CreatureScript */
- bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map);
+ bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map);
CreatureAI* GetCreatureAI(Creature* creature);
public: /* GameObjectScript */
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2f8c0f3f510..ad81daa1eac 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -148,6 +148,11 @@ World::World()
memset(m_int64_configs, 0, sizeof(m_int64_configs));
memset(m_bool_configs, 0, sizeof(m_bool_configs));
memset(m_float_configs, 0, sizeof(m_float_configs));
+
+ _guidWarn = false;
+ _guidAlert = false;
+ _warnDiff = 0;
+ _warnShutdownTime = time(NULL);
}
/// World destructor
@@ -224,13 +229,66 @@ std::vector<std::string> const& World::GetMotd() const
return _motd;
}
+void World::TriggerGuidWarning()
+{
+ // Lock this only to prevent multiple maps triggering at the same time
+ std::lock_guard<std::mutex> lock(_guidAlertLock);
+
+ time_t gameTime = GameTime::GetGameTime();
+ time_t today = (gameTime / DAY) * DAY;
+
+ // Check if our window to restart today has passed. 5 mins until quiet time
+ while (gameTime >= (today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1810))
+ today += DAY;
+
+ // Schedule restart for 30 minutes before quiet time, or as long as we have
+ _warnShutdownTime = today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1800;
+
+ _guidWarn = true;
+ SendGuidWarning();
+}
+
+void World::TriggerGuidAlert()
+{
+ // Lock this only to prevent multiple maps triggering at the same time
+ std::lock_guard<std::mutex> lock(_guidAlertLock);
+
+ DoGuidAlertRestart();
+ _guidAlert = true;
+ _guidWarn = false;
+}
+
+void World::DoGuidWarningRestart()
+{
+ if (m_ShutdownTimer)
+ return;
+
+ ShutdownServ(1800, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
+ _warnShutdownTime += HOUR;
+}
+
+void World::DoGuidAlertRestart()
+{
+ if (m_ShutdownTimer)
+ return;
+
+ ShutdownServ(300, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE, _alertRestartReason);
+}
+
+void World::SendGuidWarning()
+{
+ if (!m_ShutdownTimer && _guidWarn && getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) > 0)
+ SendServerMessage(SERVER_MSG_STRING, _guidWarningMsg.c_str());
+ _warnDiff = 0;
+}
+
/// Find a session by its id
WorldSession* World::FindSession(uint32 id) const
{
SessionMap::const_iterator itr = m_sessions.find(id);
if (itr != m_sessions.end())
- return itr->second; // also can return NULL for kicked session
+ return itr->second; // also can return nullptr for kicked session
else
return nullptr;
}
@@ -1295,6 +1353,50 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_NO_GRAY_AGGRO_BELOW] = m_int_configs[CONFIG_NO_GRAY_AGGRO_ABOVE];
}
+ // Respawn Settings
+ m_int_configs[CONFIG_RESPAWN_MINCHECKINTERVALMS] = sConfigMgr->GetIntDefault("Respawn.MinCheckIntervalMS", 5000);
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = sConfigMgr->GetIntDefault("Respawn.DynamicMode", 0);
+ if (m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] > 1)
+ {
+ TC_LOG_ERROR("server.loading", "Invalid value for Respawn.DynamicMode (%u). Set to 0.", m_int_configs[CONFIG_RESPAWN_DYNAMICMODE]);
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = 0;
+ }
+ m_bool_configs[CONFIG_RESPAWN_DYNAMIC_ESCORTNPC] = sConfigMgr->GetBoolDefault("Respawn.DynamicEscortNPC", true);
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidWarnLevel", 12000000);
+ if (m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] > 16777215)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 12000000.", m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL]);
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = 12000000;
+ }
+ m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidAlertLevel", 16000000);
+ if (m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] > 16777215)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 16000000.", m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL]);
+ m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = 16000000;
+ }
+ m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = sConfigMgr->GetIntDefault("Respawn.RestartQuietTime", 3);
+ if (m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] > 23)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.RestartQuietTime (%u) must be an hour, between 0 and 23. Set to 3.", m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME]);
+ m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = 3;
+ }
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateCreature", 10.0f);
+ if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] < 0.0f)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.DynamicRateCreature (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE]);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = 10.0f;
+ }
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumCreature", 10);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateGameObject", 10.0f);
+ if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] < 0.0f)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.DynamicRateGameObject (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT]);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = 10.0f;
+ }
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumGameObject", 10);
+ _guidWarningMsg = sConfigMgr->GetStringDefault("Respawn.WarningMessage", "There will be an unscheduled server restart at 03:00. The server will be available again shortly after.");
+ _alertRestartReason = sConfigMgr->GetStringDefault("Respawn.AlertRestartReason", "Urgent Maintenance");
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNING_FREQUENCY] = sConfigMgr->GetIntDefault("Respawn.WarningFrequency", 1800);
///- Read the "Data" directory from the config file
std::string dataPath = sConfigMgr->GetStringDefault("DataDir", "./");
if (dataPath.empty() || (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\'))
@@ -1763,6 +1865,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Creature Base Stats...");
sObjectMgr->LoadCreatureClassLevelStats();
+ TC_LOG_INFO("server.loading", "Loading Spawn Group Templates...");
+ sObjectMgr->LoadSpawnGroupTemplates();
+
TC_LOG_INFO("server.loading", "Loading Creature Data...");
sObjectMgr->LoadCreatures();
@@ -1779,10 +1884,13 @@ void World::SetInitialWorldSettings()
sObjectMgr->LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
TC_LOG_INFO("server.loading", "Loading Gameobject Data...");
- sObjectMgr->LoadGameobjects();
+ sObjectMgr->LoadGameObjects();
+
+ TC_LOG_INFO("server.loading", "Loading Spawn Group Data...");
+ sObjectMgr->LoadSpawnGroups();
TC_LOG_INFO("server.loading", "Loading GameObject Addon Data...");
- sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects()
+ sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameObjects()
TC_LOG_INFO("server.loading", "Loading GameObject Quest Items...");
sObjectMgr->LoadGameObjectQuestItems();
@@ -2507,6 +2615,16 @@ void World::Update(uint32 diff)
// update the instance reset times
sInstanceSaveMgr->Update();
+ // Check for shutdown warning
+ if (_guidWarn && !_guidAlert)
+ {
+ _warnDiff += diff;
+ if (GameTime::GetGameTime() >= _warnShutdownTime)
+ DoGuidWarningRestart();
+ else if (_warnDiff > getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) * IN_MILLISECONDS)
+ SendGuidWarning();
+ }
+
// And last, but not least handle the issued cli commands
ProcessCliCommands();
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 3f71a0e912e..cd14025fce1 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -216,7 +216,6 @@ enum WorldFloatConfigs
CONFIG_ARENA_WIN_RATING_MODIFIER_2,
CONFIG_ARENA_LOSE_RATING_MODIFIER,
CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER,
- CONFIG_RESPAWN_DYNAMICRADIUS,
CONFIG_RESPAWN_DYNAMICRATE_CREATURE,
CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT,
FLOAT_CONFIG_VALUE_COUNT
@@ -403,13 +402,11 @@ enum WorldIntConfigs
CONFIG_AUCTION_SEARCH_DELAY,
CONFIG_AUCTION_TAINTED_SEARCH_DELAY,
CONFIG_TALENTS_INSPECTING,
- CONFIG_RESPAWN_MINCELLCHECKMS,
+ CONFIG_RESPAWN_MINCHECKINTERVALMS,
CONFIG_RESPAWN_DYNAMICMODE,
CONFIG_RESPAWN_GUIDWARNLEVEL,
CONFIG_RESPAWN_GUIDALERTLEVEL,
CONFIG_RESPAWN_RESTARTQUIETTIME,
- CONFIG_RESPAWN_ACTIVITYSCOPECREATURE,
- CONFIG_RESPAWN_ACTIVITYSCOPEGAMEOBJECT,
CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE,
CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT,
CONFIG_RESPAWN_GUIDWARNING_FREQUENCY,
@@ -795,6 +792,10 @@ class TC_GAME_API World
void ReloadRBAC();
void RemoveOldCorpses();
+ void TriggerGuidWarning();
+ void TriggerGuidAlert();
+ bool IsGuidWarning() { return _guidWarn; }
+ bool IsGuidAlert() { return _guidAlert; }
protected:
void _UpdateGameTime();
@@ -899,7 +900,21 @@ class TC_GAME_API World
AutobroadcastContainer m_Autobroadcasts;
void ProcessQueryCallbacks();
+
+ void SendGuidWarning();
+ void DoGuidWarningRestart();
+ void DoGuidAlertRestart();
QueryCallbackProcessor _queryProcessor;
+
+ std::string _guidWarningMsg;
+ std::string _alertRestartReason;
+
+ std::mutex _guidAlertLock;
+
+ bool _guidWarn;
+ bool _guidAlert;
+ uint32 _warnDiff;
+ time_t _warnShutdownTime;
};
TC_GAME_API extern Realm realm;