Core/Spawns: merged Dynamic Spawning

This commit is contained in:
Ovahlord committed 2018-07-21 20:44:41 +02:00
1 parent 21795a5ae5
commit b976bc1187
87 files changed
+3200 -1007

No files matched your search

+1
View File
@@ -1819,6 +1819,7 @@ INSERT INTO `rbac_permissions` VALUES
(863,'Command: group set maintank'),
(864,'Command: group set mainassist'),
(865,'Command: npc showloot'),
(866,'Command: list spawnpoints'),
(867,'Command: reload quest_greeting_locale'),
(872,'Command: server debug');
/*!40000 ALTER TABLE `rbac_permissions` ENABLE KEYS */;
@@ -0,0 +1,7 @@
DELETE FROM `rbac_permissions` WHERE `id`=866;
INSERT INTO `rbac_permissions` (`id`,`name`) VALUES
(866, 'Command: list spawnpoints');
DELETE FROM `rbac_linked_permissions` WHERE `linkedId`=866;
INSERT INTO `rbac_linked_permissions` (`id`,`linkedId`) VALUES
(196,866);
@@ -0,0 +1,234 @@
-- Create databases for spawn group template, and spawn group membership
-- Current flags
-- 0x01 Legacy Spawn Mode (spawn using legacy spawn system)
-- 0x02 Manual Spawn (don't automatically spawn, instead spawned by core as part of script)
DROP TABLE IF EXISTS `spawn_group_template`;
CREATE TABLE `spawn_group_template` (
`groupId` int(10) unsigned NOT NULL,
`groupName` varchar(100) NOT NULL,
`groupFlags` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`groupId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `spawn_group`;
CREATE TABLE `spawn_group` (
`groupId` int(10) unsigned NOT NULL,
`spawnType` tinyint(10) unsigned NOT NULL,
`spawnId` int(10) unsigned NOT NULL,
PRIMARY KEY (`groupId`,`spawnType`,`spawnId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- Create the default groups
INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES
(0, 'Default Group', 0x01),
(1, 'Legacy Group', (0x01|0x02)),
(2, 'Dynamic Scaling (Quest objectives)', (0x01|0x08)),
(3, 'Dynamic Scaling (Escort NPCs)', (0x01|0x08|0x10)),
(4, 'Dynamic Scaling (Gathering nodes)', (0x01|0x08));
-- Create creature dynamic spawns group (creatures with quest items, or subjects of quests with less than 30min spawn time)
DROP TABLE IF EXISTS `creature_temp_group`;
CREATE TEMPORARY TABLE `creature_temp_group`
(
`creatureId` int(10) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `creature_questitem` ON `CreatureEntry` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `quest_template` ON `RequiredNpcOrGo1` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `quest_template` ON `RequiredNpcOrGo2` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `quest_template` ON `RequiredNpcOrGo3` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `quest_template` ON `RequiredNpcOrGo4` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT DISTINCT 2, 0, `creatureId`
FROM `creature_temp_group`;
DROP TABLE `creature_temp_group`;
-- Create gameobject dynamic spawns group (gameobjects with quest items, or subjects of quests with less than 30min spawn time)
DROP TABLE IF EXISTS `gameobject_temp_group`;
CREATE TEMPORARY TABLE `gameobject_temp_group`
(
`gameobjectId` int(10) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `gameobject_temp_group_ids`;
CREATE TEMPORARY TABLE `gameobject_temp_group_ids`
(
`entryid` int(10) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `gameobject_temp_group_ids` ADD INDEX (`entryid`);
INSERT INTO `gameobject_temp_group`
SELECT `guid`
FROM `gameobject` G
INNER JOIN `gameobject_questitem` ON `GameObjectEntry` = G.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `gameobject_temp_group_ids` (`entryid`)
SELECT DISTINCT `RequiredNpcOrGo1` * -1
FROM `quest_template`;
INSERT INTO `gameobject_temp_group_ids` (`entryid`)
SELECT DISTINCT `RequiredNpcOrGo2` * -1
FROM `quest_template`;
INSERT INTO `gameobject_temp_group_ids` (`entryid`)
SELECT DISTINCT `RequiredNpcOrGo3` * -1
FROM `quest_template`;
INSERT INTO `gameobject_temp_group_ids` (`entryid`)
SELECT DISTINCT `RequiredNpcOrGo4` * -1
FROM `quest_template`;
INSERT INTO `gameobject_temp_group`
SELECT `guid`
FROM `gameobject` G
INNER JOIN `gameobject_temp_group_ids` ON `entryid` = G.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT DISTINCT 2, 1, `gameobjectId`
FROM `gameobject_temp_group`;
DROP TABLE `gameobject_temp_group`;
ALTER TABLE `gameobject_temp_group_ids` DROP INDEX `entryid`;
DROP TABLE `gameobject_temp_group_ids`;
-- Add mining nodes/herb nodes to profession node group
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT 4, 1, `guid`
FROM `gameobject` g
INNER JOIN `gameobject_template` gt
ON gt.`entry` = g.`id`
WHERE `type` = 3
AND `Data0` IN (2, 8, 9, 10, 11, 18, 19, 20, 21, 22, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 47, 48, 49, 50, 51, 379, 380, 399, 400, 439, 440, 441, 442, 443, 444, 519, 521, 719, 939, 1119, 1120,
1121, 1122, 1123, 1124, 1632, 1639, 1641, 1642, 1643, 1644, 1645, 1646, 1649, 1650, 1651, 1652, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1800, 1860);
-- Add Escort NPCs
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES
(3, 0, 10873),
(3, 0, 17874),
(3, 0, 40210),
(3, 0, 11348),
(3, 0, 93301),
(3, 0, 93194),
(3, 0, 19107),
(3, 0, 21692),
(3, 0, 21584),
(3, 0, 23229),
(3, 0, 24268),
(3, 0, 21594),
(3, 0, 14387),
(3, 0, 50381),
(3, 0, 15031),
(3, 0, 26987),
(3, 0, 29241),
(3, 0, 32333),
(3, 0, 33115),
(3, 0, 37085),
(3, 0, 41759),
(3, 0, 84459),
(3, 0, 78685),
(3, 0, 62090),
(3, 0, 72388),
(3, 0, 86832),
(3, 0, 67040),
(3, 0, 78781),
(3, 0, 65108),
(3, 0, 63688),
(3, 0, 59383),
(3, 0, 63625),
(3, 0, 70021),
(3, 0, 82071),
(3, 0, 117903),
(3, 0, 111075),
(3, 0, 101136),
(3, 0, 101303),
(3, 0, 122686),
(3, 0, 117065),
(3, 0, 202337),
(3, 0, 2017),
(3, 0, 132683);
-- remove potential duplicates
DELETE FROM `spawn_group` WHERE `groupId` != 3 AND `spawnType`=0 AND `spawnId` IN (SELECT `spawnId` FROM (SELECT `spawnId` FROM `spawn_group` WHERE `groupId`=3 AND `spawnType`=0) as `temp`);
DELETE FROM `spawn_group` WHERE `groupId` != 4 AND `spawnType`=1 AND `spawnId` IN (SELECT `spawnId` FROM (SELECT `spawnId` FROM `spawn_group` WHERE `groupId`=4 AND `spawnType`=1) as `temp`);
-- Update trinity strings for various cs_list strings, to support showing spawn ID and guid.
UPDATE `trinity_string`
SET `content_default` = '%d (Entry: %d) - |cffffffff|Hgameobject:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r %s %s'
WHERE `entry` = 517;
UPDATE `trinity_string`
SET `content_default` = '%d - |cffffffff|Hcreature:%d|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r %s %s'
WHERE `entry` = 515;
UPDATE `trinity_string`
SET `content_default` = '%d - %s X:%f Y:%f Z:%f MapId:%d %s %s'
WHERE `entry` = 1111;
UPDATE `trinity_string`
SET `content_default` = '%d - %s X:%f Y:%f Z:%f MapId:%d %s %s'
WHERE `entry` = 1110;
-- Add new trinity strings for extra npc/gobject info lines
DELETE FROM `trinity_string` WHERE `entry` BETWEEN 5070 AND 5082;
INSERT INTO `trinity_string` (`entry`, `content_default`) VALUES
(5070, 'Spawn group: %s (ID: %u, Flags: %u, Active: %u)'),
(5071, 'Compatibility Mode: %u'),
(5072, 'GUID: %s'),
(5073, 'SpawnID: %u, location (%f, %f, %f)'),
(5074, 'Distance from player %f'),
(5075, 'Spawn group %u not found'),
(5076, 'Spawned a total of %zu objects:'),
(5077, 'Listing %s respawns within %uyd'),
(5078, 'Listing %s respawns for %s (zone %u)'),
(5079, 'SpawnID | Entry | GridXY| Zone | Respawn time (Full)'),
(5080, 'overdue'),
(5081, 'creatures'),
(5082, 'gameobjects');
-- Add new NPC/Gameobject commands
DELETE FROM `command` WHERE `name` IN ('npc spawngroup', 'npc despawngroup', 'gobject spawngroup', 'gobject despawngroup', 'list respawns');
INSERT INTO `command` (`name`, `permission`, `help`) VALUES
('npc spawngroup', 856, 'Syntax: .npc spawngroup $groupId [ignorerespawn] [force]'),
('npc despawngroup', 857, 'Syntax: .npc despawngroup $groupId [removerespawntime]'),
('gobject spawngroup', 858, 'Syntax: .gobject spawngroup $groupId [ignorerespawn] [force]'),
('gobject despawngroup', 859, 'Syntax: .gobject despawngroup $groupId [removerespawntime]'),
('list respawns', 860, 'Syntax: .list respawns [distance]
Lists all pending respawns within <distance> yards, or within current zone if not specified.');
@@ -0,0 +1,6 @@
--
DELETE FROM `command` WHERE `name`="list spawnpoints";
INSERT INTO `command` (`name`,`permission`,`help`) VALUES
('list spawnpoints', 866, 'Syntax: .list spawnpoints
Lists all spawn points (both creatures and GOs) in the current zone.');
@@ -0,0 +1,10 @@
--
DROP TABLE IF EXISTS `instance_spawn_groups`;
CREATE TABLE `instance_spawn_groups` (
`instanceMapId` smallint(5) unsigned not null,
`bossStateId` tinyint unsigned not null,
`bossStates` tinyint unsigned not null,
`spawnGroupId` int unsigned not null,
`flags` tinyint unsigned not null,
PRIMARY KEY (`instanceMapId`,`bossStateId`,`spawnGroupId`,`bossStates`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+15 -6
View File
@@ -111,15 +111,24 @@ void EventProcessor::KillAllEvents(bool force)
m_events.clear();
}
void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime)
void EventProcessor::AddEvent(BasicEvent* event, uint64 e_time, bool set_addtime)
{
if (set_addtime)
Event->m_addTime = m_time;
Event->m_execTime = e_time;
m_events.insert(std::pair<uint64, BasicEvent*>(e_time, Event));
event->m_addTime = m_time;
event->m_execTime = e_time;
m_events.insert(std::pair<uint64, BasicEvent*>(e_time, event));
}
uint64 EventProcessor::CalculateTime(uint64 t_offset) const
void EventProcessor::ModifyEventTime(BasicEvent* event, uint64 newTime)
{
return(m_time + t_offset);
for (auto itr = m_events.begin(); itr != m_events.end(); ++itr)
{
if (itr->second != event)
continue;
event->m_execTime = newTime;
m_events.erase(itr);
m_events.insert(std::pair<uint64, BasicEvent*>(newTime, event));
break;
}
}
+5 -2
View File
@@ -20,6 +20,7 @@
#define __EVENTPROCESSOR_H
#include "Define.h"
#include "Duration.h"
#include <map>
class EventProcessor;
@@ -78,8 +79,10 @@ class TC_COMMON_API EventProcessor
void Update(uint32 p_time);
void KillAllEvents(bool force);
void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true);
uint64 CalculateTime(uint64 t_offset) const;
void AddEvent(BasicEvent* event, uint64 e_time, bool set_addtime = true);
void AddEventAtOffset(BasicEvent* event, Milliseconds const& offset) { AddEvent(event, CalculateTime(offset.count())); }
void ModifyEventTime(BasicEvent* event, uint64 newTime);
uint64 CalculateTime(uint64 t_offset) const { return m_time + t_offset; }
protected:
uint64 m_time;
@@ -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)
@@ -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
};
+5 -2
View File
@@ -142,8 +142,8 @@ class TC_GAME_API CreatureAI : public UnitAI
virtual bool IsEscorted() const { return false; }
// Called when creature is spawned or respawned
virtual void JustRespawned() { }
// Called when creature appears in the world (spawn, respawn, grid load etc...)
virtual void JustAppeared() { }
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) { }
@@ -201,6 +201,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;
@@ -467,7 +467,7 @@ void BossAI::_Reset()
events.Reset();
summons.DespawnAll();
scheduler.CancelAll();
if (instance)
if (instance && instance->GetBossState(_bossId) != DONE)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -553,12 +553,12 @@ bool BossAI::CanAIAttack(Unit const* target) const
return CheckBoundary(target);
}
void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who)
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)
@@ -361,8 +361,8 @@ class TC_GAME_API BossAI : public ScriptedAI
void _JustEngagedWith();
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();
@@ -31,6 +31,7 @@ EndScriptData */
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "World.h"
enum Points
{
@@ -167,7 +168,7 @@ void npc_escortAI::JustDied(Unit* /*killer*/)
}
}
void npc_escortAI::JustRespawned()
void npc_escortAI::JustAppeared()
{
m_uiEscortState = STATE_ESCORT_NONE;
@@ -257,13 +258,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;
}
@@ -298,11 +303,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();
@@ -443,6 +454,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());
@@ -588,3 +615,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;
}
@@ -65,7 +65,7 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
void JustDied(Unit*) override;
void JustRespawned() override;
void JustAppeared() override;
void ReturnToLastPoint();
@@ -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();
@@ -160,7 +160,7 @@ void FollowerAI::JustDied(Unit* /*killer*/)
}
}
void FollowerAI::JustRespawned()
void FollowerAI::JustAppeared()
{
m_uiFollowState = STATE_FOLLOW_NONE;
@@ -53,7 +53,7 @@ class TC_GAME_API FollowerAI : public ScriptedAI
void JustDied(Unit*) override;
void JustRespawned() override;
void JustAppeared() override;
void UpdateAI(uint32) override; //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
+1 -1
View File
@@ -570,7 +570,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
return false;
}
void SmartAI::JustRespawned()
void SmartAI::JustAppeared()
{
mDespawnTime = 0;
mDespawnState = 0;
+1 -1
View File
@@ -71,7 +71,7 @@ class TC_GAME_API SmartAI : public CreatureAI
bool IsEscortInvokerInRange();
// Called when creature is spawned or respawned
void JustRespawned() override;
void JustAppeared() override;
// Called at reaching home after evade, InitializeAI(), EnterEvadeMode() for resetting variables
void JustReachedHome() override;
+104 -6
View File
@@ -1066,7 +1066,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
creatureTarget->DespawnOrUnsummon(respawnDelay);
}
else if (GameObject* goTarget = target->ToGameObject())
goTarget->SetRespawnTime(respawnDelay);
goTarget->DespawnOrUnsummon(Milliseconds(respawnDelay));
}
break;
}
@@ -1469,19 +1469,18 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_RESPAWN_TARGET:
case SMART_ACTION_ENABLE_TEMP_GOBJ:
{
for (WorldObject* target : targets)
{
if (IsCreature(target))
target->ToCreature()->Respawn();
TC_LOG_WARN("sql.sql", "Invalid creature target '%s' (entry %u, spawnId %u) specified for SMART_ACTION_ENABLE_TEMP_GOBJ", target->GetName().c_str(), target->GetEntry(), target->ToCreature()->GetSpawnId());
else if (IsGameObject(target))
{
// do not modify respawndelay of already spawned gameobjects
if (target->ToGameObject()->isSpawnedByDefault())
target->ToGameObject()->Respawn();
TC_LOG_WARN("sql.sql", "Invalid gameobject target '%s' (entry %u, spawnId %u) for SMART_ACTION_ENABLE_TEMP_GOBJ - the object is spawned by default", target->GetName().c_str(), target->GetEntry(), target->ToGameObject()->GetSpawnId());
else
target->ToGameObject()->SetRespawnTime(e.action.RespawnTarget.goRespawnTime);
target->ToGameObject()->SetRespawnTime(e.action.enableTempGO.duration);
}
}
break;
@@ -2132,6 +2131,91 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
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
GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, 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
GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, 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:
{
if (!IsSmart())
@@ -2234,6 +2318,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_RESPAWN_BY_SPAWNID:
{
Map* map = nullptr;
if (WorldObject* obj = GetBaseObject())
map = obj->GetMap();
else if (!targets.empty())
map = targets.front()->GetMap();
if (map)
map->RemoveRespawnTime(SpawnObjectType(e.action.respawnData.spawnType), e.action.respawnData.spawnId, true);
else
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u - tries to respawn by spawnId but does not provide a map", e.entryOrGuid, e.GetScriptType(), e.event_id);
break;
}
default:
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -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)));
@@ -998,7 +998,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;
@@ -1532,6 +1532,19 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
case SMART_ACTION_RESPAWN_BY_SPAWNID:
if (!sObjectMgr->GetSpawnData(SpawnObjectType(e.action.respawnData.spawnType), e.action.respawnData.spawnId))
{
TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u specifies invalid spawn data (%u,%u)", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.respawnData.spawnType, e.action.respawnData.spawnId);
return false;
}
break;
case SMART_ACTION_ENABLE_TEMP_GOBJ:
if (!e.action.enableTempGO.duration)
{
TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u does not specify duration", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
}
case SMART_ACTION_START_CLOSEST_WAYPOINT:
case SMART_ACTION_FOLLOW:
case SMART_ACTION_SET_ORIENTATION:
@@ -1565,7 +1578,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_REMOVE_UNIT_FLAG:
case SMART_ACTION_PLAYMOVIE:
case SMART_ACTION_MOVE_TO_POS:
case SMART_ACTION_RESPAWN_TARGET:
case SMART_ACTION_CLOSE_GOSSIP:
case SMART_ACTION_TRIGGER_TIMED_EVENT:
case SMART_ACTION_REMOVE_TIMED_EVENT:
@@ -1609,6 +1621,9 @@ 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:
case SMART_ACTION_STOP_MOTION:
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);
@@ -532,7 +532,7 @@ enum SMART_ACTION
SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance
SMART_ACTION_PLAYMOVIE = 68, // entry
SMART_ACTION_MOVE_TO_POS = 69, // PointId, transport, disablePathfinding, ContactDistance
SMART_ACTION_RESPAWN_TARGET = 70, //
SMART_ACTION_ENABLE_TEMP_GOBJ = 70, // despawnTimer (sec)
SMART_ACTION_EQUIP = 71, // entry, slotmask slot1, slot2, slot3 , only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), slots1-3 are only used if no entry is set
SMART_ACTION_CLOSE_GOSSIP = 72, // none
SMART_ACTION_TRIGGER_TIMED_EVENT = 73, // id(>1)
@@ -591,8 +591,14 @@ enum SMART_ACTION
SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126,
SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired
SMART_ACTION_PLAY_ANIMKIT = 128, // id, type (0 = oneShot, 1 = aiAnim, 2 = meleeAnim, 3 = movementAnim)
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_RESPAWN_BY_SPAWNID = 133, // spawnType, spawnId
SMART_ACTION_END = 129
SMART_ACTION_END = 134
};
struct SmartAction
@@ -1015,8 +1021,8 @@ struct SmartAction
struct
{
uint32 goRespawnTime;
} RespawnTarget;
uint32 duration;
} enableTempGO;
struct
{
@@ -1112,6 +1118,14 @@ struct SmartAction
uint32 disable;
} disableEvade;
struct
{
uint32 groupId;
uint32 minDelay;
uint32 maxDelay;
uint32 spawnflags;
} groupSpawn;
struct
{
uint32 animKit;
@@ -1146,6 +1160,12 @@ struct SmartAction
uint32 movementExpired;
} stopMotion;
struct
{
uint32 spawnType;
uint32 spawnId;
} respawnData;
//! Note for any new future actions
//! All parameters must have type uint32
@@ -1161,6 +1181,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
+5 -5
View File
@@ -754,11 +754,11 @@ enum RBACPermissions
RBAC_PERM_COMMAND_RELOAD_CONVERSATION_TEMPLATE = 853, // not on 3.3.5a or 4.3.4
RBAC_PERM_COMMAND_DEBUG_CONVERSATION = 854, // not on 3.3.5a or 4.3.4
RBAC_PERM_COMMAND_DEBUG_PLAY_MUSIC = 855,
RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856, // reserved for dynamic_spawning
RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857, // reserved for dynamic_spawning
RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858, // reserved for dynamic_spawning
RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859, // reserved for dynamic_spawning
RBAC_PERM_COMMAND_LIST_RESPAWNS = 860, // reserved for dynamic_spawning
RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856,
RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857,
RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858,
RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859,
RBAC_PERM_COMMAND_LIST_RESPAWNS = 860,
RBAC_PERM_COMMAND_GROUP_SET = 861,
RBAC_PERM_COMMAND_GROUP_ASSISTANT = 862,
RBAC_PERM_COMMAND_GROUP_MAINTANK = 863,
+2 -5
View File
@@ -776,17 +776,14 @@ 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);
@@ -1614,7 +1614,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);
@@ -316,6 +316,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type)
|| (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))
{
CreatureData &data = sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId());
data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
data.spawndist = 5;
}
//else spawndist will be 15, so creatures move maximum=10
+1 -1
View File
@@ -2120,7 +2120,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)
{
+243 -148
View File
@@ -190,7 +190,7 @@ m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(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_triggerJustAppeared(true), m_respawnCompatibilityMode(false), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
m_valuesCount = UNIT_END;
@@ -204,7 +204,6 @@ m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(
m_CombatDistance = 0;//MELEE_RANGE;
ResetLootMode(); // restore default loot mode
m_TriggerJustRespawned = false;
m_isTempWorldObject = false;
}
@@ -288,27 +287,48 @@ void Creature::RemoveCorpse(bool setSpawnTime)
if (getDeathState() != CORPSE)
return;
m_corpseRemoveTime = time(nullptr);
setDeathState(DEAD);
RemoveAllAuras();
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
loot.clear();
uint32 respawnDelay = m_respawnDelay;
if (IsAIEnabled)
AI()->CorpseRemoved(respawnDelay);
if (m_respawnCompatibilityMode)
{
m_corpseRemoveTime = time(nullptr);
setDeathState(DEAD);
RemoveAllAuras();
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
loot.clear();
uint32 respawnDelay = m_respawnDelay;
if (IsAIEnabled)
AI()->CorpseRemoved(respawnDelay);
// 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);
SetHomePosition(x, y, z, o);
GetMap()->CreatureRelocation(this, x, y, z, o);
float x, y, z, o;
GetRespawnPosition(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(nullptr) + respawnDelay, m_respawnTime);
SaveRespawnTime(0, false);
}
if (TempSummon* summon = ToTempSummon())
summon->UnSummon();
else
AddObjectToRemoveList();
}
}
/**
@@ -502,11 +522,11 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
void Creature::Update(uint32 diff)
{
if (IsAIEnabled && m_TriggerJustRespawned)
if (IsAIEnabled && m_triggerJustAppeared && m_deathState == ALIVE)
{
m_TriggerJustRespawned = false;
AI()->JustRespawned();
if (m_vehicleKit)
m_triggerJustAppeared = false;
AI()->JustAppeared();
if (m_respawnCompatibilityMode && m_vehicleKit)
m_vehicleKit->Reset();
}
@@ -520,30 +540,38 @@ 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:
{
if (!m_respawnCompatibilityMode)
{
TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: DEAD (3)", GetGUID().GetCounter(), GetEntry());
break;
}
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
// Delay respawn if spawn group is not active
if (m_creatureData && !GetMap()->IsSpawnGroupActive(m_creatureData->spawnGroupData->groupId))
{
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 // 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);
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
@@ -888,7 +916,7 @@ 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);
@@ -899,6 +927,10 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 /*phaseMask*
PhasingHandler::InitDbVisibleMapId(GetPhaseShift(), data->terrainSwapMap);
}
// Set if this creature can handle dynamic spawns
if (!dynamic)
SetRespawnCompatibilityMode();
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
@@ -908,13 +940,13 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 /*phaseMask*
//! 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;
}
@@ -948,10 +980,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 /*phaseMask*
//! 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();
@@ -1126,26 +1156,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
@@ -1160,6 +1181,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();
data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : 0;
data.phaseGroup = GetDBPhase() < 0 ? abs(GetDBPhase()) : 0;
@@ -1378,7 +1401,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)
{
@@ -1414,36 +1437,43 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
if (!data)
{
TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) not found in table `creature`, can't load. ", spawnId);
TC_LOG_ERROR("sql.sql", "Creature (SpawnID %u) not found in table `creature`, can't load. ", spawnId);
return false;
}
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))
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;
if (!m_respawnTime && !map->IsSpawnGroupActive(data->spawnGroupData->groupId))
{
// @todo pools need fixing! this is just a temporary crashfix, but they violate dynspawn principles
ASSERT(m_respawnCompatibilityMode || sPoolMgr->IsPartOfAPool<Creature>(spawnId), "Creature (SpawnID %u) trying to load in inactive spawn group %s.", spawnId, data->spawnGroupData->name.c_str());
m_respawnTime = GameTime::GetGameTime() + urand(4, 7);
}
if (m_respawnTime) // respawn on Update
{
ASSERT(m_respawnCompatibilityMode || sPoolMgr->IsPartOfAPool<Creature>(spawnId), "Creature (SpawnID %u) trying to load despite a respawn timer in progress.", spawnId);
m_deathState = DEAD;
if (CanFly())
{
float tz = map->GetHeight(GetPhaseShift(), data->posX, data->posY, data->posZ, true, MAX_FALL_DISTANCE);
if (data->posZ - tz > 0.1f && Trinity::IsValidMapCoord(tz))
Relocate(data->posX, data->posY, tz);
float tz = map->GetHeight(GetPhaseShift(), data->spawnPoint, true, MAX_FALL_DISTANCE);
if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz))
Relocate(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY(), tz);
}
}
@@ -1539,15 +1569,24 @@ void Creature::DeleteFromDB()
return;
}
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
// remove any scheduled respawns
GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
// delete data from memory
sObjectMgr->DeleteCreatureData(m_spawnId);
// delete data and all its associations from DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
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);
@@ -1561,6 +1600,11 @@ void Creature::DeleteFromDB()
trans->Append(stmt);
WorldDatabase.CommitTransaction(trans);
// then delete any active instances of the creature
auto const& spawnMap = GetMap()->GetCreatureBySpawnIdStore();
for (auto it = spawnMap.find(m_spawnId); it != spawnMap.end(); it = spawnMap.find(m_spawnId))
it->second->AddObjectToRemoveList();
}
bool Creature::IsInvisibleDueToDespawn() const
@@ -1695,14 +1739,30 @@ 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
@@ -1772,8 +1832,6 @@ void Creature::setDeathState(DeathState s)
void Creature::Respawn(bool force)
{
DestroyForNearbyPlayers();
if (force)
{
if (IsAlive())
@@ -1782,85 +1840,110 @@ void Creature::Respawn(bool force)
setDeathState(CORPSE);
}
RemoveCorpse(false);
if (m_respawnCompatibilityMode)
{
DestroyForNearbyPlayers();
RemoveCorpse(false);
if (getDeathState() == DEAD)
if (getDeathState() == DEAD)
{
if (m_spawnId)
GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str());
m_respawnTime = 0;
ResetPickPocketRefillTimer();
loot.clear();
if (m_originalEntry != GetEntry())
UpdateEntry(m_originalEntry);
SelectLevel();
setDeathState(JUST_RESPAWNED);
uint32 displayID = GetNativeDisplayId();
if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
}
GetMotionMaster()->InitDefault();
//Re-initialize reactstate that could be altered by movementgenerators
InitializeReactState();
if (IsAIEnabled) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
AI()->Reset();
m_triggerJustAppeared = true;
uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
if (poolid)
sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
}
UpdateObjectVisibility();
}
else
{
if (m_spawnId)
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
GetName().c_str(), GetGUID().ToString().c_str());
m_respawnTime = 0;
ResetPickPocketRefillTimer();
loot.clear();
if (m_originalEntry != GetEntry())
UpdateEntry(m_originalEntry);
SelectLevel();
setDeathState(JUST_RESPAWNED);
uint32 displayID = GetNativeDisplayId();
if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
}
GetMotionMaster()->InitDefault();
//Re-initialize reactstate that could be altered by movementgenerators
InitializeReactState();
//Call AI respawn virtual function
if (IsAIEnabled)
{
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
AI()->Reset();
m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
}
uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
if (poolid)
sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, true);
}
UpdateObjectVisibility();
TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
GetName().c_str(), GetGUID().ToString().c_str());
}
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawnTimer)
{
if (timeMSToDespawn)
{
ForcedDespawnDelayEvent* pEvent = new ForcedDespawnDelayEvent(*this, forceRespawnTimer);
m_Events.AddEvent(pEvent, m_Events.CalculateTime(timeMSToDespawn));
m_Events.AddEvent(new ForcedDespawnDelayEvent(*this, forceRespawnTimer), m_Events.CalculateTime(timeMSToDespawn));
return;
}
if (forceRespawnTimer > Seconds::zero())
if (m_respawnCompatibilityMode)
{
uint32 corpseDelay = GetCorpseDelay();
uint32 respawnDelay = GetRespawnDelay();
// do it before killing creature
DestroyForNearbyPlayers();
bool overrideRespawnTime = false;
if (IsAlive())
{
uint32 respawnDelay = m_respawnDelay;
uint32 corpseDelay = m_corpseDelay;
m_respawnDelay = forceRespawnTimer.count();
m_corpseDelay = 0;
if (forceRespawnTimer > Seconds::zero())
{
SetCorpseDelay(0);
SetRespawnDelay(forceRespawnTimer.count());
overrideRespawnTime = true;
}
setDeathState(JUST_DIED);
m_respawnDelay = respawnDelay;
m_corpseDelay = corpseDelay;
}
else
{
m_corpseRemoveTime = time(nullptr);
m_respawnTime = time(nullptr) + forceRespawnTimer.count();
}
// Skip corpse decay time
RemoveCorpse(!overrideRespawnTime);
SetCorpseDelay(corpseDelay);
SetRespawnDelay(respawnDelay);
}
else
if (IsAlive())
setDeathState(JUST_DIED);
{
if (forceRespawnTimer > Seconds::zero())
SaveRespawnTime(forceRespawnTimer.count());
else
{
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();
}
RemoveCorpse(false);
AddObjectToRemoveList();
}
}
void Creature::DespawnOrUnsummon(uint32 msTimeToDespawn /*= 0*/, Seconds const& forceRespawnTimer /*= 0*/)
@@ -2209,12 +2292,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;
}
time_t thisRespawnTime = forceDelay ? time(NULL) + forceDelay : m_respawnTime;
GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, GetMap()->GetZoneId(GetPhaseShift(), GetHomePosition()), Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), savetodb && m_creatureData && m_creatureData->dbData);
}
// this should not be called by petAI or
@@ -2405,29 +2495,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)
{
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;
}
x = GetPositionX();
y = GetPositionY();
z = GetPositionZ();
if (ori)
*ori = GetOrientation();
if (dist)
*dist = 0;
}
void Creature::AllLootRemovedFromCorpse()
@@ -2478,7 +2565,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;
@@ -2916,3 +3003,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);
}
+10 -5
View File
@@ -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();
@@ -168,8 +168,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);
@@ -232,7 +231,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; }
@@ -303,6 +302,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;
@@ -320,6 +323,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;
@@ -389,7 +393,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
//Formation var
CreatureGroup* m_formation;
bool m_TriggerJustRespawned;
bool m_triggerJustAppeared;
bool m_respawnCompatibilityMode;
/* Spell focus system */
Spell const* m_focusSpell; // Locks the target during spell cast for proper facing
@@ -20,6 +20,7 @@
#include "DBCEnums.h"
#include "SharedDefines.h"
#include "SpawnData.h"
#include "UnitDefines.h"
#include "WorldPacket.h"
#include <string>
@@ -237,38 +238,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), phaseUseFlags(0),
phaseId(0), phaseGroup(0), terrainSwapMap(-1), 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;
uint8 phaseUseFlags;
uint32 phaseId;
uint32 phaseGroup;
int32 terrainSwapMap;
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
@@ -57,6 +57,7 @@ void TempSummon::Update(uint32 diff)
switch (m_type)
{
case TEMPSUMMON_MANUAL_DESPAWN:
case TEMPSUMMON_DEAD_DESPAWN:
break;
case TEMPSUMMON_TIMED_DESPAWN:
{
@@ -104,7 +105,7 @@ void TempSummon::Update(uint32 diff)
case TEMPSUMMON_CORPSE_DESPAWN:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == CORPSE || m_deathState == DEAD)
if (m_deathState == CORPSE)
{
UnSummon();
return;
@@ -112,19 +113,9 @@ void TempSummon::Update(uint32 diff)
break;
}
case TEMPSUMMON_DEAD_DESPAWN:
{
if (m_deathState == DEAD)
{
UnSummon();
return;
}
break;
}
case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == CORPSE || m_deathState == DEAD)
if (m_deathState == CORPSE)
{
UnSummon();
return;
@@ -146,13 +137,6 @@ void TempSummon::Update(uint32 diff)
}
case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN:
{
// if m_deathState is DEAD, CORPSE was skipped
if (m_deathState == DEAD)
{
UnSummon();
return;
}
if (!IsInCombat() && IsAlive())
{
if (m_timer <= diff)
+209 -119
View File
@@ -55,7 +55,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;
@@ -65,6 +65,7 @@ GameObject::GameObject() : WorldObject(false), MapObject(),
m_valuesCount = GAMEOBJECT_END;
m_respawnTime = 0;
m_respawnDelayTime = 300;
m_despawnDelay = 0;
m_lootState = GO_NOT_READY;
m_spawnedByDefault = true;
m_usetimes = 0;
@@ -196,8 +197,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);
@@ -210,6 +210,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();
SetZoneScript();
if (m_zoneScript)
{
@@ -348,6 +352,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();
@@ -370,6 +377,14 @@ void GameObject::Update(uint32 diff)
else if (!AIM_Initialize())
TC_LOG_ERROR("misc", "Could not initialize GameObjectAI");
if (m_despawnDelay)
{
if (m_despawnDelay > diff)
m_despawnDelay -= diff;
else
DespawnOrUnsummon(0ms, m_despawnRespawnTime);;
}
switch (m_lootState)
{
case GO_NOT_READY:
@@ -471,83 +486,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;
}
m_respawnTime = 0;
m_SkillupList.clear();
m_usetimes = 0;
// 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
ObjectGuid dbtableHighGuid(HighGuid::GameObject, GetEntry(), m_spawnId);
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
if (linkedRespawntime) // Can't respawn, the master is dead
{
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;
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;
}
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;
m_respawnTime = 0;
m_SkillupList.clear();
m_usetimes = 0;
// 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
{
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;
}
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;
}
// 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);
}
// 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();
// 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();
@@ -726,6 +748,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;
@@ -733,12 +756,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;
@@ -762,6 +801,25 @@ void GameObject::AddUniqueUse(Player* player)
m_unique_users.insert(player->GetGUID());
}
void GameObject::DespawnOrUnsummon(Milliseconds const& delay, Seconds const& forceRespawnTime)
{
if (delay > 0ms)
{
if (!m_despawnDelay || m_despawnDelay > delay.count())
{
m_despawnDelay = delay.count();
m_despawnRespawnTime = forceRespawnTime;
}
}
else
{
uint32 const respawnDelay = (forceRespawnTime > 0s) ? forceRespawnTime.count() : m_respawnDelayTime;
if (m_goData && respawnDelay)
SaveRespawnTime(respawnDelay);
Delete();
}
}
void GameObject::Delete()
{
SetLootState(GO_NOT_READY);
@@ -838,7 +896,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!");
@@ -858,22 +916,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();
@@ -906,9 +964,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)
{
TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u) not found in table `gameobject`, can't load. ", spawnId);
@@ -918,14 +976,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;
PhasingHandler::InitDbPhaseShift(GetPhaseShift(), data->phaseUseFlags, data->phaseId, data->phaseGroup);
@@ -950,12 +1008,18 @@ 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);
}
}
}
else
{
if (!m_respawnCompatibilityMode)
{
TC_LOG_WARN("sql.sql", "GameObject %u (SpawnID %u) is not spawned by default, but tries to use a non-hack spawn system. This will not work. Defaulting to compatibility mode.", entry, spawnId);
m_respawnCompatibilityMode = true;
}
m_spawnedByDefault = false;
m_respawnDelayTime = -data->spawntimesecs;
m_respawnTime = 0;
@@ -971,20 +1035,24 @@ 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);
SQLTransaction trans = WorldDatabase.BeginTransaction();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
trans->Append(stmt);
stmt->setUInt32(0, m_spawnId);
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);
trans->Append(stmt);
WorldDatabase.Execute(stmt);
WorldDatabase.CommitTransaction(trans);
}
/*********************************************************/
@@ -1047,10 +1115,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 && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && 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
@@ -1110,12 +1187,29 @@ uint8 GameObject::getLevelForTarget(WorldObject const* target) const
return 1;
}
time_t GameObject::GetRespawnTimeEx() const
{
time_t now = GameTime::GetGameTime();
if (m_respawnTime > now)
return m_respawnTime;
else
return now;
}
void GameObject::SetRespawnTime(int32 respawn)
{
m_respawnTime = respawn > 0 ? GameTime::GetGameTime() + respawn : 0;
m_respawnDelayTime = respawn > 0 ? respawn : 0;
if (respawn && !m_spawnedByDefault)
UpdateObjectVisibility(true);
}
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);
}
}
@@ -1219,7 +1313,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;
}
@@ -1230,10 +1324,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;
@@ -1963,8 +2057,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;
@@ -2447,24 +2541,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
@@ -2508,7 +2598,7 @@ public:
virtual G3D::Vector3 GetPosition() const override { return G3D::Vector3(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); }
virtual float GetOrientation() const override { return _owner->GetOrientation(); }
virtual float GetScale() const override { return _owner->GetObjectScale(); }
virtual void DebugVisualizeCorner(G3D::Vector3 const& corner) const override { _owner->SummonCreature(1, corner.x, corner.y, corner.z, 0, TEMPSUMMON_MANUAL_DESPAWN); }
virtual void DebugVisualizeCorner(G3D::Vector3 const& corner) const override { const_cast<GameObject*>(_owner)->SummonCreature(1, corner.x, corner.y, corner.z, 0, TEMPSUMMON_MANUAL_DESPAWN); }
private:
GameObject* _owner;
@@ -91,11 +91,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;
@@ -115,8 +115,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)
@@ -140,20 +139,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetSpellId() const { return m_spellId;}
time_t GetRespawnTime() const { return m_respawnTime; }
time_t GetRespawnTimeEx() const
{
time_t now = time(nullptr);
if (m_respawnTime > now)
return m_respawnTime;
else
return now;
}
time_t GetRespawnTimeEx() const;
void SetRespawnTime(int32 respawn)
{
m_respawnTime = respawn > 0 ? time(nullptr) + respawn : 0;
m_respawnDelayTime = respawn > 0 ? respawn : 0;
}
void SetRespawnTime(int32 respawn);
void Respawn();
bool isSpawned() const
{
@@ -165,6 +153,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void SetSpawnedByDefault(bool b) { m_spawnedByDefault = b; }
uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
void Refresh();
void DespawnOrUnsummon(Milliseconds const& delay = 0ms, Seconds const& forceRespawnTime = 0s);
void Delete();
void SendGameObjectDespawn();
void getFishLoot(Loot* loot, Player* loot_owner);
@@ -216,7 +205,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;
@@ -268,6 +257,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; }
virtual uint32 GetScriptId() const;
GameObjectAI* AI() const { return m_AI; }
@@ -303,6 +296,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 m_spellId;
time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
uint32 m_despawnDelay;
Seconds m_despawnRespawnTime; // override respawn time after delayed despawn
LootState m_lootState;
ObjectGuid m_lootStateUnitGUID; // GUID of the unit passed with SetLootState(LootState, Unit*)
bool m_spawnedByDefault;
@@ -349,5 +344,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
}
GameObjectAI* m_AI;
bool m_respawnCompatibilityMode;
};
#endif
@@ -20,6 +20,7 @@
#include "Common.h"
#include "SharedDefines.h"
#include "SpawnData.h"
#include "WorldPacket.h"
#include <string>
#include <vector>
@@ -594,31 +595,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), phaseUseFlags(0), phaseId(0),
phaseGroup(0), terrainSwapMap(-1), 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;
uint8 phaseUseFlags;
uint32 phaseId;
uint32 phaseGroup;
int32 terrainSwapMap;
uint32 ScriptId;
bool dbData;
uint32 animprogress = 0;
GOState goState = GO_STATE_ACTIVE;
uint8 artKit = 0;
};
#endif // GameObjectData_h__
+8 -12
View File
@@ -2102,7 +2102,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert
break;
}
if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, 0, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId))
if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, 0, entry, pos, nullptr, vehId, true))
{
delete summon;
return nullptr;
@@ -2170,13 +2170,13 @@ void WorldObject::ClearZoneScript()
m_zoneScript = nullptr;
}
TempSummon* WorldObject::SummonCreature(uint32 entry, Position const& pos, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 duration /*= 0*/, uint32 vehId /*= 0*/, bool visibleBySummonerOnly /*= false*/)
TempSummon* WorldObject::SummonCreature(uint32 entry, Position const& pos, TempSummonType despawnType /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 despawnTime /*= 0*/, uint32 vehId /*= 0*/, bool visibleBySummonerOnly /*= false*/)
{
if (Map* map = FindMap())
{
if (TempSummon* summon = map->SummonCreature(entry, pos, nullptr, duration, ToUnit(), 0, vehId, visibleBySummonerOnly))
if (TempSummon* summon = map->SummonCreature(entry, pos, nullptr, despawnTime, ToUnit(), 0, vehId, visibleBySummonerOnly))
{
summon->SetTempSummonType(spwtype);
summon->SetTempSummonType(despawnType);
return summon;
}
}
@@ -2184,17 +2184,13 @@ TempSummon* WorldObject::SummonCreature(uint32 entry, Position const& pos, TempS
return nullptr;
}
TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang /*= 0*/, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 despwtime /*= 0*/, bool visibleBySummonerOnly /*= false*/)
TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float o /*= 0*/, TempSummonType despawnType /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 despawnTime /*= 0*/, bool visibleBySummonerOnly /*= false*/)
{
if (!x && !y && !z)
{
GetClosePoint(x, y, z, GetObjectSize());
ang = GetOrientation();
}
Position pos;
pos.Relocate(x, y, z, ang);
return SummonCreature(id, pos, spwtype, despwtime, 0, visibleBySummonerOnly);
if (!o)
o = GetOrientation();
return SummonCreature(id, { x, y, z, o }, despawnType, despawnTime, 0, visibleBySummonerOnly);
}
GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, QuaternionData const& rot, uint32 respawnTime, GOSummonType summonType)
+5 -3
View File
@@ -20,6 +20,7 @@
#define _OBJECT_H
#include "Common.h"
#include "Duration.h"
#include "GridReference.h"
#include "GridRefManager.h"
#include "ModelIgnoreFlags.h"
@@ -362,7 +363,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void PlayDirectSound(uint32 sound_id, Player* target = nullptr);
void PlayDirectMusic(uint32 music_id, Player* target = nullptr);
virtual void SaveRespawnTime() { }
virtual void SaveRespawnTime(uint32 /*forceDelay*/ = 0, bool /*saveToDB*/ = true) { }
void AddObjectToRemoveList();
float GetGridActivationRange() const;
@@ -392,8 +393,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void ClearZoneScript();
ZoneScript* GetZoneScript() const { return m_zoneScript; }
TempSummon* SummonCreature(uint32 id, Position const& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0, bool visibleBySummonerOnly = false);
TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, bool visibleBySummonerOnly = false);
TempSummon* SummonCreature(uint32 entry, Position const& pos, TempSummonType despawnType = TEMPSUMMON_MANUAL_DESPAWN, uint32 despawnTime = 0, uint32 vehId = 0, bool visibleBySummonerOnly = false);
TempSummon* SummonCreature(uint32 entry, Position const& pos, TempSummonType despawnType, Milliseconds const& despawnTime, uint32 vehId = 0, bool visibleBySummonerOnly = false) { return SummonCreature(entry, pos, despawnType, uint32(despawnTime.count()), vehId, visibleBySummonerOnly); }
TempSummon* SummonCreature(uint32 entry, float x, float y, float z, float o = 0, TempSummonType despawnType = TEMPSUMMON_MANUAL_DESPAWN, uint32 despawnTime = 0, bool visibleBySummonerOnly = false);
GameObject* SummonGameObject(uint32 entry, Position const& pos, QuaternionData const& rot, uint32 respawnTime /* s */, GOSummonType summonType = GO_SUMMON_TIMED_OR_CORPSE_DESPAWN);
GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, QuaternionData const& rot, uint32 respawnTime /* s */);
Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = nullptr);
@@ -114,6 +114,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 >;
@@ -318,6 +318,7 @@ class TC_GAME_API ObjectGuidGeneratorBase
protected:
static void HandleCounterOverflow(HighGuid high);
static void CheckGuidTrigger(ObjectGuid::LowType guid);
ObjectGuid::LowType _nextGuid;
};
@@ -331,6 +332,10 @@ class ObjectGuidGenerator : public ObjectGuidGeneratorBase
{
if (_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1)
HandleCounterOverflow(high);
if (high == HighGuid::Unit || high == HighGuid::GameObject)
CheckGuidTrigger(_nextGuid);
return _nextGuid++;
}
};
+9 -18
View File
@@ -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(const Position& center, float xradius, float yradius, float zradius) const;
bool HasInArc(float arcangle, Position const* pos, float border = 2.0f) const;
@@ -240,16 +234,13 @@ class WorldLocation : public Position
WorldLocation(WorldLocation const& loc)
: Position(loc), m_mapId(loc.GetMapId()) { }
void WorldRelocate(WorldLocation const& loc)
void WorldRelocate(WorldLocation const& loc) { m_mapId = loc.GetMapId(); Relocate(loc); }
void WorldRelocate(WorldLocation const* loc) { m_mapId = loc->GetMapId(); Relocate(loc); }
void WorldRelocate(uint32 mapId, Position const& pos) { m_mapId = mapId; Relocate(pos); }
void WorldRelocate(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
{
m_mapId = 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)
{
m_mapId = _mapId;
Relocate(_x, _y, _z, _o);
m_mapId = mapId;
Relocate(x, y, z, o);
}
WorldLocation GetWorldLocation() const
+22 -11
View File
@@ -7315,24 +7315,26 @@ void Player::UpdateArea(uint32 newArea)
void Player::UpdateZone(uint32 newZone, uint32 newArea)
{
if (m_zoneUpdateId != newZone)
if (!IsInWorld())
return;
uint32 const oldZone = m_zoneUpdateId;
m_zoneUpdateId = newZone;
m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
GetMap()->UpdatePlayerZoneStats(oldZone, newZone);
// call leave script hooks immedately (before updating flags)
if (oldZone != newZone)
{
sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
if (Guild* guild = GetGuild())
guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
}
// group update
if (GetGroup())
SetGroupUpdateFlag(GROUP_UPDATE_FULL);
m_zoneUpdateId = newZone;
m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
// zone changed, so area changed as well, update it
UpdateArea(newArea);
@@ -7349,8 +7351,6 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
WeatherMgr::SendFineWeatherUpdateToPlayer(this);
}
sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea);
// in PvP, any not controlled zone (except zone->team == 6, default case)
// in PvE, only opposition team capital
switch (zone->team)
@@ -7397,6 +7397,17 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
UpdateLocalChannels(newZone);
UpdateZoneDependentAuras(newZone);
// call enter script hooks after everyting else has processed
sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea);
if (oldZone != newZone)
{
sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
if (Guild* guild = GetGuild())
guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
}
}
//If players are too far away from the duel flag... they lose the duel
@@ -299,16 +299,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->m_movementInfo.transport.guid = GetGUID();
@@ -348,7 +346,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;
@@ -356,10 +354,8 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
ASSERT(data);
float x = data->posX;
float y = data->posY;
float z = data->posZ;
float o = data->orientation;
float x, y, z, o;
data->spawnPoint.GetPosition(x, y, z, o);
go->SetTransport(this);
go->m_movementInfo.transport.guid = GetGUID();
@@ -466,7 +462,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu
pos.GetPosition(x, y, z, o);
CalculatePassengerPosition(x, y, z, &o);
if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, 0, entry, x, y, z, o, nullptr, vehId))
if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, 0, 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));
}
}
}
+20 -20
View File
@@ -447,7 +447,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);
@@ -1193,7 +1193,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)
{
@@ -1256,13 +1256,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;
}
}
@@ -1278,19 +1278,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
{
@@ -1333,13 +1333,13 @@ 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 it = creatureBounds.first; it != creatureBounds.second; )
for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second;)
{
Creature* creature = it->second;
++it;
Creature* creature = itr2->second;
++itr2;
creature->AddObjectToRemoveList();
}
});
@@ -1359,17 +1359,17 @@ 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 it = gameObjectBounds.first; it != gameObjectBounds.second; )
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr);
for (auto itr2 = gameobjectBounds.first; itr2 != gameobjectBounds.second;)
{
GameObject* go = it->second;
++it;
GameObject* go = itr2->second;
++itr2;
go->AddObjectToRemoveList();
}
});
@@ -1397,7 +1397,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
continue;
// Update if spawned
sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr, activate](Map* map)
sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr, activate](Map* map)
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first);
for (auto it = creatureBounds.first; it != creatureBounds.second; ++it)
+286 -94
View File
@@ -34,6 +34,7 @@
#include "GroupMgr.h"
#include "GuildMgr.h"
#include "InstanceSaveMgr.h"
#include "InstanceScript.h"
#include "Language.h"
#include "LFGMgr.h"
#include "Log.h"
@@ -1216,7 +1217,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);
@@ -1552,8 +1553,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;
@@ -1581,7 +1582,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);
@@ -1589,8 +1590,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;
@@ -1610,7 +1611,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);
@@ -1618,7 +1619,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);
@@ -1626,8 +1627,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;
@@ -1647,7 +1648,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);
@@ -1663,8 +1664,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;
@@ -1717,8 +1718,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;
@@ -1832,8 +1833,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 22
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.phaseUseFlags, "
// 23 24 25 26
@@ -1875,14 +1876,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();
@@ -1900,18 +1898,19 @@ void ObjectMgr::LoadCreatures()
data.phaseId = fields[23].GetUInt32();
data.phaseGroup = fields[24].GetUInt32();
data.terrainSwapMap = fields[25].GetInt32();
data.ScriptId = GetScriptId(fields[26].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)
@@ -1970,12 +1969,6 @@ void ObjectMgr::LoadCreatures()
}
}
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 (data.phaseMask == 0)
{
TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
@@ -2015,7 +2008,7 @@ void ObjectMgr::LoadCreatures()
TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: %u Entry: %u) with `terrainSwapMap` %u does not exist, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
else if (terrainSwapEntry->rootPhaseMap != data.mapid)
else if (terrainSwapEntry->rootPhaseMap != data.spawnPoint.GetMapId())
{
TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: %u Entry: %u) with `terrainSwapMap` %u which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
@@ -2027,7 +2020,7 @@ void ObjectMgr::LoadCreatures()
uint32 zoneId = 0;
uint32 areaId = 0;
PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
@@ -2054,8 +2047,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);
}
}
@@ -2068,14 +2061,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)
@@ -2085,41 +2078,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);
data.id = entry;
data.mapid = mapId;
pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation);
GameObjectData& data = NewOrExistGameObjectData(spawnId);
data.spawnId = spawnId;
data.id = entry;
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;
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;
}
ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Position const& pos, uint32 spawntimedelay /*= 0*/)
@@ -2134,15 +2126,14 @@ 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;
@@ -2155,14 +2146,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;
@@ -2170,10 +2162,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
}
}
return guid;
return spawnId;
}
void ObjectMgr::LoadGameobjects()
void ObjectMgr::LoadGameObjects()
{
uint32 oldMSTime = getMSTime();
@@ -2240,21 +2232,20 @@ void ObjectMgr::LoadGameobjects()
GameObjectData& data = _gameObjectDataStore[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.spawnId = guid;
data.id = entry;
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;
}
@@ -2275,12 +2266,12 @@ void ObjectMgr::LoadGameobjects()
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();
@@ -2323,20 +2314,14 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: %u Entry: %u) with `terrainSwapMap` %u does not exist, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
else if (terrainSwapEntry->rootPhaseMap != data.mapid)
else if (terrainSwapEntry->rootPhaseMap != data.spawnPoint.GetMapId())
{
TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: %u Entry: %u) with `terrainSwapMap` %u which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
}
data.ScriptId = GetScriptId(fields[22].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[22].GetString());
if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
{
@@ -2362,7 +2347,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;
@@ -2379,7 +2364,7 @@ void ObjectMgr::LoadGameobjects()
uint32 zoneId = 0;
uint32 areaId = 0;
PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
@@ -2398,6 +2383,207 @@ 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);
} 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;
}
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);
}
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::LoadInstanceSpawnGroups()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4
QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
if (!result)
{
TC_LOG_ERROR("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
return;
}
uint32 n = 0;
do
{
Field* fields = result->Fetch();
uint32 const spawnGroupId = fields[3].GetUInt32();
auto it = _spawnGroupDataStore.find(spawnGroupId);
if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
{
TC_LOG_ERROR("server.loading", "Invalid spawn group %u specified for instance %u. Skipped.", spawnGroupId, fields[0].GetUInt16());
continue;
}
uint16 const instanceMapId = fields[0].GetUInt16();
auto& vector = _instanceSpawnGroupStore[instanceMapId];
vector.emplace_back();
InstanceSpawnGroupInfo& info = vector.back();
info.SpawnGroupId = spawnGroupId;
info.BossStateId = fields[1].GetUInt8();
uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
uint8 const states = fields[2].GetUInt8();
if (states & ~ALL_STATES)
{
info.BossStates = states & ALL_STATES;
TC_LOG_ERROR("server.loading", "Instance spawn group (%u,%u) had invalid boss state mask %u - truncated to %u.", instanceMapId, spawnGroupId, states, info.BossStates);
}
else
info.BossStates = states;
uint8 const flags = fields[4].GetUInt8();
if (flags & ~InstanceSpawnGroupInfo::FLAG_ALL)
{
info.Flags = flags & InstanceSpawnGroupInfo::FLAG_ALL;
TC_LOG_ERROR("server.loading", "Instance spawn group (%u,%u) had invalid flags %u - truncated to %u.", instanceMapId, spawnGroupId, flags, info.Flags);
}
else
info.Flags = flags;
++n;
} while (result->NextRow());
TC_LOG_INFO("server.loading", ">> Loaded %u instance spawn groups in %u ms", n, 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;
@@ -2405,8 +2591,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);
}
}
@@ -2419,8 +2605,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);
}
}
@@ -5037,7 +5223,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",
@@ -5087,7 +5273,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",
@@ -7811,17 +7997,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);
}
+61 -16
View File
@@ -24,8 +24,10 @@
#include "Corpse.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"
@@ -39,6 +41,7 @@
class Item;
class Unit;
class Vehicle;
class Map;
struct AccessRequirement;
struct DeclinedName;
struct DungeonEncounterEntry;
@@ -425,6 +428,21 @@ std::string GetScriptsTableNameByType(ScriptsType type);
ScriptMapMap* GetScriptsMapByType(ScriptsType type);
std::string GetScriptCommandName(ScriptCommands command);
struct TC_GAME_API InstanceSpawnGroupInfo
{
enum
{
FLAG_ACTIVATE_SPAWN = 0x01,
FLAG_BLOCK_SPAWN = 0x02,
FLAG_ALL = (FLAG_ACTIVATE_SPAWN | FLAG_BLOCK_SPAWN)
};
uint8 BossStateId;
uint8 BossStates;
uint32 SpawnGroupId;
uint8 Flags;
};
struct TC_GAME_API SpellClickInfo
{
uint32 spellId;
@@ -529,6 +547,9 @@ 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::unordered_map<uint16, std::vector<InstanceSpawnGroupInfo>> InstanceSpawnGroupContainer;
typedef std::map<TempSummonGroupKey, std::vector<TempSummonData>> TempSummonDataContainer;
typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer;
typedef std::unordered_map<uint32, GameObjectLocale> GameObjectLocaleContainer;
@@ -799,6 +820,7 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_CHARTER_NAME 24 // max allowed by client name length
bool TC_GAME_API normalizePlayerName(std::string& name);
#define SPAWNGROUP_MAP_UNSET 0xFFFFFFFF
struct LanguageDesc
{
@@ -1121,7 +1143,10 @@ class TC_GAME_API ObjectMgr
void LoadCreatureModelInfo();
void LoadEquipmentTemplates();
void LoadGameObjectLocales();
void LoadGameobjects();
void LoadGameObjects();
void LoadSpawnGroupTemplates();
void LoadSpawnGroups();
void LoadInstanceSpawnGroups();
void LoadItemTemplates();
void LoadItemTemplateAddon();
void LoadItemScriptNames();
@@ -1210,8 +1235,14 @@ class TC_GAME_API ObjectMgr
uint32 GenerateMailID();
uint32 GeneratePetNumber();
uint64 GenerateVoidStorageItemId();
uint32 GenerateCreatureSpawnId();
uint32 GenerateGameObjectSpawnId();
ObjectGuid::LowType GenerateCreatureSpawnId();
ObjectGuid::LowType GenerateGameObjectSpawnId();
SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return it != _spawnGroupDataStore.end() ? &it->second : nullptr; }
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); }
std::vector<InstanceSpawnGroupInfo> const* GetSpawnGroupsForInstance(uint32 instanceId) const { auto it = _instanceSpawnGroupStore.find(instanceId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; }
typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
typedef std::pair<ExclusiveQuestGroups::const_iterator, ExclusiveQuestGroups::const_iterator> ExclusiveQuestGroupsBounds;
@@ -1267,6 +1298,18 @@ 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);
CreatureDataContainer const& GetAllCreatureData() const { return _creatureDataStore; }
CreatureData const* GetCreatureData(ObjectGuid::LowType guid) const
{
CreatureDataContainer::const_iterator itr = _creatureDataStore.find(guid);
@@ -1287,6 +1330,15 @@ class TC_GAME_API ObjectMgr
if (itr == _creatureLocaleStore.end()) return nullptr;
return &itr->second;
}
GameObjectDataContainer const& GetAllGameObjectData() const { return _gameObjectDataStore; }
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);
@@ -1329,16 +1381,6 @@ class TC_GAME_API ObjectMgr
if (itr == _pointOfInterestLocaleStore.end()) return nullptr;
return &itr->second;
}
GameObjectData const* GetGOData(ObjectGuid::LowType guid) const
{
GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid);
if (itr == _gameObjectDataStore.end()) return nullptr;
return &itr->second;
}
GameObjectData& NewGOData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
void DeleteGOData(ObjectGuid::LowType guid);
QuestGreetingLocale const* GetQuestGreetingLocale(uint32 id) const
{
QuestGreetingLocaleContainer::const_iterator itr = _questGreetingLocaleStore.find(id);
@@ -1363,7 +1405,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
@@ -1498,8 +1540,8 @@ class TC_GAME_API ObjectMgr
std::atomic<uint32> _hiPetNumber;
uint64 _voidItemId;
uint32 _creatureSpawnId;
uint32 _gameObjectSpawnId;
ObjectGuid::LowType _creatureSpawnId;
ObjectGuid::LowType _gameObjectSpawnId;
// first free low guid for selected guid type
template<HighGuid high>
@@ -1626,6 +1668,9 @@ class TC_GAME_API ObjectMgr
GameObjectLocaleContainer _gameObjectLocaleStore;
GameObjectTemplateContainer _gameObjectTemplateStore;
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore;
SpawnGroupDataContainer _spawnGroupDataStore;
SpawnGroupLinkContainer _spawnGroupMapStore;
InstanceSpawnGroupContainer _instanceSpawnGroupStore;
/// Stores temp summon data grouped by summoner's entry, summoner's type and group id
TempSummonDataContainer _tempSummonDataStore;
@@ -565,6 +565,11 @@ namespace Trinity
: ContainerInserter<Player*>(container),
_searcher(searcher), i_check(check) { }
template<typename Container>
PlayerListSearcher(Container& container, Check & check)
: ContainerInserter<Player*>(container),
i_check(check) { }
void Visit(PlayerMapType &m);
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { }
@@ -1288,6 +1293,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:
+42 -8
View File
@@ -28,6 +28,7 @@
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "World.h"
#include "ScriptMgr.h"
void ObjectGridEvacuator::Visit(CreatureMapType &m)
{
@@ -120,15 +121,48 @@ 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))
{
delete obj;
continue;
}
AddObjectHelper(cell, m, count, map, obj);
// 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)))
{
ObjectGuid::LowType guid = *i_guid;
//TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
if (obj->GetTypeId() == TYPEID_UNIT)
{
CreatureData const* cdata = sObjectMgr->GetCreatureData(guid);
ASSERT(cdata, "Tried to load creature with spawnId %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_SYSTEM))
if (!map->IsSpawnGroupActive(group->groupId))
{
delete obj;
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_SYSTEM))
if (!map->IsSpawnGroupActive(godata->spawnGroupData->groupId))
{
delete obj;
continue;
}
}
if (!obj->LoadFromDB(guid, map, false, false))
{
delete obj;
continue;
}
AddObjectHelper(cell, m, count, map, obj);
}
else
delete obj;
}
}
+72 -24
View File
@@ -47,7 +47,7 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0), _instanceSpawnGroups(sObjectMgr->GetSpawnGroupsForInstance(map->GetId()))
{
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
@@ -187,27 +187,6 @@ void InstanceScript::LoadObjectData(ObjectData const* data, ObjectInfoMap& objec
}
}
void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
{
switch (state)
{
case NOT_STARTED:
if (!minion->IsAlive())
minion->Respawn();
else if (minion->IsInCombat())
minion->AI()->EnterEvadeMode();
break;
case IN_PROGRESS:
if (!minion->IsAlive())
minion->Respawn();
else if (!minion->GetVictim())
minion->AI()->DoZoneInCombat();
break;
default:
break;
}
}
void InstanceScript::UpdateDoorState(GameObject* door)
{
DoorInfoMapBounds range = doors.equal_range(door->GetEntry());
@@ -237,6 +216,60 @@ void InstanceScript::UpdateDoorState(GameObject* door)
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
{
switch (state)
{
case NOT_STARTED:
if (!minion->IsAlive())
minion->Respawn();
else if (minion->IsInCombat())
minion->AI()->EnterEvadeMode();
break;
case IN_PROGRESS:
if (!minion->IsAlive())
minion->Respawn();
else if (!minion->GetVictim())
minion->AI()->DoZoneInCombat();
break;
default:
break;
}
}
void InstanceScript::UpdateSpawnGroups()
{
if (!_instanceSpawnGroups)
return;
enum states { BLOCK, SPAWN, FORCEBLOCK };
std::unordered_map<uint32, states> newStates;
for (auto it = _instanceSpawnGroups->begin(), end = _instanceSpawnGroups->end(); it != end; ++it)
{
InstanceSpawnGroupInfo const& info = *it;
states& curValue = newStates[info.SpawnGroupId]; // makes sure there's a BLOCK value in the map
if (curValue == FORCEBLOCK) // nothing will change this
continue;
if (!((1 << GetBossState(info.BossStateId)) & info.BossStates))
continue;
if (info.Flags & InstanceSpawnGroupInfo::FLAG_BLOCK_SPAWN)
curValue = FORCEBLOCK;
else if (info.Flags & InstanceSpawnGroupInfo::FLAG_ACTIVATE_SPAWN)
curValue = SPAWN;
}
for (auto const& pair : newStates)
{
uint32 const groupId = pair.first;
bool const doSpawn = (pair.second == SPAWN);
if (instance->IsSpawnGroupActive(groupId) == doSpawn)
continue; // nothing to do here
// if we should spawn group, then spawn it...
if (doSpawn)
instance->SpawnGroupSpawn(groupId);
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
instance->SetSpawnGroupInactive(groupId);
}
}
BossInfo* InstanceScript::GetBossInfo(uint32 id)
{
ASSERT(id < bosses.size());
@@ -311,7 +344,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (bossInfo->state == TO_BE_DECIDED) // loading
{
bossInfo->state = state;
//TC_LOG_ERROR("misc", "Inialize boss %u state as %u.", id, (uint32)state);
TC_LOG_DEBUG("scripts", "InstanceScript: Initialize boss %u state as %s (map %u, %u).", id, GetBossStateName(state), instance->GetId(), instance->GetInstanceId());
return false;
}
@@ -320,6 +353,12 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (bossInfo->state == state)
return false;
if (bossInfo->state == DONE)
{
TC_LOG_ERROR("map", "InstanceScript: Tried to set instance state from %s back to %s for map %u, instance id %u. Blocked!", GetBossStateName(bossInfo->state), GetBossStateName(state), instance->GetId(), instance->GetInstanceId());
return false;
}
if (state == DONE)
for (GuidSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
if (Creature* minion = instance->GetCreature(*i))
@@ -363,6 +402,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (Creature* minion = instance->GetCreature(*i))
UpdateMinionState(minion, state);
UpdateSpawnGroups();
return true;
}
return false;
@@ -373,6 +413,13 @@ bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/
return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES);
}
void InstanceScript::Create()
{
for (size_t i = 0; i < bosses.size(); ++i)
SetBossState(i, NOT_STARTED);
UpdateSpawnGroups();
}
void InstanceScript::Load(char const* data)
{
if (!data)
@@ -423,6 +470,7 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data)
if (buff < TO_BE_DECIDED)
SetBossState(bossId, EncounterState(buff));
}
UpdateSpawnGroups();
}
std::string InstanceScript::GetSaveData()
@@ -720,7 +768,7 @@ void InstanceScript::UpdateEncounterStateForSpellCast(uint32 spellId, Unit* sour
UpdateEncounterState(ENCOUNTER_CREDIT_CAST_SPELL, spellId, source);
}
std::string InstanceScript::GetBossStateName(uint8 state)
/*static*/ std::string InstanceScript::GetBossStateName(uint8 state)
{
// See enum EncounterState in InstanceScript.h
switch (state)
+8 -1
View File
@@ -34,6 +34,7 @@
class AreaBoundary;
class Creature;
class GameObject;
struct InstanceSpawnGroupInfo;
class Map;
class ModuleReference;
class Player;
@@ -159,7 +160,10 @@ class TC_GAME_API InstanceScript : public ZoneScript
// KEEPING THIS METHOD ONLY FOR BACKWARD COMPATIBILITY !!!
virtual void Initialize() { }
// On load
// On instance load, exactly ONE of these methods will ALWAYS be called:
// if we're starting without any saved instance data
virtual void Create();
// if we're loading existing instance save data
virtual void Load(char const* data);
// When save is needed, this function generates the data
@@ -286,6 +290,8 @@ class TC_GAME_API InstanceScript : public ZoneScript
virtual void UpdateDoorState(GameObject* door);
void UpdateMinionState(Creature* minion, EncounterState state);
void UpdateSpawnGroups();
// Exposes private data that should never be modified unless exceptional cases.
// Pay very much attention at how the returned BossInfo data is modified to avoid issues.
BossInfo* GetBossInfo(uint32 id);
@@ -316,6 +322,7 @@ class TC_GAME_API InstanceScript : public ZoneScript
uint8 _combatResurrectionCharges; // the counter for available battle resurrections
bool _combatResurrectionTimerStarted;
std::unordered_set<uint32> _activatedAreaTriggers;
std::vector<InstanceSpawnGroupInfo> const* const _instanceSpawnGroups;
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
// Strong reference to the associated script module
+590 -67
View File
@@ -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 "PhasingHandler.h"
#include "ScriptMgr.h"
#include "Transport.h"
@@ -62,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();
@@ -272,7 +278,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))
{
if (_parent)
{
@@ -295,6 +301,8 @@ i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
}
}
_zonePlayerCountMap.clear();
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
@@ -553,6 +561,29 @@ bool Map::EnsureGridLoaded(const Cell &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));
@@ -722,6 +753,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);
@@ -737,6 +783,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();
@@ -918,6 +974,8 @@ void Map::ProcessRelocationNotifies(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
@@ -2630,10 +2688,7 @@ void Map::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x,
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;
@@ -2735,11 +2790,6 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa
return result;
}
float Map::GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
return std::max<float>(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
}
bool Map::IsInWater(PhaseShift const& phaseShift, float x, float y, float pZ, LiquidData* data) const
{
LiquidData liquid_status;
@@ -2896,6 +2946,480 @@ void Map::SendObjectUpdates()
}
}
// CheckRespawn MUST do one of the following:
// -) return true
// -) set info->respawnTime to zero, which indicates the respawn time should be deleted (and will never be processed again without outside intervention)
// -) set info->respawnTime to a new respawn time, which must be strictly GREATER than the current time (GameTime::GetGameTime())
bool Map::CheckRespawn(RespawnInfo* info)
{
SpawnData const* data = sObjectMgr->GetSpawnData(info->type, info->spawnId);
ASSERT(data, "Invalid respawn info with type %u, spawnID %u in respawn queue.", info->type, info->spawnId);
// First, check if this creature's spawn group is inactive
if (!IsSpawnGroupActive(data->spawnGroupData->groupId))
{
info->respawnTime = 0;
return false;
}
uint32 poolId = info->spawnId ? sPoolMgr->IsPartOfAPool(info->type, info->spawnId) : 0;
// Next, 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 const isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && data->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC);
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 (linkedTime == std::numeric_limits<time_t>::max())
respawnTime = linkedTime;
else 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;
}
// everything ok, let's spawn
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(std::vector<RespawnInfo*>& 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(std::vector<RespawnInfo*>& 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(std::vector<RespawnInfo*>& 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(std::vector<RespawnInfo*>& 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);
if (IsBattlegroundOrArena())
return;
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 || !(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);
}
SpawnGroupTemplateData const* Map::GetSpawnGroupData(uint32 groupId) const
{
SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId);
if (data && (data->flags & SPAWNGROUP_FLAG_SYSTEM || data->mapId == GetId()))
return data;
return nullptr;
}
bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::vector<WorldObject*>* spawnedObjects)
{
SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
{
TC_LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId());
return false;
}
for (auto& pair : sObjectMgr->GetSpawnDataForGroup(groupId))
{
SpawnData const* data = pair.second;
ASSERT(groupData->mapId == data->spawnPoint.GetMapId());
// Check if there's already an instance spawned
if (!force)
if (WorldObject* obj = GetWorldObjectBySpawnId(data->type, data->spawnId))
if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive())
continue;
time_t respawnTime = 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
RemoveRespawnTime(data->type, data->spawnId, false);
}
// don't spawn if the grid isn't loaded (will be handled in grid loader)
if (!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, this, 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, this, 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;
}
}
SetSpawnGroupActive(groupId, true); // start processing respawns for the group
return true;
}
bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes, size_t* count)
{
SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
{
TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId());
return false;
}
std::vector<WorldObject*> toUnload; // unload after iterating, otherwise iterator invalidation
for (auto const& pair : sObjectMgr->GetSpawnDataForGroup(groupId))
{
SpawnData const* data = pair.second;
ASSERT(groupData->mapId == data->spawnPoint.GetMapId());
if (deleteRespawnTimes)
RemoveRespawnTime(data->type, data->spawnId);
switch (data->type)
{
case SPAWN_TYPE_CREATURE:
{
auto bounds = 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 = 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;
}
}
if (count)
*count = toUnload.size();
// now do the actual despawning
for (WorldObject* obj : toUnload)
obj->AddObjectToRemoveList();
SetSpawnGroupActive(groupId, false); // stop processing respawns for the group, too
return true;
}
void Map::SetSpawnGroupActive(uint32 groupId, bool state)
{
SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
if (!data || data->flags & SPAWNGROUP_FLAG_SYSTEM)
{
TC_LOG_ERROR("maps", "Tried to set non-existing (or system) spawn group %u to %s on map %u. Blocked.", groupId, state ? "active" : "inactive", GetId());
return;
}
if (state != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)) // toggled
_toggledSpawnGroupIds.insert(groupId);
else
_toggledSpawnGroupIds.erase(groupId);
}
bool Map::IsSpawnGroupActive(uint32 groupId) const
{
SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
if (!data)
{
TC_LOG_ERROR("maps", "Tried to query state of non-existing spawn group %u on map %u.", groupId, GetId());
return false;
}
if (data->flags & SPAWNGROUP_FLAG_SYSTEM)
return true;
// either manual spawn group and toggled, or not manual spawn group and not toggled...
return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
}
void Map::DelayedUpdate(uint32 t_diff)
{
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
@@ -3402,6 +3926,8 @@ void InstanceMap::CreateInstanceData(bool load)
}
}
}
else
i_data->Create();
}
/*
@@ -3741,6 +4267,34 @@ DynamicObject* Map::GetDynamicObject(ObjectGuid const& guid)
return _objectsStore.Find<DynamicObject>(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);
@@ -3766,64 +4320,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);
if (writeDB)
SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace
}
void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans)
{
// 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::RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid)
{
_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);
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()
@@ -3839,7 +4366,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(PhasingHandler::GetEmptyPhaseShift(), cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
} while (result->NextRow());
}
@@ -3854,19 +4383,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(PhasingHandler::GetEmptyPhaseShift(), godata->spawnPoint), Trinity::ComputeGridCoord(godata->spawnPoint.GetPositionX(), godata->spawnPoint.GetPositionY()).GetId(), false);
} while (result->NextRow());
}
}
void Map::DeleteRespawnTimes()
{
_creatureRespawnTimes.clear();
_goRespawnTimes.clear();
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
}
void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE);
+151 -27
View File
@@ -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>
@@ -265,10 +267,40 @@ struct ZoneDynamicInfo
#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
#define MIN_UNLOAD_DELAY 1 // immediate unload
#define MAP_INVALID_ZONE 0xFFFFFFFF
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;
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>
{
@@ -318,17 +350,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
@@ -359,8 +393,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
bool GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const;
uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, bool *isOutdoors = nullptr) const;
uint32 GetAreaId(PhaseShift const& phaseShift, Position const& pos) const { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetZoneId(PhaseShift const& phaseShift, float x, float y, float z) const;
uint32 GetZoneId(PhaseShift const& phaseShift, Position const& pos) const { return GetZoneId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z) const;
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, Position const& pos) const { GetZoneAndAreaId(phaseShift, zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
bool IsOutdoors(PhaseShift const& phaseShift, float x, float y, float z) const;
@@ -465,6 +502,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
Creature* GetCreature(ObjectGuid const& guid);
DynamicObject* GetDynamicObject(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)); }
Pet* GetPet(ObjectGuid const& guid);
Transport* GetTransport(ObjectGuid const& guid);
@@ -472,9 +512,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 +546,10 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; }
float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false) const;
float GetHeight(PhaseShift const& phaseShifts, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
float GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return std::max<float>(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist)); }
float GetHeight(PhaseShift const& phaseShift, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
@@ -523,28 +568,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();
@@ -708,6 +749,80 @@ 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(std::vector<RespawnInfo*>& respawnData, bool force = false, SQLTransaction dbTrans = nullptr);
void AddRespawnInfo(RespawnInfo& info, bool replace = false);
void DeleteRespawnInfo();
void DeleteRespawnInfo(RespawnInfo* info);
void DeleteRespawnInfo(std::vector<RespawnInfo*>& toDelete)
{
for (RespawnInfo* info : toDelete)
DeleteRespawnInfo(info);
toDelete.clear();
}
void DeleteRespawnInfo(SpawnObjectTypeMask types, uint32 zoneId = 0)
{
std::vector<RespawnInfo*> 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(std::vector<RespawnInfo*>& respawnData, SpawnObjectTypeMask types, uint32 zoneId = 0) const;
RespawnInfo* GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
void ForceRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId)
{
if (RespawnInfo* info = GetRespawnInfo(type, spawnId))
Respawn(info, true);
}
void RemoveRespawnTime(RespawnInfo* info, bool doRespawn = false, SQLTransaction dbTrans = nullptr);
void RemoveRespawnTime(std::vector<RespawnInfo*>& respawnData, bool doRespawn = false, SQLTransaction dbTrans = nullptr);
void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, uint32 zoneId = 0, bool doRespawn = false, SQLTransaction dbTrans = nullptr)
{
std::vector<RespawnInfo*> 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);
}
SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const;
bool IsSpawnGroupActive(uint32 groupId) const;
// Enable the spawn group, which causes all creatures in it to respawn (unless they have a respawn timer)
// The force flag can be used to force spawning additional copies even if old copies are still around from a previous spawn
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr);
// Despawn all creatures in the spawn group if spawned, optionally delete their respawn timer, and disable the group
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false, size_t* count = nullptr);
// Disable the spawn group, which prevents any creatures in the group from respawning until re-enabled
// This will not affect any already-present creatures in the group
void SetSpawnGroupInactive(uint32 groupId) { SetSpawnGroupActive(groupId, false); }
private:
// Type specific code for add/remove to/from grid
template<class T>
void AddToGrid(T* object, Cell const& cell);
@@ -736,8 +851,17 @@ 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; }
void SetSpawnGroupActive(uint32 groupId, bool state);
std::unordered_set<uint32> _toggledSpawnGroupIds;
uint32 _respawnCheckTimer;
std::unordered_map<uint32, uint32> _zonePlayerCountMap;
ZoneDynamicInfoMap _zoneDynamicInfo;
uint32 _defaultLight;
+7 -1
View File
@@ -45,16 +45,22 @@ class TC_GAME_API MapManager
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetAreaId(phaseShift, x, y, z);
}
uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) const { return GetAreaId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetAreaId(PhaseShift const& phaseShift, WorldLocation const& loc) const { return GetAreaId(phaseShift, loc.GetMapId(), loc); }
uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetZoneId(phaseShift, x, y, z);
}
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) const { return GetZoneId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetZoneId(PhaseShift const& phaseShift, WorldLocation const& loc) const { return GetZoneId(phaseShift, loc.GetMapId(), loc); }
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
m->GetZoneAndAreaId(phaseShift, zoneid, areaid, x, y, z);
}
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) const { GetZoneAndAreaId(phaseShift, zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, WorldLocation const& loc) const { GetZoneAndAreaId(phaseShift, zoneid, areaid, loc.GetMapId(), loc); }
void Initialize(void);
void InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData);
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2008-2018 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;
};
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;
uint8 phaseUseFlags = 0;
uint32 phaseId = 0;
uint32 phaseGroup = 0;
int32 terrainSwapMap = 0;
uint32 scriptId = 0;
bool dbData = true;
protected:
SpawnData(SpawnObjectType t) : type(t) {}
};
#endif
+27 -1
View File
@@ -1042,7 +1042,33 @@ enum TrinityStrings
LANG_COMMAND_MUTEHISTORY_EMPTY = 5060,
LANG_COMMAND_MUTEHISTORY_OUTPUT = 5061,
LANG_PHASE_NOTFOUND = 5083,
// 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,
+5 -5
View File
@@ -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;
@@ -218,7 +218,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
go->Delete();
}
sObjectMgr->DeleteGOData(spawnId);
sObjectMgr->DeleteGameObjectData(spawnId);
m_ObjectTypes[m_Objects[type]] = 0;
m_Objects[type] = 0;
return true;
@@ -226,7 +226,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
bool OPvPCapturePoint::DelCapturePoint()
{
sObjectMgr->DeleteGOData(m_capturePointSpawnId);
sObjectMgr->DeleteGameObjectData(m_capturePointSpawnId);
m_capturePointSpawnId = 0;
if (m_capturePoint)
+33 -11
View File
@@ -194,7 +194,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);
@@ -202,6 +202,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();
}
}
@@ -212,11 +215,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);
@@ -224,6 +227,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();
}
}
@@ -375,13 +382,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;
@@ -394,18 +401,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;
@@ -672,7 +679,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);
@@ -1080,6 +1087,21 @@ void PoolMgr::DespawnPool(uint32 pool_id)
}
}
// Selects proper template overload to call based on passed type
uint32 PoolMgr::IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const
{
switch (type)
{
case SPAWN_TYPE_CREATURE:
return IsPartOfAPool<Creature>(spawnId);
case SPAWN_TYPE_GAMEOBJECT:
return IsPartOfAPool<GameObject>(spawnId);
default:
ASSERT(false, "Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId %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
{
+2
View File
@@ -22,6 +22,7 @@
#include "Define.h"
#include "Creature.h"
#include "GameObject.h"
#include "SpawnData.h"
#include "QuestDef.h"
struct PoolTemplateData
@@ -117,6 +118,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); }
-14
View File
@@ -1566,20 +1566,6 @@ 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)
{
ASSERT(actTemplate);
CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry);
if (!baseTemplate)
baseTemplate = actTemplate;
uint32 scriptId = baseTemplate->ScriptID;
if (cData && cData->ScriptId)
scriptId = cData->ScriptId;
GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, true);
return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map);
}
CreatureAI* ScriptMgr::GetCreatureAI(Creature* creature)
{
ASSERT(creature);
-4
View File
@@ -417,9 +417,6 @@ class TC_GAME_API CreatureScript : public UnitScript
public:
// Called when the creature tries to spawn. Return false to block spawn and re-evaluate on next tick.
virtual bool CanSpawn(ObjectGuid::LowType /*spawnId*/, uint32 /*entry*/, CreatureTemplate const* /*baseTemplate*/, CreatureTemplate const* /*actTemplate*/, CreatureData const* /*cData*/, Map const* /*map*/) const { return true; }
// Called when a CreatureAI object is needed for the creature.
virtual CreatureAI* GetAI(Creature* /*creature*/) const = 0;
};
@@ -920,7 +917,6 @@ class TC_GAME_API ScriptMgr
public: /* CreatureScript */
bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map);
CreatureAI* GetCreatureAI(Creature* creature);
public: /* GameObjectScript */
+123 -2
View File
@@ -137,6 +137,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(nullptr);
}
/// World destructor
@@ -211,6 +216,59 @@ char const* World::GetMotd() const
return m_motd.c_str();
}
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
{
@@ -1253,6 +1311,50 @@ void World::LoadConfigSettings(bool reload)
TC_LOG_ERROR("server.loading", "NoGrayAggro.Below (%i) cannot be greater than NoGrayAggro.Above (%i). Set to %i.", m_int_configs[CONFIG_NO_GRAY_AGGRO_BELOW], m_int_configs[CONFIG_NO_GRAY_AGGRO_ABOVE], m_int_configs[CONFIG_NO_GRAY_AGGRO_ABOVE]);
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", false);
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", "./");
@@ -1692,6 +1794,12 @@ 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 instance spawn groups...");
sObjectMgr->LoadInstanceSpawnGroups();
TC_LOG_INFO("server.loading", "Loading Creature Data...");
sObjectMgr->LoadCreatures();
@@ -1708,10 +1816,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();
@@ -2365,6 +2476,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();
+19 -4
View File
@@ -210,7 +210,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
@@ -406,13 +405,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,
@@ -799,6 +796,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();
@@ -896,7 +897,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;
+3 -1
View File
@@ -41,6 +41,8 @@ EndScriptData */
#include "WorldSession.h"
#include <fstream>
#include <limits>
#include <map>
#include <set>
class debug_commandscript : public CommandScript
{
@@ -973,7 +975,7 @@ public:
Creature* v = new Creature();
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;
+10 -25
View File
@@ -139,8 +139,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);
@@ -158,11 +156,8 @@ 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;
}
@@ -250,7 +245,7 @@ public:
player->SaveRecallPosition();
Map const* map = sMapMgr->CreateBaseMap(mapId);
float z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
float z = std::max(map->GetHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
player->TeleportTo(mapId, x, y, z, player->GetOrientation());
return true;
@@ -273,28 +268,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;
}
@@ -309,7 +294,7 @@ public:
else
player->SaveRecallPosition();
player->TeleportTo(mapId, x, y, z, o);
player->TeleportTo(goData->spawnPoint);
return true;
}
@@ -470,7 +455,7 @@ public:
else
player->SaveRecallPosition();
float z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
float z = std::max(map->GetHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
player->TeleportTo(zoneEntry->mapid, x, y, z, player->GetOrientation());
return true;
@@ -518,7 +503,7 @@ public:
return false;
}
Map const* map = sMapMgr->CreateBaseMap(mapId);
z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
z = std::max(map->GetHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
}
// stop flight if need
+51 -13
View File
@@ -39,6 +39,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:
@@ -58,15 +62,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 =
{
@@ -172,14 +178,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;
@@ -348,6 +354,9 @@ public:
if (!guidLow)
return false;
Player const* const player = handler->GetSession()->GetPlayer();
// force respawn to make sure we find something
player->GetMap()->ForceRespawn(SPAWN_TYPE_GAMEOBJECT, guidLow);
GameObject* object = handler->GetObjectFromPlayerMapByDbGuid(guidLow);
if (!object)
{
@@ -359,7 +368,7 @@ public:
ObjectGuid ownerGuid = object->GetOwnerGUID();
if (ownerGuid)
{
Unit* owner = ObjectAccessor::GetUnit(*handler->GetSession()->GetPlayer(), ownerGuid);
Unit* owner = ObjectAccessor::GetUnit(*player, ownerGuid);
if (!owner || !ownerGuid.IsPlayer())
{
handler->PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, ownerGuid.GetCounter(), guidLow);
@@ -587,7 +596,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;
@@ -599,9 +608,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;
@@ -610,10 +626,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, object->GetMap()->IsSpawnGroupActive(groupData->groupId));
}
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);
+169 -9
View File
@@ -25,9 +25,12 @@ EndScriptData */
#include "ScriptMgr.h"
#include "CharacterCache.h"
#include "Chat.h"
#include "Creature.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"
@@ -45,11 +48,13 @@ public:
{
static std::vector<ChatCommand> listCommandTable =
{
{ "creature", rbac::RBAC_PERM_COMMAND_LIST_CREATURE, true, &HandleListCreatureCommand, "" },
{ "item", rbac::RBAC_PERM_COMMAND_LIST_ITEM, true, &HandleListItemCommand, "" },
{ "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, "" },
{ "creature", rbac::RBAC_PERM_COMMAND_LIST_CREATURE, true, &HandleListCreatureCommand, "" },
{ "item", rbac::RBAC_PERM_COMMAND_LIST_ITEM, true, &HandleListItemCommand, "" },
{ "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, "" },
{ "spawnpoints", rbac::RBAC_PERM_COMMAND_LIST_SPAWNPOINTS, false, &HandleListSpawnPointsCommand, "" },
{ "respawns", rbac::RBAC_PERM_COMMAND_LIST_MAIL, false, &HandleListRespawnsCommand, "" }
};
static std::vector<ChatCommand> commandTable =
{
@@ -117,11 +122,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 +441,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,6 +644,103 @@ public:
handler->PSendSysMessage(LANG_LIST_MAIL_NOT_FOUND);
return true;
}
static bool HandleListSpawnPointsCommand(ChatHandler* handler, char const* /*args*/)
{
Player const* player = handler->GetSession()->GetPlayer();
Map const* map = player->GetMap();
uint32 const mapId = map->GetId();
bool const showAll = map->IsBattlegroundOrArena() || map->IsDungeon();
handler->PSendSysMessage("Listing all spawn points in map %u (%s)%s:", mapId, map->GetMapName(), showAll ? "" : " within 5000yd");
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
SpawnData const& data = pair.second;
if (data.spawnPoint.GetMapId() != mapId)
continue;
CreatureTemplate const* cTemp = sObjectMgr->GetCreatureTemplate(data.id);
if (!cTemp)
continue;
if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0))
handler->PSendSysMessage("Type: %u | SpawnId: %u | Entry: %u (%s) | X: %.3f | Y: %.3f | Z: %.3f", uint32(data.type), data.spawnId, data.id, cTemp->Name.c_str(), data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), data.spawnPoint.GetPositionZ());
}
for (auto const& pair : sObjectMgr->GetAllGameObjectData())
{
SpawnData const& data = pair.second;
if (data.spawnPoint.GetMapId() != mapId)
continue;
GameObjectTemplate const* goTemp = sObjectMgr->GetGameObjectTemplate(data.id);
if (!goTemp)
continue;
if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0))
handler->PSendSysMessage("Type: %u | SpawnId: %u | Entry: %u (%s) | X: %.3f | Y: %.3f | Z: %.3f", uint32(data.type), data.spawnId, data.id, goTemp->name.c_str(), data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), data.spawnPoint.GetPositionZ());
}
return true;
}
static char const* GetZoneName(uint32 zoneId, LocaleConstant locale)
{
AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId);
return zoneEntry ? (const char*)zoneEntry->area_name[locale] : "<unknown zone>";
}
static bool HandleListRespawnsCommand(ChatHandler* handler, char const* args)
{
Player const* player = handler->GetSession()->GetPlayer();
Map const* map = player->GetMap();
uint32 range = 0;
if (*args)
range = atoi((char*)args);
std::vector<RespawnInfo*> 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(nullptr) ? secsToTimeString(uint64(ri->respawnTime - time(nullptr)), true) : stringOverdue;
handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? 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(nullptr) ? secsToTimeString(uint64(ri->respawnTime - time(nullptr)), true) : stringOverdue;
handler->PSendSysMessage("%u | %u | [% 02u, % 02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? respawnTime.c_str() : "inactive");
}
return true;
}
};
void AddSC_list_commandscript()
+12
View File
@@ -1901,10 +1901,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.
std::vector<RespawnInfo*> 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()->ForceRespawn(info->type, info->spawnId);
}
return true;
}
+162 -83
View File
@@ -181,6 +181,86 @@ EnumName<CreatureFlagsExtra> const flagsExtra[] =
uint32 const FLAGS_EXTRA_COUNT = std::extent<decltype(flagsExtra)>::value;
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 (!player->GetMap()->SpawnGroupSpawn(groupId, 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 (!player->GetMap()->SpawnGroupDespawn(groupId, deleteRespawnTimes))
{
handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId);
handler->SetSentErrorMessage(true);
return false;
}
return true;
}
class npc_commandscript : public CommandScript
{
public:
@@ -225,21 +305,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 =
{
@@ -263,22 +345,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->GetPhaseMask();
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);
@@ -291,7 +367,7 @@ public:
}
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, *chr))
{
delete creature;
return false;
@@ -307,7 +383,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;
@@ -484,7 +560,7 @@ public:
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
{
Creature* unit = nullptr;
Creature* creature = nullptr;
if (*args)
{
@@ -496,22 +572,30 @@ public:
ObjectGuid::LowType lowguid = atoul(cId);
if (!lowguid)
return false;
unit = handler->GetCreatureFromPlayerMapByDbGuid(lowguid);
// force respawn to make sure we find something
handler->GetSession()->GetPlayer()->GetMap()->ForceRespawn(SPAWN_TYPE_CREATURE, lowguid);
// then try to find it
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);
@@ -701,15 +785,22 @@ public:
uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask;
uint32 displayid = target->GetDisplayId();
uint32 nativeid = target->GetNativeDisplayId();
uint32 Entry = target->GetEntry();
uint32 entry = target->GetEntry();
int64 curRespawnDelay = target->GetRespawnCompatibilityMode() ? target->GetRespawnTimeEx() - time(nullptr) : target->GetMap()->GetCreatureRespawnTime(target->GetSpawnId()) - time(nullptr);
int64 curRespawnDelay = target->GetRespawnTimeEx()-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);
handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetName().c_str(), target->GetSpawnId(), target->GetGUID().GetCounter(), entry, faction, npcflags, displayid, nativeid);
if (target->GetCreatureData() && target->GetCreatureData()->spawnGroupData->groupId)
{
SpawnGroupTemplateData const* const groupData = target->GetCreatureData()->spawnGroupData;
handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, target->GetMap()->IsSpawnGroupActive(groupData->groupId));
}
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());
@@ -785,7 +876,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;
}
@@ -803,8 +894,13 @@ public:
ObjectGuid::LowType lowguid = 0;
Creature* creature = handler->getSelectedCreature();
Player const* player = handler->GetSession()->GetPlayer();
if (!player)
return false;
if (!creature)
if (creature)
lowguid = creature->GetSpawnId();
else
{
// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
char* cId = handler->extractKeyFromLink((char*)args, "Hcreature");
@@ -812,63 +908,46 @@ public:
return false;
lowguid = atoul(cId);
// Attempting creature load from DB data
CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
if (!data)
{
handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
handler->SetSentErrorMessage(true);
return false;
}
uint32 map_id = data->mapid;
if (handler->GetSession()->GetPlayer()->GetMapId() != map_id)
{
handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
handler->SetSentErrorMessage(true);
return false;
}
}
else
// Attempting creature load from DB data
CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
if (!data)
{
lowguid = creature->GetSpawnId();
handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
handler->SetSentErrorMessage(true);
return false;
}
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 (player->GetMapId() != data->spawnPoint.GetMapId())
{
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->SetPosition(x, y, z, o);
creature->GetMotionMaster()->Initialize();
if (creature->IsAlive()) // dead creature will reset movement generator at respawn
{
creature->setDeathState(JUST_DIED);
creature->Respawn();
}
handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
handler->SetSentErrorMessage(true);
return false;
}
// update position in memory
const_cast<CreatureData*>(data)->spawnPoint.Relocate(*player);
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);
// respawn selected creature at the new location
if (creature)
{
if (creature->IsAlive())
creature->setDeathState(JUST_DIED);
creature->Respawn(true);
if (!creature->GetRespawnCompatibilityMode())
creature->AddObjectToRemoveList();
}
handler->PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
return true;
}
+8 -9
View File
@@ -667,7 +667,7 @@ public:
// re-create
Creature* wpCreature = new Creature();
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation()))
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), VISUAL_WAYPOINT, *chr))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
delete wpCreature;
@@ -678,8 +678,7 @@ public:
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
// 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;
@@ -881,7 +880,7 @@ public:
float o = chr->GetOrientation();
Creature* wpCreature = new Creature();
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, x, y, z, o))
if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete wpCreature;
@@ -899,7 +898,7 @@ public:
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
// 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;
@@ -945,7 +944,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -955,7 +954,7 @@ public:
PhasingHandler::InheritPhaseShift(creature, chr);
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
delete creature;
@@ -996,7 +995,7 @@ public:
Map* map = chr->GetMap();
Creature* creature = new Creature();
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, x, y, z, o))
if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMask(), id, { x, y, z, o }))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
@@ -1006,7 +1005,7 @@ public:
PhasingHandler::InheritPhaseShift(creature, chr);
creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMask());
if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map))
if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true))
{
handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
delete creature;
@@ -107,7 +107,7 @@ class npc_av_marshal_or_warmaster : public CreatureScript
events.ScheduleEvent(EVENT_CHECK_RESET, 5000);
}
void JustRespawned() override
void JustAppeared() override
{
Reset();
}
@@ -71,7 +71,7 @@ public:
events.ScheduleEvent(EVENT_RANDOM_YELL, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); //20 to 30 seconds
}
void JustRespawned() override
void JustAppeared() override
{
Reset();
Talk(SAY_RESPAWN);
@@ -186,7 +186,7 @@ public:
Talk(YELL_KILL);
}
void JustRespawned() override
void JustAppeared() override
{
Talk(YELL_BIRTH);
}
@@ -278,31 +278,24 @@ public:
_anubar.push_back(guid);
}
void Initialize()
void InitializeAI() override
{
BossAI::InitializeAI();
me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 9.0f);
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 9.0f);
_enteredCombat = false;
_doorsWebbed = false;
_lastPlayerCombatState = false;
SetStep(0);
}
void JustAppeared() override
{
BossAI::JustAppeared();
SetCombatMovement(true);
SummonCrusherPack(SUMMON_GROUP_CRUSHER_1);
}
void InitializeAI() override
{
BossAI::InitializeAI();
if (me->IsAlive())
Initialize();
}
void JustRespawned() override
{
BossAI::JustRespawned();
Initialize();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
@@ -157,15 +157,9 @@ class boss_krik_thir : public CreatureScript
me->SetReactState(REACT_PASSIVE);
}
void InitializeAI() override
void JustAppeared() override
{
BossAI::InitializeAI();
SummonAdds();
}
void JustRespawned() override
{
BossAI::JustRespawned();
BossAI::JustAppeared();
SummonAdds();
}
@@ -569,7 +569,7 @@ class npc_halion_controller : public CreatureScript
_twilightDamageTaken = 0;
}
void JustRespawned() override
void JustAppeared() override
{
if (_instance->GetGuidData(DATA_HALION) || _instance->GetBossState(DATA_GENERAL_ZARITHRIAN) != DONE)
return;
@@ -442,7 +442,7 @@ struct BloodPrincesBossAI : public BossAI
{
_spawnHealth = 1;
if (!me->isDead())
JustRespawned();
JustAppeared();
}
void Reset() override
@@ -490,7 +490,7 @@ struct BloodPrincesBossAI : public BossAI
}
}
void JustRespawned() override
void JustAppeared() override
{
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_PRINCES_CONTROL)))
if (controller->AI()->GetData(DATA_INTRO) != DATA_INTRO_DONE)
@@ -687,9 +687,9 @@ class npc_spinestalker : public CreatureScript
}
}
void JustRespawned() override
void JustAppeared() override
{
ScriptedAI::JustRespawned();
ScriptedAI::JustAppeared();
_instance->SetData(DATA_SINDRAGOSA_FROSTWYRMS, me->GetSpawnId()); // this cannot be in Reset because reset also happens on evade
}
@@ -826,9 +826,9 @@ class npc_rimefang : public CreatureScript
}
}
void JustRespawned() override
void JustAppeared() override
{
ScriptedAI::JustRespawned();
ScriptedAI::JustAppeared();
_instance->SetData(DATA_SINDRAGOSA_FROSTWYRMS, me->GetSpawnId()); // this cannot be in Reset because reset also happens on evade
}
@@ -994,9 +994,9 @@ class npc_sindragosa_trash : public CreatureScript
Initialize();
}
void JustRespawned() override
void JustAppeared() override
{
ScriptedAI::JustRespawned();
ScriptedAI::JustAppeared();
// Increase add count
if (me->GetEntry() == NPC_FROSTWING_WHELP)
@@ -261,7 +261,7 @@ class ValithriaDespawner : public BasicEvent
creature->SetRespawnDelay(10);
if (CreatureData const* data = creature->GetCreatureData())
creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation);
creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
@@ -354,7 +354,7 @@ class FrostwingGauntletRespawner
creature->SetRespawnDelay(2);
if (CreatureData const* data = creature->GetCreatureData())
creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation);
creature->UpdatePosition(data->spawnPoint);
creature->DespawnOrUnsummon();
creature->SetCorpseDelay(corpseDelay);
@@ -395,13 +395,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:
@@ -91,7 +91,7 @@ public:
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_ANUBREKHAN) != DONE)
{
Reset();
SummonGuards();
@@ -84,7 +84,7 @@ class boss_faerlina : public CreatureScript
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_FAERLINA) != DONE)
{
Reset();
SummonAdds();
@@ -270,14 +270,9 @@ struct boss_four_horsemen_baseAI : public BossAI
for (Horseman boss : horsemen)
{
if (Creature* cBoss = getHorsemanHandle(boss))
{
cBoss->DespawnOrUnsummon(0);
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));
}
}
}
@@ -74,7 +74,7 @@ public:
void InitializeAI() override
{
if (!me->isDead())
if (!me->isDead() && instance->GetBossState(BOSS_RAZUVIOUS) != DONE)
{
Reset();
SummonAdds();
@@ -31,15 +31,12 @@ enum Phases
PHASE_NOT_ENGAGED = 1,
PHASE_PETS,
PHASE_TRANSITION,
PHASE_THADDIUS,
PHASE_RESETTING
PHASE_THADDIUS
};
enum AIActions
{
ACTION_RESET_ENCOUNTER_TIMER = -1, // sent from instance AI
ACTION_BEGIN_RESET_ENCOUNTER = 0, // sent from thaddius to pets to trigger despawn and encounter reset
ACTION_RESET_ENCOUNTER, // sent from thaddius to pets to trigger respawn and full reset
ACTION_FEUGEN_DIED, // sent from respective pet to thaddius to indicate death
ACTION_STALAGG_DIED, // ^
ACTION_FEUGEN_RESET, // pet to thaddius
@@ -176,9 +173,6 @@ public:
{
events.SetPhase(PHASE_NOT_ENGAGED);
SetCombatMovement(false);
// initialize everything properly, and ensure that the coils are loaded by the time we initialize
BeginResetEncounter(true);
}
}
@@ -209,9 +203,9 @@ public:
return false;
}
void JustRespawned() override
void JustAppeared() override
{
if (events.IsInPhase(PHASE_RESETTING))
if (instance->GetBossState(BOSS_THADDIUS) != DONE)
ResetEncounter();
}
@@ -237,22 +231,13 @@ public:
{
switch (action)
{
case ACTION_RESET_ENCOUNTER_TIMER:
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
break;
case ACTION_FEUGEN_RESET:
case ACTION_STALAGG_RESET:
if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING))
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
BeginResetEncounter();
break;
case ACTION_FEUGEN_AGGRO:
case ACTION_STALAGG_AGGRO:
if (events.IsInPhase(PHASE_RESETTING))
{
BeginResetEncounter();
return;
}
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
return;
events.SetPhase(PHASE_PETS);
@@ -325,28 +310,24 @@ public:
events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION);
}
void BeginResetEncounter(bool initial = false)
void BeginResetEncounter()
{
if (instance->GetBossState(BOSS_THADDIUS) == DONE)
return;
if (events.IsInPhase(PHASE_RESETTING))
return;
// remove polarity shift debuffs on reset
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
me->DespawnOrUnsummon();
me->SetRespawnTime(initial ? 5 : 30);
me->DespawnOrUnsummon(0, Seconds(30));
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
events.SetPhase(PHASE_RESETTING);
me->setActive(false);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
me->setActive(false);
me->SetFarVisible(false);
}
@@ -359,10 +340,9 @@ public:
events.SetPhase(PHASE_NOT_ENGAGED);
me->SetReactState(REACT_PASSIVE);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER);
// @todo these guys should really be moved to a summon group - this is merely a hack to make them work in dynamic_spawning
instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130958, true); // Stalagg
instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130959, true); // Feugen
}
void UpdateAI(uint32 diff) override
@@ -535,7 +515,7 @@ public:
{
if (GameObject* coil = myCoilGO())
coil->SetGoState(GO_STATE_READY);
me->DespawnOrUnsummon();
me->DespawnOrUnsummon(0, Hours(24 * 7)); // will be force respawned by thaddius
me->setActive(false);
me->SetFarVisible(false);
}
@@ -553,9 +533,6 @@ public:
case ACTION_BEGIN_RESET_ENCOUNTER:
BeginResetEncounter();
break;
case ACTION_RESET_ENCOUNTER:
ResetEncounter();
break;
case ACTION_STALAGG_REVIVING_FX:
break;
case ACTION_STALAGG_REVIVED:
@@ -580,7 +557,7 @@ public:
break;
case ACTION_TRANSITION:
me->KillSelf(); // true death
me->DespawnOrUnsummon();
me->DespawnOrUnsummon(0, Hours(24 * 7));
if (Creature* coil = myCoil())
{
@@ -804,17 +781,11 @@ public:
{
if (GameObject* coil = myCoilGO())
coil->SetGoState(GO_STATE_READY);
me->DespawnOrUnsummon();
me->DespawnOrUnsummon(0, Hours(24 * 7)); // will be force respawned by thaddius
me->setActive(false);
me->SetFarVisible(false);
}
void ResetEncounter()
{
me->Respawn(true);
Initialize();
}
void DoAction(int32 action) override
{
switch (action)
@@ -822,9 +793,6 @@ public:
case ACTION_BEGIN_RESET_ENCOUNTER:
BeginResetEncounter();
break;
case ACTION_RESET_ENCOUNTER:
ResetEncounter();
break;
case ACTION_FEUGEN_REVIVING_FX:
break;
case ACTION_FEUGEN_REVIVED:
@@ -851,7 +819,7 @@ public:
break;
case ACTION_TRANSITION:
me->KillSelf(); // true death this time around
me->DespawnOrUnsummon();
me->DespawnOrUnsummon(0, Hours(24 * 7));
if (Creature* coil = myCoil())
{
@@ -210,7 +210,7 @@ public:
me->CastSpell(me, SPELL_ICE_PRISON, true);
}
void JustRespawned() override
void JustAppeared() override
{
Reset();
}
@@ -926,7 +926,7 @@ public:
me->CastSpell(me, STORM_VISUAL, true);
}
void JustRespawned() override
void JustAppeared() override
{
Reset();
}
@@ -733,7 +733,7 @@ public:
instance->SetGuidData(DATA_LEOTHERAS_EVENT_STARTER, who->GetGUID());
}
void JustRespawned() override
void JustAppeared() override
{
AddedBanish = false;
Reset();
@@ -83,7 +83,7 @@ class boss_doomlord_kazzak : public CreatureScript
_events.ScheduleEvent(EVENT_BERSERK, 180000);
}
void JustRespawned() override
void JustAppeared() override
{
Talk(SAY_INTRO);
}
@@ -16,6 +16,7 @@
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
# SPAWN/RESPAWN SETTINGS
# CHAT SETTINGS
# GAME MASTER SETTINGS
# VISIBILITY AND DISTANCES
@@ -1711,6 +1712,122 @@ MonsterSight = 50.000000
#
###################################################################################################
###################################################################################################
# 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 = 0
#
# 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
#