Core/Quests: Move questline handling to new QuestMgr namespace and implement SPELL_EFFECT_SKIP_QUESTLINE

This commit is contained in:
luis
2026-03-10 22:04:58 -03:00
parent 418ebd03cf
commit 8fdb729d0b
17 changed files with 716 additions and 587 deletions
@@ -41,6 +41,7 @@
#include "PetBattleDefines.h"
#include "PhasingHandler.h"
#include "Player.h"
#include "QuestMgr.h"
#include "RBAC.h"
#include "RealmList.h"
#include "ReputationMgr.h"
@@ -3286,15 +3287,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
}
case ModifierTreeType::PlayerIsOnQuestInQuestline: // 236
{
bool isOnQuest = false;
if (std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(reqValue))
{
isOnQuest = std::any_of(questLineQuests->begin(), questLineQuests->end(), [referencePlayer](QuestLineXQuestEntry const* questLineQuest)
{
return referencePlayer->FindQuestSlot(questLineQuest->QuestID) < MAX_QUEST_LOG_SIZE;
});
}
if (!isOnQuest)
if (!QuestMgr::IsQuestLineQuestActiveForPlayer(reqValue, referencePlayer))
return false;
break;
}
@@ -3321,52 +3314,26 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
}
case ModifierTreeType::PlayerCanAcceptQuestInQuestline: // 240
{
std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(reqValue);
if (!questLineQuests)
return false;
bool canTakeQuest = std::any_of(questLineQuests->begin(), questLineQuests->end(), [referencePlayer](QuestLineXQuestEntry const* questLineQuest)
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questLineQuest->QuestID))
return referencePlayer->CanTakeQuest(quest, false);
return false;
});
if (!canTakeQuest)
if (!QuestMgr::IsQuestLineQuestAvailableForPlayer(reqValue, referencePlayer))
return false;
break;
}
case ModifierTreeType::PlayerHasCompletedQuestline: // 241
{
std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(reqValue);
if (!questLineQuests)
if (!QuestMgr::IsQuestLineCompletedByPlayer(reqValue, referencePlayer))
return false;
for (QuestLineXQuestEntry const* questLineQuest : *questLineQuests)
if (!referencePlayer->GetQuestRewardStatus(questLineQuest->QuestID))
return false;
break;
}
case ModifierTreeType::PlayerHasCompletedQuestlineQuestCount: // 242
{
std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(reqValue);
if (!questLineQuests)
return false;
uint32 completedQuests = 0;
for (QuestLineXQuestEntry const* questLineQuest : *questLineQuests)
if (referencePlayer->GetQuestRewardStatus(questLineQuest->QuestID))
++completedQuests;
if (completedQuests < reqValue)
if (QuestMgr::GetQuestLineStatsForPlayer(reqValue, referencePlayer).Completed < reqValue)
return false;
break;
}
case ModifierTreeType::PlayerHasCompletedPercentageOfQuestline: // 243
{
std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(reqValue);
if (!questLineQuests || questLineQuests->empty())
return false;
std::size_t completedQuests = 0;
for (QuestLineXQuestEntry const* questLineQuest : *questLineQuests)
if (referencePlayer->GetQuestRewardStatus(questLineQuest->QuestID))
++completedQuests;
if (GetPctOf(completedQuests, questLineQuests->size()) < reqValue)
QuestMgr::QuestLineStats questLineStats = QuestMgr::GetQuestLineStatsForPlayer(reqValue, referencePlayer);
if (GetPctOf(questLineStats.Completed, questLineStats.Total) < reqValue)
return false;
break;
}
-14
View File
@@ -618,7 +618,6 @@ namespace
PhaseGroupContainer _phasesByGroup;
PowerTypesContainer _powerTypes;
PvpTalentSlotUnlockEntry const* _pvpTalentSlotUnlock[MAX_PVP_TALENT_SLOTS];
std::unordered_map<uint32, std::vector<QuestLineXQuestEntry const*>> _questsByQuestLine;
QuestPackageItemContainer _questPackages;
std::unordered_map<uint32, std::vector<RewardPackXCurrencyTypeEntry const*>> _rewardPackCurrencyTypes;
std::unordered_map<uint32, std::vector<RewardPackXItemEntry const*>> _rewardPackItems;
@@ -1698,14 +1697,6 @@ void DB2Manager::IndexLoadedStores()
}
}
{
for (QuestLineXQuestEntry const* questLineQuest : sQuestLineXQuestStore)
_questsByQuestLine[questLineQuest->QuestLineID].push_back(questLineQuest);
for (auto& [questLineId, questLineQuests] : _questsByQuestLine)
std::ranges::sort(questLineQuests, std::ranges::less(), &QuestLineXQuestEntry::OrderIndex);
}
for (QuestPackageItemEntry const* questPackageItem : sQuestPackageItemStore)
{
if (questPackageItem->DisplayType != QUEST_PACKAGE_FILTER_UNMATCHED)
@@ -3151,11 +3142,6 @@ int32 DB2Manager::GetPvpTalentNumSlotsAtLevel(uint32 level, Classes class_) cons
return slots;
}
std::vector<QuestLineXQuestEntry const*> const* DB2Manager::GetQuestsForQuestLine(uint32 questLineId) const
{
return Trinity::Containers::MapGetValuePtr(_questsByQuestLine, questLineId);
}
std::vector<QuestPackageItemEntry const*> const* DB2Manager::GetQuestPackageItems(uint32 questPackageID) const
{
auto itr = _questPackages.find(questPackageID);
+1 -1
View File
@@ -217,6 +217,7 @@ TC_GAME_API extern DB2Storage<PvpTalentSlotUnlockEntry> sPvpTalentSl
TC_GAME_API extern DB2Storage<PvpTierEntry> sPvpTierStore;
TC_GAME_API extern DB2Storage<QuestFactionRewardEntry> sQuestFactionRewardStore;
TC_GAME_API extern DB2Storage<QuestInfoEntry> sQuestInfoStore;
TC_GAME_API extern DB2Storage<QuestLineXQuestEntry> sQuestLineXQuestStore;
TC_GAME_API extern DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore;
TC_GAME_API extern DB2Storage<QuestSortEntry> sQuestSortStore;
TC_GAME_API extern DB2Storage<QuestXPEntry> sQuestXPStore;
@@ -625,7 +626,6 @@ public:
static PVPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id);
uint32 GetRequiredLevelForPvpTalentSlot(uint8 slot, Classes class_) const;
int32 GetPvpTalentNumSlotsAtLevel(uint32 level, Classes class_) const;
std::vector<QuestLineXQuestEntry const*> const* GetQuestsForQuestLine(uint32 questLineId) const;
std::vector<QuestPackageItemEntry const*> const* GetQuestPackageItems(uint32 questPackageID) const;
std::vector<QuestPackageItemEntry const*> const* GetQuestPackageItemsFallback(uint32 questPackageID) const;
uint32 GetQuestUniqueBitFlag(uint32 questId);
@@ -3498,6 +3498,8 @@ struct QuestLineXQuestEntry
uint32 OrderIndex;
int32 Flags;
int32 Unknown1110;
bool HasFlag(QuestLineXQuestFlags flag) const { return EnumFlag(static_cast<QuestLineXQuestFlags>(Flags)).HasFlag(flag); }
};
struct QuestMoneyRewardEntry
+7
View File
@@ -2280,6 +2280,13 @@ enum PrestigeLevelInfoFlags : uint8
PRESTIGE_FLAG_DISABLED = 0x01 // Prestige levels with this flag won't be included to calculate max prestigelevel.
};
enum class QuestLineXQuestFlags : int32
{
IgnoreForCompletion = 0x01
};
DEFINE_ENUM_FLAG(QuestLineXQuestFlags);
enum QuestPackageFilter
{
QUEST_PACKAGE_FILTER_LOOT_SPECIALIZATION = 0, // Players can select this quest reward if it matches their selected loot specialization
+2 -2
View File
@@ -16368,10 +16368,10 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object const* questgiver) const
return result;
}
void Player::SkipQuests(std::vector<uint32> const& questIds)
void Player::SkipQuests(std::span<uint32 const> questIds)
{
bool updateVisibility = false;
for (uint32 const& questId : questIds)
for (uint32 questId : questIds)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
+1 -1
View File
@@ -1733,7 +1733,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void RemoveRewardedQuest(uint32 questId, bool update = true);
void SendQuestUpdate(uint32 questId, bool updateInteractions = true, bool updateGameObjectQuestGiverStatus = false);
QuestGiverStatus GetQuestDialogStatus(Object const* questGiver) const;
void SkipQuests(std::vector<uint32> const& questIds); // removes quest from log, flags rewarded, but does not give any rewards to player
void SkipQuests(std::span<uint32 const> questIds); // removes quest from log, flags rewarded, but does not give any rewards to player
void DespawnPersonalSummonsForQuest(uint32 questId);
void SetDailyQuestStatus(uint32 quest_id);
+2 -1
View File
@@ -56,6 +56,7 @@
#include "QueryPackets.h"
#include "QueryResultStructured.h"
#include "QuestDef.h"
#include "QuestMgr.h"
#include "Random.h"
#include "RealmList.h"
#include "ReputationMgr.h"
@@ -11525,7 +11526,7 @@ void ObjectMgr::LoadUiMapQuestLines()
continue;
}
if (!sDB2Manager.GetQuestsForQuestLine(questLineId))
if (QuestMgr::GetQuestsForQuestLine(questLineId).empty())
{
TC_LOG_ERROR("sql.sql", "Table `ui_map_quest_line` references empty or non-existing questline {}, skipped", questLineId);
continue;
+4 -3
View File
@@ -35,6 +35,7 @@
#include "PlayerChoice.h"
#include "PoolMgr.h"
#include "QuestDef.h"
#include "QuestMgr.h"
#include "QuestPackets.h"
#include "QuestPools.h"
#include "ReputationMgr.h"
@@ -824,12 +825,12 @@ void WorldSession::HandleUiMapQuestLinesRequest(WorldPackets::Quest::UiMapQuestL
{
for (uint32 questLineId : *questLines)
{
std::vector<QuestLineXQuestEntry const*> const* questLineQuests = sDB2Manager.GetQuestsForQuestLine(questLineId);
if (!questLineQuests)
std::span<QuestLineXQuestEntry const* const> questLineQuests = QuestMgr::GetQuestsForQuestLine(questLineId);
if (questLineQuests.empty())
continue;
bool isQuestLineCompleted = true;
for (QuestLineXQuestEntry const* questLineQuest : *questLineQuests)
for (QuestLineXQuestEntry const* questLineQuest : questLineQuests)
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questLineQuest->QuestID))
{
@@ -1650,7 +1650,7 @@ enum SpellEffectName
SPELL_EFFECT_CANCEL_PRELOAD_WORLD = 308,
SPELL_EFFECT_PRELOAD_WORLD = 309,
SPELL_EFFECT_310 = 310,
SPELL_EFFECT_ENSURE_WORLD_LOADED = 311,
SPELL_EFFECT_SKIP_QUESTLINE = 311,
SPELL_EFFECT_312 = 312,
SPELL_EFFECT_CHANGE_ITEM_BONUSES_2 = 313, // MiscValue[0] = ItemBonusTreeID to preserve
SPELL_EFFECT_ADD_SOCKET_BONUS = 314, // MiscValue[0] = required ItemBonusTreeID
+101
View File
@@ -0,0 +1,101 @@
/*
* 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 "QuestMgr.h"
#include "DB2Stores.h"
#include "ObjectMgr.h"
#include "Player.h"
#include <algorithm>
namespace
{
std::unordered_map<uint32, std::vector<QuestLineXQuestEntry const*>> QuestsByQuestLine;
}
void QuestMgr::Load()
{
for (QuestLineXQuestEntry const* questLineQuest : sQuestLineXQuestStore)
QuestsByQuestLine[questLineQuest->QuestLineID].push_back(questLineQuest);
for (auto& [_, questLineQuests] : QuestsByQuestLine)
std::ranges::sort(questLineQuests, std::ranges::less(), &QuestLineXQuestEntry::OrderIndex);
}
std::span<QuestLineXQuestEntry const* const> QuestMgr::GetQuestsForQuestLine(uint32 questLineId)
{
std::span<QuestLineXQuestEntry const* const> result;
if (auto itr = QuestsByQuestLine.find(questLineId); itr != QuestsByQuestLine.end())
result = itr->second;
return result;
}
bool QuestMgr::IsQuestLineQuestAvailableForPlayer(uint32 questLineId, Player const* player)
{
for (QuestLineXQuestEntry const* questLineQuest : GetQuestsForQuestLine(questLineId))
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questLineQuest->QuestID))
if (player->CanTakeQuest(quest, false))
return true;
return false;
}
bool QuestMgr::IsQuestLineQuestActiveForPlayer(uint32 questLineId, Player const* player)
{
for (QuestLineXQuestEntry const* questLineQuest : GetQuestsForQuestLine(questLineId))
if (player->IsActiveQuest(questLineQuest->QuestID))
return true;
return false;
}
bool QuestMgr::IsQuestLineCompletedByPlayer(uint32 questLineId, Player const* player)
{
for (QuestLineXQuestEntry const* questLineQuest : GetQuestsForQuestLine(questLineId))
{
if (questLineQuest->HasFlag(QuestLineXQuestFlags::IgnoreForCompletion))
continue;
if (!player->IsQuestCompletedBitSet(questLineQuest->QuestID))
return false;
}
return true;
}
QuestMgr::QuestLineStats QuestMgr::GetQuestLineStatsForPlayer(uint32 questLineId, Player const* player)
{
QuestLineStats stats;
for (QuestLineXQuestEntry const* questLineQuest : GetQuestsForQuestLine(questLineId))
{
if (questLineQuest->HasFlag(QuestLineXQuestFlags::IgnoreForCompletion))
continue;
stats.Completed += player->IsQuestCompletedBitSet(questLineQuest->QuestID) ? 1 : 0;
++stats.Total;
}
return stats;
}
void QuestMgr::SkipQuestLineForPlayer(uint32 questLineId, Player* player)
{
std::span<QuestLineXQuestEntry const* const> questLineQuests = GetQuestsForQuestLine(questLineId);
std::vector<uint32> questIds(questLineQuests.size());
std::ranges::transform(questLineQuests, questIds.begin(), &QuestLineXQuestEntry::QuestID);
player->SkipQuests(questIds);
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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 TRINITYCORE_QUEST_MGR_H
#define TRINITYCORE_QUEST_MGR_H
#include "Define.h"
#include <span>
struct QuestLineXQuestEntry;
class Player;
namespace QuestMgr
{
void Load();
// QuestLine
TC_GAME_API std::span<QuestLineXQuestEntry const* const> GetQuestsForQuestLine(uint32 questLineId);
TC_GAME_API bool IsQuestLineQuestAvailableForPlayer(uint32 questLineId, Player const* player);
TC_GAME_API bool IsQuestLineQuestActiveForPlayer(uint32 questLineId, Player const* player);
TC_GAME_API bool IsQuestLineCompletedByPlayer(uint32 questLineId, Player const* player);
struct QuestLineStats { uint32 Completed = 0; uint32 Total = 0; };
TC_GAME_API QuestLineStats GetQuestLineStatsForPlayer(uint32 questLineId, Player const* player);
TC_GAME_API void SkipQuestLineForPlayer(uint32 questLineId, Player* player);
}
#endif // TRINITYCORE_CAMPAIGN_MGR_H
+1
View File
@@ -474,6 +474,7 @@ class TC_GAME_API Spell
void EffectChangeActiveCombatTraitConfig();
void EffectTeleportGraveyard();
void EffectUpdateInteractions();
void EffectSkipQuestLine();
void EffectLearnWarbandScene();
void EffectSetPlayerDataElementAccount();
void EffectSetPlayerDataElementCharacter();
+14 -1
View File
@@ -63,6 +63,7 @@
#include "Pet.h"
#include "PhasingHandler.h"
#include "Player.h"
#include "QuestMgr.h"
#include "ReputationMgr.h"
#include "RestMgr.h"
#include "SceneObject.h"
@@ -413,7 +414,7 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF
&Spell::EffectNULL, //308 SPELL_EFFECT_CANCEL_PRELOAD_WORLD
&Spell::EffectNULL, //309 SPELL_EFFECT_PRELOAD_WORLD
&Spell::EffectNULL, //310 SPELL_EFFECT_310
&Spell::EffectNULL, //311 SPELL_EFFECT_ENSURE_WORLD_LOADED
&Spell::EffectSkipQuestLine, //311 SPELL_EFFECT_SKIP_QUESTLINE
&Spell::EffectNULL, //312 SPELL_EFFECT_312
&Spell::EffectNULL, //313 SPELL_EFFECT_CHANGE_ITEM_BONUSES_2
&Spell::EffectNULL, //314 SPELL_EFFECT_ADD_SOCKET_BONUS
@@ -6281,6 +6282,18 @@ void Spell::EffectUpdateInteractions()
target->UpdateVisibleObjectInteractions(true, false, true, true);
}
void Spell::EffectSkipQuestLine()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* target = Object::ToPlayer(unitTarget);
if (!target)
return;
QuestMgr::SkipQuestLineForPlayer(effectInfo->MiscValue, target);
}
void Spell::EffectLearnWarbandScene()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+1 -1
View File
@@ -1240,7 +1240,7 @@ std::array<SpellEffectInfo::StaticData, TOTAL_SPELL_EFFECTS> SpellEffectInfo::_d
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 308 SPELL_EFFECT_CANCEL_PRELOAD_WORLD
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 309 SPELL_EFFECT_PRELOAD_WORLD
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 310 SPELL_EFFECT_310
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 311 SPELL_EFFECT_ENSURE_WORLD_LOADED
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 311 SPELL_EFFECT_SKIP_QUESTLINE
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 312 SPELL_EFFECT_312
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 313 SPELL_EFFECT_CHANGE_ITEM_BONUSES_2
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 314 SPELL_EFFECT_ADD_SOCKET_BONUS
+2
View File
@@ -86,6 +86,7 @@
#include "Player.h"
#include "PlayerDump.h"
#include "PoolMgr.h"
#include "QuestMgr.h"
#include "QuestPools.h"
#include "RealmList.h"
#include "ScenarioMgr.h"
@@ -1613,6 +1614,7 @@ bool World::SetInitialWorldSettings()
WeatherMgr::LoadWeatherData();
TC_LOG_INFO("server.loading", "Loading Quests...");
QuestMgr::Load();
sObjectMgr->LoadQuests(); // must be loaded after DBCs, creature_template, items, gameobject tables
TC_LOG_INFO("server.loading", "Checking Quest Disables");
File diff suppressed because it is too large Load Diff