aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp5
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h5
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp1
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h1
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp16
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp16
-rw-r--r--src/server/game/Entities/Player/Player.cpp48
-rw-r--r--src/server/game/Events/GameEventMgr.cpp4
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp40
-rw-r--r--src/server/game/Globals/ObjectMgr.h101
-rw-r--r--src/server/game/Handlers/QueryHandler.cpp12
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp3
-rw-r--r--src/server/game/Pools/PoolMgr.cpp339
-rw-r--r--src/server/game/Pools/PoolMgr.h29
-rw-r--r--src/server/game/Pools/QuestPools.cpp295
-rw-r--r--src/server/game/Pools/QuestPools.h65
-rw-r--r--src/server/game/Quests/QuestDef.cpp9
-rw-r--r--src/server/game/Quests/QuestDef.h3
-rw-r--r--src/server/game/World/World.cpp268
-rw-r--r--src/server/game/World/World.h11
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp15
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp4
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp8
-rw-r--r--src/server/worldserver/worldserver.conf.dist9
24 files changed, 646 insertions, 661 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index ff1c1b3e7e6..0879d50c074 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -31,8 +31,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"ig.gemItemId1, ig.gemBonuses1, ig.gemContext1, ig.gemScalingLevel1, ig.gemItemId2, ig.gemBonuses2, ig.gemContext2, ig.gemScalingLevel2, ig.gemItemId3, ig.gemBonuses3, ig.gemContext3, ig.gemScalingLevel3, " \
"im.fixedScalingLevel, im.artifactKnowledgeLevel"
- 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);
@@ -548,7 +548,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, orientation FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC, "SELECT guid, name, online 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_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters, entranceId FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 7460f5d8e28..e1862ce8eaf 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,
@@ -436,7 +436,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_CHAR_CUSTOMIZE_INFO,
CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS,
CHAR_SEL_INSTANCE,
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index 9352eeeedca..abd602636e5 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_LINKED_RESPAWN, "DELETE FROM linked_respawn WHERE guid = ? AND linkType = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_LINKED_RESPAWN_MASTER, "DELETE FROM linked_respawn WHERE linkedGuid = ? AND linkType = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_REP_LINKED_RESPAWN, "REPLACE INTO linked_respawn (guid, linkedGuid, linkType) VALUES (?, ?, ?)", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
index 2922fbde8f4..cf2688c193c 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_LINKED_RESPAWN,
WORLD_DEL_LINKED_RESPAWN_MASTER,
WORLD_REP_LINKED_RESPAWN,
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index b7c17ae085f..e5fb9f6a7dc 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1864,24 +1864,12 @@ void Creature::LoadTemplateRoot()
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)
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 50ed73a832d..6540087ce07 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -1330,24 +1330,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 22afdf05479..0906e47c203 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -14918,15 +14918,15 @@ int32 Player::GetQuestLevel(Quest const* quest) const
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
{
@@ -14937,8 +14937,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;
@@ -14947,9 +14947,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);
@@ -14959,9 +14958,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;
@@ -15024,7 +15022,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())
@@ -15037,7 +15035,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;
@@ -15049,7 +15047,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;
@@ -15058,12 +15056,9 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const
return nullptr;
}
- // for unit and go state
- for (QuestRelations::const_iterator itr = objectQR.first; itr != objectQR.second; ++itr)
- {
- if (itr->second == nextQuestID)
+ if (uint32 nextQuestID = quest->GetNextQuestInChain())
+ if (quests.HasQuest(nextQuestID))
return sObjectMgr->GetQuestTemplate(nextQuestID);
- }
return nullptr;
}
@@ -16567,8 +16562,7 @@ void Player::SendQuestUpdate(uint32 questId)
QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
{
- QuestRelationBounds qr;
- QuestRelationBounds qir;
+ QuestRelationResult qr, qir;
switch (questgiver->GetTypeId())
{
@@ -16577,8 +16571,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
if (GameObjectAI* ai = questgiver->ToGameObject()->AI())
if (Optional<QuestGiverStatus> questStatus = ai->GetDialogStatus(this))
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:
@@ -16586,8 +16580,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
if (CreatureAI* ai = questgiver->ToCreature()->AI())
if (Optional<QuestGiverStatus> questStatus = ai->GetDialogStatus(this))
return *questStatus;
- qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
- qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
+ qr = sObjectMgr->GetCreatureQuestRelations(questgiver->GetEntry());
+ qir = sObjectMgr->GetCreatureQuestInvolvedRelations(questgiver->GetEntry());
break;
}
default:
@@ -16599,9 +16593,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
QuestGiverStatus result = QuestGiverStatus::None;
- for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
+ for (uint32 questId : qir)
{
- uint32 questId = i->second;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
continue;
@@ -16635,9 +16628,8 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
}
}
- for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
+ for (uint32 questId : qr)
{
- 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 b7f7998e911..4a09390301e 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -1487,7 +1487,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
@@ -1512,7 +1512,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 2502bb8c61f..01125c54a61 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -8367,7 +8367,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* reverseMap, std::string const& table)
{
uint32 oldMSTime = getMSTime();
@@ -8375,7 +8375,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)
{
@@ -8383,15 +8383,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.count(quest))
{
@@ -8399,15 +8394,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 (reverseMap)
+ reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
++count;
} while (result->NextRow());
@@ -8416,7 +8405,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)
{
@@ -8430,7 +8419,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)
{
@@ -8444,7 +8433,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)
{
@@ -8458,7 +8447,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)
{
@@ -8470,6 +8459,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 2ed245776aa..db973b2fd56 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -37,6 +37,7 @@
#include "SharedDefines.h"
#include "Trainer.h"
#include "VehicleDefines.h"
+#include <iterator>
#include <map>
#include <unordered_map>
@@ -556,8 +557,54 @@ typedef std::unordered_map<uint32, TrinityString> TrinityStringContainer;
typedef std::multimap<uint32, uint32> QuestRelations; // unit/go -> quest
typedef std::multimap<uint32, uint32> QuestRelationsReverse; // quest -> unit/go
-typedef std::pair<QuestRelations::const_iterator, QuestRelations::const_iterator> QuestRelationBounds;
-typedef std::pair<QuestRelationsReverse::const_iterator, QuestRelationsReverse::const_iterator> 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<QuestRelations::const_iterator, QuestRelations::const_iterator> 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<int32, uint32> ExclusiveQuestGroups; // exclusiveGroupId -> quest
typedef std::pair<ExclusiveQuestGroups::const_iterator, ExclusiveQuestGroups::const_iterator> ExclusiveQuestGroupsBounds;
@@ -1220,45 +1267,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); }
+ Trinity::IteratorPair<QuestRelationsReverse::const_iterator> GetGOQuestInvolvedRelationReverseBounds(uint32 questId) const { return _goQuestInvolvedRelationsReverse.equal_range(questId); }
+ QuestRelations* GetCreatureQuestRelationMapHACK() { return &_creatureQuestRelations; }
+ QuestRelationResult GetCreatureQuestRelations(uint32 entry) const { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); }
+ QuestRelationResult GetCreatureQuestInvolvedRelations(uint32 entry) const { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); }
+ Trinity::IteratorPair<QuestRelationsReverse::const_iterator> GetCreatureQuestInvolvedRelationReverseBounds(uint32 questId) const { return _creatureQuestInvolvedRelationsReverse.equal_range(questId); }
ExclusiveQuestGroupsBounds GetExclusiveQuestGroupBounds(int32 exclusiveGroupId) const
{
@@ -1805,7 +1821,8 @@ 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 }; }
void PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count);
MailLevelRewardContainer _mailLevelRewardStore;
diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp
index c146cefcb91..eca0a1e7556 100644
--- a/src/server/game/Handlers/QueryHandler.cpp
+++ b/src/server/game/Handlers/QueryHandler.cpp
@@ -258,15 +258,13 @@ void WorldSession::HandleQueryQuestCompletionNPCs(WorldPackets::Query::QueryQues
questCompletionNPC.QuestID = questID;
- auto creatures = sObjectMgr->GetCreatureQuestInvolvedRelationReverseBounds(questID);
- for (auto it = creatures.first; it != creatures.second; ++it)
- questCompletionNPC.NPCs.push_back(it->second);
+ for (auto const& creatures : sObjectMgr->GetCreatureQuestInvolvedRelationReverseBounds(questID))
+ questCompletionNPC.NPCs.push_back(creatures.second);
- auto gos = sObjectMgr->GetGOQuestInvolvedRelationReverseBounds(questID);
- for (auto it = gos.first; it != gos.second; ++it)
- questCompletionNPC.NPCs.push_back(it->second | 0x80000000); // GO mask
+ for (auto const& gos : sObjectMgr->GetGOQuestInvolvedRelationReverseBounds(questID))
+ questCompletionNPC.NPCs.push_back(gos.second | 0x80000000); // GO mask
- response.QuestCompletionNPCs.push_back(questCompletionNPC);
+ response.QuestCompletionNPCs.push_back(std::move(questCompletionNPC));
}
SendPacket(response.Write());
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index 467b6b0d421..19e9d914a83 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -33,6 +33,7 @@
#include "PoolMgr.h"
#include "QuestDef.h"
#include "QuestPackets.h"
+#include "QuestPools.h"
#include "ReputationMgr.h"
#include "ScriptMgr.h"
#include "World.h"
@@ -641,7 +642,7 @@ void WorldSession::HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty&
}
// in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share
- if (sPoolMgr->IsPartOfAPool<Quest>(packet.QuestID) && !sPoolMgr->IsSpawnedObject<Quest>(packet.QuestID))
+ if (!sQuestPoolMgr->IsQuestActive(packet.QuestID))
{
sender->SendPushToPartyResponse(sender, QuestPushReason::NotDaily);
return;
diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp
index 89d6e1ab450..10af28c306d 100644
--- a/src/server/game/Pools/PoolMgr.cpp
+++ b/src/server/game/Pools/PoolMgr.cpp
@@ -60,13 +60,6 @@ TC_GAME_API bool ActivePoolData::IsActiveObject<Pool>(uint64 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<Quest>(uint64 quest_id) const
-{
- return mActiveQuests.find(quest_id) != mActiveQuests.end();
-}
-
template<>
void ActivePoolData::ActivateObject<Creature>(uint64 db_guid, uint32 pool_id)
{
@@ -89,13 +82,6 @@ void ActivePoolData::ActivateObject<Pool>(uint64 sub_pool_id, uint32 pool_id)
}
template<>
-void ActivePoolData::ActivateObject<Quest>(uint64 quest_id, uint32 pool_id)
-{
- mActiveQuests.insert(quest_id);
- ++mSpawnedPools[pool_id];
-}
-
-template<>
void ActivePoolData::RemoveObject<Creature>(uint64 db_guid, uint32 pool_id)
{
mSpawnedCreatures.erase(db_guid);
@@ -122,15 +108,6 @@ void ActivePoolData::RemoveObject<Pool>(uint64 sub_pool_id, uint32 pool_id)
--val;
}
-template<>
-void ActivePoolData::RemoveObject<Quest>(uint64 quest_id, uint32 pool_id)
-{
- mActiveQuests.erase(quest_id);
- uint32& val = mSpawnedPools[pool_id];
- if (val > 0)
- --val;
-}
-
////////////////////////////////////////////////////////////
// Methods of template class PoolGroup
@@ -250,49 +227,6 @@ void PoolGroup<Pool>::Despawn1Object(uint64 child_pool_id)
sPoolMgr->DespawnPool(child_pool_id);
}
-// Same on one quest
-template<>
-void PoolGroup<Quest>::Despawn1Object(uint64 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
- }
- }
- }
-}
-
// Method for a pool only to remove any found record causing a circular dependency loop
template<>
void PoolGroup<Pool>::RemoveOneRelation(uint32 child_pool_id)
@@ -428,100 +362,6 @@ void PoolGroup<Pool>::Spawn1Object(PoolObject* obj)
sPoolMgr->SpawnPool(obj->guid);
}
-// Same for 1 quest
-template<>
-void PoolGroup<Quest>::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<Quest>: 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<Quest>: Adding quest %u to GO %u", itr->first, itr->second);
- questMap->insert(QuestRelations::value_type(itr->second, itr->first));
- }
-}
-
-template <>
-void PoolGroup<Quest>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint64 triggerFrom)
-{
- TC_LOG_DEBUG("pool", "PoolGroup<Quest>: 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<Quest>(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<Quest>(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 <limit> random quests
- do
- {
- uint32 questId = Trinity::Containers::SelectRandomContainerElement(newQuests);
- spawns.ActivateObject<Quest>(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<Creature>::ReSpawn1Object(PoolObject* /*obj*/)
@@ -540,10 +380,6 @@ void PoolGroup<GameObject>::ReSpawn1Object(PoolObject* /*obj*/)
template <>
void PoolGroup<Pool>::ReSpawn1Object(PoolObject* /*obj*/) { }
-// Nothing to do for a quest
-template <>
-void PoolGroup<Quest>::ReSpawn1Object(PoolObject* /*obj*/) { }
-
////////////////////////////////////////////////////////////
// Methods of class PoolMgr
@@ -551,7 +387,6 @@ PoolMgr::PoolMgr() { }
void PoolMgr::Initialize()
{
- mQuestSearchMap.clear();
mGameobjectSearchMap.clear();
mCreatureSearchMap.clear();
}
@@ -811,95 +646,6 @@ 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<uint32, int32> 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<Quest>& 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...");
{
@@ -948,70 +694,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<Quest>(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<Quest>(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<Quest>(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<>
@@ -1042,21 +724,11 @@ void PoolMgr::SpawnPool<Pool>(uint32 pool_id, uint64 sub_pool_id)
it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
}
-// Call to spawn a pool
-template<>
-void PoolMgr::SpawnPool<Quest>(uint32 pool_id, uint64 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>(pool_id, 0);
SpawnPool<GameObject>(pool_id, 0);
SpawnPool<Creature>(pool_id, 0);
- SpawnPool<Quest>(pool_id, 0);
}
// Call to despawn a pool, all gameobjects/creatures in this pool are removed
@@ -1077,11 +749,6 @@ void PoolMgr::DespawnPool(uint32 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);
- }
}
// Selects proper template overload to call based on passed type
@@ -1117,11 +784,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;
}
@@ -1140,4 +802,3 @@ void PoolMgr::UpdatePool(uint32 pool_id, uint64 db_guid_or_pool_id)
template void PoolMgr::UpdatePool<Pool>(uint32 pool_id, uint64 db_guid_or_pool_id);
template void PoolMgr::UpdatePool<GameObject>(uint32 pool_id, uint64 db_guid_or_pool_id);
template void PoolMgr::UpdatePool<Creature>(uint32 pool_id, uint64 db_guid_or_pool_id);
-template void PoolMgr::UpdatePool<Quest>(uint32 pool_id, uint64 db_guid_or_pool_id);
diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h
index 839f16cdc4e..215cbdb0f8b 100644
--- a/src/server/game/Pools/PoolMgr.h
+++ b/src/server/game/Pools/PoolMgr.h
@@ -61,12 +61,9 @@ class TC_GAME_API ActivePoolData
template<typename T>
void RemoveObject(uint64 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;
};
@@ -101,10 +98,6 @@ class TC_GAME_API PoolGroup
PoolObjectList EqualChanced;
};
-typedef std::multimap<uint32, uint32> PooledQuestRelation;
-typedef std::pair<PooledQuestRelation::const_iterator, PooledQuestRelation::const_iterator> PooledQuestRelationBounds;
-typedef std::pair<PooledQuestRelation::iterator, PooledQuestRelation::iterator> PooledQuestRelationBoundsNC;
-
class TC_GAME_API PoolMgr
{
private:
@@ -115,8 +108,6 @@ class TC_GAME_API PoolMgr
static PoolMgr* instance();
void LoadFromDB();
- void LoadQuestPools();
- void SaveQuestsToDB();
void Initialize();
@@ -135,12 +126,6 @@ class TC_GAME_API PoolMgr
template<typename T>
void UpdatePool(uint32 pool_id, uint64 db_guid_or_pool_id);
- void ChangeDailyQuests();
- void ChangeWeeklyQuests();
-
- PooledQuestRelation mQuestCreatureRelation;
- PooledQuestRelation mQuestGORelation;
-
private:
template<typename T>
void SpawnPool(uint32 pool_id, uint64 db_guid_or_pool_id);
@@ -149,7 +134,6 @@ class TC_GAME_API PoolMgr
typedef std::unordered_map<uint32, PoolGroup<Creature>> PoolGroupCreatureMap;
typedef std::unordered_map<uint32, PoolGroup<GameObject>> PoolGroupGameObjectMap;
typedef std::unordered_map<uint32, PoolGroup<Pool>> PoolGroupPoolMap;
- typedef std::unordered_map<uint32, PoolGroup<Quest>> PoolGroupQuestMap;
typedef std::pair<uint64, uint32> SearchPair;
typedef std::map<uint64, uint32> SearchMap;
@@ -157,11 +141,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;
@@ -191,17 +173,6 @@ inline uint32 PoolMgr::IsPartOfAPool<GameObject>(uint64 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<Quest>(uint64 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<Pool>(uint64 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..e60eda8441d
--- /dev/null
+++ b/src/server/game/Pools/QuestPools.cpp
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "QuestPools.h"
+#include "Containers.h"
+#include "DatabaseEnv.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "QuestDef.h"
+#include "Timer.h"
+#include <unordered_map>
+#include <unordered_set>
+
+/*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, std::pair<std::vector<QuestPool>*, 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<uint32> 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 const& 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<uint32> 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<QuestPool>& 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_QUESTPOOLS_H
+#define TRINITY_QUESTPOOLS_H
+
+#include "Define.h"
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+struct QuestPool
+{
+ using Member = std::vector<uint32>;
+ using Members = std::vector<Member>;
+
+ uint32 poolId;
+ uint32 numActive;
+ Members members;
+ std::unordered_set<uint32> 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<QuestPool>& pools);
+ std::vector<QuestPool> _dailyPools;
+ std::vector<QuestPool> _weeklyPools;
+ std::vector<QuestPool> _monthlyPools;
+ std::unordered_map<uint32, QuestPool*> _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 8f2a01dae38..e85f559ec2a 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -24,6 +24,7 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "QuestPackets.h"
+#include "QuestPools.h"
#include "SpellMgr.h"
#include "World.h"
#include "WorldSession.h"
@@ -308,6 +309,14 @@ uint32 Quest::XPValue(Player const* player) const
return 0;
}
+/*static*/ bool Quest::IsTakingQuestEnabled(uint32 questId)
+{
+ if (!sQuestPoolMgr->IsQuestActive(questId))
+ return false;
+
+ return true;
+}
+
uint32 Quest::MoneyValue(Player const* player) const
{
if (QuestMoneyRewardEntry const* money = sQuestMoneyRewardStore.LookupEntry(player->GetQuestLevel(this)))
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index 8bf1db4407e..87d562cdd67 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -508,6 +508,9 @@ class TC_GAME_API Quest
// Possibly deprecated flag
bool IsUnavailable() const { return HasFlag(QUEST_FLAGS_UNAVAILABLE); }
+ // 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 GetQuestType() const { return _type; }
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index b002ec150dd..ede13f04dae 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -77,6 +77,7 @@
#include "Player.h"
#include "PlayerDump.h"
#include "PoolMgr.h"
+#include "QuestPools.h"
#include "Realm.h"
#include "ScenarioMgr.h"
#include "ScriptMgr.h"
@@ -1090,7 +1091,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] < 0 || 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] < 0 || 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", 4);
@@ -1988,6 +2002,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->LoadFromDB();
@@ -2377,14 +2393,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();
+ CheckScheduledResetTimes();
TC_LOG_INFO("server.loading", "Calculate random battleground reset time...");
InitRandomBGResetTime();
@@ -2507,20 +2518,7 @@ void World::Update(uint32 diff)
}
}
- /// Handle daily quests reset time
- if (currentGameTime > m_NextDailyQuestReset)
- {
- DailyReset();
- InitDailyQuestResetTime(false);
- }
-
- /// Handle weekly quests reset time
- if (currentGameTime > m_NextWeeklyQuestReset)
- ResetWeeklyQuests();
-
- /// Handle monthly quests reset time
- if (currentGameTime > m_NextMonthlyQuestReset)
- ResetMonthlyQuests();
+ CheckScheduledResetTimes();
if (currentGameTime > m_NextRandomBGReset)
ResetRandomBG();
@@ -3364,55 +3362,128 @@ 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)
- {
- QueryResult result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily");
- if (result)
- {
- Field* fields = result->Fetch();
- mostRecentQuestTime = time_t(fields[0].GetUInt32());
- }
- }
+void World::DailyReset()
+{
+ // reset all saved quest status
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY);
+ CharacterDatabase.Execute(stmt);
- // 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;
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_GARRISON_FOLLOWER_ACTIVATIONS);
+ stmt->setUInt32(0, 1);
+ CharacterDatabase.Execute(stmt);
- // current day reset time
- time_t curDayResetTime = mktime(&localTm);
+ // 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->DailyReset();
+
+ // reselect pools
+ sQuestPoolMgr->ChangeDailyQuests();
- // last reset time before current moment
- time_t resetTime = (curTime < curDayResetTime) ? curDayResetTime - DAY : curDayResetTime;
+ // store next reset time
+ time_t now = GameTime::GetGameTime();
+ time_t next = GetNextDailyResetTime(now);
+ ASSERT(now < 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;
+ m_NextDailyQuestReset = next;
+ sWorld->setWorldState(WS_DAILY_QUEST_RESET_TIME, uint64(next));
- sWorld->setWorldState(WS_DAILY_QUEST_RESET_TIME, uint64(m_NextDailyQuestReset));
+ TC_LOG_INFO("misc", "Daily quests for all characters have been reset.");
}
-void World::InitMonthlyQuestResetTime()
+static time_t GetNextWeeklyResetTime(time_t t)
+{
+ 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::ResetWeeklyQuests()
{
- time_t wstime = uint64(sWorld->getWorldState(WS_MONTHLY_QUEST_RESET_TIME));
- time_t curtime = GameTime::GetGameTime();
- m_NextMonthlyQuestReset = wstime < curtime ? curtime : time_t(wstime);
+ // 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();
+
+ // 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::CheckScheduledResetTimes()
+{
+ time_t const now = GameTime::GetGameTime();
+ if (m_NextDailyQuestReset <= now)
+ DailyReset();
+ if (m_NextWeeklyQuestReset <= now)
+ ResetWeeklyQuests();
+ if (m_NextMonthlyQuestReset <= now)
+ ResetMonthlyQuests();
}
void World::InitRandomBGResetTime()
@@ -3501,25 +3572,6 @@ void World::InitCurrencyResetTime()
sWorld->setWorldState(WS_CURRENCY_RESET_TIME, uint32(m_NextCurrencyReset));
}
-void World::DailyReset()
-{
- TC_LOG_INFO("misc", "Daily quests reset for all characters.");
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY);
- CharacterDatabase.Execute(stmt);
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_GARRISON_FOLLOWER_ACTIVATIONS);
- stmt->setUInt32(0, 1);
- CharacterDatabase.Execute(stmt);
-
- for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- if (itr->second->GetPlayer())
- itr->second->GetPlayer()->DailyReset();
-
- // change available dailies
- sPoolMgr->ChangeDailyQuests();
-}
-
void World::ResetCurrencyWeekCap()
{
CharacterDatabase.Execute("UPDATE `character_currency` SET `WeeklyQuantity` = 0");
@@ -3532,68 +3584,6 @@ void World::ResetCurrencyWeekCap()
sWorld->setWorldState(WS_CURRENCY_RESET_TIME, uint32(m_NextCurrencyReset));
}
-void World::ResetWeeklyQuests()
-{
- TC_LOG_INFO("misc", "Weekly quests reset for all characters.");
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_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();
-
- m_NextWeeklyQuestReset = time_t(m_NextWeeklyQuestReset + WEEK);
- sWorld->setWorldState(WS_WEEKLY_QUEST_RESET_TIME, uint32(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, uint32(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 36939ab6970..0e09a7eaf97 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -266,6 +266,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,
@@ -811,15 +812,15 @@ 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 InitQuestResetTimes();
+ void CheckScheduledResetTimes();
void InitCurrencyResetTime();
void DailyReset();
void ResetWeeklyQuests();
void ResetMonthlyQuests();
+
+ 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 956400e1814..952f5a316c9 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -44,6 +44,8 @@ EndScriptData */
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "PhasingHandler.h"
+#include "PoolMgr.h"
+#include "QuestPools.h"
#include "RBAC.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
@@ -1482,21 +1484,20 @@ public:
else
return false;
- time_t const now = GameTime::GetGameTime();
if (daily)
{
- sWorld->m_NextDailyQuestReset = now;
- handler->SendSysMessage("Daily quest reset scheduled for next tick.");
+ sWorld->DailyReset();
+ handler->PSendSysMessage("Daily quests have been reset. Next scheduled reset: %s", TimeToHumanReadable(sWorld->getWorldState(WS_DAILY_QUEST_RESET_TIME)).c_str());
}
if (weekly)
{
- sWorld->m_NextWeeklyQuestReset = now;
- handler->SendSysMessage("Weekly quest reset scheduled for next tick.");
+ sWorld->ResetWeeklyQuests();
+ handler->PSendSysMessage("Weekly quests have been reset. Next scheduled reset: %s", TimeToHumanReadable(sWorld->getWorldState(WS_WEEKLY_QUEST_RESET_TIME)).c_str());
}
if (monthly)
{
- sWorld->m_NextMonthlyQuestReset = now;
- handler->SendSysMessage("Monthly quest reset scheduled for next tick.");
+ sWorld->ResetMonthlyQuests();
+ handler->PSendSysMessage("Monthly quests have been reset. Next scheduled reset: %s", TimeToHumanReadable(sWorld->getWorldState(WS_MONTHLY_QUEST_RESET_TIME)).c_str());
}
return true;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
index 124054ffd47..3cb361ac63a 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
@@ -21,7 +21,7 @@
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
-#include "PoolMgr.h"
+#include "QuestPools.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "SpellScript.h"
@@ -511,7 +511,7 @@ class boss_lady_deathwhisper : public CreatureScript
uint8 addIndexOther = uint8(addIndex ^ 1);
// Summon first add, replace it with Darnavan if weekly quest is active
- if (_waveCounter || !sPoolMgr->IsSpawnedObject<Quest>(QUEST_DEPROGRAMMING))
+ if (_waveCounter || !sQuestPoolMgr->IsQuestActive(QUEST_DEPROGRAMMING))
Summon(SummonEntries[addIndex], SummonPositions[addIndex * 3]);
else
Summon(NPC_DARNAVAN, SummonPositions[addIndex * 3]);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 35f40a8f0db..1623d85fc5e 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -23,7 +23,7 @@
#include "Map.h"
#include "ObjectMgr.h"
#include "Player.h"
-#include "PoolMgr.h"
+#include "QuestPools.h"
#include "TemporarySummon.h"
#include "Transport.h"
#include "TransportMgr.h"
@@ -346,7 +346,7 @@ class instance_icecrown_citadel : public InstanceMapScript
if (WeeklyQuestData[questIndex].creatureEntry == entry)
{
uint8 diffIndex = instance->Is25ManRaid() ? 1 : 0;
- if (!sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[questIndex].questId[diffIndex]))
+ if (!sQuestPoolMgr->IsQuestActive(WeeklyQuestData[questIndex].questId[diffIndex]))
return 0;
break;
}
@@ -964,7 +964,7 @@ class instance_icecrown_citadel : public InstanceMapScript
case DATA_VALITHRIA_DREAMWALKER:
if (state == DONE)
{
- if (sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[8].questId[instance->Is25ManRaid() ? 1 : 0]))
+ if (sQuestPoolMgr->IsQuestActive(WeeklyQuestData[8].questId[instance->Is25ManRaid() ? 1 : 0]))
instance->SummonCreature(NPC_VALITHRIA_DREAMWALKER_QUEST, ValithriaSpawnPos);
if (GameObject* teleporter = instance->GetGameObject(TeleporterSindragosaGUID))
SetTeleporterState(teleporter, true);
@@ -1051,7 +1051,7 @@ class instance_icecrown_citadel : public InstanceMapScript
break;
// 5 is the index of Blood Quickening
- if (!sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[5].questId[instance->Is25ManRaid() ? 1 : 0]))
+ if (!sQuestPoolMgr->IsQuestActive(WeeklyQuestData[5].questId[instance->Is25ManRaid() ? 1 : 0]))
break;
switch (data)
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index f12a58da1d2..138816786ab 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1075,6 +1075,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
# will be overwritten if the number of log entries exceed the configured value.