Core/ Implement bonus quest system
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
|
||||
--
|
||||
-- Table structure for table `quest_v2_cli_task`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `quest_v2_cli_task`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `quest_v2_cli_task` (
|
||||
`ID` int unsigned NOT NULL DEFAULT '0',
|
||||
`QuestTitle` text,
|
||||
`BulletText` text,
|
||||
`UniqueBitFlag` int NOT NULL DEFAULT '0',
|
||||
`ConditionID` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltActiveQuest` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltClasses` smallint NOT NULL DEFAULT '0',
|
||||
`FiltCompletedQuestLogic` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMaxFactionID` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMaxFactionValue` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMinFactionID` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMinFactionValue` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMinSkillID` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltMinSkillValue` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltNonActiveQuest` int unsigned NOT NULL DEFAULT '0',
|
||||
`BreadCrumbID` int unsigned NOT NULL DEFAULT '0',
|
||||
`StartItem` int NOT NULL DEFAULT '0',
|
||||
`WorldStateExpressionID` int NOT NULL DEFAULT '0',
|
||||
`QuestInfoID` int unsigned NOT NULL DEFAULT '0',
|
||||
`ContentTuningID` int NOT NULL DEFAULT '0',
|
||||
`CovenantID` int NOT NULL DEFAULT '0',
|
||||
`Field_9_0_2_36206_022` int NOT NULL DEFAULT '0',
|
||||
`Flags1` int NOT NULL DEFAULT '0',
|
||||
`Flags2` int NOT NULL DEFAULT '0',
|
||||
`Flags3` int NOT NULL DEFAULT '0',
|
||||
`FiltCompletedQuest1` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltCompletedQuest2` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltCompletedQuest3` int unsigned NOT NULL DEFAULT '0',
|
||||
`FiltRaceMasks1` int NOT NULL DEFAULT '0',
|
||||
`FiltRaceMasks2` int NOT NULL DEFAULT '0',
|
||||
`VerifiedBuild` int NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`ID`,`VerifiedBuild`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `quest_v2_cli_task_locale`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `quest_v2_cli_task_locale`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `quest_v2_cli_task_locale` (
|
||||
`ID` int unsigned NOT NULL DEFAULT '0',
|
||||
`locale` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`QuestTitle_lang` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`BulletText_lang` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`VerifiedBuild` int NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
/*!50500 PARTITION BY LIST COLUMNS(locale)
|
||||
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
|
||||
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
|
||||
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
|
||||
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
|
||||
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
|
||||
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
|
||||
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
|
||||
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
|
||||
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
|
||||
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB) */;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
@@ -1935,6 +1935,16 @@ void HotfixDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(HOTFIX_SEL_QUEST_V2, "SELECT ID, UniqueBitFlag, UiQuestDetailsTheme FROM quest_v2 WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
|
||||
PREPARE_MAX_ID_STMT(HOTFIX_SEL_QUEST_V2, "SELECT MAX(ID) + 1 FROM quest_v2", CONNECTION_SYNCH);
|
||||
|
||||
// QuestV2CliTask.db2
|
||||
PrepareStatement(HOTFIX_SEL_QUEST_V2_CLI_TASK, "SELECT ID, QuestTitle, BulletText, UniqueBitFlag, ConditionID, FiltActiveQuest, FiltClasses, "
|
||||
"FiltCompletedQuestLogic, FiltMaxFactionID, FiltMaxFactionValue, FiltMinFactionID, FiltMinFactionValue, FiltMinSkillID, FiltMinSkillValue, "
|
||||
"FiltNonActiveQuest, BreadCrumbID, StartItem, WorldStateExpressionID, QuestInfoID, ContentTuningID, CovenantID, Field_9_0_2_36206_022, "
|
||||
"Flags1, Flags2, Flags3, FiltCompletedQuest1, FiltCompletedQuest2, FiltCompletedQuest3, FiltRaceMasks1, FiltRaceMasks2 "
|
||||
"FROM quest_v2_cli_task WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
|
||||
PREPARE_MAX_ID_STMT(HOTFIX_SEL_QUEST_V2_CLI_TASK, "SELECT MAX(ID) + 1 FROM quest_v2_cli_task", CONNECTION_SYNCH);
|
||||
PREPARE_LOCALE_STMT(HOTFIX_SEL_QUEST_V2_CLI_TASK, "SELECT ID, QuestTitle_lang, BulletText_lang FROM quest_v2_cli_task_locale"
|
||||
" WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH);
|
||||
|
||||
// QuestXp.db2
|
||||
PrepareStatement(HOTFIX_SEL_QUEST_XP, "SELECT ID, Difficulty1, Difficulty2, Difficulty3, Difficulty4, Difficulty5, Difficulty6, Difficulty7, "
|
||||
"Difficulty8, Difficulty9, Difficulty10 FROM quest_xp WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
|
||||
|
||||
@@ -5441,6 +5441,45 @@ struct QuestV2LoadInfo
|
||||
static constexpr DB2LoadInfo Instance{ Fields, 3, &QuestV2Meta::Instance, HOTFIX_SEL_QUEST_V2 };
|
||||
};
|
||||
|
||||
struct QuestV2CliTaskLoadInfo
|
||||
{
|
||||
static constexpr DB2FieldMeta Fields[30] =
|
||||
{
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
|
||||
{.IsSigned = false, .Type = FT_STRING, .Name = "QuestTitle" },
|
||||
{.IsSigned = false, .Type = FT_STRING, .Name = "BulletText" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "UniqueBitFlag" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "ConditionID" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltActiveQuest" },
|
||||
{.IsSigned = true, .Type = FT_SHORT, .Name = "FiltClasses" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltCompletedQuestLogic" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMaxFactionID" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMaxFactionValue" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMinFactionID" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMinFactionValue" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMinSkillID" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltMinSkillValue" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltNonActiveQuest" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "BreadCrumbID" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "StartItem" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "WorldStateExpressionID" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "QuestInfoID" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "ContentTuningID" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "CovenantID" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "Field_9_0_2_36206_022" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "Flags1" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "Flags2" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "Flags3" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltCompletedQuest1" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltCompletedQuest2" },
|
||||
{.IsSigned = false, .Type = FT_INT, .Name = "FiltCompletedQuest3" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "FiltRaceMasks1" },
|
||||
{.IsSigned = true, .Type = FT_INT, .Name = "FiltRaceMasks2" },
|
||||
};
|
||||
|
||||
static constexpr DB2LoadInfo Instance{ Fields, 30, &QuestV2CliTaskMeta::Instance, HOTFIX_SEL_QUEST_V2_CLI_TASK };
|
||||
};
|
||||
|
||||
struct QuestXpLoadInfo
|
||||
{
|
||||
static constexpr DB2FieldMeta Fields[11] =
|
||||
|
||||
@@ -379,6 +379,7 @@ DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore("QuestMon
|
||||
DB2Storage<QuestPackageItemEntry> sQuestPackageItemStore("QuestPackageItem.db2", &QuestPackageItemLoadInfo::Instance);
|
||||
DB2Storage<QuestSortEntry> sQuestSortStore("QuestSort.db2", &QuestSortLoadInfo::Instance);
|
||||
DB2Storage<QuestV2Entry> sQuestV2Store("QuestV2.db2", &QuestV2LoadInfo::Instance);
|
||||
DB2Storage<QuestV2CliTaskEntry> sQuestV2CliTaskStore("QuestV2CliTask.db2", &QuestV2CliTaskLoadInfo::Instance);
|
||||
DB2Storage<QuestXPEntry> sQuestXPStore("QuestXP.db2", &QuestXpLoadInfo::Instance);
|
||||
DB2Storage<RandPropPointsEntry> sRandPropPointsStore("RandPropPoints.db2", &RandPropPointsLoadInfo::Instance);
|
||||
DB2Storage<RewardPackEntry> sRewardPackStore("RewardPack.db2", &RewardPackLoadInfo::Instance);
|
||||
@@ -1505,6 +1506,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
|
||||
LOAD_DB2(sQuestPackageItemStore);
|
||||
LOAD_DB2(sQuestSortStore);
|
||||
LOAD_DB2(sQuestV2Store);
|
||||
LOAD_DB2(sQuestV2CliTaskStore);
|
||||
LOAD_DB2(sQuestXPStore);
|
||||
LOAD_DB2(sRandPropPointsStore);
|
||||
LOAD_DB2(sRewardPackStore);
|
||||
|
||||
@@ -446,6 +446,7 @@ TC_GAME_API extern DB2Storage<RenownRewardsEntry> sRenownRewar
|
||||
TC_GAME_API extern DB2Storage<RenownRewardsPlunderstormEntry> sRenownRewardsPlunderstormStore;
|
||||
TC_GAME_API extern DB2Storage<CampaignXConditionEntry> sCampaignXConditionStore;
|
||||
TC_GAME_API extern DB2Storage<QuestLineEntry> sQuestLineStore;
|
||||
TC_GAME_API extern DB2Storage<QuestV2CliTaskEntry> sQuestV2CliTaskStore;
|
||||
TC_GAME_API extern DB2Storage<CurrencyCategoryEntry> sCurrencyCategoryStore;
|
||||
TC_GAME_API extern DB2Storage<FactionGroupEntry> sFactionGroupStore;
|
||||
TC_GAME_API extern DB2Storage<UiTextureKitEntry> sUiTextureKitStore;
|
||||
|
||||
@@ -6761,6 +6761,34 @@ struct BattlePetStateEntry
|
||||
uint16 BattlePetVisualID;
|
||||
};
|
||||
|
||||
struct QuestV2CliTaskEntry
|
||||
{
|
||||
uint32 ID;
|
||||
LocalizedString QuestTitle;
|
||||
LocalizedString BulletText;
|
||||
int32 UniqueBitFlag;
|
||||
uint32 ConditionID;
|
||||
uint32 FiltActiveQuest;
|
||||
int16 FiltClasses;
|
||||
uint32 FiltCompletedQuestLogic;
|
||||
uint32 FiltMaxFactionID;
|
||||
uint32 FiltMaxFactionValue;
|
||||
uint32 FiltMinFactionID;
|
||||
uint32 FiltMinFactionValue;
|
||||
uint32 FiltMinSkillID;
|
||||
uint32 FiltMinSkillValue;
|
||||
uint32 FiltNonActiveQuest;
|
||||
uint32 BreadCrumbID;
|
||||
int32 StartItem;
|
||||
int32 WorldStateExpressionID;
|
||||
uint32 QuestInfoID;
|
||||
int32 ContentTuningID;
|
||||
int32 CovenantID;
|
||||
int32 Field_9_0_2_36206_022;
|
||||
std::array<int32, 3> Flags;
|
||||
std::array<uint32, 3> FiltCompletedQuest;
|
||||
std::array<int32, 2> FiltRaceMasks;
|
||||
};
|
||||
//WowCommunity
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
class ItemTemplate;
|
||||
struct ItemTemplate;
|
||||
class Player;
|
||||
struct ItemBonusEntry;
|
||||
struct MapDifficultyEntry;
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
#include "OmniumFolioMgr.h"
|
||||
#include "CovenantPackets.h"
|
||||
#include "WeeklyRewardsMgr.h"
|
||||
#include "WorldQuestMgr.h"
|
||||
#include "QuaternionData.h"
|
||||
#include "LFGPackets.h"
|
||||
#include <cmath>
|
||||
@@ -6279,6 +6280,7 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t
|
||||
|
||||
CheckAreaExplore();
|
||||
//WowCommunity
|
||||
UpdateWorldQuestPosition(x, y);
|
||||
_UpdateArchaeologySurveyIndicator();
|
||||
//WowCommunity
|
||||
|
||||
@@ -15196,6 +15198,12 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
|
||||
if (questGiver && questGiver->GetTypeId() == TYPEID_PLAYER)
|
||||
limittime = questGiver->ToPlayer()->getQuestStatusMap()[quest_id].Timer / IN_MILLISECONDS;
|
||||
|
||||
// World quests rotate on their own schedule, so the static LimitTime from the quest
|
||||
// template is wrong. Override before the timer is derived below, otherwise this is a no-op.
|
||||
if (quest->IsWorldQuest())
|
||||
if (uint32 worldQuestTimer = sWorldQuestMgr->GetTimerForQuest(uint32(quest_id)))
|
||||
limittime = worldQuestTimer;
|
||||
|
||||
AddTimedQuest(quest_id);
|
||||
questStatusData.Timer = limittime * IN_MILLISECONDS;
|
||||
endTime = GameTime::GetGameTime() + limittime;
|
||||
@@ -15203,6 +15211,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
|
||||
else
|
||||
questStatusData.Timer = 0;
|
||||
|
||||
|
||||
if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
|
||||
{
|
||||
pvpInfo.IsHostile = true;
|
||||
@@ -36116,3 +36125,56 @@ void Player::UpdateDynamicFlight(bool apply)
|
||||
RemoveAura(SPELL_DYNAMIC_FLIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::UpdateWorldQuestPosition(float x, float y)
|
||||
{
|
||||
if (time(nullptr) < m_areaQuestTimer)
|
||||
return;
|
||||
|
||||
m_areaQuestTimer = time(nullptr) + 2;
|
||||
|
||||
for (auto bonus_quest : sObjectMgr->BonusQuestsRects)
|
||||
{
|
||||
if (IsQuestRewarded(bonus_quest.first))
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (uint32 i = 0; i < bonus_quest.second.size(); ++i)
|
||||
{
|
||||
if (bonus_quest.second[i].IsIn(GetMapId(), x, y))
|
||||
found = true;
|
||||
}
|
||||
|
||||
uint32 slot = FindQuestSlot(bonus_quest.first);
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(bonus_quest.first);
|
||||
if (!quest)
|
||||
continue;
|
||||
|
||||
if (!found && slot < MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
SetQuestSlot(slot, 0);
|
||||
RemoveActiveQuest(uint32(bonus_quest.first), true);
|
||||
}
|
||||
else if (found && slot >= MAX_QUEST_LOG_SIZE)
|
||||
{
|
||||
if (!CanTakeQuest(quest, false))
|
||||
continue;
|
||||
|
||||
//if (quest->IsWorldQuest())
|
||||
//{
|
||||
// // Uniting the Isles, required for Legion world quests
|
||||
// if (!HasWorldQuestEnabled(quest->GetExpansion()))
|
||||
// continue;
|
||||
|
||||
// if (!sWorldQuestMgr->IsQuestActive(quest->GetQuestId()))
|
||||
// continue;
|
||||
//}
|
||||
|
||||
// AddQuest -> SetQuestSlot already zeroes UF::QuestLog::ObjectiveProgress for the
|
||||
// slot, and AdjustQuestObjectiveProgress seeds real progress, so there are no
|
||||
// server-side per-objective counters left to copy here.
|
||||
AddQuest(quest, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3973,6 +3973,10 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
|
||||
int32 GetQuestObjectiveProgress(uint32 questId, int8 storageIndex) const;
|
||||
void PlayConversation(uint32 conversationId);
|
||||
void UpdateDynamicFlight(bool apply = false);
|
||||
void UpdateWorldQuestPosition(float x, float y);
|
||||
|
||||
protected:
|
||||
uint32 m_areaQuestTimer;
|
||||
//WowCommunity
|
||||
};
|
||||
|
||||
|
||||
@@ -12411,3 +12411,71 @@ bool ObjectMgr::HasEnabledSpellScript(uint32 spellId, std::string_view scriptNam
|
||||
return script.second.second && GetScriptName(script.second.first) == scriptName;
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadQuestTasks()
|
||||
{
|
||||
for (auto const& it : _questTemplates)
|
||||
{
|
||||
Quest const* quest_template = it.second.get();
|
||||
|
||||
//CriteriaEntry const* criteria = sCriteriaStore.LookupEntry(l_I);
|
||||
//if (!criteria || criteria->Type != CRITERIA_TYPE_COMPLETE_QUEST)
|
||||
// continue;
|
||||
|
||||
QuestV2CliTaskEntry const* cliTask = sQuestV2CliTaskStore.LookupEntry(quest_template->GetQuestId());
|
||||
if (!cliTask)
|
||||
continue;
|
||||
|
||||
if (!quest_template || quest_template->GetQuestType() != QUEST_TYPE_TASK)
|
||||
continue;
|
||||
|
||||
if (QuestPOIData const* questPOIs = GetQuestPOIData(quest_template->GetQuestId()))
|
||||
{
|
||||
if (questPOIs->Blobs.size() <= 0)
|
||||
continue;
|
||||
|
||||
for (auto itr = questPOIs->Blobs.begin(); itr != questPOIs->Blobs.end(); ++itr)
|
||||
{
|
||||
BonusQuestRectEntry rec;
|
||||
rec.MapID = itr->MapID;
|
||||
rec.MinX = 5000000.f;
|
||||
rec.MinY = 5000000.f;
|
||||
rec.MaxX = -5000000.f;
|
||||
rec.MaxY = -5000000.f;
|
||||
|
||||
for (auto const& poi : itr->Points)
|
||||
{
|
||||
if (poi.X == 0 && poi.Y == 0)
|
||||
continue;
|
||||
|
||||
rec.MaxX = std::max(rec.MaxX, poi.X);
|
||||
rec.MaxY = std::max(rec.MaxY, poi.Y);
|
||||
|
||||
rec.MinX = std::min(rec.MinX, poi.X);
|
||||
rec.MinY = std::min(rec.MinY, poi.Y);
|
||||
}
|
||||
|
||||
// If the area is a single point, we assume 50m
|
||||
if (itr->Points.size() == 1)
|
||||
{
|
||||
rec.MinX -= 50;
|
||||
rec.MaxX += 50;
|
||||
rec.MinY -= 50;
|
||||
rec.MaxY += 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 areaWidth = std::abs(rec.MaxX - rec.MinX);
|
||||
rec.MinX -= 0.175f * float(areaWidth);
|
||||
rec.MaxX += 0.175f * float(areaWidth);
|
||||
|
||||
int32 areaHeight = std::abs(rec.MaxY - rec.MinY);
|
||||
rec.MinY -= 0.175f * float(areaHeight);
|
||||
rec.MaxY += 0.175f * float(areaHeight);
|
||||
}
|
||||
|
||||
BonusQuestsRects[quest_template->GetQuestId()].push_back(rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1199,6 +1199,19 @@ class TC_GAME_API ObjectMgr
|
||||
SpellScriptsBounds GetSpellScriptsBounds(uint32 spellId);
|
||||
//WowCommunity
|
||||
bool HasEnabledSpellScript(uint32 spellId, std::string_view scriptName);
|
||||
void LoadQuestTasks();
|
||||
|
||||
struct BonusQuestRectEntry
|
||||
{
|
||||
int32 MinX, MinY, MaxX, MaxY;
|
||||
uint32 MapID;
|
||||
|
||||
bool IsIn(uint32 mapID, int x, int y)
|
||||
{
|
||||
return MapID == mapID && MinX <= x && MaxX >= x && MinY <= y && MaxY >= y;
|
||||
}
|
||||
};
|
||||
std::map<uint32, std::vector<BonusQuestRectEntry>> BonusQuestsRects;
|
||||
//WowCommunity
|
||||
RepRewardRate const* GetRepRewardRate(uint32 factionId) const
|
||||
{
|
||||
|
||||
@@ -165,3 +165,37 @@ void WorldQuestMgr::FillActiveWorldQuests(std::vector<WorldPackets::Quest::World
|
||||
updates.emplace_back(active.StartTime, active.QuestID, remaining, active.VariableID, active.Value);
|
||||
}
|
||||
}
|
||||
|
||||
TeamId WorldQuestMgr::GetQuestTeamId(Quest const* quest)
|
||||
{
|
||||
// Quest::GetAllowableRaces() is a RaceMask<int32, 2>, not a raw integer - RawValue is a
|
||||
// std::array, so it cannot be compared/masked directly. Use the RaceMask operators with
|
||||
// the matching <int32, 2> specialisations.
|
||||
Trinity::RaceMask<int32, 2> const allowableRaces = quest->GetAllowableRaces();
|
||||
|
||||
// No race restriction at all: usable by both teams.
|
||||
if (allowableRaces == RACEMASK_ALL_v<int32, 2>)
|
||||
return TEAM_NEUTRAL;
|
||||
|
||||
if (!(allowableRaces & RACEMASK_ALLIANCE_v<int32, 2>).IsEmpty())
|
||||
return TEAM_ALLIANCE;
|
||||
|
||||
if (!(allowableRaces & RACEMASK_HORDE_v<int32, 2>).IsEmpty())
|
||||
return TEAM_HORDE;
|
||||
|
||||
return TEAM_NEUTRAL;
|
||||
}
|
||||
|
||||
ActiveWorldQuest* WorldQuestMgr::GetActiveWorldQuest(uint32 questId)
|
||||
{
|
||||
auto const itr = _active.find(questId);
|
||||
return itr != _active.end() ? &itr->second : nullptr;
|
||||
}
|
||||
|
||||
uint32 WorldQuestMgr::GetTimerForQuest(uint32 questId)
|
||||
{
|
||||
if (ActiveWorldQuest const* worldQuest = GetActiveWorldQuest(questId))
|
||||
return worldQuest->GetRemainingTime();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
#define TRINITY_WORLDQUESTMGR_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "GameTime.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class Quest;
|
||||
|
||||
namespace WorldPackets::Quest
|
||||
{
|
||||
struct WorldQuestUpdateInfo;
|
||||
@@ -45,6 +49,15 @@ struct ActiveWorldQuest
|
||||
time_t EndTime = 0; // StartTime + template Duration
|
||||
int32 VariableID = 0;
|
||||
int32 Value = 0;
|
||||
|
||||
// Remaining seconds until expiry, clamped at 0. Derived from EndTime (populated by
|
||||
// WorldQuestMgr::Activate) rather than a template lookup, so this stays valid without
|
||||
// needing WorldQuestMgr to be complete at this point in the header.
|
||||
uint32 GetRemainingTime() const
|
||||
{
|
||||
time_t const now = GameTime::GetGameTime();
|
||||
return EndTime > now ? uint32(EndTime - now) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Activates world quests from `world_quest_template`, tracks their expiry, refreshes them on a
|
||||
@@ -73,6 +86,9 @@ class TC_GAME_API WorldQuestMgr
|
||||
// Appends every currently-active world quest to the SMSG_WORLD_QUEST_UPDATE_RESPONSE payload.
|
||||
void FillActiveWorldQuests(std::vector<WorldPackets::Quest::WorldQuestUpdateInfo>& updates) const;
|
||||
|
||||
ActiveWorldQuest* GetActiveWorldQuest(uint32 questId);
|
||||
TeamId GetQuestTeamId(Quest const* quest);
|
||||
uint32 GetTimerForQuest(uint32 questId);
|
||||
bool IsWorldQuestActive(uint32 questId) const { return _active.find(questId) != _active.end(); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -2220,6 +2220,9 @@ bool World::SetInitialWorldSettings()
|
||||
TC_LOG_INFO("server.loading", "Loading delve data...");
|
||||
sDelveMgr->Initialize();
|
||||
|
||||
TC_LOG_INFO("server.loading", "Loading Quest task...");
|
||||
sObjectMgr->LoadQuestTasks();
|
||||
|
||||
TC_LOG_INFO("server.loading", "Loading Perks Program vendor items...");
|
||||
sPerksProgramMgr->LoadVendorItems();
|
||||
|
||||
|
||||
@@ -27,7 +27,13 @@ enum Spells
|
||||
SPELL_NECROTIC_BOLT = 320170,
|
||||
SPELL_UNHOLY_FRENZY = 320012,
|
||||
SPELL_NECROTIC_BREATH_TRIGGER = 337074,
|
||||
EVENT_LAND_OF_THE_DEAD = 1,
|
||||
};
|
||||
|
||||
enum events
|
||||
{
|
||||
EVENT_NECROTIC_BOLT = 1,
|
||||
EVENT_LAND_OF_THE_DEAD,
|
||||
EVENT_FINAL_HARVEST,
|
||||
EVENT_NECROTIC_BREATH
|
||||
};
|
||||
|
||||
@@ -48,9 +54,9 @@ struct boss_amarth : public BossAI
|
||||
{
|
||||
_JustEngagedWith(who);
|
||||
Talk(0);
|
||||
events.ScheduleEvent(SPELL_NECROTIC_BOLT, 3s);
|
||||
events.ScheduleEvent(EVENT_NECROTIC_BOLT, 3s);
|
||||
events.ScheduleEvent(EVENT_LAND_OF_THE_DEAD, 5s);
|
||||
events.ScheduleEvent(SPELL_FINAL_HARVEST, 8s);
|
||||
events.ScheduleEvent(EVENT_FINAL_HARVEST, 8s);
|
||||
events.ScheduleEvent(EVENT_NECROTIC_BREATH, 10s);
|
||||
if (Creature* amarth = me->FindNearestCreature(NPC_AMARTH, 30.0f, true))
|
||||
amarth->EnterVehicle(me);
|
||||
@@ -70,7 +76,7 @@ struct boss_amarth : public BossAI
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case SPELL_NECROTIC_BOLT:
|
||||
case EVENT_NECROTIC_BOLT:
|
||||
DoCastVictim(SPELL_NECROTIC_BOLT, false);
|
||||
events.Repeat(15s);
|
||||
break;
|
||||
@@ -83,7 +89,7 @@ struct boss_amarth : public BossAI
|
||||
events.Repeat(20s);
|
||||
break;
|
||||
|
||||
case SPELL_FINAL_HARVEST:
|
||||
case EVENT_FINAL_HARVEST:
|
||||
me->CastSpell(nullptr, SPELL_FINAL_HARVEST, false);
|
||||
events.Repeat(25s);
|
||||
break;
|
||||
|
||||
@@ -47,7 +47,8 @@ enum Events
|
||||
EVENT_SPELL_CAST_START = 1,
|
||||
EVENT_SPELL_PERIODIC_DAMAGE,
|
||||
EVENT_SPELL_PERIODIC_MISSED,
|
||||
EVENT_UNIT_SPELLCAST_START
|
||||
EVENT_UNIT_SPELLCAST_START,
|
||||
EVENT_BLOOD_GORGE
|
||||
};
|
||||
|
||||
//162691
|
||||
@@ -184,7 +185,7 @@ struct npc_carrion_worm_nw : public ScriptedAI
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
_events.ScheduleEvent(SPELL_BLOOD_GORGE, 3s);
|
||||
_events.ScheduleEvent(EVENT_BLOOD_GORGE, 3s);
|
||||
}
|
||||
|
||||
void ExecuteEvent(uint32 eventId) override
|
||||
@@ -198,7 +199,7 @@ struct npc_carrion_worm_nw : public ScriptedAI
|
||||
|
||||
switch (eventId)
|
||||
{
|
||||
case SPELL_BLOOD_GORGE:
|
||||
case EVENT_BLOOD_GORGE:
|
||||
DoCastVictim(SPELL_BLOOD_GORGE, false);
|
||||
me->AddAura(SPELL_BLOOD_GORGE_BUFF, me);
|
||||
_events.Repeat(10s);
|
||||
|
||||
@@ -51,16 +51,6 @@ public:
|
||||
if (!leaderguid)
|
||||
return;
|
||||
|
||||
if (Player* leader = ObjectAccessor::GetPlayer(*player, leaderguid))
|
||||
{
|
||||
if (leader->GetTeam() == HORDE)
|
||||
faction_group = HORDE;
|
||||
else if (leader->GetTeam() == ALLIANCE)
|
||||
faction_group = ALLIANCE;
|
||||
else
|
||||
faction_group = HORDE;
|
||||
}
|
||||
|
||||
player->GetGroup()->AddFollowerModeBots(player);
|
||||
|
||||
}
|
||||
|
||||
@@ -49,16 +49,6 @@ public:
|
||||
if (!leaderguid)
|
||||
return;
|
||||
|
||||
if (Player* leader = ObjectAccessor::GetPlayer(*player, leaderguid))
|
||||
{
|
||||
if (leader->GetTeam() == HORDE)
|
||||
faction_group = HORDE;
|
||||
else if (leader->GetTeam() == ALLIANCE)
|
||||
faction_group = ALLIANCE;
|
||||
else
|
||||
faction_group = HORDE;
|
||||
}
|
||||
|
||||
player->GetGroup()->AddFollowerModeBots(player);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,16 +54,6 @@ public:
|
||||
if (!leaderguid)
|
||||
return;
|
||||
|
||||
if (Player* leader = ObjectAccessor::GetPlayer(*player, leaderguid))
|
||||
{
|
||||
if (leader->GetTeam() == HORDE)
|
||||
faction_group = HORDE;
|
||||
else if (leader->GetTeam() == ALLIANCE)
|
||||
faction_group = ALLIANCE;
|
||||
else
|
||||
faction_group = HORDE;
|
||||
}
|
||||
|
||||
player->GetGroup()->AddFollowerModeBots(player);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user