aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorr00ty-tc <r00ty-tc@users.noreply.github.com>2017-05-07 21:48:41 +0100
committerTreeston <treeston.mmoc@gmail.com>2017-07-31 21:21:04 +0200
commit59db2eeea0a35028779fd76372ae06cc98c8086f (patch)
treea9532989868948cb309cb8d76a2e76a017fa9246 /src/server
parentd24ce1739a799042d5a164794c09674227c8572c (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.
Diffstat (limited to 'src/server')
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp1
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h1
-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.cpp41
-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.h21
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp7
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp2
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp384
-rw-r--r--src/server/game/Entities/Creature/Creature.h15
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h39
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp263
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h14
-rw-r--r--src/server/game/Entities/GameObject/GameObjectData.h25
-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.h5
-rw-r--r--src/server/game/Entities/Object/Position.h25
-rw-r--r--src/server/game/Entities/Player/Player.cpp2
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp20
-rw-r--r--src/server/game/Events/GameEventMgr.cpp26
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp453
-rw-r--r--src/server/game/Globals/ObjectMgr.h58
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h26
-rw-r--r--src/server/game/Grids/ObjectGridLoader.cpp47
-rw-r--r--src/server/game/Maps/Map.cpp496
-rw-r--r--src/server/game/Maps/Map.h160
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Maps/SpawnData.h77
-rw-r--r--src/server/game/Miscellaneous/Language.h27
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp10
-rw-r--r--src/server/game/Pools/PoolMgr.cpp44
-rw-r--r--src/server/game/Pools/PoolMgr.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp30
-rw-r--r--src/server/game/Scripting/ScriptMgr.h2
-rw-r--r--src/server/game/World/World.cpp122
-rw-r--r--src/server/game/World/World.h23
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp2
-rw-r--r--src/server/scripts/Commands/cs_go.cpp30
-rw-r--r--src/server/scripts/Commands/cs_gobject.cpp65
-rw-r--r--src/server/scripts/Commands/cs_list.cpp140
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp12
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp194
-rw-r--r--src/server/scripts/Commands/cs_wp.cpp17
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp8
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp7
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp2
-rw-r--r--src/server/worldserver/worldserver.conf.dist117
58 files changed, 2453 insertions, 749 deletions
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index c64e7a9c2fc..654074d594b 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -92,6 +92,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_SPAWNGROUP_MEMBER, "DELETE FROM spawn_group WHERE spawnType = ? AND spawnId = ?", CONNECTION_ASYNC);
}
WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
index b68f53bb173..f7784ad256f 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.h
+++ b/src/server/database/Database/Implementation/WorldDatabase.h
@@ -98,6 +98,7 @@ enum WorldDatabaseStatements : uint32
WORLD_DEL_DISABLES,
WORLD_UPD_CREATURE_ZONE_AREA_DATA,
WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA,
+ WORLD_DEL_SPAWNGROUP_MEMBER,
MAX_WORLDDATABASE_STATEMENTS
};
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index bd7a6efab22..c355eaacb58 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -187,6 +187,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 e9e252bcf35..afa7e789b7a 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -478,7 +478,7 @@ void BossAI::_Reset()
events.Reset();
summons.DespawnAll();
scheduler.CancelAll();
- if (instance)
+ if (instance && instance->GetBossState(_bossId) != DONE)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -564,12 +564,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 %ld 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 6fc85b6a81a..9f2e49c1ffb 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -366,8 +366,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 bcf060e2ad6..5f5330c3c8d 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -239,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;
}
@@ -280,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();
@@ -424,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 +593,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())
+ return true;
+
+ return false;
+}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index 754d96dced9..7d6f210f034 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -107,6 +107,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 03ad4df397a..79f3b7ce652 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2052,6 +2052,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 31282a934ac..3555575b2b2 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -228,7 +228,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
}
case SMART_SCRIPT_TYPE_GAMEOBJECT:
{
- GameObjectData const* gameObject = sObjectMgr->GetGOData(uint32(std::abs(temp.entryOrGuid)));
+ GameObjectData const* gameObject = sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid)));
if (!gameObject)
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid (%u) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid)));
@@ -932,7 +932,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;
@@ -1508,6 +1508,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 %d 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 7a1433bc7fb..f2ba244aaae 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -589,8 +589,12 @@ enum SMART_ACTION
SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126,
SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired
SMART_ACTION_PLAY_ANIMKIT = 128, // don't use on 3.3.5a
+ SMART_ACTION_SCENE_PLAY = 129, // don't use on 3.3.5a
+ SMART_ACTION_SCENE_CANCEL = 130, // don't use on 3.3.5a
+ SMART_ACTION_SPAWN_SPAWNGROUP = 131, // Group ID, min secs, max secs, spawnflags
+ SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // Group ID, min secs, max secs, spawnflags
- SMART_ACTION_END = 129
+ SMART_ACTION_END = 133
};
struct SmartAction
@@ -1094,6 +1098,13 @@ struct SmartAction
{
uint32 disable;
} disableEvade;
+ struct
+ {
+ uint32 groupId;
+ uint32 minDelay;
+ uint32 maxDelay;
+ uint32 spawnflags;
+ } groupSpawn;
struct
{
@@ -1138,6 +1149,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/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index 27e83185e27..92b78216cb0 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -776,18 +776,15 @@ Creature* Battlefield::SpawnCreature(uint32 entry, Position const& pos)
return nullptr;
}
- float x, y, z, o;
- pos.GetPosition(x, y, z, o);
-
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, pos))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u", entry);
delete creature;
return nullptr;
}
- creature->SetHomePosition(x, y, z, o);
+ creature->SetHomePosition(pos);
// Set creature in world
map->AddToMap(creature);
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 41591c419e9..e184c22354e 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1515,7 +1515,7 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y,
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, { x, y, z, o }))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: cannot create creature (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 146d2cb3bde..ba456998042 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -2011,7 +2011,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 cbfab8004f6..bcca31ed94e 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -239,7 +239,7 @@ m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), m_corpseR
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(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;
@@ -333,43 +333,60 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers)
if (getDeathState() != CORPSE)
return;
- m_corpseRemoveTime = time(nullptr);
- setDeathState(DEAD);
- RemoveAllAuras();
- 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();
+ }
}
/**
@@ -579,30 +596,34 @@ 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 (GUID: %u Entry: %u) in wrong state: JUST_DEAD (1)", GetGUID().GetCounter(), GetEntry());
+ TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: JUST_DIED (1)", GetGUID().GetCounter(), GetEntry());
break;
case DEAD:
{
time_t now = time(nullptr);
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(HighGuid::Unit, 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
@@ -947,12 +968,16 @@ void Creature::Motion_Initialize()
GetMotionMaster()->Initialize();
}
-bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, 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 phaseMask, uint32 entry, Position const& pos, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/, bool dynamic)
{
ASSERT(map);
SetMap(map);
SetPhaseMask(phaseMask, false);
+ // Set if this creature can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
@@ -962,13 +987,13 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! 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 %d, 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 %d, 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();
@@ -1005,10 +1030,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
if (HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
{
- z += GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
-
//! Relocate again with updated Z coord
- Relocate(x, y, z, ang);
+ m_positionZ += GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
}
LastUsedScriptID = GetScriptId();
@@ -1188,27 +1211,17 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
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.phaseMask = phaseMask;
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;
@@ -1222,6 +1235,8 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
data.npcflag = npcflag;
data.unit_flags = unit_flags;
data.dynamicflags = dynamicflags;
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
// update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
@@ -1426,7 +1441,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)
{
@@ -1467,31 +1482,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::Unit>(), map, data->phaseMask, data->id, data->posX, data->posY, data->posZ, data->orientation, data))
+
+ // 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::Unit>(), map, data->phaseMask, 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(GetPhaseMask(), 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(GetPhaseMask(), data->spawnPoint, true, MAX_FALL_DISTANCE);
+ if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz))
+ Relocate(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY(), tz);
}
}
@@ -1587,7 +1612,7 @@ void Creature::DeleteFromDB()
return;
}
- GetMap()->RemoveCreatureRespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
sObjectMgr->DeleteCreatureData(m_spawnId);
SQLTransaction trans = WorldDatabase.BeginTransaction();
@@ -1596,6 +1621,11 @@ void Creature::DeleteFromDB()
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_CREATURE));
+ stmt->setUInt32(1, m_spawnId);
+ trans->Append(stmt);
+
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
@@ -1735,14 +1765,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
@@ -1810,8 +1857,6 @@ void Creature::setDeathState(DeathState s)
void Creature::Respawn(bool force)
{
- DestroyForNearbyPlayers();
-
if (force)
{
if (IsAlive())
@@ -1820,51 +1865,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);
- if (m_originalEntry != GetEntry())
- UpdateEntry(m_originalEntry);
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str());
+ m_respawnTime = 0;
+ ResetPickPocketRefillTimer();
+ loot.clear();
- SelectLevel();
+ if (m_originalEntry != GetEntry())
+ UpdateEntry(m_originalEntry);
- setDeathState(JUST_RESPAWNED);
+ SelectLevel();
- uint32 displayID = GetNativeDisplayId();
- if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
- {
- SetDisplayId(displayID);
- SetNativeDisplayId(displayID);
- }
+ setDeathState(JUST_RESPAWNED);
- GetMotionMaster()->InitDefault();
- //Re-initialize reactstate that could be altered by movementgenerators
- InitializeReactState();
+ uint32 displayID = GetNativeDisplayId();
+ if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
+ {
+ SetDisplayId(displayID);
+ SetNativeDisplayId(displayID);
+ }
- //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
- }
+ GetMotionMaster()->InitDefault();
+ //Re-initialize reactstate that could be altered by movementgenerators
+ InitializeReactState();
+
+ //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);
+ }
+
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
+ GetName().c_str(), GetGUID().ToString().c_str());
- UpdateObjectVisibility();
}
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawnTimer)
@@ -1875,30 +1931,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 = true;
- 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*/)
@@ -2245,12 +2319,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(GetHomePosition()), Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), m_creatureData->dbData && savetodb);
}
// this should not be called by petAI or
@@ -2462,33 +2543,26 @@ time_t Creature::GetRespawnTimeEx() const
void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, float* dist) const
{
- if (m_spawnId)
+ if (m_creatureData)
{
- // for npcs on transport, this will return transport offset
- 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;
+ if (ori)
+ m_creatureData->spawnPoint.GetPosition(x, y, z, *ori);
+ else
+ m_creatureData->spawnPoint.GetPosition(x, y, z);
- return;
- }
+ 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;
}
-
- // 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;
}
void Creature::AllLootRemovedFromCorpse()
@@ -2539,7 +2613,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;
@@ -3082,3 +3156,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 cc12efbd7eb..8de86af1dff 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -72,7 +72,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void DisappearAndDie();
- bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0);
+ bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, Position const& pos, CreatureData const* data = nullptr, uint32 vehId = 0, bool dynamic = false);
bool LoadCreaturesAddon();
void SelectLevel();
void UpdateLevelDependantStats();
@@ -83,7 +83,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; }
@@ -176,8 +176,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); }
- bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false);
+ bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate);
void SaveToDB();
// overriden in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
@@ -239,7 +238,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; }
@@ -313,6 +312,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;
@@ -336,6 +339,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;
@@ -404,6 +408,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 754963f9812..de169c97136 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -20,6 +20,7 @@
#include "DBCEnums.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "UnitDefines.h"
#include "WorldPacket.h"
#include <string>
@@ -235,33 +236,19 @@ struct EquipmentInfo
};
// from `creature` table
-struct CreatureData
+struct CreatureData : public SpawnData
{
- CreatureData() : id(0), mapid(0), phaseMask(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),
- spawnMask(0), npcflag(0), unit_flags(0), dynamicflags(0), ScriptId(0), dbData(true) { }
- uint32 id; // entry in creature_template
- uint16 mapid;
- uint32 phaseMask;
- uint32 displayid;
- int8 equipmentId;
- float posX;
- float posY;
- float posZ;
- float orientation;
- uint32 spawntimesecs;
- float spawndist;
- uint32 currentwaypoint;
- uint32 curhealth;
- uint32 curmana;
- uint8 movementType;
- uint8 spawnMask;
- uint32 npcflag;
- uint32 unit_flags; // enum UnitFlags mask values
- uint32 dynamicflags;
- uint32 ScriptId;
- bool dbData;
+ 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;
+ uint32 npcflag = 0;
+ uint32 unit_flags = 0;
+ uint32 dynamicflags = 0;
};
struct CreatureModelInfo
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 6ae82ea3ceb..3717b16280e 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -101,7 +101,7 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X)
}
GameObject::GameObject() : WorldObject(false), MapObject(),
- m_model(nullptr), m_goValue(), m_AI(nullptr)
+ m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
@@ -240,7 +240,7 @@ void GameObject::RemoveFromWorld()
}
}
-bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/)
+bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/, bool dynamic, ObjectGuid::LowType spawnid)
{
ASSERT(map);
SetMap(map);
@@ -253,6 +253,10 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
return false;
}
+ // Set if this object can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
SetPhaseMask(phaseMask, false);
UpdatePositionData();
@@ -376,6 +380,9 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
if (map->Is25ManRaid())
loot.maxDuplicates = 3;
+ if (spawnid)
+ m_spawnId = spawnid;
+
if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject();
@@ -487,83 +494,90 @@ void GameObject::Update(uint32 diff)
}
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(HighGuid::GameObject, 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(HighGuid::GameObject, 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);
-
- WorldPacket data(SMSG_FISH_ESCAPED, 0);
- caster->ToPlayer()->SendDirectMessage(&data);
+ Unit* caster = GetOwner();
+ if (caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ caster->ToPlayer()->RemoveGameObject(this, false);
+
+ WorldPacket data(SMSG_FISH_ESCAPED, 0);
+ caster->ToPlayer()->SendDirectMessage(&data);
+ }
+ // 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.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
+ 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.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
- 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();
@@ -751,6 +765,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;
@@ -758,12 +773,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;
}
@@ -849,7 +880,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!");
@@ -869,22 +900,22 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
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.spawnPoint.WorldRelocate(this);
data.phaseMask = phaseMask;
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZ();
- data.orientation = GetOrientation();
data.rotation = m_worldRotation;
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
- data.go_state = GetGoState();
+ data.goState = GetGoState();
data.spawnMask = spawnMask;
data.artKit = GetGoArtKit();
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
// Update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
@@ -917,9 +948,9 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
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)
{
@@ -930,14 +961,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
uint32 phaseMask = data->phaseMask;
- 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(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, pos, data->rotation, animprogress, go_state, artKit))
+ m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0);
+ if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode))
return false;
if (data->spawntimesecs >= 0)
@@ -959,7 +990,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);
}
}
}
@@ -980,20 +1011,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);
- PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
+ SQLTransaction trans = WorldDatabase.BeginTransaction();
+ PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_spawnId);
+ trans->Append(stmt);
- WorldDatabase.Execute(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_GAMEOBJECT));
+ stmt->setUInt32(1, m_spawnId);
+ trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
-
stmt->setUInt32(0, m_spawnId);
-
- WorldDatabase.Execute(stmt);
+ trans->Append(stmt);
+
+ WorldDatabase.CommitTransaction(trans);
}
/*********************************************************/
@@ -1056,10 +1092,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::IsNeverVisible() const
@@ -1124,7 +1169,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);
}
}
@@ -1228,7 +1273,7 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f
void GameObject::SetGoArtKit(uint8 kit)
{
SetByteValue(GAMEOBJECT_BYTES_1, 2, kit);
- GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGOData(m_spawnId));
+ GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId));
if (data)
data->artKit = kit;
}
@@ -1239,10 +1284,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;
@@ -1972,8 +2017,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;
@@ -2411,24 +2456,20 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t
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 3080bd94860..784af8fda27 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -89,11 +89,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void RemoveFromWorld() override;
void CleanupsBeforeDelete(bool finalCleanup = true) override;
- bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0);
+ bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0);
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;
@@ -113,8 +113,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void SaveToDB();
void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
- bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadGameObjectFromDB(spawnId, map, false); }
- bool LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true);
+ 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)
@@ -212,7 +211,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetUseCount() const { return m_usetimes; }
uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
- void SaveRespawnTime() override;
+ void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override;
Loot loot;
@@ -264,6 +263,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; }
@@ -344,5 +347,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
}
GameObjectAI* m_AI;
+ bool m_respawnCompatibilityMode;
};
#endif
diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h
index e2d2f27e5b5..6e1a07786f9 100644
--- a/src/server/game/Entities/GameObject/GameObjectData.h
+++ b/src/server/game/Entities/GameObject/GameObjectData.h
@@ -20,6 +20,7 @@
#include "Common.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "WorldPacket.h"
#include <string>
#include <vector>
@@ -593,26 +594,14 @@ struct GameObjectAddon
uint32 InvisibilityValue;
};
-// from `gameobject`
-struct GameObjectData
+// `gameobject` table
+struct GameObjectData : public SpawnData
{
- explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
- animprogress(0), go_state(GO_STATE_ACTIVE), spawnMask(0), artKit(0), ScriptId(0), dbData(true) { }
- uint32 id; // entry in gamobject_template
- uint16 mapid;
- uint32 phaseMask;
- float posX;
- float posY;
- float posZ;
- float orientation;
+ GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) { }
QuaternionData rotation;
- int32 spawntimesecs;
- uint32 animprogress;
- GOState go_state;
- uint8 spawnMask;
- uint8 artKit;
- 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 508a89a86c8..d7dc720f55f 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1908,7 +1908,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert
break;
}
- if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId))
+ if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, 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 ca1b547f68b..2e0bef23916 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -342,7 +342,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void SendObjectDeSpawnAnim(ObjectGuid guid);
- 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 3a26dfd0d7f..71837474ca9 100644
--- a/src/server/game/Entities/Object/ObjectGuid.cpp
+++ b/src/server/game/Entities/Object/ObjectGuid.cpp
@@ -98,6 +98,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();
+}
+
#define GUID_TRAIT_INSTANTIATE_GUID( HIGH_GUID ) \
template class TC_GAME_API ObjectGuidGenerator< HIGH_GUID >;
diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h
index 75410aa4f28..d1e34d3a38c 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -287,6 +287,7 @@ public:
protected:
static void HandleCounterOverflow(HighGuid high);
+ static void CheckGuidTrigger(ObjectGuid::LowType guid);
ObjectGuid::LowType _nextGuid;
};
@@ -300,6 +301,10 @@ public:
{
if (_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1)
HandleCounterOverflow(high);
+
+ if (high == HighGuid::Unit || high == HighGuid::GameObject)
+ CheckGuidTrigger(_nextGuid);
+
return _nextGuid++;
}
};
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index 98cb82df2e1..11867c6fed6 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -207,15 +207,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(Position const& center, float xradius, float yradius, float zradius) const;
@@ -245,15 +239,12 @@ class WorldLocation : public Position
WorldLocation(WorldLocation const& loc)
: Position(loc), m_mapId(loc.GetMapId()) { }
- void WorldRelocate(WorldLocation const& loc)
- {
- m_mapId = loc.GetMapId();
- Relocate(loc);
- }
-
- void WorldRelocate(uint32 _mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
+ 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;
+ m_mapId = mapId;
Relocate(x, y, z, o);
}
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 9d9323e8d3f..c445f53190c 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -7006,6 +7006,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())
SetGroupUpdateFlag(GROUP_UPDATE_FULL);
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index 6d36095ee7d..2b9b82b392b 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -302,16 +302,14 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c
Map* map = GetMap();
Creature* creature = new Creature();
- if (!creature->LoadCreatureFromDB(guid, map, false))
+ if (!creature->LoadFromDB(guid, map, false, true))
{
delete creature;
return nullptr;
}
- 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->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
@@ -349,7 +347,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
Map* map = GetMap();
GameObject* go = new GameObject();
- if (!go->LoadGameObjectFromDB(guid, map, false))
+ if (!go->LoadFromDB(guid, map, false))
{
delete go;
return nullptr;
@@ -357,10 +355,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();
@@ -468,7 +464,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::Unit>(), map, phase, entry, x, y, z, o, nullptr, vehId))
+ if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, phase, 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 fc00ce5ee9e..307f5674b9c 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -436,7 +436,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: %u) not found in `gameobject` table.", guid);
@@ -1140,7 +1140,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)
{
@@ -1203,13 +1203,13 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
sObjectMgr->AddCreatureToGrid(*itr, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
Creature* creature = new Creature();
//TC_LOG_DEBUG("misc", "Spawning creature %u", *itr);
- if (!creature->LoadCreatureFromDB(*itr, map))
+ if (!creature->LoadFromDB(*itr, map, true, false))
delete creature;
}
}
@@ -1225,19 +1225,19 @@ 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->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
GameObject* pGameobject = new GameObject;
//TC_LOG_DEBUG("misc", "Spawning gameobject %u", *itr);
/// @todo find out when it is add to map
- if (!pGameobject->LoadGameObjectFromDB(*itr, map, false))
+ if (!pGameobject->LoadFromDB(*itr, map, false))
delete pGameobject;
else
{
@@ -1280,7 +1280,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;)
@@ -1306,11 +1306,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;)
@@ -1344,7 +1344,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);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index c6cb3c06b78..451cd847213 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -1123,7 +1123,7 @@ void ObjectMgr::LoadGameObjectAddons()
ObjectGuid::LowType guid = fields[0].GetUInt32();
- GameObjectData const* goData = GetGOData(guid);
+ GameObjectData const* goData = GetGameObjectData(guid);
if (!goData)
{
TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid);
@@ -1461,8 +1461,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* 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 '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1490,7 +1490,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- GameObjectData const* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
@@ -1498,8 +1498,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* 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 '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1519,7 +1519,7 @@ void ObjectMgr::LoadLinkedRespawn()
}
case GO_TO_GO:
{
- GameObjectData const* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
@@ -1527,7 +1527,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- GameObjectData const* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
@@ -1535,8 +1535,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* 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 '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1556,7 +1556,7 @@ void ObjectMgr::LoadLinkedRespawn()
}
case GO_TO_CREATURE:
{
- GameObjectData const* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
@@ -1572,8 +1572,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* 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 '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1626,8 +1626,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 '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
return false;
@@ -1741,8 +1741,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, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
// 22
@@ -1782,14 +1782,11 @@ 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();
@@ -1803,18 +1800,19 @@ void ObjectMgr::LoadCreatures()
data.npcflag = fields[19].GetUInt32();
data.unit_flags = fields[20].GetUInt32();
data.dynamicflags = fields[21].GetUInt32();
- data.ScriptId = GetScriptId(fields[22].GetString());
+ data.scriptId = GetScriptId(fields[22].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: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId());
continue;
}
// Skip spawnMask check for transport maps
- if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid);
+ if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.spawnPoint.GetMapId());
bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
@@ -1879,17 +1877,11 @@ void ObjectMgr::LoadCreatures()
data.phaseMask = 1;
}
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position::NormalizeOrientation(data.orientation);
- }
-
if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA))
{
uint32 zoneId = 0;
uint32 areaId = 0;
- sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
@@ -1916,8 +1908,8 @@ void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const*
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.creatures.insert(guid);
}
}
@@ -1930,14 +1922,14 @@ void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData co
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][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)
@@ -1947,41 +1939,40 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position co
if (!map)
return 0;
- ObjectGuid::LowType guid = GenerateGameObjectSpawnId();
+ ObjectGuid::LowType spawnId = GenerateGameObjectSpawnId();
- GameObjectData& data = NewGOData(guid);
+ 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.spawnMask = 1;
- data.go_state = GO_STATE_READY;
+ data.goState = GO_STATE_READY;
data.phaseMask = PHASEMASK_NORMAL;
data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 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 = new GameObject;
- if (!go->LoadGameObjectFromDB(guid, map))
+ if (!go->LoadFromDB(spawnId, map, true))
{
- 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);
delete go;
return 0;
}
}
- TC_LOG_DEBUG("maps", "AddGOData: dbguid %u 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 %u entry %u map %u pos %s", spawnId, entry, mapId, data.spawnPoint.ToString().c_str());
- return guid;
+ return spawnId;
}
@@ -1997,15 +1988,13 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
if (!map)
return 0;
- 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;
@@ -2018,14 +2007,15 @@ 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 = new Creature();
- if (!creature->LoadCreatureFromDB(guid, map))
+ if (!creature->LoadFromDB(spawnId, map, true, true))
{
TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry);
delete creature;
@@ -2033,10 +2023,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
}
}
- return guid;
+ return spawnId;
}
-void ObjectMgr::LoadGameobjects()
+void ObjectMgr::LoadGameObjects()
{
uint32 oldMSTime = getMSTime();
@@ -2100,22 +2090,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: %u 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: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.spawnPoint.GetMapId());
continue;
}
@@ -2133,24 +2121,18 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
continue;
}
- data.go_state = GOState(go_state);
+ data.goState = GOState(go_state);
data.spawnMask = fields[14].GetUInt8();
- if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid);
+ if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.spawnPoint.GetMapId());
data.phaseMask = fields[15].GetUInt32();
int16 gameEvent = fields[16].GetInt8();
uint32 PoolId = fields[17].GetUInt32();
- data.ScriptId = GetScriptId(fields[18].GetString());
-
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u 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[18].GetString());
if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
{
@@ -2176,7 +2158,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: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
continue;
@@ -2192,7 +2174,7 @@ void ObjectMgr::LoadGameobjects()
{
uint32 zoneId = 0;
uint32 areaId = 0;
- sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
@@ -2211,6 +2193,153 @@ 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].GetUInt32();
+
+ SpawnData const* data = GetSpawnData(spawnType, spawnId);
+ if (!data)
+ {
+ TC_LOG_ERROR("server.loading", "Spawn data with ID (%u,%u) 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,%u) 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,%u), 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,%u) 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,%u) 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,%u) 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)
{
uint8 mask = data->spawnMask;
@@ -2218,8 +2347,8 @@ void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData con
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.gameobjects.insert(guid);
}
}
@@ -2232,8 +2361,8 @@ void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectDat
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.gameobjects.erase(guid);
}
}
@@ -4881,7 +5010,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",
@@ -4931,7 +5060,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",
@@ -6586,12 +6715,142 @@ uint32 ObjectMgr::GenerateGameObjectSpawnId()
{
if (_gameObjectSpawnId >= uint32(0xFFFFFF))
{
- 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 %u", 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 %u.", 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();
@@ -7497,17 +7756,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 95e7019dacf..7c28b21ebea 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -23,8 +23,10 @@
#include "ConditionMgr.h"
#include "CreatureData.h"
#include "DatabaseEnvFwd.h"
+#include "Errors.h"
#include "GameObjectData.h"
#include "ItemTemplate.h"
+#include "IteratorPair.h"
#include "NPCHandler.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
@@ -38,6 +40,7 @@
class Item;
class Unit;
class Vehicle;
+class Map;
struct AccessRequirement;
struct DeclinedName;
struct DungeonEncounterEntry;
@@ -529,6 +532,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;
@@ -803,6 +808,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 LanguageDesc
{
@@ -1120,7 +1126,9 @@ class TC_GAME_API ObjectMgr
void LoadCreatureModelInfo();
void LoadEquipmentTemplates();
void LoadGameObjectLocales();
- void LoadGameobjects();
+ void LoadGameObjects();
+ void LoadSpawnGroupTemplates();
+ void LoadSpawnGroups();
void LoadItemTemplates();
void LoadItemLocales();
void LoadItemSetNames();
@@ -1202,8 +1210,16 @@ class TC_GAME_API ObjectMgr
uint64 GenerateEquipmentSetGuid();
uint32 GenerateMailID();
uint32 GeneratePetNumber();
- uint32 GenerateCreatureSpawnId();
- uint32 GenerateGameObjectSpawnId();
+ ObjectGuid::LowType GenerateCreatureSpawnId();
+ ObjectGuid::LowType GenerateGameObjectSpawnId();
+
+ 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(uint32 level, uint32 raceMask) const
{
@@ -1254,6 +1270,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);
@@ -1274,6 +1301,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);
@@ -1323,15 +1358,6 @@ class TC_GAME_API ObjectMgr
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);
@@ -1349,7 +1375,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
@@ -1458,8 +1484,8 @@ class TC_GAME_API ObjectMgr
std::atomic<uint32> _mailId;
std::atomic<uint32> _hiPetNumber;
- uint32 _creatureSpawnId;
- uint32 _gameObjectSpawnId;
+ ObjectGuid::LowType _creatureSpawnId;
+ ObjectGuid::LowType _gameObjectSpawnId;
// first free low guid for selected guid type
template<HighGuid high>
@@ -1579,6 +1605,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;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 897e43d5e03..acbe5e641e8 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -552,6 +552,11 @@ namespace Trinity
: ContainerInserter<Player*>(container),
i_phaseMask(searcher->GetPhaseMask()), i_check(check) { }
+ template<typename Container>
+ PlayerListSearcher(uint32 phaseMask, Container& container, Check & check)
+ : ContainerInserter<Player*>(container),
+ i_phaseMask(phaseMask), i_check(check) { }
+
void Visit(PlayerMapType &m);
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { }
@@ -1321,6 +1326,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 653c9d51d11..6400e1532a6 100644
--- a/src/server/game/Grids/ObjectGridLoader.cpp
+++ b/src/server/game/Grids/ObjectGridLoader.cpp
@@ -27,6 +27,7 @@
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "World.h"
+#include "ScriptMgr.h"
void ObjectGridEvacuator::Visit(CreatureMapType &m)
{
@@ -119,15 +120,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);
- AddObjectHelper(cell, m, count, map, obj);
+ if (obj->GetTypeId() == TYPEID_UNIT)
+ {
+ CreatureData const* cdata = sObjectMgr->GetCreatureData(guid);
+ ASSERT(cdata, "Tried to load creature with spawnId %u, 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(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 %u, but no such object exists.", guid);
+ if ((godata->spawnGroupData->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN) && !godata->spawnGroupData->isActive)
+ continue;
+ }
+
+ 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 0793422f8ad..2e9974d79d2 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"
@@ -37,6 +38,7 @@
#include "ObjectGridLoader.h"
#include "ObjectMgr.h"
#include "Pet.h"
+#include "PoolMgr.h"
#include "ScriptMgr.h"
#include "Transport.h"
#include "Vehicle.h"
@@ -52,6 +54,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];
@@ -61,6 +64,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();
@@ -253,7 +260,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(GetDefaultMapLight(id))
+i_scriptLock(false), _respawnCheckTimer(0), _defaultLight(GetDefaultMapLight(id))
{
m_parentMap = (_parent ? _parent : this);
for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
@@ -266,6 +273,8 @@ i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
}
}
+ _zonePlayerCountMap.clear();
+
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
@@ -522,6 +531,29 @@ bool Map::EnsureGridLoaded(Cell const& cell)
return false;
}
+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));
@@ -689,6 +721,21 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::Obj
}
}
+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);
@@ -704,6 +751,16 @@ void Map::Update(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();
@@ -885,6 +942,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
@@ -2639,10 +2698,7 @@ void Map::GetFullTerrainStatusForPosition(float x, float y, float z, PositionFul
else
{
data.floorZ = mapHeight;
- if (gmap)
- data.areaId = gmap->getArea(x, y);
- else
- data.areaId = 0;
+ data.areaId = gmap->getArea(x, y);
if (!data.areaId)
data.areaId = i_mapEntry->linked_zone;
@@ -2744,11 +2800,6 @@ bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float
return result;
}
-float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist/*=DEFAULT_HEIGHT_SEARCH*/) const
-{
- return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist));
-}
-
bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const
{
LiquidData liquid_status;
@@ -2867,6 +2918,326 @@ void Map::SendObjectUpdates()
}
}
+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 %u 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 = ObjectGuid((info->type == SPAWN_TYPE_GAMEOBJECT) ? HighGuid::GameObject : HighGuid::Unit, 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 %u) 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 %u) on map %u", uint32(type), spawnId, GetId());
+ }
+}
+
+void Map::Respawn(RespawnInfo* info, bool force, SQLTransaction 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, SQLTransaction dbTrans)
+{
+ SQLTransaction 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,%u) 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 %u (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, SQLTransaction dbTrans)
+{
+ PreparedStatement* 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 %u map %u", uint32(info->type), info->spawnId, GetId());
+ return;
+ }
+ stmt->setUInt32(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, SQLTransaction dbTrans)
+{
+ SQLTransaction 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();)
@@ -3693,6 +4064,34 @@ Creature* Map::GetCreature(ObjectGuid const& guid)
return _objectsStore.Find<Creature>(guid);
}
+Creature* Map::GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const
+{
+ auto const bounds = GetCreatureBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
+
+ std::unordered_multimap<uint32, 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;
+}
+
+GameObject* Map::GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const
+{
+ auto const bounds = GetGameObjectBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
+
+ std::unordered_multimap<uint32, GameObject*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::GameObjectBySpawnIdContainer::value_type const& pair)
+ {
+ return pair.second->isSpawned();
+ });
+
+ return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
+}
+
GameObject* Map::GetGameObject(ObjectGuid const& guid)
{
return _objectsStore.Find<GameObject>(guid);
@@ -3723,64 +4122,37 @@ void Map::UpdateIteratorBack(Player* player)
m_mapRefIter = m_mapRefIter->nocheck_prev();
}
-void Map::SaveCreatureRespawnTime(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, SQLTransaction dbTrans)
{
if (!respawnTime)
{
// Delete only
- RemoveCreatureRespawnTime(dbGuid);
+ RemoveRespawnTime(type, spawnId, false, dbTrans);
return;
}
- _creatureRespawnTimes[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);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN);
- stmt->setUInt32(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::RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid)
+void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans)
{
- _creatureRespawnTimes.erase(dbGuid);
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
- stmt->setUInt32(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
-}
-
-void Map::SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime)
-{
- if (!respawnTime)
- {
- // Delete only
- RemoveGORespawnTime(dbGuid);
- return;
- }
-
- _goRespawnTimes[dbGuid] = respawnTime;
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN);
- stmt->setUInt32(0, dbGuid);
+ // Just here for support of compatibility mode
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement((type == SPAWN_TYPE_GAMEOBJECT) ? CHAR_REP_GO_RESPAWN : CHAR_REP_CREATURE_RESPAWN);
+ stmt->setUInt32(0, spawnId);
stmt->setUInt64(1, uint64(respawnTime));
stmt->setUInt16(2, GetId());
stmt->setUInt32(3, GetInstanceId());
- CharacterDatabase.Execute(stmt);
-}
-
-void Map::RemoveGORespawnTime(ObjectGuid::LowType dbGuid)
-{
- _goRespawnTimes.erase(dbGuid);
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
- stmt->setUInt32(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
}
void Map::LoadRespawnTimes()
@@ -3796,7 +4168,9 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt32();
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(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
+
} while (result->NextRow());
}
@@ -3811,19 +4185,13 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt32();
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(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)
{
PreparedStatement* 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 dd0e43158c1..d7613f79de5 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -21,16 +21,18 @@
#include "Define.h"
-#include "GridDefines.h"
#include "Cell.h"
-#include "Timer.h"
-#include "SharedDefines.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 "Transaction.h"
+#include <boost/heap/fibonacci_heap.hpp>
#include <bitset>
#include <list>
#include <memory>
@@ -271,7 +273,37 @@ struct ZoneDynamicInfo
typedef std::map<uint32/*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<uint32, 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,%u) found!", a->type, a->spawnId);
+ return a->type < b->type;
+}
class TC_GAME_API Map : public GridRefManager<NGridType>
{
@@ -321,17 +353,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(GridCoord const& p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); }
void SetUnloadLock(GridCoord const& 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
@@ -350,18 +384,16 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
Map const* GetParent() const { return m_parentMap; }
- // 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 GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
- float GetMinHeight(float x, float y) const;
-
void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const;
ZLiquidStatus GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const;
bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const;
uint32 GetAreaId(float x, float y, float z, bool *isOutdoors = nullptr) const;
+ uint32 GetAreaId(Position const& pos) { return GetAreaId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetZoneId(float x, float y, float z) const;
+ uint32 GetZoneId(Position const& pos) { return GetZoneId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const;
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, Position const& pos) { GetZoneAndAreaId(zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
bool IsOutdoors(float x, float y, float z) const;
@@ -464,6 +496,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
Corpse* GetCorpse(ObjectGuid const& guid);
Creature* GetCreature(ObjectGuid const& guid);
GameObject* GetGameObject(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)); }
Transport* GetTransport(ObjectGuid const& guid);
DynamicObject* GetDynamicObject(ObjectGuid const& guid);
Pet* GetPet(ObjectGuid const& guid);
@@ -472,9 +507,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
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
{
@@ -504,7 +541,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(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false) const;
- float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
+ float GetMinHeight(float x, float y) const;
+ float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
+ float GetHeight(Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
+ float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist)); }
+ float GetHeight(uint32 phasemask, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); }
@@ -522,28 +563,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, SQLTransaction dbTrans = nullptr);
+ void SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans = nullptr);
void LoadRespawnTimes();
- void DeleteRespawnTimes();
+ void DeleteRespawnTimes() { DeleteRespawnInfo(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); }
void LoadCorpseData();
void DeleteCorpseData();
@@ -702,6 +739,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, SQLTransaction dbTrans = nullptr);
+ void Respawn(RespawnVector& respawnData, bool force = false, SQLTransaction 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, SQLTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn = false, SQLTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, uint32 zoneId = 0, bool doRespawn = false, SQLTransaction 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, SQLTransaction 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);
@@ -730,8 +820,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;
uint32 _defaultLight;
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index e628a7030ae..96147c7ded7 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 const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetAreaId(x, y, z);
}
+ uint32 GetAreaId(uint32 mapid, Position const& pos) const { return GetAreaId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetAreaId(WorldLocation const& loc) const { return GetAreaId(loc.GetMapId(), loc); }
uint32 GetZoneId(uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetZoneId(x, y, z);
}
- void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
+ uint32 GetZoneId(uint32 mapid, Position const& pos) const { return GetZoneId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetZoneId(WorldLocation const& loc) const { return GetZoneId(loc.GetMapId(), loc); }
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
m->GetZoneAndAreaId(zoneid, areaid, x, y, z);
}
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) const { GetZoneAndAreaId(zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, WorldLocation const& loc) const { GetZoneAndAreaId(zoneid, areaid, loc.GetMapId(), loc); }
void Initialize(void);
void Update(uint32);
diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h
new file mode 100644
index 00000000000..700311e64c7
--- /dev/null
+++ b/src/server/game/Maps/SpawnData.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 "Position.h"
+
+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;
+ uint32 spawnId = 0;
+ uint32 id = 0; // entry in respective _template table
+ WorldLocation spawnPoint;
+ uint32 phaseMask = 0;
+ int32 spawntimesecs = 0;
+ uint8 spawnMask = 0;
+ 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 d96b33fd968..400685c1c1b 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1147,7 +1147,32 @@ enum TrinityStrings
LANG_COMMAND_MUTEHISTORY_EMPTY = 5060,
LANG_COMMAND_MUTEHISTORY_OUTPUT = 5061,
- // Room for more Trinity strings 5062-9999
+ // Scene debugs commands [Master only, not used in 3.3.5]
+ /*LANG_COMMAND_SCENE_DEBUG_ON = 5062,
+ LANG_COMMAND_SCENE_DEBUG_OFF = 5063,
+ LANG_COMMAND_SCENE_DEBUG_PLAY = 5064,
+ LANG_COMMAND_SCENE_DEBUG_TRIGGER = 5065,
+ LANG_COMMAND_SCENE_DEBUG_CANCEL = 5066,
+ LANG_COMMAND_SCENE_DEBUG_COMPLETE = 5067,
+ LANG_DEBUG_SCENE_OBJECT_LIST = 5068,
+ LANG_DEBUG_SCENE_OBJECT_DETAIL = 5069, */
+
+ // 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,
+
+ // Room for more Trinity strings 5084-6603
// Level requirement notifications
LANG_SAY_REQ = 6604,
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index a2d195bea20..b2e962abc0f 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -91,7 +91,7 @@ void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid, uint32 entry
{
if (!entry)
{
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
return;
entry = data->id;
@@ -117,7 +117,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr
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, entry);
return true;
@@ -149,7 +149,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 == 0)
return false;
@@ -217,7 +217,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;
@@ -225,7 +225,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 9305e226421..7ab6c43d410 100644
--- a/src/server/game/Pools/PoolMgr.cpp
+++ b/src/server/game/Pools/PoolMgr.cpp
@@ -222,7 +222,7 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
{
sObjectMgr->RemoveCreatureFromGrid(guid, data);
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
if (!map->Instanceable())
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid);
@@ -230,6 +230,9 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
{
Creature* creature = itr->second;
++itr;
+ // For dynamic spawns, save respawn time here
+ if (!creature->GetRespawnCompatibilityMode())
+ creature->SaveRespawnTime(0, false);
creature->AddObjectToRemoveList();
}
}
@@ -240,11 +243,11 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
template<>
void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(guid))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid))
{
sObjectMgr->RemoveGameobjectFromGrid(guid, data);
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
if (!map->Instanceable())
{
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid);
@@ -252,6 +255,10 @@ void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid)
{
GameObject* go = itr->second;
++itr;
+
+ // For dynamic spawns, save respawn time here
+ if (!go->GetRespawnCompatibilityMode())
+ go->SaveRespawnTime(0, false);
go->AddObjectToRemoveList();
}
}
@@ -379,13 +386,13 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj)
sObjectMgr->AddCreatureToGrid(obj->guid, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
Creature* creature = new Creature();
//TC_LOG_DEBUG("pool", "Spawning creature %u", guid);
- if (!creature->LoadCreatureFromDB(obj->guid, map))
+ if (!creature->LoadFromDB(obj->guid, map, true, false))
{
delete creature;
return;
@@ -398,18 +405,18 @@ 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->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
GameObject* pGameobject = new GameObject;
//TC_LOG_DEBUG("pool", "Spawning gameobject %u", guid);
- if (!pGameobject->LoadGameObjectFromDB(obj->guid, map, false))
+ if (!pGameobject->LoadFromDB(obj->guid, map, false))
{
delete pGameobject;
return;
@@ -688,7 +695,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: %u) defined for pool id (%u), skipped.", guid, pool_id);
@@ -1074,6 +1081,21 @@ void PoolMgr::DespawnPool(uint32 pool_id)
mPoolQuestGroups[pool_id].DespawnObject(mSpawnedData);
}
+// 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 %u)", 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 6c270e28851..c36e7bd29ef 100644
--- a/src/server/game/Pools/PoolMgr.h
+++ b/src/server/game/Pools/PoolMgr.h
@@ -22,6 +22,7 @@
#include "Define.h"
#include "Creature.h"
#include "GameObject.h"
+#include "SpawnData.h"
#include "QuestDef.h"
struct PoolTemplateData
@@ -118,6 +119,7 @@ class TC_GAME_API PoolMgr
template<typename T>
uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const;
+ uint32 IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
template<typename T>
bool IsSpawnedObject(uint32 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 9f42ded8cc6..cfc64b83005 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -1566,16 +1566,32 @@ 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 = baseTemplate;
+ for (uint8 diff = uint8(map->GetSpawnMode()); diff > 0;)
+ {
+ if (uint32 diffEntry = baseTemplate->DifficultyEntry[diff - 1])
+ if (CreatureTemplate const* diffTemplate = sObjectMgr->GetCreatureTemplate(diffEntry))
+ {
+ actTemplate = diffTemplate;
+ break;
+ }
+ if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && map->IsRaid())
+ diff -= 2;
+ else
+ diff -= 1;
+ }
+
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 dab3b42329c..65c5be6be37 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -907,7 +907,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 ae5cc87d097..9706567ce47 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -134,6 +134,11 @@ World::World()
memset(m_int_configs, 0, sizeof(m_int_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
@@ -196,6 +201,59 @@ void World::SetClosed(bool val)
sScriptMgr->OnOpenStateChange(!val);
}
+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
{
@@ -1192,6 +1250,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) != '\\'))
@@ -1601,6 +1703,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();
@@ -1617,10 +1722,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();
@@ -2289,6 +2397,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 5254b32bbdf..6c44b1abe1a 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -202,7 +202,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
@@ -379,13 +378,11 @@ enum WorldIntConfigs
CONFIG_AUCTION_GETALL_DELAY,
CONFIG_AUCTION_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,
@@ -765,6 +762,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();
@@ -859,7 +860,21 @@ class TC_GAME_API World
AutobroadcastsWeightMap m_AutobroadcastsWeights;
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;
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 69f7ecb3f9a..a7ac9028bc8 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -972,7 +972,7 @@ public:
Map* map = handler->GetSession()->GetPlayer()->GetMap();
- if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, x, y, z, o, nullptr, id))
+ if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, { x, y, z, o }, nullptr, id))
{
delete v;
return false;
diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp
index 6a27060c2ed..2b8f89da7ac 100644
--- a/src/server/scripts/Commands/cs_go.cpp
+++ b/src/server/scripts/Commands/cs_go.cpp
@@ -138,8 +138,6 @@ public:
float o = fields[3].GetFloat();
uint32 mapId = fields[4].GetUInt16();
- Transport* transport = nullptr;
-
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
{
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
@@ -157,11 +155,7 @@ public:
else
player->SaveRecallPosition();
- if (player->TeleportTo(mapId, x, y, z, o))
- {
- if (transport)
- transport->AddPassenger(player);
- }
+ player->TeleportTo(mapId, x, y, z, o);
return true;
}
@@ -272,28 +266,18 @@ public:
if (!guidLow)
return false;
- float x, y, z, o;
- uint32 mapId;
-
// by DB guid
- if (GameObjectData const* goData = sObjectMgr->GetGOData(guidLow))
- {
- x = goData->posX;
- y = goData->posY;
- z = goData->posZ;
- o = goData->orientation;
- mapId = goData->mapid;
- }
- else
+ GameObjectData const* goData = sObjectMgr->GetGameObjectData(guidLow);
+ if (!goData)
{
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
handler->SetSentErrorMessage(true);
return false;
}
-
- if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
+
+ if (!MapManager::IsValidMapCoord(goData->spawnPoint) || sObjectMgr->IsTransportMap(goData->spawnPoint.GetMapId()))
{
- handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
+ handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, goData->spawnPoint.GetPositionX(), goData->spawnPoint.GetPositionY(), goData->spawnPoint.GetMapId());
handler->SetSentErrorMessage(true);
return false;
}
@@ -308,7 +292,7 @@ public:
else
player->SaveRecallPosition();
- player->TeleportTo(mapId, x, y, z, o);
+ player->TeleportTo(goData->spawnPoint);
return true;
}
diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp
index a315355ff44..46927a1c8f6 100644
--- a/src/server/scripts/Commands/cs_gobject.cpp
+++ b/src/server/scripts/Commands/cs_gobject.cpp
@@ -38,6 +38,10 @@ EndScriptData */
#include "RBAC.h"
#include "WorldSession.h"
+// definitions are over in cs_npc.cpp
+bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args);
+bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args);
+
class gobject_commandscript : public CommandScript
{
public:
@@ -57,15 +61,17 @@ public:
};
static std::vector<ChatCommand> gobjectCommandTable =
{
- { "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" },
- { "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" },
- { "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" },
- { "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" },
- { "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" },
- { "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" },
- { "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" },
- { "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable },
+ { "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" },
+ { "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" },
+ { "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" },
+ { "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" },
+ { "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" },
+ { "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" },
+ { "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" },
+ { "spawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" },
+ { "despawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP, false, &HandleNpcDespawnGroup,""},
+ { "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable },
};
static std::vector<ChatCommand> commandTable =
{
@@ -169,14 +175,14 @@ public:
object = new GameObject();
// this will generate a new guid if the object is in an instance
- if (!object->LoadGameObjectFromDB(guidLow, map))
+ if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
}
/// @todo is it really necessary to add both the real and DB table guid here ?
- sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGOData(guidLow));
+ sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGameObjectData(guidLow));
handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
return true;
@@ -430,7 +436,7 @@ public:
object->Delete();
object = new GameObject();
- if (!object->LoadGameObjectFromDB(guidLow, map))
+ if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
@@ -499,7 +505,7 @@ public:
object->Delete();
object = new GameObject();
- if (!object->LoadGameObjectFromDB(guidLow, map))
+ if (!object->LoadFromDB(guidLow, map, true))
{
delete object;
return false;
@@ -578,7 +584,7 @@ public:
if (!gameObjectInfo)
continue;
- handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId);
+ handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId, "", "");
++count;
} while (result->NextRow());
@@ -611,7 +617,7 @@ public:
if (!cValue)
return false;
ObjectGuid::LowType guidLow = atoul(cValue);
- GameObjectData const* data = sObjectMgr->GetGOData(guidLow);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guidLow);
if (!data)
return false;
entry = data->id;
@@ -623,9 +629,16 @@ public:
GameObjectTemplate const* gameObjectInfo = sObjectMgr->GetGameObjectTemplate(entry);
+ GameObject* thisGO = nullptr;
+
if (!gameObjectInfo)
return false;
+ if (*args && handler->GetSession()->GetPlayer())
+ thisGO = handler->GetSession()->GetPlayer()->FindNearestGameObject(entry, 30);
+ else if (handler->getSelectedObject() && handler->getSelectedObject()->GetTypeId() == TYPEID_GAMEOBJECT)
+ thisGO = handler->getSelectedObject()->ToGameObject();
+
type = gameObjectInfo->type;
displayId = gameObjectInfo->displayId;
name = gameObjectInfo->name;
@@ -634,10 +647,32 @@ public:
else if (type == GAMEOBJECT_TYPE_FISHINGHOLE)
lootId = gameObjectInfo->fishinghole.lootId;
+ // If we have a real object, send some info about it
+ if (thisGO)
+ {
+ handler->PSendSysMessage(LANG_SPAWNINFO_GUIDINFO, thisGO->GetGUID().ToString().c_str());
+ handler->PSendSysMessage(LANG_SPAWNINFO_SPAWNID_LOCATION, thisGO->GetSpawnId(), thisGO->GetPositionX(), thisGO->GetPositionY(), thisGO->GetPositionZ());
+ if (Player* player = handler->GetSession()->GetPlayer())
+ {
+ Position playerPos = player->GetPosition();
+ float dist = thisGO->GetExactDist(&playerPos);
+ handler->PSendSysMessage(LANG_SPAWNINFO_DISTANCEFROMPLAYER, dist);
+ }
+ }
handler->PSendSysMessage(LANG_GOINFO_ENTRY, entry);
handler->PSendSysMessage(LANG_GOINFO_TYPE, type);
handler->PSendSysMessage(LANG_GOINFO_LOOTID, lootId);
handler->PSendSysMessage(LANG_GOINFO_DISPLAYID, displayId);
+ if (WorldObject* object = handler->getSelectedObject())
+ {
+ if (object->ToGameObject() && object->ToGameObject()->GetGameObjectData() && object->ToGameObject()->GetGameObjectData()->spawnGroupData->groupId)
+ {
+ SpawnGroupTemplateData const* groupData = object->ToGameObject()->GetGameObjectData()->spawnGroupData;
+ handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, groupData->isActive);
+ }
+ if (object->ToGameObject())
+ handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, object->ToGameObject()->GetRespawnCompatibilityMode());
+ }
handler->PSendSysMessage(LANG_GOINFO_NAME, name.c_str());
handler->PSendSysMessage(LANG_GOINFO_SIZE, gameObjectInfo->size);
diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp
index 8b502f60e11..ea57f29a771 100644
--- a/src/server/scripts/Commands/cs_list.cpp
+++ b/src/server/scripts/Commands/cs_list.cpp
@@ -27,7 +27,9 @@ EndScriptData */
#include "Chat.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
+#include "GameObject.h"
#include "Language.h"
+#include "MapManager.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
@@ -50,6 +52,7 @@ public:
{ "object", rbac::RBAC_PERM_COMMAND_LIST_OBJECT, true, &HandleListObjectCommand, "" },
{ "auras", rbac::RBAC_PERM_COMMAND_LIST_AURAS, false, &HandleListAurasCommand, "" },
{ "mail", rbac::RBAC_PERM_COMMAND_LIST_MAIL, true, &HandleListMailCommand, "" },
+ { "respawns", rbac::RBAC_PERM_COMMAND_LIST_MAIL, false, &HandleListRespawnsCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
@@ -117,11 +120,40 @@ public:
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
uint16 mapId = fields[4].GetUInt16();
+ bool liveFound = false;
+ // Get map (only support base map from console)
+ Map* thisMap;
if (handler->GetSession())
- handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId);
+ thisMap = handler->GetSession()->GetPlayer()->GetMap();
else
- handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId);
+ thisMap = sMapMgr->FindBaseNonInstanceMap(mapId);
+
+ // If map found, try to find active version of this creature
+ if (thisMap)
+ {
+ auto const creBounds = thisMap->GetCreatureBySpawnIdStore().equal_range(guid);
+ if (creBounds.first != creBounds.second)
+ {
+ for (std::unordered_multimap<uint32, Creature*>::const_iterator itr = creBounds.first; itr != creBounds.second;)
+ {
+ if (handler->GetSession())
+ handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
+ else
+ handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
+ ++itr;
+ }
+ liveFound = true;
+ }
+ }
+
+ if (!liveFound)
+ {
+ if (handler->GetSession())
+ handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
+ else
+ handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
+ }
}
while (result->NextRow());
}
@@ -407,11 +439,40 @@ public:
float z = fields[3].GetFloat();
uint16 mapId = fields[4].GetUInt16();
uint32 entry = fields[5].GetUInt32();
+ bool liveFound = false;
+ // Get map (only support base map from console)
+ Map* thisMap;
if (handler->GetSession())
- handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId);
+ thisMap = handler->GetSession()->GetPlayer()->GetMap();
else
- handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId);
+ thisMap = sMapMgr->FindBaseNonInstanceMap(mapId);
+
+ // If map found, try to find active version of this object
+ if (thisMap)
+ {
+ auto const goBounds = thisMap->GetGameObjectBySpawnIdStore().equal_range(guid);
+ if (goBounds.first != goBounds.second)
+ {
+ for (std::unordered_multimap<uint32, GameObject*>::const_iterator itr = goBounds.first; itr != goBounds.second;)
+ {
+ if (handler->GetSession())
+ handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " ");
+ else
+ handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " ");
+ ++itr;
+ }
+ liveFound = true;
+ }
+ }
+
+ if (!liveFound)
+ {
+ if (handler->GetSession())
+ handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, "", "");
+ else
+ handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, "", "");
+ }
}
while (result->NextRow());
}
@@ -581,8 +642,79 @@ public:
handler->PSendSysMessage(LANG_LIST_MAIL_NOT_FOUND);
return true;
}
+
+ static char const* GetZoneName(uint32 zoneId, LocaleConstant locale)
+ {
+ AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId);
+ return zoneEntry ? zoneEntry->area_name[locale] : "<unknown zone>";
+ }
+ static bool HandleListRespawnsCommand(ChatHandler* handler, char const* args)
+ {
+ // We need a player
+ Player const* player = handler->GetSession()->GetPlayer();
+ if (!player)
+ return false;
+ // And we need a map
+ Map const* map = player->GetMap();
+ if (!map)
+ return false;
+
+ uint32 range = 0;
+ if (*args)
+ range = atoi((char*)args);
+
+ RespawnVector respawns;
+ LocaleConstant locale = handler->GetSession()->GetSessionDbcLocale();
+ char const* stringOverdue = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_OVERDUE, locale);
+ char const* stringCreature = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_CREATURES, locale);
+ char const* stringGameobject = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_GAMEOBJECTS, locale);
+
+ uint32 zoneId = player->GetZoneId();
+ if (range)
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringCreature, range);
+ else
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringCreature, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId);
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER);
+ map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_CREATURE, range ? 0 : zoneId);
+ for (RespawnInfo* ri : respawns)
+ {
+ CreatureData const* data = sObjectMgr->GetCreatureData(ri->spawnId);
+ if (!data)
+ continue;
+ if (range && !player->IsInDist(data->spawnPoint, range))
+ continue;
+ uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS;
+ uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS;
+
+ std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue;
+ handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, data->spawnGroupData->isActive ? respawnTime.c_str() : "inactive");
+ }
+
+ respawns.clear();
+ if (range)
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringGameobject, range);
+ else
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringGameobject, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId);
+ handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER);
+ map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_GAMEOBJECT, range ? 0 : zoneId);
+ for (RespawnInfo* ri : respawns)
+ {
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(ri->spawnId);
+ if (!data)
+ continue;
+ if (range && !player->IsInDist(data->spawnPoint, range))
+ continue;
+ uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS;
+ uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS;
+
+ std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue;
+ handler->PSendSysMessage("%u | %u | [% 02u, % 02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, data->spawnGroupData->isActive ? respawnTime.c_str() : "inactive");
+ }
+ return true;
+ }
};
+
void AddSC_list_commandscript()
{
new list_commandscript();
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index f57d960d08a..dbf50d65640 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -1911,10 +1911,22 @@ public:
return true;
}
+ // First handle any creatures that still have a corpse around
Trinity::RespawnDo u_do;
Trinity::WorldObjectWorker<Trinity::RespawnDo> worker(player, u_do);
Cell::VisitGridObjects(player, worker, player->GetGridActivationRange());
+ // Now handle any that had despawned, but had respawn time logged.
+ RespawnVector data;
+ player->GetMap()->GetRespawnInfo(data, SPAWN_TYPEMASK_ALL, 0);
+ if (!data.empty())
+ {
+ uint32 const gridId = Trinity::ComputeGridCoord(player->GetPositionX(), player->GetPositionY()).GetId();
+ for (RespawnInfo* info : data)
+ if (info->gridId == gridId)
+ player->GetMap()->RemoveRespawnTime(info, true);
+ }
+
return true;
}
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index dfb9ddb66a6..cf63419303d 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -176,6 +176,86 @@ EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] =
CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
};
+bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args)
+{
+ if (!*args)
+ return false;
+
+ bool ignoreRespawn = false;
+ bool force = false;
+ uint32 groupId = 0;
+
+ // Decode arguments
+ char* arg = strtok((char*)args, " ");
+ while (arg)
+ {
+ std::string thisArg = arg;
+ std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
+ if (thisArg == "ignorerespawn")
+ ignoreRespawn = true;
+ else if (thisArg == "force")
+ force = true;
+ else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
+ return false;
+ else
+ groupId = atoi(thisArg.c_str());
+
+ arg = strtok(NULL, " ");
+ }
+
+ Player* player = handler->GetSession()->GetPlayer();
+
+ std::vector <WorldObject*> creatureList;
+ if (!sObjectMgr->SpawnGroupSpawn(groupId, player->GetMap(), ignoreRespawn, force, &creatureList))
+ {
+ handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage(LANG_SPAWNGROUP_SPAWNCOUNT, creatureList.size());
+ for (WorldObject* obj : creatureList)
+ handler->PSendSysMessage("%s (%s)", obj->GetName(), obj->GetGUID().ToString().c_str());
+
+ return true;
+}
+
+bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args)
+{
+ if (!*args)
+ return false;
+
+ bool deleteRespawnTimes = false;
+ uint32 groupId = 0;
+
+ // Decode arguments
+ char* arg = strtok((char*)args, " ");
+ while (arg)
+ {
+ std::string thisArg = arg;
+ std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower);
+ if (thisArg == "removerespawntime")
+ deleteRespawnTimes = true;
+ else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size()))
+ return false;
+ else
+ groupId = atoi(thisArg.c_str());
+
+ arg = strtok(nullptr, " ");
+ }
+
+ Player* player = handler->GetSession()->GetPlayer();
+
+ if (!sObjectMgr->SpawnGroupDespawn(groupId, player->GetMap(), deleteRespawnTimes))
+ {
+ handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ return true;
+}
+
class npc_commandscript : public CommandScript
{
public:
@@ -219,21 +299,23 @@ public:
};
static std::vector<ChatCommand> npcCommandTable =
{
- { "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" },
- { "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" },
- { "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" },
- { "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" },
- { "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" },
- { "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" },
- { "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
- { "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
- { "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
- { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
- { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
- { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
- { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
- { "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" },
+ { "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" },
+ { "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" },
+ { "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" },
+ { "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" },
+ { "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" },
+ { "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" },
+ { "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
+ { "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
+ { "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
+ { "spawngroup", rbac::RBAC_PERM_COMMAND_NPC_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" },
+ { "despawngroup", rbac::RBAC_PERM_COMMAND_NPC_DESPAWNGROUP, false, &HandleNpcDespawnGroup, "" },
+ { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
+ { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
+ { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
+ { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
+ { "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
@@ -257,22 +339,16 @@ public:
return false;
Player* chr = handler->GetSession()->GetPlayer();
- float x = chr->GetPositionX();
- float y = chr->GetPositionY();
- float z = chr->GetPositionZ();
- float o = chr->GetOrientation();
Map* map = chr->GetMap();
if (Transport* trans = chr->GetTransport())
{
ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Unit>();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
+ data.spawnId = guid;
data.id = id;
data.phaseMask = chr->GetPhaseMaskForSpawn();
- data.posX = chr->GetTransOffsetX();
- data.posY = chr->GetTransOffsetY();
- data.posZ = chr->GetTransOffsetZ();
- data.orientation = chr->GetTransOffsetO();
+ data.spawnPoint.Relocate(chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO());
Creature* creature = trans->CreateNPCPassenger(guid, &data);
@@ -283,7 +359,7 @@ public:
}
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, *chr))
{
delete creature;
return false;
@@ -298,7 +374,7 @@ public:
creature->CleanupsBeforeDelete();
delete creature;
creature = new Creature();
- if (!creature->LoadCreatureFromDB(db_guid, map))
+ if (!creature->LoadFromDB(db_guid, map, true, true))
{
delete creature;
return false;
@@ -473,7 +549,7 @@ public:
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
{
- Creature* unit = nullptr;
+ Creature* creature = nullptr;
if (*args)
{
@@ -485,22 +561,27 @@ public:
ObjectGuid::LowType lowguid = atoul(cId);
if (!lowguid)
return false;
- unit = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
+ creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
}
else
- unit = handler->getSelectedCreature();
+ creature = handler->getSelectedCreature();
- if (!unit || unit->IsPet() || unit->IsTotem())
+ if (!creature || creature->IsPet() || creature->IsTotem())
{
handler->SendSysMessage(LANG_SELECT_CREATURE);
handler->SetSentErrorMessage(true);
return false;
}
- // Delete the creature
- unit->CombatStop();
- unit->DeleteFromDB();
- unit->AddObjectToRemoveList();
+ if (TempSummon* summon = creature->ToTempSummon())
+ summon->UnSummon();
+ else
+ {
+ // Delete the creature
+ creature->CombatStop();
+ creature->DeleteFromDB();
+ creature->AddObjectToRemoveList();
+ }
handler->SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
@@ -690,13 +771,20 @@ public:
uint32 nativeid = target->GetNativeDisplayId();
uint32 Entry = target->GetEntry();
- int64 curRespawnDelay = target->GetRespawnTimeEx()-time(nullptr);
+ int64 curRespawnDelay = target->GetRespawnCompatibilityMode() ? target->GetRespawnTimeEx() - time(nullptr) : target->GetMap()->GetCreatureRespawnTime(target->GetSpawnId()) - time(nullptr);
+
if (curRespawnDelay < 0)
curRespawnDelay = 0;
std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true);
std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true);
handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().GetCounter(), faction, npcflags, Entry, displayid, nativeid);
+ if (target->GetCreatureData() && target->GetCreatureData()->spawnGroupData->groupId)
+ {
+ if (SpawnGroupTemplateData const* groupData = target->GetCreatureData()->spawnGroupData)
+ handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, groupData->isActive);
+ }
+ handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, target->GetRespawnCompatibilityMode());
handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId());
handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
@@ -766,7 +854,7 @@ public:
if (!creatureTemplate)
continue;
- handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId);
+ handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId, "", "");
++count;
}
@@ -784,6 +872,9 @@ public:
ObjectGuid::LowType lowguid = 0;
Creature* creature = handler->getSelectedCreature();
+ Player const* player = handler->GetSession()->GetPlayer();
+ if (!player)
+ return false;
if (!creature)
{
@@ -803,9 +894,7 @@ public:
return false;
}
- uint32 map_id = data->mapid;
-
- if (handler->GetSession()->GetPlayer()->GetMapId() != map_id)
+ if (player->GetMapId() != data->spawnPoint.GetMapId())
{
handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
handler->SetSentErrorMessage(true);
@@ -813,25 +902,12 @@ public:
}
}
else
- {
lowguid = creature->GetSpawnId();
- }
-
- float x = handler->GetSession()->GetPlayer()->GetPositionX();
- float y = handler->GetSession()->GetPlayer()->GetPositionY();
- float z = handler->GetSession()->GetPlayer()->GetPositionZ();
- float o = handler->GetSession()->GetPlayer()->GetOrientation();
if (creature)
{
- if (CreatureData const* data = sObjectMgr->GetCreatureData(creature->GetSpawnId()))
- {
- const_cast<CreatureData*>(data)->posX = x;
- const_cast<CreatureData*>(data)->posY = y;
- const_cast<CreatureData*>(data)->posZ = z;
- const_cast<CreatureData*>(data)->orientation = o;
- }
- creature->UpdatePosition(x, y, z, o);
+ sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId()).spawnPoint.Relocate(*player);
+ creature->UpdatePosition(*player);
creature->GetMotionMaster()->Initialize();
if (creature->IsAlive()) // dead creature will reset movement generator at respawn
{
@@ -842,10 +918,10 @@ public:
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION);
- stmt->setFloat(0, x);
- stmt->setFloat(1, y);
- stmt->setFloat(2, z);
- stmt->setFloat(3, o);
+ stmt->setFloat(0, player->GetPositionX());
+ stmt->setFloat(1, player->GetPositionY());
+ stmt->setFloat(2, player->GetPositionZ());
+ stmt->setFloat(3, player->GetOrientation());
stmt->setUInt32(4, lowguid);
WorldDatabase.Execute(stmt);
@@ -1529,13 +1605,13 @@ public:
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items");
_IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items);
}
-
+
if (!loot.GetPlayerFFAItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player");
_IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items);
}
-
+
if (!loot.GetPlayerNonQuestNonFFAConditionalItems().empty())
{
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items");
diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp
index efd02314d0e..6298a34644f 100644
--- a/src/server/scripts/Commands/cs_wp.cpp
+++ b/src/server/scripts/Commands/cs_wp.cpp
@@ -662,7 +662,7 @@ public:
// re-create
Creature* wpCreature = new Creature();
- if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation()))
+ if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, *chr))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
delete wpCreature;
@@ -671,8 +671,7 @@ public:
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- /// @todo Should we first use "Create" then use "LoadFromDB"?
- if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map))
+ if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
delete wpCreature;
@@ -874,7 +873,7 @@ public:
float o = chr->GetOrientation();
Creature* wpCreature = new Creature();
- if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
+ if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete wpCreature;
@@ -891,7 +890,7 @@ public:
WorldDatabase.Execute(stmt);
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map))
+ if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete wpCreature;
@@ -937,7 +936,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -945,7 +944,7 @@ public:
}
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
- if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
+ if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -986,7 +985,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
@@ -994,7 +993,7 @@ public:
}
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
- if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
+ if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
index 89bc369fd6f..d2cc08dcc6e 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
@@ -264,7 +264,7 @@ class ValithriaDespawner : public BasicEvent
creature->SetRespawnDelay(10);
if (CreatureData const* data = creature->GetCreatureData())
- creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation);
+ creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
index ed85ee3fa17..90d764cd9db 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
@@ -354,7 +354,7 @@ class FrostwingGauntletRespawner
creature->SetRespawnDelay(2);
if (CreatureData const* data = creature->GetCreatureData())
- creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation);
+ creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 53caf9fda7a..c98b086b203 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -392,13 +392,13 @@ class instance_icecrown_citadel : public InstanceMapScript
break;
case NPC_ZAFOD_BOOMBOX:
if (GameObjectTemplate const* go = sObjectMgr->GetGameObjectTemplate(GO_THE_SKYBREAKER_A))
- if ((TeamInInstance == ALLIANCE && data->mapid == go->moTransport.mapID) ||
- (TeamInInstance == HORDE && data->mapid != go->moTransport.mapID))
+ if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetMapId() == go->moTransport.mapID) ||
+ (TeamInInstance == HORDE && data->spawnPoint.GetMapId() != go->moTransport.mapID))
return entry;
return 0;
case NPC_IGB_MURADIN_BRONZEBEARD:
- if ((TeamInInstance == ALLIANCE && data->posX > 10.0f) ||
- (TeamInInstance == HORDE && data->posX < 10.0f))
+ if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetPositionX() > 10.0f) ||
+ (TeamInInstance == HORDE && data->spawnPoint.GetPositionX() < 10.0f))
return entry;
return 0;
default:
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
index 5454ff7c848..cb04d154f8a 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
@@ -96,7 +96,7 @@ public:
void InitializeAI() override
{
- if (!me->isDead())
+ if (!me->isDead() && instance->GetBossState(BOSS_ANUBREKHAN) != DONE)
{
Reset();
SummonGuards();
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
index da3d95a5173..65d70f1e398 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
@@ -83,7 +83,7 @@ class boss_faerlina : public CreatureScript
void InitializeAI() override
{
- if (!me->isDead())
+ if (!me->isDead() && instance->GetBossState(BOSS_FAERLINA) != DONE)
{
Reset();
SummonAdds();
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
index 0fb00d18fd3..51deb5b4375 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
@@ -265,14 +265,9 @@ struct boss_four_horsemen_baseAI : public BossAI
for (Horseman boss : horsemen)
{
if (Creature* cBoss = getHorsemanHandle(boss))
- {
- cBoss->DespawnOrUnsummon();
- cBoss->SetRespawnTime(15);
- }
+ cBoss->DespawnOrUnsummon(0, Seconds(15));
else
- {
TC_LOG_WARN("scripts", "FourHorsemenAI: Encounter resetting but horseman with id %u is not present", uint32(boss));
- }
}
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
index 58da5a92295..ac27c44d1de 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
@@ -79,7 +79,7 @@ public:
void InitializeAI() override
{
- if (!me->isDead())
+ if (!me->isDead() && instance->GetBossState(BOSS_RAZUVIOUS) != DONE)
{
Reset();
SummonAdds();
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index ba8a10c759d..d1ce8ca36b0 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -16,6 +16,7 @@
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
+# SPAWN/RESPAWN SETTINGS
# CHAT SETTINGS
# GAME MASTER SETTINGS
# VISIBILITY AND DISTANCES
@@ -1714,6 +1715,122 @@ Creature.MovingStopTimeForPlayer = 180000
###################################################################################################
###################################################################################################
+# SPAWN/RESPAWN SETTINGS
+#
+# Respawn.MinCheckIntervalMS
+# Description: Minimum time that needs to pass between respawn checks for any given map.
+# Default: 5000 - 5 seconds
+
+Respawn.MinCheckIntervalMS = 5000
+
+#
+# Respawn.GuidWarnLevel
+# Description: The point at which the highest guid for creatures or gameobjects in any map must reach
+# before the warning logic is enabled. A restart will then be queued at the next quiet time
+# The maximum guid per map is 16,777,216. So, it must be less than this value.
+# Default: 12000000 - 12 million
+
+Respawn.GuidWarnLevel = 12000000
+
+#
+# Respawn.WarningMessage
+# Description: This message will be periodically shown (Frequency specified by Respawn.WarningFrequency) to
+# all users of the server, once the Respawn.GuidWarnLevel has been passed, and a restart scheduled.
+# It's used to warn users that there will be an out of schedule server restart soon.
+# Default: "There will be an unscheduled server restart at 03:00 server time. The server will be available again shortly after."
+
+Respawn.WarningMessage = "There will be an unscheduled server restart at 03:00. The server will be available again shortly after."
+
+#
+# Respawn.WarningFrequency
+# Description: The frequency (in seconds) that the warning message will be sent to users after a quiet time restart is triggered.
+# The message will repeat each time this many seconds passed until the server is restarted.
+# If set to 0, no warnings will be sent.
+# Default: 1800 - (30 minutes)
+
+Respawn.WarningFrequency = 1800
+
+#
+# Respawn.GuidAlertLevel
+# Description: The point at which the highest guid for creatures or gameobjects in any map must reach
+# before the alert logic is enabled. A restart will then be triggered for 30 mins from that
+# point. The maximum guid per map is 16,777,216. So, it must be less than this value.
+# Default: 16000000 - 16 million
+
+Respawn.GuidAlertLevel = 16000000
+
+#
+# Respawn.AlertRestartReason
+# Description: The shutdown reason given when the alert level is reached. The server will use a fixed time of
+# 5 minutes and the reason for shutdown will be this message
+# Default: "Urgent Maintenance"
+
+Respawn.AlertRestartReason = "Urgent Maintenance"
+
+#
+# Respawn.RestartQuietTime
+# Description: The hour at which the server will be restarted after the Respawn.GuidWarnLevel
+# threshold has been reached. This can be between 0 and 23. 20 will be 8pm server time
+# Default: 3 - 3am
+
+Respawn.RestartQuietTime = 3
+
+#
+# Respawn.DynamicMode
+# Description: Select which mode (if any) should be used to adjust respawn of creatures.
+# This will only affect creatures that have dynamic spawn rate scaling enabled in
+# the spawn group table (by default, gathering nodes and quest targets with respawn time <30min
+# 1 - Use number of players in zone
+# Default: 0 - No dynamic respawn function
+
+Respawn.DynamicMode = 0
+
+#
+# Respawn.DynamicEscortNPC
+# Description: This switch controls the dynamic respawn system for escort NPCs not in instancable maps (base maps only).
+# This will cause the respawn timer to begin when an escort event begins, and potentially
+# allow multiple instances of the NPC to be alive at the same time (when combined with Respawn.DynamicMode > 0)
+# 1 - Enabled
+# Default: 0 - Disabled
+
+Respawn.DynamicEscortNPC = 1
+
+#
+# Respawn.DynamicRateCreature
+# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for creatures).
+# Up to this number of players, the respawn rate is unchanged.
+# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
+# Default: 10
+
+Respawn.DynamicRateCreature = 10
+
+#
+# Respawn.DynamicMinimumCreature
+# Description: The minimum respawn time for a creature under dynamic scaling.
+# Default: 10 - (10 seconds)
+
+Respawn.DynamicMinimumCreature = 10
+
+#
+# Respawn.DynamicRateGameObject
+# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for gameobjects).
+# Up to this number of players, the respawn rate is unchanged.
+# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
+# Default: 10
+
+Respawn.DynamicRateGameObject = 10
+
+#
+# Respawn.DynamicMinimumGameObject
+# Description: The minimum respawn time for a gameobject under dynamic scaling.
+# Default: 10 - (10 seconds)
+
+Respawn.DynamicMinimumGameObject = 10
+
+#
+###################################################################################################
+
+###################################################################################################
# CHAT SETTINGS
#
# ChatFakeMessagePreventing