diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 9faa8869f13..84495182a74 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -3046,7 +3046,8 @@ INSERT INTO `updates` VALUES ('custom_2019_08_01_00_characters.sql','980B1647EBB41A3644ED825E116F052EFEB6E5D1','ARCHIVED','2019-11-06 00:17:45',0), ('custom_2019_08_20_00_characters.sql','09DC2B6A0E602E377F240CB29F6E1E3209FD346B','ARCHIVED','2019-11-06 00:17:45',0), ('custom_2020_01_05_00_character.sql','DEC981779DA0311FA1E20FF0424BE5F997D21BEE','ARCHIVED','2020-01-06 10:44:59',0), -('2020_05_19_00_characters.sql','5FFAB4D7060E872AB6221D759EC1BCF461F1E9E3','ARCHIVED','2020-05-19 01:44:34',0); +('2020_05_19_00_characters.sql','5FFAB4D7060E872AB6221D759EC1BCF461F1E9E3','ARCHIVED','2020-05-19 01:44:34',0), +('2020_06_15_00_characters.sql','4422FE31AABBA352C473E33B56927F6695492BCB','RELEASED','2020-06-15 10:23:44',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; @@ -3125,7 +3126,10 @@ INSERT INTO `worldstates` VALUES (20001,0,'NextArenaPointDistributionTime'), (20002,0,'NextWeeklyQuestResetTime'), (20003,0,'NextBGRandomDailyResetTime'), -(20004,0,'cleaning_flags'); +(20004,0,'cleaning_flags'), +(20006,0,'NextGuildDailyResetTime'), +(20007,0,'NextMonthlyQuestResetTime'), +(20008,0,'NextDailyQuestResetTime'); /*!40000 ALTER TABLE `worldstates` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; diff --git a/sql/updates/characters/4.3.4/2020_06_15_00_characters.sql b/sql/updates/characters/4.3.4/2020_06_15_00_characters.sql new file mode 100644 index 00000000000..229ab29d836 --- /dev/null +++ b/sql/updates/characters/4.3.4/2020_06_15_00_characters.sql @@ -0,0 +1,6 @@ +-- +DELETE FROM `worldstates` WHERE `entry` IN (20006, 20007, 20008); +INSERT INTO `worldstates` (`entry`,`value`,`comment`) VALUES +(20006,0,'NextGuildDailyResetTime'), +(20007,0,'NextMonthlyQuestResetTime'), +(20008,0,'NextDailyQuestResetTime'); diff --git a/sql/updates/world/4.3.4/2020_07_13_00_world.sql b/sql/updates/world/4.3.4/2020_07_13_00_world.sql new file mode 100644 index 00000000000..07f2e452daa --- /dev/null +++ b/sql/updates/world/4.3.4/2020_07_13_00_world.sql @@ -0,0 +1,102 @@ +-- +DROP TABLE IF EXISTS `pool_members`; +CREATE TABLE `pool_members` ( + `type` smallint(10) unsigned NOT NULL, + `spawnId` int(10) unsigned NOT NULL, + `poolSpawnId` int(10) unsigned NOT NULL, + `chance` double unsigned NOT NULL, + `description` varchar(255) DEFAULT NULL, + PRIMARY KEY (`type`,`spawnId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `pool_members` (`type`,`spawnId`,`poolSpawnId`,`chance`,`description`) +SELECT 0 as `type`, `guid` as `spawnId`, `pool_entry` as `poolSpawnId`, `chance`, `description` FROM `pool_creature`; + +INSERT INTO `pool_members` (`type`,`spawnId`,`poolSpawnId`,`chance`,`description`) +SELECT 1 as `type`, `guid` as `spawnId`, `pool_entry` as `poolSpawnId`, `chance`, `description` FROM `pool_gameobject`; + +INSERT INTO `pool_members` (`type`,`spawnId`,`poolSpawnId`,`chance`,`description`) +SELECT 2 as `type`, `pool_id` as `spawnId`, `mother_pool` as `poolSpawnId`, `chance`, `description` FROM `pool_pool`; + +DROP TABLE IF EXISTS `pool_creature`; +DROP TABLE IF EXISTS `pool_gameobject`; +DROP TABLE IF EXISTS `pool_pool`; + +-- quest pools no longer support nesting, but they also don't need it +DROP TABLE IF EXISTS `quest_pool_members`; +CREATE TABLE `quest_pool_members` ( + `questId` int(10) unsigned not null, + `poolId` int(10) unsigned not null, + `poolIndex` tinyint(2) unsigned not null COMMENT 'Multiple quests with the same index will always spawn together!', + `description` varchar(255) default null, + PRIMARY KEY (`questId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TEMPORARY TABLE `_temp_pool_quests` ( + `sortIndex` int auto_increment not null, + `questId` int(10) unsigned not null, + `subPool` int(10) unsigned, + `topPool` int(10) unsigned not null, + `description` varchar(255) default null, + PRIMARY KEY (`sortIndex`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- build a lookup table of questid <-> nested pool <-> toplevel pool +INSERT INTO `_temp_pool_quests` (`questId`,`subPool`,`topPool`,`description`) +SELECT pq.`entry` as `questId`, IF(pp.`poolSpawnId` is null, null, pq.`pool_entry`) as `subPool`, IFNULL(pp.`poolSpawnId`,pq.`pool_entry`) AS `topPool`, pq.`description` +FROM `pool_quest` as pq LEFT JOIN `pool_members` pp ON (pp.`type`=2) and (pp.`spawnId` = pq.`pool_entry`) +ORDER BY `topPool` ASC, `subPool` ASC; + +-- delete any nested pools whose members we'll remove +DELETE FROM `pool_template` WHERE `entry` IN (SELECT DISTINCT `subPool` FROM `_temp_pool_quests` WHERE `subPool` is not null); +DELETE FROM `pool_members` WHERE `type`=2 AND `spawnId` IN (SELECT DISTINCT `subPool` FROM `_temp_pool_quests` WHERE `subPool` is not null); + +-- ensure quests without a subPool have different subPool values +UPDATE `_temp_pool_quests` SET `subPool`=`sortIndex` WHERE `subPool` is null; + +SET @pool_index = 0; +SET @last_pool = 0; +SET @last_subpool = 0; + +-- poolIndex is chosen as follows: +-- *) if we're starting a new pool, the index is 0 +-- *) if the preceding element had the same subpool, the index remains the same +-- *) if the preceding element had a different subpool, the index increases by 1 +INSERT INTO `quest_pool_members` (`questId`, `poolId`, `poolIndex`, `description`) +SELECT + `questId`, + `topPool` as `poolId`, + (CASE WHEN @last_subpool = `subPool` THEN @pool_index ELSE (@pool_index := (((@last_subpool := `subPool`) and 0) + (CASE WHEN @last_pool = `topPool` THEN @pool_index+1 ELSE ((@last_pool := `topPool`) and 0) END))) END) as `poolIndex`, + `description` +FROM `_temp_pool_quests`; + +-- drop the old table +DROP TABLE `pool_quest`; + +DROP TABLE IF EXISTS `quest_pool_template`; +CREATE TABLE `quest_pool_template` ( + `poolId` mediumint(8) unsigned not null, + `numActive` int(10) unsigned not null COMMENT 'Number of indices to have active at any time', + `description` varchar(255) default null, + PRIMARY KEY (`poolId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- copy any quest pool templates over +INSERT INTO `quest_pool_template` (`poolId`, `numActive`, `description`) +SELECT DISTINCT pt.`entry`,pt.`max_limit`,pt.`description` FROM `quest_pool_members` qpm LEFT JOIN `pool_template` pt ON (qpm.`poolId` = pt.`entry`); + +-- and delete them from the original table +DELETE pt FROM `pool_template` pt LEFT JOIN `quest_pool_template` qpt ON qpt.`poolId`=pt.`entry` WHERE qpt.`poolId` is not null; + +DELETE FROM `pool_template` WHERE `entry` IN (380, 381, 368, 375, 373, 374, 383, 382, 369, 377); +INSERT INTO `pool_template` (`entry`, `max_limit`, `description`) VALUES +(380, 1, ''), +(381, 1, ''), +(368, 1, ''), +(375, 1, ''), +(373, 1, ''), +(374, 1, ''), +(383, 1, ''), +(369, 1, ''), +(377, 1, ''), +(382, 1, ''); diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index 2d51b04fb92..fbaac516844 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -111,6 +111,37 @@ struct tm* localtime_r(time_t const* time, struct tm *result) } #endif +tm TimeBreakdown(time_t time) +{ + tm timeLocal; + localtime_r(&time, &timeLocal); + return timeLocal; +} + +time_t LocalTimeToUTCTime(time_t time) +{ +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + return time + _timezone; +#else + return time + timezone; +#endif +} + +time_t GetLocalHourTimestamp(time_t time, uint8 hour, bool onlyAfterTime) +{ + tm timeLocal = TimeBreakdown(time); + timeLocal.tm_hour = 0; + timeLocal.tm_min = 0; + timeLocal.tm_sec = 0; + time_t midnightLocal = mktime(&timeLocal); + time_t hourLocal = midnightLocal + hour * HOUR; + + if (onlyAfterTime && hourLocal <= time) + hourLocal += DAY; + + return hourLocal; +} + std::string secsToTimeString(uint64 timeInSecs, bool shortText, bool hoursOnly) { uint64 secs = timeInSecs % MINUTE; @@ -210,6 +241,15 @@ std::string TimeToTimestampStr(time_t t) return Trinity::StringFormat("%04d-%02d-%02d_%02d-%02d-%02d", aTm.tm_year + 1900, aTm.tm_mon + 1, aTm.tm_mday, aTm.tm_hour, aTm.tm_min, aTm.tm_sec); } +std::string TimeToHumanReadable(time_t t) +{ + tm time; + localtime_r(&t, &time); + char buf[30]; + strftime(buf, 30, "%c", &time); + return std::string(buf); +} + /// Check if the string is a valid ip address representation bool IsIPAddress(char const* ipaddress) { diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 9e0d41fef64..7530a623d58 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -58,10 +58,14 @@ TC_COMMON_API void stripLineInvisibleChars(std::string &src); TC_COMMON_API int64 MoneyStringToMoney(std::string const& moneyString); TC_COMMON_API struct tm* localtime_r(time_t const* time, struct tm *result); +TC_COMMON_API time_t LocalTimeToUTCTime(time_t time); +TC_COMMON_API time_t GetLocalHourTimestamp(time_t time, uint8 hour, bool onlyAfterTime = true); +TC_COMMON_API tm TimeBreakdown(time_t t); TC_COMMON_API std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hoursOnly = false); TC_COMMON_API uint32 TimeStringToSecs(std::string const& timestring); TC_COMMON_API std::string TimeToTimestampStr(time_t t); +TC_COMMON_API std::string TimeToHumanReadable(time_t t); inline void ApplyPercentModFloatVar(float& var, float val, bool apply) { diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 590a411006b..7149b880901 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -23,8 +23,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() if (!m_reconnecting) m_stmts.resize(MAX_CHARACTERDATABASE_STATEMENTS); - PrepareStatement(CHAR_DEL_QUEST_POOL_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_QUEST_POOL_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_POOL_QUEST_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_POOL_QUEST_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM, "DELETE FROM guild_bank_item WHERE guildid = ? AND TabId = ? AND SlotId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_EXPIRED_BANS, "UPDATE character_banned SET active = 0 WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate <> bandate", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHECK_NAME, "SELECT 1 FROM characters WHERE name = ?", CONNECTION_BOTH); @@ -458,7 +458,6 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_PINFO_XP, "SELECT a.xp, b.guid FROM characters a LEFT JOIN guild_member b ON a.guid = b.guid WHERE a.guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC, "SELECT guid, name FROM characters WHERE account = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 6abb97b20bf..67c888fe939 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -28,8 +28,8 @@ enum CharacterDatabaseStatements : uint32 name for a suiting suffix. */ - CHAR_DEL_QUEST_POOL_SAVE, - CHAR_INS_QUEST_POOL_SAVE, + CHAR_DEL_POOL_QUEST_SAVE, + CHAR_INS_POOL_QUEST_SAVE, CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM, CHAR_DEL_EXPIRED_BANS, CHAR_SEL_CHECK_NAME, @@ -386,7 +386,6 @@ enum CharacterDatabaseStatements : uint32 CHAR_SEL_PINFO_BANS, CHAR_SEL_CHAR_HOMEBIND, CHAR_SEL_CHAR_GUID_NAME_BY_ACC, - CHAR_SEL_POOL_QUEST_SAVE, CHAR_SEL_CHARACTER_AT_LOGIN, CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, CHAR_SEL_CHAR_CUSTOMIZE_INFO, diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 54af7748542..ceaa2f9abae 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -23,7 +23,6 @@ void WorldDatabaseConnection::DoPrepareStatements() if (!m_reconnecting) m_stmts.resize(MAX_WORLDDATABASE_STATEMENTS); - PrepareStatement(WORLD_SEL_QUEST_POOLS, "SELECT entry, pool_entry FROM pool_quest", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CRELINKED_RESPAWN, "DELETE FROM linked_respawn WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_REP_CREATURE_LINKED_RESPAWN, "REPLACE INTO linked_respawn (guid, linkedGuid) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_CREATURE_TEXT, "SELECT CreatureID, GroupID, ID, Text, Type, Language, Probability, Emote, Duration, Sound, SoundType, BroadcastTextId, TextRange FROM creature_text", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index c913f467971..2ce5bbda180 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -28,7 +28,6 @@ enum WorldDatabaseStatements : uint32 name for a suiting suffix. */ - WORLD_SEL_QUEST_POOLS, WORLD_DEL_CRELINKED_RESPAWN, WORLD_REP_CREATURE_LINKED_RESPAWN, WORLD_SEL_CREATURE_TEXT, diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 64b99c128de..3a3b656197e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1189,10 +1189,15 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask) data.displayid = displayId; data.equipmentId = GetCurrentEquipmentId(); if (!GetTransport()) - data.spawnPoint.WorldRelocate(this); + { + data.mapId = GetMapId(); + data.spawnPoint.Relocate(this); + } else - data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO()); - + { + data.mapId = mapid; + data.spawnPoint.Relocate(GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO()); + } data.spawntimesecs = m_respawnDelay; // prevent add data integrity problems data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius; @@ -1579,24 +1584,12 @@ void Creature::SetSpawnHealth() bool Creature::hasQuest(uint32 quest_id) const { - QuestRelationBounds qr = sObjectMgr->GetCreatureQuestRelationBounds(GetEntry()); - for (QuestRelations::const_iterator itr = qr.first; itr != qr.second; ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; + return sObjectMgr->GetCreatureQuestRelations(GetEntry()).HasQuest(quest_id); } bool Creature::hasInvolvedQuest(uint32 quest_id) const { - QuestRelationBounds qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(GetEntry()); - for (QuestRelations::const_iterator itr = qir.first; itr != qir.second; ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; + return sObjectMgr->GetCreatureQuestInvolvedRelations(GetEntry()).HasQuest(quest_id); } /*static*/ bool Creature::DeleteFromDB(ObjectGuid::LowType spawnId) @@ -1607,7 +1600,7 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), + sMapMgr->DoForAllMapsWithMapId(data->mapId, [spawnId, trans](Map* map) -> void { // despawn all active creatures, and remove their respawns diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 4369939d343..b2f9cc107c7 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -83,7 +83,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void Update(uint32 time) override; // overwrited Unit::Update void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr, float* dist =nullptr) const; - bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->spawnPoint.GetMapId() != GetMapId(); } + bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->mapId != GetMapId(); } void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } uint32 GetCorpseDelay() const { return m_corpseDelay; } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index d8ca4e47c70..963df83fd9b 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -892,7 +892,8 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask) data.spawnId = m_spawnId; ASSERT(data.spawnId == m_spawnId); data.id = GetEntry(); - data.spawnPoint.WorldRelocate(this); + data.mapId = GetMapId(); + data.spawnPoint.Relocate(this); data.rotation = m_localRotation; data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); @@ -1015,7 +1016,7 @@ bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), + sMapMgr->DoForAllMapsWithMapId(data->mapId, [spawnId, trans](Map* map) -> void { // despawn all active objects, and remove their respawns @@ -1055,24 +1056,12 @@ bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap /*********************************************************/ bool GameObject::hasQuest(uint32 quest_id) const { - QuestRelationBounds qr = sObjectMgr->GetGOQuestRelationBounds(GetEntry()); - for (QuestRelations::const_iterator itr = qr.first; itr != qr.second; ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; + return sObjectMgr->GetGOQuestRelations(GetEntry()).HasQuest(quest_id); } bool GameObject::hasInvolvedQuest(uint32 quest_id) const { - QuestRelationBounds qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(GetEntry()); - for (QuestRelations::const_iterator itr = qir.first; itr != qir.second; ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; + return sObjectMgr->GetGOQuestInvolvedRelations(GetEntry()).HasQuest(quest_id); } bool GameObject::IsTransport() const diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 989fbf2b0b9..41d291065f8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -82,6 +82,7 @@ #include "QueryHolder.h" #include "QuestDef.h" #include "QuestPackets.h" +#include "QuestPools.h" #include "Realm.h" #include "ReputationMgr.h" #include "SkillDiscovery.h" @@ -14270,15 +14271,15 @@ uint32 Player::GetDefaultGossipMenuForSource(WorldObject* source) void Player::PrepareQuestMenu(ObjectGuid guid) { - QuestRelationBounds objectQR; - QuestRelationBounds objectQIR; + QuestRelationResult objectQR; + QuestRelationResult objectQIR; // pets also can have quests Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid); if (creature) { - objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry()); - objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry()); + objectQR = sObjectMgr->GetCreatureQuestRelations(creature->GetEntry()); + objectQIR = sObjectMgr->GetCreatureQuestInvolvedRelations(creature->GetEntry()); } else { @@ -14289,8 +14290,8 @@ void Player::PrepareQuestMenu(ObjectGuid guid) GameObject* gameObject = _map->GetGameObject(guid); if (gameObject) { - objectQR = sObjectMgr->GetGOQuestRelationBounds(gameObject->GetEntry()); - objectQIR = sObjectMgr->GetGOQuestInvolvedRelationBounds(gameObject->GetEntry()); + objectQR = sObjectMgr->GetGOQuestRelations(gameObject->GetEntry()); + objectQIR = sObjectMgr->GetGOQuestInvolvedRelations(gameObject->GetEntry()); } else return; @@ -14299,9 +14300,8 @@ void Player::PrepareQuestMenu(ObjectGuid guid) QuestMenu &qm = PlayerTalkClass->GetQuestMenu(); qm.ClearMenu(); - for (QuestRelations::const_iterator i = objectQIR.first; i != objectQIR.second; ++i) + for (uint32 quest_id : objectQIR) { - uint32 quest_id = i->second; QuestStatus status = GetQuestStatus(quest_id); if (status == QUEST_STATUS_COMPLETE) qm.AddMenuItem(quest_id, 4); @@ -14311,9 +14311,8 @@ void Player::PrepareQuestMenu(ObjectGuid guid) // qm.AddMenuItem(quest_id, 2); } - for (QuestRelations::const_iterator i = objectQR.first; i != objectQR.second; ++i) + for (uint32 quest_id : objectQR) { - uint32 quest_id = i->second; Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id); if (!quest) continue; @@ -14423,7 +14422,7 @@ bool Player::IsActiveQuest(uint32 quest_id) const Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const { - QuestRelationBounds objectQR; + QuestRelationResult quests; uint32 nextQuestID = quest->GetNextQuestInChain(); switch (guid.GetHigh()) @@ -14436,7 +14435,7 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const case HighGuid::Vehicle: { if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid)) - objectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry()); + quests = sObjectMgr->GetCreatureQuestRelations(creature->GetEntry()); else return nullptr; break; @@ -14448,7 +14447,7 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId()); ASSERT(_map); if (GameObject* gameObject = _map->GetGameObject(guid)) - objectQR = sObjectMgr->GetGOQuestRelationBounds(gameObject->GetEntry()); + quests = sObjectMgr->GetGOQuestRelations(gameObject->GetEntry()); else return nullptr; break; @@ -14458,11 +14457,9 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const } // for unit and go state - for (QuestRelations::const_iterator itr = objectQR.first; itr != objectQR.second; ++itr) - { - if (itr->second == nextQuestID) + if (nextQuestID) + if (quests.HasQuest(nextQuestID)) return sObjectMgr->GetQuestTemplate(nextQuestID); - } return nullptr; } @@ -15730,7 +15727,7 @@ bool Player::CanShareQuest(uint32 quest_id) const if (itr != m_QuestStatus.end()) { // in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share - if (sPoolMgr->IsPartOfAPool(quest_id) && !sPoolMgr->IsSpawnedObject(quest_id)) + if (sQuestPoolMgr->IsQuestActive(quest_id)) { SendPushToPartyResponse(this, QUEST_PARTY_MSG_CANT_BE_SHARED_TODAY); return false; @@ -15839,8 +15836,7 @@ uint8 Player::GetFirstRewardCountForDungeonId(uint32 dungeonId) QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) { - QuestRelationBounds qr; - QuestRelationBounds qir; + QuestRelationResult qr, qir; switch (questgiver->GetTypeId()) { @@ -15849,8 +15845,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) QuestGiverStatus questStatus = QuestGiverStatus(questgiver->ToGameObject()->AI()->GetDialogStatus(this)); if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS) return questStatus; - qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry()); - qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry()); + qr = sObjectMgr->GetGOQuestRelations(questgiver->GetEntry()); + qir = sObjectMgr->GetGOQuestInvolvedRelations(questgiver->GetEntry()); break; } case TYPEID_UNIT: @@ -15858,8 +15854,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) QuestGiverStatus questStatus = QuestGiverStatus(questgiver->ToCreature()->AI()->GetDialogStatus(this)); if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS) return questStatus; - qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry()); - qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry()); + qr = sObjectMgr->GetCreatureQuestRelations(questgiver->GetEntry()); + qir = sObjectMgr->GetCreatureQuestInvolvedRelations(questgiver->GetEntry()); break; } default: @@ -15870,10 +15866,9 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) QuestGiverStatus result = DIALOG_STATUS_NONE; - for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i) + for (uint32 questId : qir) { QuestGiverStatus result2 = DIALOG_STATUS_NONE; - uint32 questId = i->second; Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!quest) continue; @@ -15891,10 +15886,9 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) result = result2; } - for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i) + for (uint32 questId : qr) { QuestGiverStatus result2 = DIALOG_STATUS_NONE; - uint32 questId = i->second; Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!quest) continue; diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index c5c4f5775c5..e52500839dd 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -1196,7 +1196,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->spawnPoint.GetMapId()].insert(itr->first); + creaturesByMap[data->mapId].insert(itr->first); for (auto const& p : creaturesByMap) { @@ -1260,7 +1260,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) sObjectMgr->AddCreatureToGrid(*itr, data); // Spawn if necessary (loaded grids only) - Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, *itr); // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) @@ -1288,7 +1288,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) 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->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, *itr); // We use current coords to unspawn, not spawn coords since creature can have changed grid if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) @@ -1343,7 +1343,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) { sObjectMgr->RemoveCreatureFromGrid(*itr, data); - sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr](Map* map) { map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, *itr); auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(*itr); @@ -1374,7 +1374,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) { sObjectMgr->RemoveGameobjectFromGrid(*itr, data); - sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr](Map* map) { map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, *itr); auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr); @@ -1395,7 +1395,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin(); itr != mGameEventPoolIds[internal_event_id].end(); ++itr) { - sPoolMgr->DespawnPool(*itr); + sPoolMgr->DespawnPool(*itr, true); } } @@ -1409,7 +1409,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) continue; // Update if spawned - sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr, activate](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->mapId, [&itr, activate](Map* map) { auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first); for (auto it = creatureBounds.first; it != creatureBounds.second; ++it) @@ -1522,7 +1522,7 @@ void GameEventMgr::UpdateEventQuests(uint16 event_id, bool activate) QuestRelList::iterator itr; for (itr = mGameEventCreatureQuests[event_id].begin(); itr != mGameEventCreatureQuests[event_id].end(); ++itr) { - QuestRelations* CreatureQuestMap = sObjectMgr->GetCreatureQuestRelationMap(); + QuestRelations* CreatureQuestMap = sObjectMgr->GetCreatureQuestRelationMapHACK(); if (activate) // Add the pair(id, quest) to the multimap CreatureQuestMap->insert(QuestRelations::value_type(itr->first, itr->second)); else @@ -1547,7 +1547,7 @@ void GameEventMgr::UpdateEventQuests(uint16 event_id, bool activate) } for (itr = mGameEventGameObjectQuests[event_id].begin(); itr != mGameEventGameObjectQuests[event_id].end(); ++itr) { - QuestRelations* GameObjectQuestMap = sObjectMgr->GetGOQuestRelationMap(); + QuestRelations* GameObjectQuestMap = sObjectMgr->GetGOQuestRelationMapHACK(); if (activate) // Add the pair(id, quest) to the multimap GameObjectQuestMap->insert(QuestRelations::value_type(itr->first, itr->second)); else diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4c1e8785557..b800b878284 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1653,8 +1653,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); - if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) + MapEntry const* const map = sMapStore.LookupEntry(master->mapId); + if (!map || !map->Instanceable() || (master->mapId != slave->mapId)) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1690,8 +1690,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); - if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) + MapEntry const* const map = sMapStore.LookupEntry(master->mapId); + if (!map || !map->Instanceable() || (master->mapId != slave->mapId)) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1727,8 +1727,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); - if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) + MapEntry const* const map = sMapStore.LookupEntry(master->mapId); + if (!map || !map->Instanceable() || (master->mapId != slave->mapId)) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1764,8 +1764,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); - if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) + MapEntry const* const map = sMapStore.LookupEntry(master->mapId); + if (!map || !map->Instanceable() || (master->mapId != slave->mapId)) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1818,8 +1818,8 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid return false; } - MapEntry const* map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); - if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) + MapEntry const* map = sMapStore.LookupEntry(master->mapId); + if (!map || !map->Instanceable() || (master->mapId != slave->mapId)) { TC_LOG_ERROR("sql.sql", "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow); return false; @@ -1935,13 +1935,13 @@ void ObjectMgr::LoadCreatures() // 0 1 2 3 4 5 6 7 8 9 10 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, spawndist, " - // 11 12 13 14 15 16 17 18 19 20 21 - "currentwaypoint, curhealth, curmana, MovementType, spawnMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.phaseUseFlags, " + // 11 12 13 14 15 16 17 18 19 20 21 + "currentwaypoint, curhealth, curmana, MovementType, spawnMask, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.phaseUseFlags, " // 22 23 24 25 "creature.PhaseId, creature.PhaseGroup, creature.terrainSwapMap, creature.ScriptName " "FROM creature " "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid " - "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid"); + "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId"); if (!result) { @@ -1978,7 +1978,8 @@ void ObjectMgr::LoadCreatures() CreatureData& data = _creatureDataStore[guid]; 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.mapId = fields[2].GetUInt16(); + data.spawnPoint.Relocate(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(); @@ -1998,23 +1999,23 @@ void ObjectMgr::LoadCreatures() data.phaseGroup = fields[23].GetUInt32(); data.terrainSwapMap = fields[24].GetInt32(); data.scriptId = GetScriptId(fields[25].GetString()); - data.spawnGroupData = &_spawnGroupDataStore[0]; + data.spawnGroupData = GetDefaultSpawnGroup(); - MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId()); + MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId); if (!mapEntry) { - TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId()); + TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapId); continue; } // Skip spawnMask check for transport maps - if (!IsTransportMap(data.spawnPoint.GetMapId())) + if (!IsTransportMap(data.mapId)) { - if (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()); + if (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); } else - data.spawnGroupData = &_spawnGroupDataStore[1]; // force compatibility group for transport spawns + data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns bool ok = true; for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff) @@ -2106,7 +2107,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->ParentMapID != int32(data.spawnPoint.GetMapId())) + else if (terrainSwapEntry->ParentMapID != int32(data.mapId)) { 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; @@ -2118,7 +2119,7 @@ void ObjectMgr::LoadCreatures() uint32 zoneId = 0; uint32 areaId = 0; PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint); + sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); @@ -2146,7 +2147,7 @@ void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* if (mask & 1) { CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()]; cell_guids.creatures.insert(guid); } } @@ -2160,7 +2161,7 @@ void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData co if (mask & 1) { CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()]; cell_guids.creatures.erase(guid); } } @@ -2181,8 +2182,8 @@ ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Pos GameObjectData& data = NewOrExistGameObjectData(spawnId); data.spawnId = spawnId; data.id = entry; - data.spawnPoint.WorldRelocate(mapId, pos); - + data.mapId = mapId; + data.spawnPoint.Relocate(pos); data.rotation = rot; data.spawntimesecs = spawntimedelay; data.animprogress = 100; @@ -2233,10 +2234,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit CreatureData& data = NewOrExistCreatureData(spawnId); data.spawnId = spawnId; data.id = entry; - data.spawnPoint.WorldRelocate(mapId, pos); + data.mapId = mapId; + data.spawnPoint.Relocate(pos); data.displayid = 0; data.equipmentId = 0; - data.spawntimesecs = spawntimedelay; data.spawndist = 0; data.currentwaypoint = 0; @@ -2273,12 +2274,12 @@ void ObjectMgr::LoadGameObjects() // 0 1 2 3 4 5 6 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, " - // 7 8 9 10 11 12 13 14 15 16 - "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, eventEntry, pool_entry, " + // 7 8 9 10 11 12 13 14 15 16 + "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, eventEntry, poolSpawnId, " // 17 18 19 20 21 "phaseUseFlags, PhaseId, PhaseGroup, terrainSwapMap, ScriptName " "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid " - "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid"); + "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId"); if (!result) { @@ -2333,21 +2334,21 @@ void ObjectMgr::LoadGameObjects() GameObjectData& data = _gameObjectDataStore[guid]; - data.id = entry; 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.mapId = fields[2].GetUInt16(); + data.spawnPoint.Relocate(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]; + data.spawnGroupData = GetDefaultSpawnGroup(); - MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId()); + MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId); 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.spawnPoint.GetMapId()); + 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); continue; } @@ -2372,13 +2373,13 @@ void ObjectMgr::LoadGameObjects() data.spawnMask = fields[14].GetUInt8(); - if (!IsTransportMap(data.spawnPoint.GetMapId())) + if (!IsTransportMap(data.mapId)) { - if (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()); + if (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); } else - data.spawnGroupData = &_spawnGroupDataStore[1]; // force compatibility group for transport spawns + data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns int16 gameEvent = fields[15].GetInt8(); uint32 PoolId = fields[16].GetUInt32(); @@ -2420,7 +2421,7 @@ 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->ParentMapID != int32(data.spawnPoint.GetMapId())) + else if (terrainSwapEntry->ParentMapID != int32(data.mapId)) { 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; @@ -2453,7 +2454,7 @@ void ObjectMgr::LoadGameObjects() continue; } - if (!MapManager::IsValidMapCoord(data.spawnPoint)) + if (!MapManager::IsValidMapCoord(data.mapId, data.spawnPoint)) { TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id); continue; @@ -2464,7 +2465,7 @@ void ObjectMgr::LoadGameObjects() uint32 zoneId = 0; uint32 areaId = 0; PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.spawnPoint); + sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); @@ -2560,19 +2561,15 @@ void ObjectMgr::LoadSpawnGroups() { Field* fields = result->Fetch(); uint32 groupId = fields[0].GetUInt32(); - SpawnObjectType spawnType; + SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8()); + if (!SpawnData::TypeIsValid(spawnType)) { - uint32 type = fields[1].GetUInt8(); - if (type >= SPAWN_TYPE_MAX) - { - TC_LOG_ERROR("sql.sql", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", type, groupId); - continue; - } - spawnType = SpawnObjectType(type); + TC_LOG_ERROR("sql.sql", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", uint32(spawnType), groupId); + continue; } ObjectGuid::LowType spawnId = fields[2].GetUInt32(); - SpawnData const* data = GetSpawnData(spawnType, spawnId); + SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId); if (!data) { TC_LOG_ERROR("sql.sql", "Spawn data with ID (%u,%u) not found, but is listed as a member of spawn group %u!", uint32(spawnType), spawnId, groupId); @@ -2586,20 +2583,20 @@ void ObjectMgr::LoadSpawnGroups() auto it = _spawnGroupDataStore.find(groupId); if (it == _spawnGroupDataStore.end()) { - TC_LOG_ERROR("sql.sql", "Spawn group %u assigned to spawn ID (%u,%u), but group is found!", groupId, uint32(spawnType), spawnId); + TC_LOG_ERROR("sql.sql", "Spawn group %u assigned to spawn ID (%u,%u), but group does not exist!", 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)) + groupTemplate.mapId = data->mapId; + else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM)) { - TC_LOG_ERROR("sql.sql", "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()); + TC_LOG_ERROR("sql.sql", "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->mapId); continue; } - const_cast(data)->spawnGroupData = &groupTemplate; + const_cast(data)->spawnGroupData = &groupTemplate; if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM)) _spawnGroupMapStore.emplace(groupId, data); ++numMembers; @@ -2699,7 +2696,7 @@ void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData con if (mask & 1) { CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()]; cell_guids.gameobjects.insert(guid); } } @@ -2713,7 +2710,7 @@ void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectDat if (mask & 1) { CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()]; cell_guids.gameobjects.erase(guid); } } @@ -7900,7 +7897,7 @@ void ObjectMgr::DeleteGameObjectData(ObjectGuid::LowType guid) _gameObjectDataStore.erase(guid); } -void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go) +void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reversedMap, std::string const& table) { uint32 oldMSTime = getMSTime(); @@ -7908,7 +7905,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve uint32 count = 0; - QueryResult result = WorldDatabase.PQuery("SELECT id, quest, pool_entry FROM %s qr LEFT JOIN pool_quest pq ON qr.quest = pq.entry", table.c_str()); + QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM %s", table.c_str()); if (!result) { @@ -7916,15 +7913,10 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve return; } - PooledQuestRelation* poolRelationMap = go ? &sPoolMgr->mQuestGORelation : &sPoolMgr->mQuestCreatureRelation; - if (starter) - poolRelationMap->clear(); - do { uint32 id = result->Fetch()[0].GetUInt32(); uint32 quest = result->Fetch()[1].GetUInt32(); - uint32 poolId = result->Fetch()[2].GetUInt32(); if (_questTemplates.find(quest) == _questTemplates.end()) { @@ -7932,14 +7924,9 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve continue; } - if (!poolId || !starter) - { - map.insert(QuestRelations::value_type(id, quest)); - if (reverseMap) - reverseMap->insert(QuestRelationsReverse::value_type(quest, id)); - } - else - poolRelationMap->insert(PooledQuestRelation::value_type(quest, id)); + map.insert(QuestRelations::value_type(id, quest)); + if (reversedMap) + reversedMap->insert(QuestRelationsReverse::value_type(quest, id)); ++count; } while (result->NextRow()); @@ -7949,7 +7936,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve void ObjectMgr::LoadGameobjectQuestStarters() { - LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter", true, true); + LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter"); for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr) { @@ -7963,7 +7950,7 @@ void ObjectMgr::LoadGameobjectQuestStarters() void ObjectMgr::LoadGameobjectQuestEnders() { - LoadQuestRelationsHelper(_goQuestInvolvedRelations, &_goQuestInvolvedRelationsReverse, "gameobject_questender", false, true); + LoadQuestRelationsHelper(_goQuestInvolvedRelations, &_goQuestInvolvedRelationsReverse, "gameobject_questender"); for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr) { @@ -7977,7 +7964,7 @@ void ObjectMgr::LoadGameobjectQuestEnders() void ObjectMgr::LoadCreatureQuestStarters() { - LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter", true, false); + LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter"); for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr) { @@ -7991,7 +7978,7 @@ void ObjectMgr::LoadCreatureQuestStarters() void ObjectMgr::LoadCreatureQuestEnders() { - LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, &_creatureQuestInvolvedRelationsReverse, "creature_questender", false, false); + LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, &_creatureQuestInvolvedRelationsReverse, "creature_questender"); for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr) { @@ -8003,6 +7990,17 @@ void ObjectMgr::LoadCreatureQuestEnders() } } +void QuestRelationResult::Iterator::_skip() +{ + while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second)) + ++_it; +} + +bool QuestRelationResult::HasQuest(uint32 questId) const +{ + return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId)); +} + void ObjectMgr::LoadReservedPlayersNames() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 692aa83e6e3..cd501c0eab3 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -36,6 +36,7 @@ #include "SharedDefines.h" #include "Trainer.h" #include "VehicleDefines.h" +#include #include #include @@ -550,7 +551,7 @@ typedef std::unordered_map GameObjectDataCo typedef std::unordered_map GameObjectAddonContainer; typedef std::unordered_map> GameObjectQuestItemMap; typedef std::unordered_map SpawnGroupDataContainer; -typedef std::multimap SpawnGroupLinkContainer; +typedef std::multimap SpawnGroupLinkContainer; typedef std::unordered_map> InstanceSpawnGroupContainer; typedef std::map> TempSummonDataContainer; typedef std::unordered_map CreatureLocaleContainer; @@ -593,8 +594,54 @@ typedef std::unordered_map TrinityStringContainer; typedef std::multimap QuestRelations; // unit/go -> quest typedef std::multimap QuestRelationsReverse; // quest -> unit/go -typedef std::pair QuestRelationBounds; -typedef std::pair QuestRelationReverseBounds; + +struct QuestRelationResult +{ + public: + struct Iterator + { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = QuestRelations::mapped_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = void; + + Iterator(QuestRelations::const_iterator it, QuestRelations::const_iterator end, bool onlyActive) + : _it(it), _end(end), _onlyActive(onlyActive) + { + skip(); + } + + bool operator==(Iterator const& other) const { return _it == other._it; } + bool operator!=(Iterator const& other) const { return _it != other._it; } + + Iterator& operator++() { ++_it; skip(); return *this; } + Iterator operator++(int) { Iterator t = *this; ++*this; return t; } + + value_type operator*() const { return _it->second; } + + private: + void skip() { if (_onlyActive) _skip(); } + void _skip(); + + QuestRelations::const_iterator _it, _end; + bool _onlyActive; + }; + + QuestRelationResult() : _onlyActive(false) {} + QuestRelationResult(std::pair range, bool onlyActive) + : _begin(range.first), _end(range.second), _onlyActive(onlyActive) {} + + Iterator begin() const { return { _begin, _end, _onlyActive }; } + Iterator end() const { return { _end, _end, _onlyActive }; } + + bool HasQuest(uint32 questId) const; + + private: + QuestRelations::const_iterator _begin, _end; + bool _onlyActive; +}; typedef std::multimap ExclusiveQuestGroups; // exclusiveGroupId -> quest typedef std::pair ExclusiveQuestGroupsBounds; @@ -1075,45 +1122,14 @@ class TC_GAME_API ObjectMgr void LoadCreatureQuestStarters(); void LoadCreatureQuestEnders(); - QuestRelations* GetGOQuestRelationMap() - { - return &_goQuestRelations; - } - - QuestRelationBounds GetGOQuestRelationBounds(uint32 go_entry) const - { - return _goQuestRelations.equal_range(go_entry); - } - - QuestRelationBounds GetGOQuestInvolvedRelationBounds(uint32 go_entry) const - { - return _goQuestInvolvedRelations.equal_range(go_entry); - } - - QuestRelationReverseBounds GetGOQuestInvolvedRelationReverseBounds(uint32 questId) const - { - return _goQuestInvolvedRelationsReverse.equal_range(questId); - } - - QuestRelations* GetCreatureQuestRelationMap() - { - return &_creatureQuestRelations; - } - - QuestRelationBounds GetCreatureQuestRelationBounds(uint32 creature_entry) const - { - return _creatureQuestRelations.equal_range(creature_entry); - } - - QuestRelationBounds GetCreatureQuestInvolvedRelationBounds(uint32 creature_entry) const - { - return _creatureQuestInvolvedRelations.equal_range(creature_entry); - } - - QuestRelationReverseBounds GetCreatureQuestInvolvedRelationReverseBounds(uint32 questId) const - { - return _creatureQuestInvolvedRelationsReverse.equal_range(questId); - } + QuestRelations* GetGOQuestRelationMapHACK() { return &_goQuestRelations; } + QuestRelationResult GetGOQuestRelations(uint32 entry) const { return GetQuestRelationsFrom(_goQuestRelations, entry, true); } + QuestRelationResult GetGOQuestInvolvedRelations(uint32 entry) const { return GetQuestRelationsFrom(_goQuestInvolvedRelations, entry, false); } + QuestRelations* GetCreatureQuestRelationMapHACK() { return &_creatureQuestRelations; } + QuestRelationResult GetCreatureQuestRelations(uint32 entry) const { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); } + QuestRelationResult GetCreatureQuestInvolvedRelations(uint32 entry) const { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); } + QuestRelationResult GetCreatureQuestInvolvedRelationsReverse(uint32 questId) const { return GetQuestRelationsReverseFrom(_creatureQuestInvolvedRelationsReverse, questId, false); } + QuestRelationResult GetGOQuestInvolvedRelationsReverse(uint32 questId) const { return GetQuestRelationsReverseFrom(_goQuestInvolvedRelationsReverse, questId, false); } ExclusiveQuestGroupsBounds GetExclusiveQuestGroupBounds(int32 exclusiveGroupId) const { @@ -1250,9 +1266,10 @@ class TC_GAME_API ObjectMgr ObjectGuid::LowType GenerateGameObjectSpawnId(); SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return it != _spawnGroupDataStore.end() ? &it->second : nullptr; } + SpawnGroupTemplateData const* GetSpawnGroupData(SpawnObjectType type, ObjectGuid::LowType spawnId) const { SpawnMetadata const* data = GetSpawnMetadata(type, spawnId); return data ? data->spawnGroupData : nullptr; } SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); } SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); } - Trinity::IteratorPair GetSpawnDataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); } + Trinity::IteratorPair GetSpawnMetadataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); } std::vector const* GetSpawnGroupsForInstance(uint32 instanceId) const { auto it = _instanceSpawnGroupStore.find(instanceId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; } MailLevelReward const* GetMailLevelReward(uint32 level, uint32 raceMask) const @@ -1304,29 +1321,42 @@ class TC_GAME_API ObjectMgr return nullptr; } - SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType guid) + SpawnMetadata const* GetSpawnMetadata(SpawnObjectType type, ObjectGuid::LowType spawnId) const { - if (type == SPAWN_TYPE_CREATURE) - return GetCreatureData(guid); - else if (type == SPAWN_TYPE_GAMEOBJECT) - return GetGameObjectData(guid); + if (SpawnData::TypeHasData(type)) + return GetSpawnData(type, spawnId); else - ASSERT(false, "Invalid spawn object type %u", uint32(type)); - return nullptr; + return nullptr; + } + + SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const + { + if (!SpawnData::TypeHasData(type)) + return nullptr; + switch (type) + { + case SPAWN_TYPE_CREATURE: + return GetCreatureData(spawnId); + case SPAWN_TYPE_GAMEOBJECT: + return GetGameObjectData(spawnId); + default: + 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 + CreatureData const* GetCreatureData(ObjectGuid::LowType spawnId) const { - CreatureDataContainer::const_iterator itr = _creatureDataStore.find(guid); + CreatureDataContainer::const_iterator itr = _creatureDataStore.find(spawnId); if (itr == _creatureDataStore.end()) return nullptr; return &itr->second; } - CreatureData& NewOrExistCreatureData(ObjectGuid::LowType guid) { return _creatureDataStore[guid]; } - void DeleteCreatureData(ObjectGuid::LowType guid); - ObjectGuid GetLinkedRespawnGuid(ObjectGuid guid) const + CreatureData& NewOrExistCreatureData(ObjectGuid::LowType spawnId) { return _creatureDataStore[spawnId]; } + void DeleteCreatureData(ObjectGuid::LowType spawnId); + ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId) const { - LinkedRespawnContainer::const_iterator itr = _linkedRespawnStore.find(guid); + LinkedRespawnContainer::const_iterator itr = _linkedRespawnStore.find(spawnId); if (itr == _linkedRespawnStore.end()) return ObjectGuid::Empty; return itr->second; } @@ -1337,14 +1367,14 @@ class TC_GAME_API ObjectMgr return &itr->second; } GameObjectDataContainer const& GetAllGameObjectData() const { return _gameObjectDataStore; } - GameObjectData const* GetGameObjectData(ObjectGuid::LowType guid) const + GameObjectData const* GetGameObjectData(ObjectGuid::LowType spawnId) const { - GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid); + GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(spawnId); if (itr == _gameObjectDataStore.end()) return nullptr; return &itr->second; } - GameObjectData& NewOrExistGameObjectData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; } - void DeleteGameObjectData(ObjectGuid::LowType guid); + GameObjectData& NewOrExistGameObjectData(ObjectGuid::LowType spawnId) { return _gameObjectDataStore[spawnId]; } + void DeleteGameObjectData(ObjectGuid::LowType spawnId); GameObjectLocale const* GetGameObjectLocale(uint32 entry) const { GameObjectLocaleContainer::const_iterator itr = _gameObjectLocaleStore.find(entry); @@ -1637,7 +1667,9 @@ class TC_GAME_API ObjectMgr private: void LoadScripts(ScriptsType type); - void LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go); + void LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table); + QuestRelationResult GetQuestRelationsFrom(QuestRelations const& map, uint32 key, bool onlyActive) const { return { map.equal_range(key), onlyActive }; } + QuestRelationResult GetQuestRelationsReverseFrom(QuestRelationsReverse const& map, uint32 key, bool onlyActive) const { return { map.equal_range(key), onlyActive }; } void PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count); MailLevelRewardContainer _mailLevelRewardStore; diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 9897fa0a2e0..d84bc03063b 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -120,94 +120,46 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager { for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) { + // Don't spawn at all if there's a respawn timer + ObjectGuid::LowType guid = *i_guid; + if (!map->ShouldBeSpawnedOnGridLoad(guid)) + continue; + T* obj = new T; - - // 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))) + //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid); + if (!obj->LoadFromDB(guid, map, false, false)) { - 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; + continue; + } + AddObjectHelper(cell, m, count, map, obj); } } template <> -void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager &m, uint32 &count, Map* map) +void LoadHelper(CellGuidSet const& guid_set, CellCoord& cell, GridRefManager& m, uint32& count, Map* map) { for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) { - uint32 guid = *i_guid; - GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); + // Don't spawn at all if there's a respawn timer + ObjectGuid::LowType guid = *i_guid; + if (!map->ShouldBeSpawnedOnGridLoad(guid)) + continue; GameObject* obj = nullptr; + GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); if (data && sObjectMgr->GetGameObjectTypeByEntry(data->id) == GAMEOBJECT_TYPE_TRANSPORT) obj = new Transport(); else obj = new GameObject(); - // Don't spawn at all if there's a respawn time - if (obj->GetTypeId() == TYPEID_GAMEOBJECT && !map->GetGORespawnTime(*i_guid)) + //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid); + if (!obj->LoadFromDB(guid, map, false, false)) { - ObjectGuid::LowType guid = *i_guid; - 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; - + continue; + } + AddObjectHelper(cell, m, count, map, obj); } } diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index f260821e412..5c62c632d6d 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -446,13 +446,11 @@ void WorldSession::HandleQuestNPCQuery(WorldPacket& recvData) continue; } - auto creatures = sObjectMgr->GetCreatureQuestInvolvedRelationReverseBounds(questId); - for (auto it = creatures.first; it != creatures.second; ++it) - quests[questId].push_back(it->second); + for (uint32 creatureId : sObjectMgr->GetCreatureQuestInvolvedRelationsReverse(questId)) + quests[questId].push_back(creatureId); - auto gos = sObjectMgr->GetGOQuestInvolvedRelationReverseBounds(questId); - for (auto it = gos.first; it != gos.second; ++it) - quests[questId].push_back(it->second | 0x80000000); // GO mask + for (uint32 goId : sObjectMgr->GetGOQuestInvolvedRelationsReverse(questId)) + quests[questId].push_back(goId | 0x80000000); // GO mask } WorldPacket data(SMSG_QUEST_NPC_QUERY_RESPONSE, 3 + quests.size() * 14); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 97bd0c11886..66a8a2b017a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3108,6 +3108,7 @@ bool Map::CheckRespawn(RespawnInfo* info) 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 @@ -3185,75 +3186,65 @@ bool Map::CheckRespawn(RespawnInfo* info) return true; } -void Map::DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId) +void Map::Respawn(RespawnInfo* info, CharacterDatabaseTransaction dbTrans) { - if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler - return; + info->respawnTime = GameTime::GetGameTime(); + _respawnTimes.increase(info->handle); + SaveRespawnInfoDB(*info, dbTrans); +} +size_t Map::DespawnAll(SpawnObjectType type, ObjectGuid::LowType spawnId) +{ + std::vector toUnload; switch (type) { case SPAWN_TYPE_CREATURE: - { - Creature* obj = new Creature(); - if (!obj->LoadFromDB(spawnId, this, true, true)) - delete obj; + for (auto const& pair : Trinity::Containers::MapEqualRange(GetCreatureBySpawnIdStore(), spawnId)) + toUnload.push_back(pair.second); break; - } case SPAWN_TYPE_GAMEOBJECT: - { - GameObject* obj = new GameObject(); - if (!obj->LoadFromDB(spawnId, this, true)) - delete obj; - break; - } + for (auto const& pair : Trinity::Containers::MapEqualRange(GetGameObjectBySpawnIdStore(), spawnId)) + toUnload.push_back(pair.second); default: - ASSERT(false, "Invalid spawn type %u (spawnid %u) on map %u", uint32(type), spawnId, GetId()); - } -} - -void Map::Respawn(RespawnInfo* info, CharacterDatabaseTransaction dbTrans) -{ - if (!CheckRespawn(info)) - { - if (info->respawnTime) - { - _respawnTimes.decrease(info->handle); - SaveRespawnInfoDB(*info, dbTrans); - } - else - DeleteRespawnInfo(info, dbTrans); - return; + break; } - // 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; - DeleteRespawnInfo(info, dbTrans); - DoRespawn(type, spawnId, gridId); + for (WorldObject* o : toUnload) + AddObjectToRemoveList(o); + + return toUnload.size(); } bool Map::AddRespawnInfo(RespawnInfo const& info) { - ASSERT(info.spawnId, "Attempt to schedule respawn with zero spawnid (type %u)", uint32(info.type)); + if (!info.spawnId) + { + TC_LOG_ERROR("maps", "Attempt to insert respawn info for zero spawn id (type %u)", uint32(info.type)); + return false; + } RespawnInfoMap& bySpawnIdMap = GetRespawnMapForType(info.type); - auto it = bySpawnIdMap.find(info.spawnId); - if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled + // check if we already have the maximum possible number of respawns scheduled + if (SpawnData::TypeHasData(info.type)) { - RespawnInfo* const existing = it->second; - if (info.respawnTime < existing->respawnTime) // delete existing in this case - DeleteRespawnInfo(existing); - else - return false; + auto it = bySpawnIdMap.find(info.spawnId); + if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled + { + RespawnInfo* const existing = it->second; + if (info.respawnTime <= existing->respawnTime) // delete existing in this case + DeleteRespawnInfo(existing); + else + return false; + } + ASSERT(bySpawnIdMap.find(info.spawnId) == bySpawnIdMap.end(), "Insertion of respawn info with id (%u,%u) into spawn id map failed - state desync.", uint32(info.type), info.spawnId); } + else + ASSERT(false, "Invalid respawn info for spawn id (%u,%u) being inserted", uint32(info.type), info.spawnId); - // 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); + 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); + bySpawnIdMap.emplace(ri->spawnId, ri); return true; } @@ -3296,24 +3287,59 @@ void Map::DeleteRespawnInfo(RespawnInfo* info, CharacterDatabaseTransaction dbTr 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)); + auto& spawnMap = GetRespawnMapForType(info->type); + auto range = spawnMap.equal_range(info->spawnId); + auto it = std::find_if(range.first, range.second, [info](RespawnInfoMap::value_type const& pair) { return (pair.second == info); }); + ASSERT(it != range.second, "Respawn stores inconsistent for map %u, spawnid %u (type %u)", GetId(), info->spawnId, uint32(info->type)); + spawnMap.erase(it); // respawn heap _respawnTimes.erase(info->handle); // database - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESPAWN); - stmt->setUInt16(0, info->type); - stmt->setUInt32(1, info->spawnId); - stmt->setUInt16(2, GetId()); - stmt->setUInt32(3, GetInstanceId()); - CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); + DeleteRespawnInfoFromDB(info->type, info->spawnId, dbTrans); // then cleanup the object delete info; } +void Map::DeleteRespawnInfoFromDB(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans) +{ + // database + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESPAWN); + stmt->setUInt16(0, type); + stmt->setUInt32(1, spawnId); + stmt->setUInt16(2, GetId()); + stmt->setUInt32(3, GetInstanceId()); + CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); +} + +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::ProcessRespawns() { time_t now = GameTime::GetGameTime(); @@ -3366,8 +3392,11 @@ void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::Low return; } - SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId); - if (!data || !data->spawnGroupData || !(data->spawnGroupData->flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE)) + SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(type, spawnId); + if (!data) + return; + + if (!(data->spawnGroupData->flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE)) return; auto it = _zonePlayerCountMap.find(obj->GetZoneId()); @@ -3386,6 +3415,24 @@ void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::Low respawnDelay = std::max(ceil(respawnDelay * adjustFactor), timeMinimum); } +bool Map::ShouldBeSpawnedOnGridLoad(SpawnObjectType type, ObjectGuid::LowType spawnId) const +{ + ASSERT(SpawnData::TypeHasData(type)); + + // check if the object is on its respawn timer + if (GetRespawnTime(type, spawnId)) + return false; + + SpawnMetadata const* spawnData = ASSERT_NOTNULL(sObjectMgr->GetSpawnMetadata(type, spawnId)); + // check if the object is part of a spawn group + SpawnGroupTemplateData const* spawnGroup = ASSERT_NOTNULL(spawnData->spawnGroupData); + if (!(spawnGroup->flags & SPAWNGROUP_FLAG_SYSTEM)) + if (!IsSpawnGroupActive(spawnGroup->groupId)) + return false; + + return true; +} + SpawnGroupTemplateData const* Map::GetSpawnGroupData(uint32 groupId) const { SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId); @@ -3404,31 +3451,40 @@ bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::v } SetSpawnGroupActive(groupId, true); // start processing respawns for the group - 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) + std::vector toSpawn; + for (auto& pair : sObjectMgr->GetSpawnMetadataForGroup(groupId)) + { + SpawnMetadata const* data = pair.second; + ASSERT(groupData->mapId == data->mapId); + + if (force || ignoreRespawn) + RemoveRespawnTime(data->type, data->spawnId); + + uint32 nRespawnTimers = GetRespawnMapForType(data->type).count(data->spawnId); + if (SpawnData::TypeHasData(data->type)) { - if (!force && !ignoreRespawn && (respawnTime > GameTime::GetGameTime())) + // has a respawn timer + if (nRespawnTimers) continue; - // we need to remove the respawn time, otherwise we'd end up double spawning - RemoveRespawnTime(data->type, data->spawnId); - } + // has a spawn already active + if (!force) + if (WorldObject* obj = GetWorldObjectBySpawnId(data->type, data->spawnId)) + if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive()) + continue; + toSpawn.push_back(ASSERT_NOTNULL(data->ToSpawnData())); + } + } + + for (SpawnData const* data : toSpawn) + { // 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 + // now do the actual (re)spawn switch (data->type) { case SPAWN_TYPE_CREATURE: @@ -3466,41 +3522,16 @@ bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes, size_t* cou return false; } - std::vector toUnload; // unload after iterating, otherwise iterator invalidation - for (auto const& pair : sObjectMgr->GetSpawnDataForGroup(groupId)) + for (auto const& pair : sObjectMgr->GetSpawnMetadataForGroup(groupId)) { - SpawnData const* data = pair.second; - ASSERT(groupData->mapId == data->spawnPoint.GetMapId()); + SpawnMetadata const* data = pair.second; + ASSERT(groupData->mapId == data->mapId); 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; - } + size_t c = DespawnAll(data->type, data->spawnId); + if (count) + *count += c; } - - 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; } @@ -4441,19 +4472,23 @@ void Map::UpdateIteratorBack(Player* player) void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, CharacterDatabaseTransaction dbTrans, bool startup) { - if (!spawnId) + SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(type, spawnId); + if (!data) + { + TC_LOG_ERROR("maps", "Map %u attempt to save respawn time for nonexistant spawnid (%u,%u).", GetId(), type, spawnId); return; + } if (!respawnTime) { // Delete only - RemoveRespawnTime(type, spawnId, dbTrans); + RemoveRespawnTime(data->type, data->spawnId, dbTrans); return; } RespawnInfo ri; - ri.type = type; - ri.spawnId = spawnId; + ri.type = data->type; + ri.spawnId = data->spawnId; ri.entry = entry; ri.respawnTime = respawnTime; ri.gridId = gridId; @@ -4493,7 +4528,7 @@ void Map::LoadRespawnTimes() ObjectGuid::LowType spawnId = fields[1].GetUInt32(); uint64 respawnTime = fields[2].GetUInt64(); - if (type < SPAWN_TYPE_MAX) + if (SpawnData::TypeHasData(type)) { if (SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId)) SaveRespawnTime(type, spawnId, data->id, time_t(respawnTime), Trinity::ComputeGridCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()).GetId(), nullptr, true); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 686f6e1e025..9568db9f043 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -624,6 +624,7 @@ class TC_GAME_API Map : public GridRefManager void SaveRespawnInfoDB(RespawnInfo const& info, CharacterDatabaseTransaction dbTrans = nullptr); void LoadRespawnTimes(); void DeleteRespawnTimes() { UnloadAllRespawnInfos(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); } + static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId); void LoadCorpseData(); void DeleteCorpseData(); @@ -632,8 +633,6 @@ class TC_GAME_API Map : public GridRefManager Corpse* ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia = false); void RemoveOldCorpses(); - static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId); - void SendInitTransports(Player* player); void SendRemoveTransports(Player* player); void SendUpdateTransportVisibility(Player* player); @@ -802,10 +801,10 @@ class TC_GAME_API Map : public GridRefManager // 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, CharacterDatabaseTransaction dbTrans = nullptr); bool AddRespawnInfo(RespawnInfo const& info); void UnloadAllRespawnInfos(); void DeleteRespawnInfo(RespawnInfo* info, CharacterDatabaseTransaction dbTrans = nullptr); + void DeleteRespawnInfoFromDB(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans = nullptr); public: void GetRespawnInfo(std::vector& respawnData, SpawnObjectTypeMask types) const; @@ -815,11 +814,19 @@ class TC_GAME_API Map : public GridRefManager if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) Respawn(info, dbTrans); } - void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans = nullptr) + void Respawn(RespawnInfo* info, CharacterDatabaseTransaction dbTrans = nullptr); + void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans = nullptr, bool alwaysDeleteFromDB = false) { if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) DeleteRespawnInfo(info, dbTrans); + // Some callers might need to make sure the database doesn't contain any respawn time + else if (alwaysDeleteFromDB) + DeleteRespawnInfoFromDB(type, spawnId, dbTrans); } + size_t DespawnAll(SpawnObjectType type, ObjectGuid::LowType spawnId); + + bool ShouldBeSpawnedOnGridLoad(SpawnObjectType type, ObjectGuid::LowType spawnId) const; + template bool ShouldBeSpawnedOnGridLoad(ObjectGuid::LowType spawnId) const { return ShouldBeSpawnedOnGridLoad(SpawnData::TypeFor, spawnId); } SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const; diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 07a680079d4..e3649d79c33 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -103,9 +103,14 @@ class TC_GAME_API MapManager return IsValidMAP(mapid, false) && Trinity::IsValidMapCoord(x, y, z, o); } + static bool IsValidMapCoord(uint32 mapid, Position const& pos) + { + return IsValidMapCoord(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + } + static bool IsValidMapCoord(WorldLocation const& loc) { - return IsValidMapCoord(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation()); + return IsValidMapCoord(loc.GetMapId(), loc); } void DoDelayedMovesAndRemoves(); diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h index a96ff643437..1c0ef20d320 100644 --- a/src/server/game/Maps/SpawnData.h +++ b/src/server/game/Maps/SpawnData.h @@ -21,12 +21,17 @@ #include "Position.h" #include "PhaseShift.h" +class Creature; +class GameObject; +class Pool; +struct PoolTemplate; + enum SpawnObjectType { SPAWN_TYPE_CREATURE = 0, SPAWN_TYPE_GAMEOBJECT = 1, - - SPAWN_TYPE_MAX + NUM_SPAWN_TYPES_WITH_DATA, // SKIP + NUM_SPAWN_TYPES = NUM_SPAWN_TYPES_WITH_DATA // SKIP }; enum SpawnObjectTypeMask @@ -34,7 +39,8 @@ enum SpawnObjectTypeMask SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE), SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT), - SPAWN_TYPEMASK_ALL = (1 << SPAWN_TYPE_MAX)-1 + SPAWN_TYPEMASK_WITH_DATA = (1 << NUM_SPAWN_TYPES_WITH_DATA) - 1, + SPAWN_TYPEMASK_ALL = (1 << NUM_SPAWN_TYPES) - 1 }; enum SpawnGroupFlags @@ -57,24 +63,48 @@ struct SpawnGroupTemplateData SpawnGroupFlags flags; }; -struct SpawnData +namespace Trinity { namespace Impl { + template + struct SpawnObjectTypeForImpl { static_assert(!std::is_same::value, "This type does not have an associated spawn type!"); }; + template <> struct SpawnObjectTypeForImpl { static constexpr SpawnObjectType value = SPAWN_TYPE_CREATURE; }; + template <> struct SpawnObjectTypeForImpl { static constexpr SpawnObjectType value = SPAWN_TYPE_GAMEOBJECT; }; +}} + +struct SpawnData; +struct SpawnMetadata { + static constexpr bool TypeInMask(SpawnObjectType type, SpawnObjectTypeMask mask) { return ((1 << type) & mask); } + static constexpr bool TypeHasData(SpawnObjectType type) { return (type < NUM_SPAWN_TYPES_WITH_DATA); } + static constexpr bool TypeIsValid(SpawnObjectType type) { return (type < NUM_SPAWN_TYPES); } + template + static constexpr SpawnObjectType TypeFor = Trinity::Impl::SpawnObjectTypeForImpl::value; + + SpawnData const* ToSpawnData() const { return TypeHasData(type) ? reinterpret_cast(this) : nullptr; } + SpawnObjectType const type; uint32 spawnId = 0; + uint32 mapId = MAPID_INVALID; + bool dbData = true; + SpawnGroupTemplateData const* spawnGroupData = nullptr; + + protected: + SpawnMetadata(SpawnObjectType t) : type(t) {} +}; + +struct SpawnData : public SpawnMetadata +{ uint32 id = 0; // entry in respective _template table - WorldLocation spawnPoint; + Position spawnPoint; int32 spawntimesecs = 0; uint8 spawnMask = 0; - SpawnGroupTemplateData const* spawnGroupData = nullptr; uint8 phaseUseFlags = 0; uint32 phaseId = DEFAULT_PHASE; uint32 phaseGroup = 0; int32 terrainSwapMap = -1; uint32 scriptId = 0; - bool dbData = true; protected: - SpawnData(SpawnObjectType t) : type(t) {} + SpawnData(SpawnObjectType t) : SpawnMetadata(t) {} }; #endif diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp index 3c87c015fdb..e174f58507b 100644 --- a/src/server/game/Pools/PoolMgr.cpp +++ b/src/server/game/Pools/PoolMgr.cpp @@ -54,13 +54,6 @@ TC_GAME_API bool ActivePoolData::IsActiveObject(uint32 sub_pool_id) const return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end(); } -// Method that tell if a quest can be started -template<> -TC_GAME_API bool ActivePoolData::IsActiveObject(uint32 quest_id) const -{ - return mActiveQuests.find(quest_id) != mActiveQuests.end(); -} - template<> void ActivePoolData::ActivateObject(uint32 db_guid, uint32 pool_id) { @@ -82,13 +75,6 @@ void ActivePoolData::ActivateObject(uint32 sub_pool_id, uint32 pool_id) ++mSpawnedPools[pool_id]; } -template<> -void ActivePoolData::ActivateObject(uint32 quest_id, uint32 pool_id) -{ - mActiveQuests.insert(quest_id); - ++mSpawnedPools[pool_id]; -} - template<> void ActivePoolData::RemoveObject(uint32 db_guid, uint32 pool_id) { @@ -116,15 +102,6 @@ void ActivePoolData::RemoveObject(uint32 sub_pool_id, uint32 pool_id) --val; } -template<> -void ActivePoolData::RemoveObject(uint32 quest_id, uint32 pool_id) -{ - mActiveQuests.erase(quest_id); - uint32& val = mSpawnedPools[pool_id]; - if (val > 0) - --val; -} - //////////////////////////////////////////////////////////// // Methods of template class PoolGroup @@ -157,7 +134,7 @@ bool PoolGroup::CheckPool() const // If no guid is passed, the pool is just removed (event end case) // If guid is filled, cache will be used and no removal will occur, it just fill the cache template -void PoolGroup::DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid) +void PoolGroup::DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime) { for (size_t i=0; i < EqualChanced.size(); ++i) { @@ -166,10 +143,12 @@ void PoolGroup::DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType gui { if (!guid || EqualChanced[i].guid == guid) { - Despawn1Object(EqualChanced[i].guid); + Despawn1Object(EqualChanced[i].guid, alwaysDeleteRespawnTime); spawns.RemoveObject(EqualChanced[i].guid, poolId); } } + else if (alwaysDeleteRespawnTime) + RemoveRespawnTimeFromDB(EqualChanced[i].guid); } for (size_t i = 0; i < ExplicitlyChanced.size(); ++i) @@ -179,22 +158,24 @@ void PoolGroup::DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType gui { if (!guid || ExplicitlyChanced[i].guid == guid) { - Despawn1Object(ExplicitlyChanced[i].guid); + Despawn1Object(ExplicitlyChanced[i].guid, alwaysDeleteRespawnTime); spawns.RemoveObject(ExplicitlyChanced[i].guid, poolId); } } + else if (alwaysDeleteRespawnTime) + RemoveRespawnTimeFromDB(ExplicitlyChanced[i].guid); } } // Method that is actualy doing the removal job on one creature template<> -void PoolGroup::Despawn1Object(ObjectGuid::LowType guid) +void PoolGroup::Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime) { if (CreatureData const* data = sObjectMgr->GetCreatureData(guid)) { sObjectMgr->RemoveCreatureFromGrid(guid, data); - Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); if (!map->Instanceable()) { auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid); @@ -207,19 +188,21 @@ void PoolGroup::Despawn1Object(ObjectGuid::LowType guid) creature->SaveRespawnTime(); creature->AddObjectToRemoveList(); } + if (alwaysDeleteRespawnTime) + map->RemoveRespawnTime(SpawnObjectType::SPAWN_TYPE_CREATURE, guid, nullptr, true); } } } // Same on one gameobject template<> -void PoolGroup::Despawn1Object(ObjectGuid::LowType guid) +void PoolGroup::Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime) { if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid)) { sObjectMgr->RemoveGameobjectFromGrid(guid, data); - Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); if (!map->Instanceable()) { auto gameObjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid); @@ -233,58 +216,17 @@ void PoolGroup::Despawn1Object(ObjectGuid::LowType guid) go->SaveRespawnTime(); go->AddObjectToRemoveList(); } + if (alwaysDeleteRespawnTime) + map->RemoveRespawnTime(SpawnObjectType::SPAWN_TYPE_GAMEOBJECT, guid, nullptr, true); } } } // Same on one pool template<> -void PoolGroup::Despawn1Object(uint32 child_pool_id) +void PoolGroup::Despawn1Object(uint32 child_pool_id, bool alwaysDeleteRespawnTime) { - sPoolMgr->DespawnPool(child_pool_id); -} - -// Same on one quest -template<> -void PoolGroup::Despawn1Object(uint32 quest_id) -{ - // Creatures - QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap(); - PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(quest_id); - for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr) - { - QuestRelations::iterator qitr = questMap->find(itr->second); - if (qitr == questMap->end()) - continue; - QuestRelations::iterator lastElement = questMap->upper_bound(itr->second); - for (; qitr != lastElement; ++qitr) - { - if (qitr->first == itr->second && qitr->second == itr->first) - { - questMap->erase(qitr); // iterator is now no more valid - break; // but we can exit loop since the element is found - } - } - } - - // Gameobjects - questMap = sObjectMgr->GetGOQuestRelationMap(); - qr = sPoolMgr->mQuestGORelation.equal_range(quest_id); - for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr) - { - QuestRelations::iterator qitr = questMap->find(itr->second); - if (qitr == questMap->end()) - continue; - QuestRelations::iterator lastElement = questMap->upper_bound(itr->second); - for (; qitr != lastElement; ++qitr) - { - if (qitr->first == itr->second && qitr->second == itr->first) - { - questMap->erase(qitr); // iterator is now no more valid - break; // but we can exit loop since the element is found - } - } - } + sPoolMgr->DespawnPool(child_pool_id, alwaysDeleteRespawnTime); } // Method for a pool only to remove any found record causing a circular dependency loop @@ -335,7 +277,7 @@ void PoolGroup::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig roll -= obj.chance; // Triggering object is marked as spawned at this time and can be also rolled (respawn case) // so this need explicit check for this case - if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsActiveObject(obj.guid))) + if (roll < 0 && (/*obj.guid == triggerFrom ||*/ !spawns.IsActiveObject(obj.guid))) { rolledObjects.push_back(obj); break; @@ -345,9 +287,9 @@ void PoolGroup::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig if (!EqualChanced.empty() && rolledObjects.empty()) { - std::copy_if(EqualChanced.begin(), EqualChanced.end(), std::back_inserter(rolledObjects), [triggerFrom, &spawns](PoolObject const& object) + std::copy_if(EqualChanced.begin(), EqualChanced.end(), std::back_inserter(rolledObjects), [/*triggerFrom, */&spawns](PoolObject const& object) { - return object.guid == triggerFrom || !spawns.IsActiveObject(object.guid); + return /*object.guid == triggerFrom ||*/ !spawns.IsActiveObject(object.guid); }); Trinity::Containers::RandomResize(rolledObjects, count); @@ -382,7 +324,7 @@ void PoolGroup::Spawn1Object(PoolObject* obj) sObjectMgr->AddCreatureToGrid(obj->guid, data); // Spawn if necessary (loaded grids only) - Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { @@ -406,7 +348,7 @@ void PoolGroup::Spawn1Object(PoolObject* obj) 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->spawnPoint.GetMapId()); + Map* map = sMapMgr->CreateBaseMap(data->mapId); // We use current coords to unspawn, not spawn coords since creature can have changed grid if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { @@ -437,100 +379,6 @@ void PoolGroup::Spawn1Object(PoolObject* obj) sPoolMgr->SpawnPool(obj->guid); } -// Same for 1 quest -template<> -void PoolGroup::Spawn1Object(PoolObject* obj) -{ - // Creatures - QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap(); - PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(obj->guid); - for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr) - { - TC_LOG_DEBUG("pool", "PoolGroup: Adding quest %u to creature %u", itr->first, itr->second); - questMap->insert(QuestRelations::value_type(itr->second, itr->first)); - } - - // Gameobjects - questMap = sObjectMgr->GetGOQuestRelationMap(); - qr = sPoolMgr->mQuestGORelation.equal_range(obj->guid); - for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr) - { - TC_LOG_DEBUG("pool", "PoolGroup: Adding quest %u to GO %u", itr->first, itr->second); - questMap->insert(QuestRelations::value_type(itr->second, itr->first)); - } -} - -template <> -void PoolGroup::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom) -{ - TC_LOG_DEBUG("pool", "PoolGroup: Spawning pool %u", poolId); - // load state from db - if (!triggerFrom) - { - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_POOL_QUEST_SAVE); - - stmt->setUInt32(0, poolId); - - PreparedQueryResult result = CharacterDatabase.Query(stmt); - - if (result) - { - do - { - uint32 questId = result->Fetch()[0].GetUInt32(); - spawns.ActivateObject(questId, poolId); - PoolObject tempObj(questId, 0.0f); - Spawn1Object(&tempObj); - --limit; - } while (result->NextRow() && limit); - return; - } - } - - ActivePoolObjects currentQuests = spawns.GetActiveQuests(); - ActivePoolObjects newQuests; - - // always try to select different quests - for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr) - { - if (spawns.IsActiveObject(itr->guid)) - continue; - newQuests.insert(itr->guid); - } - - // clear the pool - DespawnObject(spawns); - - // recycle minimal amount of quests if possible count is lower than limit - if (limit > newQuests.size() && !currentQuests.empty()) - { - do - { - uint32 questId = Trinity::Containers::SelectRandomContainerElement(currentQuests); - newQuests.insert(questId); - currentQuests.erase(questId); - } while (newQuests.size() < limit && !currentQuests.empty()); // failsafe - } - - if (newQuests.empty()) - return; - - // activate random quests - do - { - uint32 questId = Trinity::Containers::SelectRandomContainerElement(newQuests); - spawns.ActivateObject(questId, poolId); - PoolObject tempObj(questId, 0.0f); - Spawn1Object(&tempObj); - newQuests.erase(questId); - --limit; - } while (limit && !newQuests.empty()); - - // if we are here it means the pool is initialized at startup and did not have previous saved state - if (!triggerFrom) - sPoolMgr->SaveQuestsToDB(); -} - // Method that does the respawn job on the specified creature template <> void PoolGroup::ReSpawn1Object(PoolObject* /*obj*/) @@ -549,9 +397,34 @@ void PoolGroup::ReSpawn1Object(PoolObject* /*obj*/) template <> void PoolGroup::ReSpawn1Object(PoolObject* /*obj*/) { } -// Nothing to do for a quest template <> -void PoolGroup::ReSpawn1Object(PoolObject* /*obj*/) { } +void PoolGroup::RemoveRespawnTimeFromDB(ObjectGuid::LowType guid) +{ + if (CreatureData const* data = sObjectMgr->GetCreatureData(guid)) + { + Map* map = sMapMgr->CreateBaseMap(data->mapId); + if (!map->Instanceable()) + { + map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, guid, nullptr, true); + } + } +} + +template <> +void PoolGroup::RemoveRespawnTimeFromDB(ObjectGuid::LowType guid) +{ + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid)) + { + Map* map = sMapMgr->CreateBaseMap(data->mapId); + if (!map->Instanceable()) + { + map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, guid, nullptr, true); + } + } +} + +template <> +void PoolGroup::RemoveRespawnTimeFromDB(ObjectGuid::LowType /*guid*/) { } //////////////////////////////////////////////////////////// // Methods of class PoolMgr @@ -560,7 +433,6 @@ PoolMgr::PoolMgr() { } void PoolMgr::Initialize() { - mQuestSearchMap.clear(); mGameobjectSearchMap.clear(); mCreatureSearchMap.clear(); } @@ -608,8 +480,8 @@ void PoolMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - // 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); + // 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 0"); if (!result) { @@ -665,8 +537,8 @@ void PoolMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - // 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); + // 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 1"); if (!result) { @@ -734,8 +606,8 @@ void PoolMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - // 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); + // 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 2"); if (!result) { @@ -819,103 +691,14 @@ void PoolMgr::LoadFromDB() } } - TC_LOG_INFO("server.loading", "Loading Quest Pooling Data..."); - { - uint32 oldMSTime = getMSTime(); - - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_QUEST_POOLS); - PreparedQueryResult result = WorldDatabase.Query(stmt); - - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 quests in pools"); - } - else - { - PooledQuestRelationBounds creBounds; - PooledQuestRelationBounds goBounds; - - enum eQuestTypes - { - QUEST_NONE = 0, - QUEST_DAILY = 1, - QUEST_WEEKLY = 2 - }; - - std::map poolTypeMap; - uint32 count = 0; - do - { - Field* fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - uint32 pool_id = fields[1].GetUInt32(); - - Quest const* quest = sObjectMgr->GetQuestTemplate(entry); - if (!quest) - { - TC_LOG_ERROR("sql.sql", "`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id); - continue; - } - - auto it = mPoolTemplate.find(pool_id); - if (it == mPoolTemplate.end()) - { - TC_LOG_ERROR("sql.sql", "`pool_quest` pool id (%u) is not in `pool_template`, skipped.", pool_id); - continue; - } - - if (!quest->IsDailyOrWeekly()) - { - TC_LOG_ERROR("sql.sql", "`pool_quest` has an quest (%u) which is not daily or weekly in pool id (%u), use ExclusiveGroup instead, skipped.", entry, pool_id); - continue; - } - - if (poolTypeMap[pool_id] == QUEST_NONE) - poolTypeMap[pool_id] = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; - - int32 currType = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; - - if (poolTypeMap[pool_id] != currType) - { - TC_LOG_ERROR("sql.sql", "`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.", - entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY"); - continue; - } - - creBounds = mQuestCreatureRelation.equal_range(entry); - goBounds = mQuestGORelation.equal_range(entry); - - if (creBounds.first == creBounds.second && goBounds.first == goBounds.second) - { - TC_LOG_ERROR("sql.sql", "`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id); - continue; - } - - PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; - PoolObject plObject = PoolObject(entry, 0.0f); - PoolGroup& questgroup = mPoolQuestGroups[pool_id]; - questgroup.SetPoolId(pool_id); - questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit); - SearchPair p(entry, pool_id); - mQuestSearchMap.insert(p); - - ++count; - } - while (result->NextRow()); - - TC_LOG_INFO("server.loading", ">> Loaded %u quests in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - } - } - // The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks TC_LOG_INFO("server.loading", "Starting objects pooling system..."); { uint32 oldMSTime = getMSTime(); - QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_pool.pool_id, pool_pool.mother_pool FROM pool_template" - " LEFT JOIN game_event_pool ON pool_template.entry=game_event_pool.pool_entry" - " LEFT JOIN pool_pool ON pool_template.entry=pool_pool.pool_id WHERE game_event_pool.pool_entry IS NULL"); + QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_members.spawnId, pool_members.poolSpawnId FROM pool_template" + " LEFT JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry" + " LEFT JOIN pool_members ON pool_members.type = 2 AND pool_template.entry = pool_members.spawnId WHERE game_event_pool.pool_entry IS NULL"); if (!result) { @@ -956,69 +739,6 @@ void PoolMgr::LoadFromDB() } } -void PoolMgr::LoadQuestPools() -{ -} - -void PoolMgr::SaveQuestsToDB() -{ - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - - for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr) - { - if (itr->second.isEmpty()) - continue; - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_POOL_SAVE); - stmt->setUInt32(0, itr->second.GetPoolId()); - trans->Append(stmt); - } - - for (SearchMap::iterator itr = mQuestSearchMap.begin(); itr != mQuestSearchMap.end(); ++itr) - { - if (IsSpawnedObject(itr->first)) - { - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_POOL_SAVE); - stmt->setUInt32(0, itr->second); - stmt->setUInt32(1, itr->first); - trans->Append(stmt); - } - } - - CharacterDatabase.CommitTransaction(trans); -} - -void PoolMgr::ChangeDailyQuests() -{ - for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr) - { - if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->second.GetFirstEqualChancedObjectId())) - { - if (quest->IsWeekly()) - continue; - - UpdatePool(itr->second.GetPoolId(), 1); // anything non-zero means don't load from db - } - } - - SaveQuestsToDB(); -} - -void PoolMgr::ChangeWeeklyQuests() -{ - for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr) - { - if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->second.GetFirstEqualChancedObjectId())) - { - if (quest->IsDaily()) - continue; - - UpdatePool(itr->second.GetPoolId(), 1); - } - } - - SaveQuestsToDB(); -} - // Call to spawn a pool, if cache if true the method will spawn only if cached entry is different // If it's same, the creature is respawned only (added back to map) template<> @@ -1049,45 +769,30 @@ void PoolMgr::SpawnPool(uint32 pool_id, uint32 sub_pool_id) it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id); } -// Call to spawn a pool -template<> -void PoolMgr::SpawnPool(uint32 pool_id, uint32 quest_id) -{ - auto it = mPoolQuestGroups.find(pool_id); - if (it != mPoolQuestGroups.end() && !it->second.isEmpty()) - it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, quest_id); -} - void PoolMgr::SpawnPool(uint32 pool_id) { SpawnPool(pool_id, 0); SpawnPool(pool_id, 0); SpawnPool(pool_id, 0); - SpawnPool(pool_id, 0); } // Call to despawn a pool, all gameobjects/creatures in this pool are removed -void PoolMgr::DespawnPool(uint32 pool_id) +void PoolMgr::DespawnPool(uint32 pool_id, bool alwaysDeleteRespawnTime) { { auto it = mPoolCreatureGroups.find(pool_id); if (it != mPoolCreatureGroups.end() && !it->second.isEmpty()) - it->second.DespawnObject(mSpawnedData); + it->second.DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); } { auto it = mPoolGameobjectGroups.find(pool_id); if (it != mPoolGameobjectGroups.end() && !it->second.isEmpty()) - it->second.DespawnObject(mSpawnedData); + it->second.DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); } { auto it = mPoolPoolGroups.find(pool_id); if (it != mPoolPoolGroups.end() && !it->second.isEmpty()) - it->second.DespawnObject(mSpawnedData); - } - { - auto it = mPoolQuestGroups.find(pool_id); - if (it != mPoolQuestGroups.end() && !it->second.isEmpty()) - it->second.DespawnObject(mSpawnedData); + it->second.DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); } } @@ -1124,11 +829,6 @@ bool PoolMgr::CheckPool(uint32 pool_id) const if (it != mPoolPoolGroups.end() && !it->second.CheckPool()) return false; } - { - auto it = mPoolQuestGroups.find(pool_id); - if (it != mPoolQuestGroups.end() && !it->second.CheckPool()) - return false; - } return true; } @@ -1147,4 +847,3 @@ void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id) template void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id); template void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id); template void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id); -template void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id); diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h index ce9958f58df..d10294787be 100644 --- a/src/server/game/Pools/PoolMgr.h +++ b/src/server/game/Pools/PoolMgr.h @@ -22,7 +22,6 @@ #include "Creature.h" #include "GameObject.h" #include "SpawnData.h" -#include "QuestDef.h" struct PoolTemplateData { @@ -56,12 +55,9 @@ class TC_GAME_API ActivePoolData template void RemoveObject(uint32 db_guid_or_pool_id, uint32 pool_id); - - ActivePoolObjects GetActiveQuests() const { return mActiveQuests; } // a copy of the set private: ActivePoolObjects mSpawnedCreatures; ActivePoolObjects mSpawnedGameobjects; - ActivePoolObjects mActiveQuests; ActivePoolPools mSpawnedPools; }; @@ -76,9 +72,10 @@ class TC_GAME_API PoolGroup bool isEmpty() const { return ExplicitlyChanced.empty() && EqualChanced.empty(); } void AddEntry(PoolObject& poolitem, uint32 maxentries); bool CheckPool() const; - void DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid=0); - void Despawn1Object(ObjectGuid::LowType guid); + void DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid = 0, bool alwaysDeleteRespawnTime = false); + void Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime = false); void SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom); + void RemoveRespawnTimeFromDB(ObjectGuid::LowType guid); void Spawn1Object(PoolObject* obj); void ReSpawn1Object(PoolObject* obj); @@ -96,10 +93,6 @@ class TC_GAME_API PoolGroup PoolObjectList EqualChanced; }; -typedef std::multimap PooledQuestRelation; -typedef std::pair PooledQuestRelationBounds; -typedef std::pair PooledQuestRelationBoundsNC; - class TC_GAME_API PoolMgr { private: @@ -110,8 +103,6 @@ class TC_GAME_API PoolMgr static PoolMgr* instance(); void LoadFromDB(); - void LoadQuestPools(); - void SaveQuestsToDB(); void Initialize(); @@ -125,17 +116,11 @@ class TC_GAME_API PoolMgr bool CheckPool(uint32 pool_id) const; void SpawnPool(uint32 pool_id); - void DespawnPool(uint32 pool_id); + void DespawnPool(uint32 pool_id, bool alwaysDeleteRespawnTime = false); template void UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id); - void ChangeDailyQuests(); - void ChangeWeeklyQuests(); - - PooledQuestRelation mQuestCreatureRelation; - PooledQuestRelation mQuestGORelation; - private: template void SpawnPool(uint32 pool_id, uint32 db_guid_or_pool_id); @@ -144,7 +129,6 @@ class TC_GAME_API PoolMgr typedef std::unordered_map> PoolGroupCreatureMap; typedef std::unordered_map> PoolGroupGameObjectMap; typedef std::unordered_map> PoolGroupPoolMap; - typedef std::unordered_map> PoolGroupQuestMap; typedef std::pair SearchPair; typedef std::map SearchMap; @@ -152,11 +136,9 @@ class TC_GAME_API PoolMgr PoolGroupCreatureMap mPoolCreatureGroups; PoolGroupGameObjectMap mPoolGameobjectGroups; PoolGroupPoolMap mPoolPoolGroups; - PoolGroupQuestMap mPoolQuestGroups; SearchMap mCreatureSearchMap; SearchMap mGameobjectSearchMap; SearchMap mPoolSearchMap; - SearchMap mQuestSearchMap; // dynamic data ActivePoolData mSpawnedData; @@ -186,17 +168,6 @@ inline uint32 PoolMgr::IsPartOfAPool(uint32 db_guid) const return 0; } -// Method that tell if the quest is part of another pool and return the pool id if yes -template<> -inline uint32 PoolMgr::IsPartOfAPool(uint32 pool_id) const -{ - SearchMap::const_iterator itr = mQuestSearchMap.find(pool_id); - if (itr != mQuestSearchMap.end()) - return itr->second; - - return 0; -} - // Method that tell if the pool is part of another pool and return the pool id if yes template<> inline uint32 PoolMgr::IsPartOfAPool(uint32 pool_id) const diff --git a/src/server/game/Pools/QuestPools.cpp b/src/server/game/Pools/QuestPools.cpp new file mode 100644 index 00000000000..f6e3bb121d2 --- /dev/null +++ b/src/server/game/Pools/QuestPools.cpp @@ -0,0 +1,296 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "QuestPools.h" +#include "Containers.h" +#include "DatabaseEnv.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "QuestDef.h" +#include "Timer.h" +#include "Transaction.h" +#include +#include + +/*static*/ QuestPoolMgr* QuestPoolMgr::instance() +{ + static QuestPoolMgr instance; + return &instance; +} + +static void RegeneratePool(QuestPool& pool) +{ + ASSERT(!pool.members.empty(), "Quest pool %u is empty", pool.poolId); + uint32 const n = pool.members.size()-1; + ASSERT(pool.numActive <= pool.members.size(), "Quest Pool %u requests %u spawns, but has only %u members.", pool.poolId, pool.numActive, uint32(pool.members.size())); + pool.activeQuests.clear(); + for (uint32 i = 0; i < pool.numActive; ++i) + { + uint32 j = urand(i,n); + if (i != j) + std::swap(pool.members[i], pool.members[j]); + for (uint32 quest : pool.members[i]) + pool.activeQuests.insert(quest); + } +} + +static void SaveToDB(QuestPool const& pool, CharacterDatabaseTransaction trans) +{ + CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_POOL_QUEST_SAVE); + delStmt->setUInt32(0, pool.poolId); + trans->Append(delStmt); + + for (uint32 questId : pool.activeQuests) + { + CharacterDatabasePreparedStatement* insStmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_POOL_QUEST_SAVE); + insStmt->setUInt32(0, pool.poolId); + insStmt->setUInt32(1, questId); + trans->Append(insStmt); + } +} + +void QuestPoolMgr::LoadFromDB() +{ + uint32 oldMSTime = getMSTime(); + std::unordered_map*, uint32>> lookup; // poolId -> (vector, index) + + _poolLookup.clear(); + _dailyPools.clear(); + _weeklyPools.clear(); + _monthlyPools.clear(); + + // load template data from world DB + { + QueryResult result = WorldDatabase.Query("SELECT qpm.questId, qpm.poolId, qpm.poolIndex, qpt.numActive FROM quest_pool_members qpm LEFT JOIN quest_pool_template qpt ON qpm.poolId = qpt.poolId"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 quest pools. DB table `quest_pool_members` is empty."); + return; + } + + do + { + Field* fields = result->Fetch(); + if (fields[2].IsNull()) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to non-existing pool %u. Skipped.", fields[1].GetUInt32()); + continue; + } + + uint32 questId = fields[0].GetUInt32(); + uint32 poolId = fields[1].GetUInt32(); + uint32 poolIndex = fields[2].GetUInt8(); + uint32 numActive = fields[3].GetUInt32(); + + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + if (!quest) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to non-existing quest %u. Skipped.", questId); + continue; + } + if (!quest->IsDailyOrWeekly() && !quest->IsMonthly()) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to quest %u, which is neither daily, weekly nor monthly. Skipped.", questId); + continue; + } + + auto& pair = lookup[poolId]; + if (!pair.first) + { + pair.first = (quest->IsDaily() ? &_dailyPools : quest->IsWeekly() ? &_weeklyPools : &_monthlyPools); + pair.first->emplace_back(); + pair.second = (pair.first->size()-1); + + pair.first->back().poolId = poolId; + pair.first->back().numActive = numActive; + } + + QuestPool::Members& members = (*pair.first)[pair.second].members; + if (!(poolIndex < members.size())) + members.resize(poolIndex+1); + members[poolIndex].push_back(questId); + } while (result->NextRow()); + } + + // load saved spawns from character DB + { + QueryResult result = CharacterDatabase.Query("SELECT pool_id, quest_id FROM pool_quest_save"); + if (result) + { + std::unordered_set unknownPoolIds; + do + { + Field* fields = result->Fetch(); + + uint32 poolId = fields[0].GetUInt32(); + uint32 questId = fields[1].GetUInt32(); + + auto it = lookup.find(poolId); + if (it == lookup.end() || !it->second.first) + { + TC_LOG_ERROR("sql.sql", "Table `pool_quest_save` contains reference to non-existant quest pool %u. Deleted.", poolId); + unknownPoolIds.insert(poolId); + continue; + } + + (*it->second.first)[it->second.second].activeQuests.insert(questId); + } while (result->NextRow()); + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + for (uint32 poolId : unknownPoolIds) + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_POOL_QUEST_SAVE); + stmt->setUInt32(0, poolId); + trans->Append(stmt); + } + CharacterDatabase.CommitTransaction(trans); + } + } + + // post-processing and sanity checks + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + for (auto pair : lookup) + { + if (!pair.second.first) + continue; + QuestPool& pool = (*pair.second.first)[pair.second.second]; + if (pool.members.size() < pool.numActive) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_template` contains quest pool %u requesting %u spawns, but only has %zu members. Requested spawns reduced.", pool.poolId, pool.numActive, pool.members.size()); + pool.numActive = pool.members.size(); + } + + bool doRegenerate = pool.activeQuests.empty(); + if (!doRegenerate) + { + std::unordered_set accountedFor; + uint32 activeCount = 0; + for (size_t i = pool.members.size(); (i--);) + { + QuestPool::Member& member = pool.members[i]; + if (member.empty()) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains no entries at index %zu for quest pool %u. Index removed.", i, pool.poolId); + std::swap(pool.members[i], pool.members.back()); + pool.members.pop_back(); + continue; + } + + // check if the first member is active + auto itFirst = pool.activeQuests.find(member[0]); + bool status = (itFirst != pool.activeQuests.end()); + // temporarily remove any spawns that are accounted for + if (status) + { + accountedFor.insert(member[0]); + pool.activeQuests.erase(itFirst); + } + + // now check if all other members also have the same status, and warn if not + for (auto it = member.begin(); (++it) != member.end();) + { + auto itOther = pool.activeQuests.find(*it); + bool otherStatus = (itOther != pool.activeQuests.end()); + if (status != otherStatus) + TC_LOG_WARN("sql.sql", "Table `pool_quest_save` %s quest %u (in pool %u, index %zu) saved, but its index is%s active (because quest %u is%s in the table). Set quest %u to %sactive.", (status ? "does not have" : "has"), *it, pool.poolId, i, (status ? "" : " not"), member[0], (status ? "" : " not"), *it, (status ? "" : "in")); + if (otherStatus) + pool.activeQuests.erase(itOther); + if (status) + accountedFor.insert(*it); + } + + if (status) + ++activeCount; + } + + // warn for any remaining active spawns (not part of the pool) + for (uint32 quest : pool.activeQuests) + TC_LOG_WARN("sql.sql", "Table `pool_quest_save` has saved quest %u for pool %u, but that quest is not part of the pool. Skipped.", quest, pool.poolId); + + // only the previously-found spawns should actually be active + std::swap(pool.activeQuests, accountedFor); + + if (activeCount != pool.numActive) + { + doRegenerate = true; + TC_LOG_ERROR("sql.sql", "Table `pool_quest_save` has %u active members saved for pool %u, which requests %u active members. Pool spawns re-generated.", activeCount, pool.poolId, pool.numActive); + } + } + + if (doRegenerate) + { + RegeneratePool(pool); + SaveToDB(pool, trans); + } + + for (QuestPool::Member const& member : pool.members) + { + for (uint32 quest : member) + { + QuestPool*& ref = _poolLookup[quest]; + if (ref) + { + TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` lists quest %u as member of pool %u, but it is already a member of pool %u. Skipped.", quest, pool.poolId, ref->poolId); + continue; + } + ref = &pool; + } + } + } + CharacterDatabase.CommitTransaction(trans); + + TC_LOG_INFO("server.loading", ">> Loaded %zu daily, %zu weekly and %zu monthly quest pools in %u ms", _dailyPools.size(), _weeklyPools.size(), _monthlyPools.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +void QuestPoolMgr::Regenerate(std::vector& pools) +{ + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + for (QuestPool& pool : pools) + { + RegeneratePool(pool); + SaveToDB(pool, trans); + } + CharacterDatabase.CommitTransaction(trans); +} + +// the storage structure ends up making this kind of inefficient +// we don't use it in practice (only in debug commands), so that's fine +QuestPool const* QuestPoolMgr::FindQuestPool(uint32 poolId) const +{ + auto lambda = [poolId](QuestPool const& p) { return (p.poolId == poolId); }; + auto it = std::find_if(_dailyPools.begin(), _dailyPools.end(), lambda); + if (it != _dailyPools.end()) + return &*it; + it = std::find_if(_weeklyPools.begin(), _weeklyPools.end(), lambda); + if (it != _weeklyPools.end()) + return &*it; + it = std::find_if(_monthlyPools.begin(), _monthlyPools.end(), lambda); + if (it != _monthlyPools.end()) + return &*it; + return nullptr; +} + +bool QuestPoolMgr::IsQuestActive(uint32 questId) const +{ + auto it = _poolLookup.find(questId); + if (it == _poolLookup.end()) // not pooled + return true; + + return (it->second->activeQuests.find(questId) != it->second->activeQuests.end()); +} + + diff --git a/src/server/game/Pools/QuestPools.h b/src/server/game/Pools/QuestPools.h new file mode 100644 index 00000000000..d889e933a7d --- /dev/null +++ b/src/server/game/Pools/QuestPools.h @@ -0,0 +1,65 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_QUESTPOOLS_H +#define TRINITY_QUESTPOOLS_H + +#include "Define.h" +#include +#include +#include + +struct QuestPool +{ + using Member = std::vector; + using Members = std::vector; + + uint32 poolId; + uint32 numActive; + Members members; + std::unordered_set activeQuests; +}; + +class TC_GAME_API QuestPoolMgr +{ + private: + QuestPoolMgr() {} + ~QuestPoolMgr() {} + + public: + static QuestPoolMgr* instance(); + + void LoadFromDB(); + void ChangeDailyQuests() { Regenerate(_dailyPools); } + void ChangeWeeklyQuests() { Regenerate(_weeklyPools); } + void ChangeMonthlyQuests() { Regenerate(_monthlyPools); } + + QuestPool const* FindQuestPool(uint32 poolId) const; + bool IsQuestPooled(uint32 questId) const { return _poolLookup.find(questId) != _poolLookup.end(); } + bool IsQuestActive(uint32 questId) const; + + private: + void Regenerate(std::vector& pools); + std::vector _dailyPools; + std::vector _weeklyPools; + std::vector _monthlyPools; + std::unordered_map _poolLookup; // questId -> pool +}; + +#define sQuestPoolMgr QuestPoolMgr::instance() + +#endif diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index 1f797331c5c..d3c12968082 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -22,6 +22,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "QuestPackets.h" +#include "QuestPools.h" #include "World.h" Quest::Quest(Field* questRecord) @@ -265,6 +266,14 @@ uint32 Quest::GetXPReward(Player const* player) const return 0; } +/*static*/ bool Quest::IsTakingQuestEnabled(uint32 questId) +{ + if (!sQuestPoolMgr->IsQuestActive(questId)) + return false; + + return true; +} + int32 Quest::GetRewOrReqMoney(Player const* player) const { // RequiredMoney: the amount is the negative copper sum. diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 8bfdb44475d..1b0d0433e6f 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -245,6 +245,9 @@ class TC_GAME_API Quest bool HasSpecialFlag(uint32 flag) const { return (_specialFlags & flag) != 0; } void SetSpecialFlag(uint32 flag) { _specialFlags |= flag; } + // whether the quest is globally enabled (spawned by pool, game event active etc.) + static bool IsTakingQuestEnabled(uint32 questId); + // table data accessors: uint32 GetQuestId() const { return _id; } uint32 GetQuestMethod() const { return _method; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index d2a92e00598..46852a0ef0f 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -69,6 +69,7 @@ #include "PlayerDump.h" #include "PoolMgr.h" #include "QueryCallback.h" +#include "QuestPools.h" #include "Realm.h" #include "ScriptMgr.h" #include "ScriptReloadMgr.h" @@ -1039,7 +1040,20 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_CAST_UNSTUCK] = sConfigMgr->GetBoolDefault("CastUnstuck", true); m_int_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfigMgr->GetIntDefault("Instance.ResetTimeHour", 4); m_int_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfigMgr->GetIntDefault("Instance.UnloadDelay", 30 * MINUTE * IN_MILLISECONDS); + m_int_configs[CONFIG_DAILY_QUEST_RESET_TIME_HOUR] = sConfigMgr->GetIntDefault("Quests.DailyResetTime", 3); + if (m_int_configs[CONFIG_DAILY_QUEST_RESET_TIME_HOUR] > 23) + { + TC_LOG_ERROR("server.loading", "Quests.DailyResetTime (%i) must be in range 0..23. Set to 3.", m_int_configs[CONFIG_DAILY_QUEST_RESET_TIME_HOUR]); + m_int_configs[CONFIG_DAILY_QUEST_RESET_TIME_HOUR] = 3; + } + + m_int_configs[CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY] = sConfigMgr->GetIntDefault("Quests.WeeklyResetWDay", 3); + if (m_int_configs[CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY] > 6) + { + TC_LOG_ERROR("server.loading", "Quests.WeeklyResetDay (%i) must be in range 0..6. Set to 3 (Wednesday).", m_int_configs[CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY]); + m_int_configs[CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY] = 3; + } m_int_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfigMgr->GetIntDefault("MaxPrimaryTradeSkill", 2); m_int_configs[CONFIG_MIN_PETITION_SIGNS] = sConfigMgr->GetIntDefault("MinPetitionSigns", 9); @@ -1911,6 +1925,8 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Objects Pooling Data..."); sPoolMgr->LoadFromDB(); + TC_LOG_INFO("server.loading", "Loading Quest Pooling Data..."); + sQuestPoolMgr->LoadFromDB(); // must be after quest templates TC_LOG_INFO("server.loading", "Loading Game Event Data..."); // must be after loading pools fully sGameEventMgr->LoadHolidayDates(); // Must be after loading DBC @@ -2257,14 +2273,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Deleting expired bans..."); LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate<>bandate"); // One-time query - TC_LOG_INFO("server.loading", "Calculate next daily quest reset time..."); - InitDailyQuestResetTime(); - - TC_LOG_INFO("server.loading", "Calculate next weekly quest reset time..."); - InitWeeklyQuestResetTime(); - - TC_LOG_INFO("server.loading", "Calculate next monthly quest reset time..."); - InitMonthlyQuestResetTime(); + TC_LOG_INFO("server.loading", "Initializing quest reset times..."); + InitQuestResetTimes(); + CheckQuestResetTimes(); TC_LOG_INFO("server.loading", "Calculate random battleground reset time..."); InitRandomBGResetTime(); @@ -2360,21 +2371,6 @@ void World::Update(uint32 diff) sWhoListStorageMgr->Update(); } - /// Handle daily quests reset time - if (currentGameTime > m_NextDailyQuestReset) - { - ResetDailyQuestsAndRewards(); - InitDailyQuestResetTime(false); - } - - /// Handle weekly quests reset time - if (currentGameTime > m_NextWeeklyQuestReset) - ResetWeeklyQuestsAndRewards(); - - /// Handle monthly quests reset time - if (currentGameTime > m_NextMonthlyQuestReset) - ResetMonthlyQuests(); - if (currentGameTime > m_NextRandomBGReset) ResetRandomBG(); @@ -3212,53 +3208,133 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) } } -void World::InitWeeklyQuestResetTime() +void World::InitQuestResetTimes() { - time_t wstime = uint64(sWorld->getWorldState(WS_WEEKLY_QUEST_RESET_TIME)); - time_t curtime = GameTime::GetGameTime(); - m_NextWeeklyQuestReset = wstime < curtime ? curtime : time_t(wstime); + m_NextDailyQuestReset = sWorld->getWorldState(WS_DAILY_QUEST_RESET_TIME); + m_NextWeeklyQuestReset = sWorld->getWorldState(WS_WEEKLY_QUEST_RESET_TIME); + m_NextMonthlyQuestReset = sWorld->getWorldState(WS_MONTHLY_QUEST_RESET_TIME); } -void World::InitDailyQuestResetTime(bool loading) +static time_t GetNextDailyResetTime(time_t t) { - time_t mostRecentQuestTime = 0; + return GetLocalHourTimestamp(t, sWorld->getIntConfig(CONFIG_DAILY_QUEST_RESET_TIME_HOUR), true); +} - if (loading) +void World::ResetDailyQuestsAndRewards() +{ + // reset all saved quest status + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY); + CharacterDatabase.Execute(stmt); + // reset all quest status in memory + for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) { - QueryResult result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily"); - if (result) + if (Player* player = itr->second->GetPlayer()) { - Field* fields = result->Fetch(); - mostRecentQuestTime = time_t(fields[0].GetUInt32()); + player->ResetDailyQuestStatus(); + player->ResetDailyLFGRewardStatus(); } } - // FIX ME: client not show day start time - time_t curTime = GameTime::GetGameTime(); - tm localTm; - localtime_r(&curTime, &localTm); - localTm.tm_hour = getIntConfig(CONFIG_DAILY_QUEST_RESET_TIME_HOUR); - localTm.tm_min = 0; - localTm.tm_sec = 0; + // reselect pools + sQuestPoolMgr->ChangeDailyQuests(); - // current day reset time - time_t curDayResetTime = mktime(&localTm); + // store next reset time + time_t now = GameTime::GetGameTime(); + time_t next = GetNextDailyResetTime(now); + ASSERT(now < next); - // last reset time before current moment - time_t resetTime = (curTime < curDayResetTime) ? curDayResetTime - DAY : curDayResetTime; + m_NextDailyQuestReset = next; + sWorld->setWorldState(WS_DAILY_QUEST_RESET_TIME, uint64(next)); - // need reset (if we have quest time before last reset time (not processed by some reason) - if (mostRecentQuestTime && mostRecentQuestTime <= resetTime) - m_NextDailyQuestReset = mostRecentQuestTime; - else // plan next reset time - m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime; + TC_LOG_INFO("misc", "Daily quests for all characters have been reset."); } -void World::InitMonthlyQuestResetTime() +static time_t GetNextWeeklyResetTime(time_t t) { - time_t wstime = uint64(sWorld->getWorldState(WS_MONTHLY_QUEST_RESET_TIME)); - time_t curtime = GameTime::GetGameTime(); - m_NextMonthlyQuestReset = wstime < curtime ? curtime : time_t(wstime); + t = GetNextDailyResetTime(t); + tm time = TimeBreakdown(t); + int wday = time.tm_wday; + int target = sWorld->getIntConfig(CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY); + if (target < wday) + wday -= 7; + t += (DAY * (target - wday)); + return t; +} + +void World::ResetWeeklyQuestsAndRewards() +{ + // reset all saved quest status + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY); + CharacterDatabase.Execute(stmt); + // reset all quest status in memory + for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + { + if (Player* player = itr->second->GetPlayer()) + { + player->ResetWeeklyQuestStatus(); + player->ResetWeeklyLFGRewardStatus(); + } + } + + // reselect pools + sQuestPoolMgr->ChangeWeeklyQuests(); + + // store next reset time + time_t now = GameTime::GetGameTime(); + time_t next = GetNextWeeklyResetTime(now); + ASSERT(now < next); + + m_NextWeeklyQuestReset = next; + sWorld->setWorldState(WS_WEEKLY_QUEST_RESET_TIME, uint64(next)); + + TC_LOG_INFO("misc", "Weekly quests for all characters have been reset."); +} + +static time_t GetNextMonthlyResetTime(time_t t) +{ + t = GetNextDailyResetTime(t); + tm time = TimeBreakdown(t); + if (time.tm_mday == 1) + return t; + + time.tm_mday = 1; + time.tm_mon += 1; + return mktime(&time); +} + +void World::ResetMonthlyQuests() +{ + // reset all saved quest status + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY); + CharacterDatabase.Execute(stmt); + // reset all quest status in memory + for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + if (Player* player = itr->second->GetPlayer()) + player->ResetMonthlyQuestStatus(); + + // reselect pools + sQuestPoolMgr->ChangeMonthlyQuests(); + + // store next reset time + time_t now = GameTime::GetGameTime(); + time_t next = GetNextMonthlyResetTime(now); + ASSERT(now < next); + + m_NextMonthlyQuestReset = next; + sWorld->setWorldState(WS_MONTHLY_QUEST_RESET_TIME, uint64(next)); + + TC_LOG_INFO("misc", "Monthly quests for all characters have been reset."); +} + +void World::CheckQuestResetTimes() +{ + time_t const now = GameTime::GetGameTime(); + if (m_NextDailyQuestReset <= now) + ResetDailyQuestsAndRewards(); + if (m_NextWeeklyQuestReset <= now) + ResetWeeklyQuestsAndRewards(); + if (m_NextMonthlyQuestReset <= now) + ResetMonthlyQuests(); } void World::InitRandomBGResetTime() @@ -3346,31 +3422,6 @@ void World::InitCurrencyResetTime() sWorld->setWorldState(WS_CURRENCY_RESET_TIME, uint64(m_NextCurrencyReset)); } -void World::ResetDailyQuestsAndRewards() -{ - TC_LOG_INFO("misc", "Daily quests and rewards reset for all characters."); - - // Reset daily quest status - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY); - CharacterDatabase.Execute(stmt); - - // Reset daily lfg rewards - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_REWARDSTATUS_LFG_DAILY); - CharacterDatabase.Execute(stmt); - - for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) - { - if (itr->second->GetPlayer()) - { - itr->second->GetPlayer()->ResetDailyQuestStatus(); - itr->second->GetPlayer()->ResetDailyLFGRewardStatus(); - } - } - - // change available dailies - sPoolMgr->ChangeDailyQuests(); -} - void World::ResetCurrencyWeekCap() { CharacterDatabase.Execute("UPDATE `character_currency` SET `WeeklyQuantity` = 0"); @@ -3402,78 +3453,6 @@ void World::SetPlayerSecurityLimit(AccountTypes _sec) KickAllLess(m_allowedSecurityLevel); } -void World::ResetWeeklyQuestsAndRewards() -{ - TC_LOG_INFO("misc", "Weekly quests and rewards reset for all characters."); - - // Reset weekly quests - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY); - CharacterDatabase.Execute(stmt); - - // Reset weekly lfg rewards - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_REWARDSTATUS_LFG_WEEKLY); - CharacterDatabase.Execute(stmt); - - for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) - { - if (itr->second->GetPlayer()) - { - itr->second->GetPlayer()->ResetWeeklyQuestStatus(); - itr->second->GetPlayer()->ResetWeeklyLFGRewardStatus(); - } - } - - m_NextWeeklyQuestReset = time_t(m_NextWeeklyQuestReset + WEEK); - sWorld->setWorldState(WS_WEEKLY_QUEST_RESET_TIME, uint64(m_NextWeeklyQuestReset)); - - // change available weeklies - sPoolMgr->ChangeWeeklyQuests(); -} - -void World::ResetMonthlyQuests() -{ - TC_LOG_INFO("misc", "Monthly quests reset for all characters."); - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY); - CharacterDatabase.Execute(stmt); - - for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) - if (itr->second->GetPlayer()) - itr->second->GetPlayer()->ResetMonthlyQuestStatus(); - - // generate time - time_t curTime = GameTime::GetGameTime(); - tm localTm; - localtime_r(&curTime, &localTm); - - int month = localTm.tm_mon; - int year = localTm.tm_year; - - ++month; - - // month 11 is december, next is january (0) - if (month > 11) - { - month = 0; - year += 1; - } - - // reset time for next month - localTm.tm_year = year; - localTm.tm_mon = month; - localTm.tm_mday = 1; // don't know if we really need config option for day / hour - localTm.tm_hour = 0; - localTm.tm_min = 0; - localTm.tm_sec = 0; - - time_t nextMonthResetTime = mktime(&localTm); - - // plan next reset time - m_NextMonthlyQuestReset = (curTime >= nextMonthResetTime) ? nextMonthResetTime + MONTH : nextMonthResetTime; - - sWorld->setWorldState(WS_MONTHLY_QUEST_RESET_TIME, uint64(m_NextMonthlyQuestReset)); -} - void World::ResetEventSeasonalQuests(uint16 event_id) { TC_LOG_INFO("misc", "Seasonal quests reset for all characters."); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 454b9c96451..ba4a18d7ab5 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -261,6 +261,7 @@ enum WorldIntConfigs CONFIG_INSTANCE_RESET_TIME_HOUR, CONFIG_INSTANCE_UNLOAD_DELAY, CONFIG_DAILY_QUEST_RESET_TIME_HOUR, + CONFIG_WEEKLY_QUEST_RESET_TIME_WDAY, CONFIG_MAX_PRIMARY_TRADE_SKILL, CONFIG_MIN_PETITION_SIGNS, CONFIG_MIN_QUEST_SCALED_XP_RATIO, @@ -551,11 +552,12 @@ enum RealmZone enum WorldStates { WS_ARENA_DISTRIBUTION_TIME = 20001, // Next arena distribution time - WS_WEEKLY_QUEST_RESET_TIME = 20002, // Next weekly reset time + WS_WEEKLY_QUEST_RESET_TIME = 20002, // Next weekly quest reset time WS_BG_DAILY_RESET_TIME = 20003, // Next daily BG reset time WS_CLEANING_FLAGS = 20004, // Cleaning Flags WS_GUILD_DAILY_RESET_TIME = 20006, // Next guild cap reset time - WS_MONTHLY_QUEST_RESET_TIME = 20007, // Next monthly reset time + WS_MONTHLY_QUEST_RESET_TIME = 20007, // Next monthly quest reset time + WS_DAILY_QUEST_RESET_TIME = 20008, // Next daily quest reset time // Cata specific custom worldstates WS_GUILD_WEEKLY_RESET_TIME = 20050, // Next guild week reset time WS_CURRENCY_RESET_TIME = 20051, // Custom worldstate @@ -813,15 +815,16 @@ class TC_GAME_API World // callback for UpdateRealmCharacters void _UpdateRealmCharCount(PreparedQueryResult resultCharCount); - void InitDailyQuestResetTime(bool loading = true); - void InitWeeklyQuestResetTime(); - void InitMonthlyQuestResetTime(); - void InitRandomBGResetTime(); - void InitGuildResetTime(); - void InitCurrencyResetTime(); + void InitQuestResetTimes(); + void CheckQuestResetTimes(); + void ResetDailyQuestsAndRewards(); void ResetWeeklyQuestsAndRewards(); void ResetMonthlyQuests(); + + void InitCurrencyResetTime(); + void InitRandomBGResetTime(); + void InitGuildResetTime(); void ResetRandomBG(); void ResetGuildCap(); void ResetCurrencyWeekCap(); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index d40295c601f..c09d42cfb55 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -36,6 +36,8 @@ EndScriptData */ #include "M2Stores.h" #include "ObjectMgr.h" #include "PhasingHandler.h" +#include "PoolMgr.h" +#include "QuestPools.h" #include "RBAC.h" #include "Transport.h" #include "WorldSession.h" diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 2a7312ef7fd..5f904869383 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -79,13 +79,15 @@ public: return commandTable; } - static bool DoTeleport(ChatHandler* handler, WorldLocation loc) + static bool DoTeleport(ChatHandler* handler, Position pos, uint32 mapId = MAPID_INVALID) { Player* player = handler->GetSession()->GetPlayer(); - if (!MapManager::IsValidMapCoord(loc) || sObjectMgr->IsTransportMap(loc.GetMapId())) + if (mapId == MAPID_INVALID) + mapId = player->GetMapId(); + if (!MapManager::IsValidMapCoord(mapId, pos) || sObjectMgr->IsTransportMap(mapId)) { - handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, loc.GetPositionX(), loc.GetPositionY(), loc.GetMapId()); + handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, pos.GetPositionX(), pos.GetPositionY(), mapId); handler->SetSentErrorMessage(true); return false; } @@ -99,7 +101,7 @@ public: else player->SaveRecallPosition(); // save only in non-flight case - player->TeleportTo(loc); + player->TeleportTo({ mapId, pos }); return true; } @@ -113,7 +115,7 @@ public: return false; } - return DoTeleport(handler, spawnpoint->spawnPoint); + return DoTeleport(handler, spawnpoint->spawnPoint, spawnpoint->mapId); } static bool HandleGoCreatureCIdCommand(ChatHandler* handler, Variant, uint32> cId) { @@ -139,7 +141,7 @@ public: return false; } - return DoTeleport(handler, spawnpoint->spawnPoint); + return DoTeleport(handler, spawnpoint->spawnPoint, spawnpoint->mapId); } static bool HandleGoGameObjectSpawnIdCommand(ChatHandler* handler, uint32 spawnId) @@ -152,7 +154,7 @@ public: return false; } - return DoTeleport(handler, spawnpoint->spawnPoint); + return DoTeleport(handler, spawnpoint->spawnPoint, spawnpoint->mapId); } static bool HandleGoGameObjectGOIdCommand(ChatHandler* handler, uint32 goId) @@ -179,7 +181,7 @@ public: return false; } - return DoTeleport(handler, spawnpoint->spawnPoint); + return DoTeleport(handler, spawnpoint->spawnPoint, spawnpoint->mapId); } static bool HandleGoGraveyardCommand(ChatHandler* handler, uint32 gyId) @@ -259,7 +261,7 @@ public: return false; } - return DoTeleport(handler, { node->ContinentID, { node->Pos.X, node->Pos.Y, node->Pos.Z } }); + return DoTeleport(handler, { node->Pos.X, node->Pos.Y, node->Pos.Z }, node->ContinentID); } static bool HandleGoAreaTriggerCommand(ChatHandler* handler, Variant, uint32> areaTriggerId) @@ -272,7 +274,7 @@ public: return false; } - return DoTeleport(handler, { at->ContinentID, { at->Pos.X, at->Pos.Y, at->Pos.Z } }); + return DoTeleport(handler, { at->Pos.X, at->Pos.Y, at->Pos.Z }, at->ContinentID); } //teleport at coordinates @@ -375,7 +377,7 @@ public: z = std::max(map->GetHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); } - return DoTeleport(handler, { mapId, { x, y, *z, o.get_value_or(0.0f) } }); + return DoTeleport(handler, { x, y, *z, o.get_value_or(0.0f) }, mapId); } static bool HandleGoTicketCommand(ChatHandler* handler, uint32 ticketId) @@ -402,7 +404,7 @@ public: static bool HandleGoOffsetCommand(ChatHandler* handler, float dX, Optional dY, Optional dZ, Optional dO) { - WorldLocation loc = handler->GetSession()->GetPlayer()->GetWorldLocation(); + Position loc = handler->GetSession()->GetPlayer()->GetPosition(); loc.RelocateOffset({ dX, dY.get_value_or(0.0f), dZ.get_value_or(0.0f), dO.get_value_or(0.0f) }); return DoTeleport(handler, loc); diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 4e2d1ca2fe2..8bcc0eec518 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -31,12 +31,14 @@ EndScriptData */ #include "GameObject.h" #include "GameTime.h" #include "Language.h" +#include "Map.h" #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "PhasingHandler.h" #include "Player.h" #include "RBAC.h" +#include "SpawnData.h" #include "SpellAuraEffects.h" #include "WorldSession.h" #include @@ -658,7 +660,7 @@ public: for (auto const& pair : sObjectMgr->GetAllCreatureData()) { SpawnData const& data = pair.second; - if (data.spawnPoint.GetMapId() != mapId) + if (data.mapId != mapId) continue; CreatureTemplate const* cTemp = sObjectMgr->GetCreatureTemplate(data.id); if (!cTemp) @@ -669,7 +671,7 @@ public: for (auto const& pair : sObjectMgr->GetAllGameObjectData()) { SpawnData const& data = pair.second; - if (data.spawnPoint.GetMapId() != mapId) + if (data.mapId != mapId) continue; GameObjectTemplate const* goTemp = sObjectMgr->GetGameObjectTemplate(data.id); if (!goTemp) @@ -689,81 +691,55 @@ public: { Player const* player = handler->GetSession()->GetPlayer(); Map* map = player->GetMap(); - uint32 range = 0; if (*args) range = atoi((char*)args); - std::vector 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(); char const* zoneName = GetZoneName(zoneId, locale); - if (range) - handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringCreature, range); - else - handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringCreature, zoneName, zoneId); - handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER); - map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_CREATURE); - for (RespawnInfo const* ri : respawns) + char const* stringCreature = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_CREATURES, locale); + char const* stringGameobject = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_GAMEOBJECTS, locale); + + for (SpawnObjectType type : { SPAWN_TYPE_CREATURE, SPAWN_TYPE_GAMEOBJECT }) { - CreatureData const* data = sObjectMgr->GetCreatureData(ri->spawnId); - if (!data) - continue; - - uint32 respawnZoneId = map->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), data->spawnPoint); if (range) - { - if (!player->IsInDist(data->spawnPoint, range)) - continue; - } + handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, type == SPAWN_TYPE_CREATURE ? stringCreature : stringGameobject, range); else + handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, type == SPAWN_TYPE_CREATURE ? stringCreature : stringGameobject, zoneName, zoneId); + + handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER); + std::vector respawns; + map->GetRespawnInfo(respawns, SpawnObjectTypeMask(1 << type)); + for (RespawnInfo const* ri : respawns) { - if (zoneId != respawnZoneId) + SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(ri->type, ri->spawnId); + if (!data) continue; + + uint32 respawnZoneId = 0; + if (SpawnData const* edata = data->ToSpawnData()) + { + respawnZoneId = map->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), edata->spawnPoint); + if (range) + { + if (!player->IsInDist(edata->spawnPoint, range)) + continue; + } + else + { + if (zoneId != respawnZoneId) + continue; + } + } + uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS; + uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS; + std::string respawnTime = ri->respawnTime > GameTime::GetGameTime() ? secsToTimeString(uint64(ri->respawnTime - GameTime::GetGameTime()), true) : stringOverdue; + handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s%s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(respawnZoneId, locale), respawnZoneId, respawnTime.c_str(), map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? "" : " (inactive)"); } - - uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS; - uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS; - - std::string respawnTime = ri->respawnTime > GameTime::GetGameTime() ? secsToTimeString(uint64(ri->respawnTime - GameTime::GetGameTime()), true) : stringOverdue; - handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(respawnZoneId, locale), respawnZoneId, 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); - for (RespawnInfo const* ri : respawns) - { - GameObjectData const* data = sObjectMgr->GetGameObjectData(ri->spawnId); - if (!data) - continue; - - uint32 respawnZoneId = map->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), data->spawnPoint); - if (range) - { - if (!player->IsInDist(data->spawnPoint, range)) - continue; - } - else - { - if (zoneId != respawnZoneId) - continue; - } - - uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS; - uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS; - - std::string respawnTime = ri->respawnTime > GameTime::GetGameTime() ? secsToTimeString(uint64(ri->respawnTime - GameTime::GetGameTime()), true) : stringOverdue; - handler->PSendSysMessage("%u | %u | [% 02u, % 02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(respawnZoneId, locale), respawnZoneId, map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? respawnTime.c_str() : "inactive"); } return true; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 55a0ba77610..1abcd4160eb 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -918,7 +918,7 @@ public: return false; } - if (player->GetMapId() != data->spawnPoint.GetMapId()) + if (player->GetMapId() != data->mapId) { handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); handler->SetSentErrorMessage(true); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index 0600ff562b8..8e2f2f6b636 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -22,7 +22,6 @@ #include "MotionMaster.h" #include "ObjectAccessor.h" #include "Player.h" -#include "PoolMgr.h" #include "ScriptedCreature.h" #include "SpellInfo.h" #include "SpellScript.h" diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 78a4bb42895..79df83eb681 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -24,7 +24,6 @@ #include "Map.h" #include "ObjectMgr.h" #include "Player.h" -#include "PoolMgr.h" #include "TemporarySummon.h" #include "Transport.h" #include "TransportMgr.h" @@ -297,8 +296,8 @@ 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->spawnPoint.GetMapId() == go->moTransport.mapID) || - (TeamInInstance == HORDE && data->spawnPoint.GetMapId() != go->moTransport.mapID)) + if ((TeamInInstance == ALLIANCE && data->mapId == go->moTransport.mapID) || + (TeamInInstance == HORDE && data->mapId != go->moTransport.mapID)) return entry; return 0; case NPC_IGB_MURADIN_BRONZEBEARD: diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 4525afc85f2..04b3756a715 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1004,6 +1004,15 @@ Quests.IgnoreAutoComplete = 0 Quests.DailyResetTime = 3 +# +# Quests.WeeklyResetWDay +# Description: Day of the week when weekly quest reset occurs. +# Range: 0 (Sunday) to 6 (Saturday) +# Default: 3 - (Wednesday) +# + +Quests.WeeklyResetWDay = 3 + # # Guild.EventLogRecordsCount # Description: Number of log entries for guild events that are stored per guild. Old entries