Core/Spawns: merged Dynamic Spawning
This commit is contained in:
1 parent
21795a5ae5
commit
b976bc1187
87 files changed
+3200
-1007
No files matched your search
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -570,7 +570,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
|
||||
return false;
|
||||
}
|
||||
|
||||
void SmartAI::JustRespawned()
|
||||
void SmartAI::JustAppeared()
|
||||
{
|
||||
mDespawnTime = 0;
|
||||
mDespawnState = 0;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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__
|
||||
@@ -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)
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
+2
-8
@@ -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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
#
|
||||
|
||||
Reference in new issue
Block a user