aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPolarCookie <sei009@post.uit.no>2019-04-10 12:58:40 +0200
committerGiacomo Pozzoni <giacomopoz@gmail.com>2019-04-10 12:58:40 +0200
commit5ed77113b637c49e99a610b00f385c51da67c3bf (patch)
tree8d65d995762acb0d58590d5da17e234a5a7c0ce3 /src
parent9b3b56f60cc3553d6edcc422b04ee42a4b55678f (diff)
Core support for breadcrumb quests (#23157)
* Breadcrumb quests support in core * To Winterspring! & Starfall * translating ZenoX92's list, part 1 * Carendin Summons is Blood Elf only * Breadcrumb to an exclusive group * translating ZenoX92's list, part 2 * class quests * breadcrumb trails * better prevquest check * less harsh error warnings * minor optimization * Rename 9999_99_99_99_world.sql to 2019_04_10_00_world.sql
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp58
-rw-r--r--src/server/game/Entities/Player/Player.h10
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp56
-rw-r--r--src/server/game/Quests/QuestDef.cpp21
-rw-r--r--src/server/game/Quests/QuestDef.h3
5 files changed, 124 insertions, 24 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index eb3ab14ae9b..23a691cde76 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -14635,7 +14635,7 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const
return nullptr;
}
-bool Player::CanSeeStartQuest(Quest const* quest)
+bool Player::CanSeeStartQuest(Quest const* quest) const
{
if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) &&
SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
@@ -14649,7 +14649,7 @@ bool Player::CanSeeStartQuest(Quest const* quest)
return false;
}
-bool Player::CanTakeQuest(Quest const* quest, bool msg)
+bool Player::CanTakeQuest(Quest const* quest, bool msg) const
{
return !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
&& SatisfyQuestStatus(quest, msg) && SatisfyQuestExclusiveGroup(quest, msg)
@@ -15362,7 +15362,8 @@ bool Player::SatisfyQuestLog(bool msg) const
bool Player::SatisfyQuestDependentQuests(Quest const* qInfo, bool msg) const
{
- return SatisfyQuestPreviousQuest(qInfo, msg) && SatisfyQuestDependentPreviousQuests(qInfo, msg);
+ return SatisfyQuestPreviousQuest(qInfo, msg) && SatisfyQuestDependentPreviousQuests(qInfo, msg) &&
+ SatisfyQuestBreadcrumbQuest(qInfo, msg) && SatisfyQuestDependentBreadcrumbQuests(qInfo, msg);
}
bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const
@@ -15450,6 +15451,51 @@ bool Player::SatisfyQuestDependentPreviousQuests(Quest const* qInfo, bool msg) c
return false;
}
+bool Player::SatisfyQuestBreadcrumbQuest(Quest const* qInfo, bool msg) const
+{
+ uint32 breadcrumbTargetQuestId = std::abs(qInfo->GetBreadcrumbForQuestId());
+
+ //If this is not a breadcrumb quest.
+ if (!breadcrumbTargetQuestId)
+ return true;
+
+ // If the target quest is not available
+ if (!CanTakeQuest(sObjectMgr->GetQuestTemplate(breadcrumbTargetQuestId), false))
+ {
+ if (msg)
+ {
+ SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
+ TC_LOG_DEBUG("misc", "Player::SatisfyQuestBreadcrumbQuest: Sent INVALIDREASON_DONT_HAVE_REQ (QuestID: %u) because target quest (QuestID: %u) is not available to player '%s' (%s).",
+ qInfo->GetQuestId(), breadcrumbTargetQuestId, GetName().c_str(), GetGUID().ToString().c_str());
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::SatisfyQuestDependentBreadcrumbQuests(Quest const* qInfo, bool msg) const
+{
+ for (uint32 breadcrumbQuestId : qInfo->DependentBreadcrumbQuests)
+ {
+ QuestStatus status = GetQuestStatus(breadcrumbQuestId);
+ // If any of the breadcrumb quests are in the quest log, return false.
+ if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_FAILED)
+ {
+ if (msg)
+ {
+ SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
+ TC_LOG_DEBUG("misc", "Player::SatisfyQuestDependentBreadcrumbQuests: Sent INVALIDREASON_DONT_HAVE_REQ (QuestID: %u) because player '%s' (%s) has a breadcrumb quest towards this quest in the quest log.",
+ qInfo->GetQuestId(), GetName().c_str(), GetGUID().ToString().c_str());
+ }
+
+ return false;
+ }
+ }
+ return true;
+}
+
bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const
{
uint32 reqClass = qInfo->GetRequiredClasses();
@@ -15491,7 +15537,7 @@ bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) const
return true;
}
-bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg)
+bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) const
{
uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
if (fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
@@ -15559,9 +15605,9 @@ bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) const
return true;
}
-bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg)
+bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg) const
{
- if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, qInfo->GetQuestId(), this))
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, qInfo->GetQuestId(), const_cast<Player*>(this)))
{
if (msg)
{
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 337149a5082..f5530c06468 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1184,8 +1184,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendPreparedQuest(ObjectGuid guid);
bool IsActiveQuest(uint32 quest_id) const;
Quest const* GetNextQuest(ObjectGuid guid, Quest const* quest) const;
- bool CanSeeStartQuest(Quest const* quest);
- bool CanTakeQuest(Quest const* quest, bool msg);
+ bool CanSeeStartQuest(Quest const* quest) const;
+ bool CanTakeQuest(Quest const* quest, bool msg) const;
bool CanAddQuest(Quest const* quest, bool msg) const;
bool CanCompleteQuest(uint32 quest_id);
bool CanCompleteRepeatableQuest(Quest const* quest);
@@ -1205,11 +1205,13 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool SatisfyQuestDependentQuests(Quest const* qInfo, bool msg) const;
bool SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const;
bool SatisfyQuestDependentPreviousQuests(Quest const* qInfo, bool msg) const;
+ bool SatisfyQuestBreadcrumbQuest(Quest const* qInfo, bool msg) const;
+ bool SatisfyQuestDependentBreadcrumbQuests(Quest const* qInfo, bool msg) const;
bool SatisfyQuestClass(Quest const* qInfo, bool msg) const;
bool SatisfyQuestRace(Quest const* qInfo, bool msg) const;
- bool SatisfyQuestReputation(Quest const* qInfo, bool msg);
+ bool SatisfyQuestReputation(Quest const* qInfo, bool msg) const;
bool SatisfyQuestStatus(Quest const* qInfo, bool msg) const;
- bool SatisfyQuestConditions(Quest const* qInfo, bool msg);
+ bool SatisfyQuestConditions(Quest const* qInfo, bool msg) const;
bool SatisfyQuestTimed(Quest const* qInfo, bool msg) const;
bool SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const;
bool SatisfyQuestDay(Quest const* qInfo, bool msg) const;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 486c1712d35..365f23323fb 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -4437,9 +4437,9 @@ void ObjectMgr::LoadQuests()
// 0 1 2 3 4 5 6 7 8 9
{ "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "reward emotes", &Quest::LoadQuestOfferReward },
- // 0 1 2 3 4 5 6 7 8
- { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay,"
- // 9 10 11 12 13 14 15 16
+ // 0 1 2 3 4 5 6 7 8 9
+ { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
+ // 10 11 12 13 14 15 16 17
" RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags", "quest_template_addon", "template addons", &Quest::LoadQuestTemplateAddon },
// 0 1
@@ -4978,8 +4978,11 @@ void ObjectMgr::LoadQuests()
// fill additional data stores
if (uint32 prevQuestId = std::abs(qinfo->_prevQuestId))
{
- if (!_questTemplates.count(prevQuestId))
+ auto prevQuestItr = _questTemplates.find(prevQuestId);
+ if (prevQuestItr == _questTemplates.end())
TC_LOG_ERROR("sql.sql", "Quest %u has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->_prevQuestId);
+ else if (prevQuestItr->second._breadcrumbForQuestId)
+ TC_LOG_ERROR("sql.sql", "Quest %u should not be unlocked by breadcrumb quest %u", qinfo->_id, prevQuestId);
}
if (uint32 nextQuestId = qinfo->_nextQuestId)
@@ -4991,6 +4994,19 @@ void ObjectMgr::LoadQuests()
nextQuestItr->second.DependentPreviousQuests.push_back(qinfo->GetQuestId());
}
+ if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
+ {
+ if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
+ {
+ TC_LOG_ERROR("sql.sql", "Quest %u is a breadcrumb for quest %u, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
+ qinfo->_breadcrumbForQuestId = 0;
+ }
+ if (qinfo->_nextQuestId)
+ TC_LOG_ERROR("sql.sql", "Quest %u is a breadcrumb, should not unlock quest %u", qinfo->_id, qinfo->_nextQuestId);
+ if (qinfo->_exclusiveGroup)
+ TC_LOG_ERROR("sql.sql", "Quest %u is a breadcrumb in exclusive group %i", qinfo->_id, qinfo->_exclusiveGroup);
+ }
+
if (qinfo->_exclusiveGroup)
_exclusiveQuestGroups.emplace(qinfo->_exclusiveGroup, qinfo->GetQuestId());
if (qinfo->_timeAllowed)
@@ -5019,6 +5035,38 @@ void ObjectMgr::LoadQuests()
}
}
+ // Disallow any breadcrumb loops and inform quests of their breadcrumbs
+ for (auto& questPair : _questTemplates)
+ {
+ // skip post-loading checks for disabled quests
+ if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
+ continue;
+
+ Quest* qinfo = &questPair.second;
+ uint32 qid = qinfo->GetQuestId();
+ uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
+ std::set<uint32> questSet;
+
+ while(breadcrumbForQuestId)
+ {
+ //a previously visited quest was found as a breadcrumb quest
+ //breadcrumb loop found!
+ if (!questSet.insert(qinfo->_id).second)
+ {
+ TC_LOG_ERROR("sql.sql", "Breadcrumb quests %u and %u are in a loop", qid, breadcrumbForQuestId);
+ qinfo->_breadcrumbForQuestId = 0;
+ break;
+ }
+
+ qinfo = const_cast<Quest*>(sObjectMgr->GetQuestTemplate(breadcrumbForQuestId));
+
+ //every quest has a list of every breadcrumb towards it
+ qinfo->DependentBreadcrumbQuests.push_back(qid);
+
+ breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
+ }
+ }
+
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
{
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 8eda8a40337..684f1b37f56 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -178,16 +178,17 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
_prevQuestId = fields[4].GetInt32();
_nextQuestId = fields[5].GetUInt32();
_exclusiveGroup = fields[6].GetInt32();
- _rewardMailTemplateId = fields[7].GetUInt32();
- _rewardMailDelay = fields[8].GetUInt32();
- _requiredSkillId = fields[9].GetUInt16();
- _requiredSkillPoints = fields[10].GetUInt16();
- _requiredMinRepFaction = fields[11].GetUInt16();
- _requiredMaxRepFaction = fields[12].GetUInt16();
- _requiredMinRepValue = fields[13].GetInt32();
- _requiredMaxRepValue = fields[14].GetInt32();
- _startItemCount = fields[15].GetUInt8();
- _specialFlags = fields[16].GetUInt8();
+ _breadcrumbForQuestId = fields[7].GetInt32();
+ _rewardMailTemplateId = fields[8].GetUInt32();
+ _rewardMailDelay = fields[9].GetUInt32();
+ _requiredSkillId = fields[10].GetUInt16();
+ _requiredSkillPoints = fields[11].GetUInt16();
+ _requiredMinRepFaction = fields[12].GetUInt16();
+ _requiredMaxRepFaction = fields[13].GetUInt16();
+ _requiredMinRepValue = fields[14].GetInt32();
+ _requiredMaxRepValue = fields[15].GetInt32();
+ _startItemCount = fields[16].GetUInt8();
+ _specialFlags = fields[17].GetUInt8();
if (_specialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT)
_flags |= QUEST_FLAGS_AUTO_ACCEPT;
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index f611494dfdb..0d2ecb40975 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -240,6 +240,7 @@ class TC_GAME_API Quest
int32 GetPrevQuestId() const { return _prevQuestId; }
uint32 GetNextQuestId() const { return _nextQuestId; }
int32 GetExclusiveGroup() const { return _exclusiveGroup; }
+ int32 GetBreadcrumbForQuestId() const { return _breadcrumbForQuestId; }
uint32 GetNextQuestInChain() const { return _rewardNextQuest; }
uint32 GetCharTitleId() const { return _rewardTitleId; }
uint32 GetPlayersSlain() const { return _requiredPlayerKills; }
@@ -321,6 +322,7 @@ class TC_GAME_API Quest
WorldPacket BuildQueryData(LocaleConstant loc) const;
std::vector<uint32> DependentPreviousQuests;
+ std::vector<uint32> DependentBreadcrumbQuests;
WorldPacket QueryData[TOTAL_LOCALES];
// cached data
@@ -380,6 +382,7 @@ class TC_GAME_API Quest
int32 _prevQuestId = 0;
uint32 _nextQuestId = 0;
int32 _exclusiveGroup = 0;
+ int32 _breadcrumbForQuestId = 0;
uint32 _rewardMailTemplateId = 0;
uint32 _rewardMailDelay = 0;
uint32 _requiredSkillId = 0;