using System; using System.Collections.Generic; using System.Linq; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; using LLib.GameData; using LLib.GameUI; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Controller.Utils; using Questionable.Data; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Functions; internal sealed class QuestFunctions { private static readonly Dictionary CloseToHomeQuests = new Dictionary { { 108, EClassJob.Marauder }, { 109, EClassJob.Arcanist }, { 85, EClassJob.Lancer }, { 123, EClassJob.Archer }, { 124, EClassJob.Conjurer }, { 568, EClassJob.Gladiator }, { 569, EClassJob.Pugilist }, { 570, EClassJob.Thaumaturge } }; private readonly QuestRegistry _questRegistry; private readonly QuestData _questData; private readonly JournalData _journalData; private readonly AetheryteFunctions _aetheryteFunctions; private readonly AlliedSocietyQuestFunctions _alliedSocietyQuestFunctions; private readonly AlliedSocietyData _alliedSocietyData; private readonly Configuration _configuration; private readonly IDataManager _dataManager; private readonly IObjectTable _objectTable; private readonly IGameGui _gameGui; private readonly ILogger _logger; private readonly HashSet _alreadyLoggedUnobtainableQuestsDetailed = new HashSet(); private readonly HashSet _alreadyLoggedLevelRequirements = new HashSet(); private readonly HashSet _alreadyLoggedGcLockedQuests = new HashSet(); private bool _lastLoggedFallbackLookup; private ElementId? _lastLoggedNotReadyQuest; private ElementId? _lastLoggedAcceptedHiddenMsq; private static readonly HashSet RemovedQuestIds = new HashSet { 487, 1428, 1429 }; public QuestFunctions(QuestRegistry questRegistry, QuestData questData, JournalData journalData, AetheryteFunctions aetheryteFunctions, AlliedSocietyQuestFunctions alliedSocietyQuestFunctions, AlliedSocietyData alliedSocietyData, Configuration configuration, IDataManager dataManager, IObjectTable objectTable, IGameGui gameGui, ILogger logger) { _questRegistry = questRegistry; _questData = questData; _journalData = journalData; _aetheryteFunctions = aetheryteFunctions; _alliedSocietyQuestFunctions = alliedSocietyQuestFunctions; _alliedSocietyData = alliedSocietyData; _configuration = configuration; _dataManager = dataManager; _objectTable = objectTable; _gameGui = gameGui; _logger = logger; } public unsafe (QuestReference, string?) GetMainScenarioQuest() { (QuestReference, string)? tuple = TryGetNewGamePlusQuest(); if (tuple.HasValue) { return tuple.Value; } AgentScenarioTree* ptr = AgentScenarioTree.Instance(); if (ptr == null) { _logger.LogTrace("GetMainScenarioQuest: scenarioTree is null"); return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No Scenario Tree"); } if (ptr->Data == null) { _logger.LogTrace("GetMainScenarioQuest: scenarioTree->Data is null"); return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "Scenario Tree Data is null"); } QuestId resolvedQuest = new QuestId(ptr->Data->MainScenarioQuestIds[0]); string item = $"sq: {resolvedQuest}"; if (resolvedQuest.Value == 0) { (QuestReference, string)? tuple2 = TryResolveFallbackMsq(out resolvedQuest); if (tuple2.HasValue) { return tuple2.Value; } } else { _lastLoggedFallbackLookup = false; } QuestManager* ptr2 = QuestManager.Instance(); if (IsQuestAccepted(resolvedQuest) && ptr2->GetQuestById(resolvedQuest.Value)->IsHidden) { if (_lastLoggedAcceptedHiddenMsq != resolvedQuest) { _logger.LogDebug("GetMainScenarioQuest: Quest {QuestId} is accepted but hidden", resolvedQuest); _lastLoggedAcceptedHiddenMsq = resolvedQuest; } return (QuestReference.NoQuest(MainScenarioQuestState.Available), "Quest accepted but hidden"); } if (IsQuestComplete(resolvedQuest)) { _logger.LogDebug("GetMainScenarioQuest: Quest {QuestId} is already complete", resolvedQuest); return (new QuestReference(resolvedQuest, byte.MaxValue, MainScenarioQuestState.Available), $"Quest {resolvedQuest.Value} complete"); } if (!IsReadyToAcceptQuest(resolvedQuest, ignoreLevel: true)) { if (_lastLoggedNotReadyQuest != resolvedQuest) { _logger.LogDebug("GetMainScenarioQuest: Quest {QuestId} is not ready to accept (ignoring level)", resolvedQuest); _lastLoggedNotReadyQuest = resolvedQuest; } return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), $"Not ready to accept quest {resolvedQuest.Value}"); } _lastLoggedNotReadyQuest = null; IPlayerCharacter? localPlayer = _objectTable.LocalPlayer; if (localPlayer != null) { _ = localPlayer.Level; if (0 == 0) { _lastLoggedAcceptedHiddenMsq = null; return (new QuestReference(resolvedQuest, QuestManager.GetQuestSequence(resolvedQuest.Value), MainScenarioQuestState.Available), item); } } _logger.LogTrace("GetMainScenarioQuest: In loading screen"); return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "In loading screen"); } private unsafe (QuestReference, string?)? TryGetNewGamePlusQuest() { if (!QuestManager.IsQuestComplete(3759)) { return null; } AgentInterface* agentByInternalId = AgentModule.Instance()->GetAgentByInternalId(AgentId.QuestRedoHud); if (agentByInternalId == null || !agentByInternalId->IsAgentActive()) { return null; } AtkUnitBase* addonPtr; bool flag = !_gameGui.TryGetAddonByName("QuestRedoHud", out addonPtr) || addonPtr->AtkValuesCount != 4; if (!flag) { uint uInt = addonPtr->AtkValues->UInt; bool flag2 = ((uInt == 0 || uInt - 2 <= 2) ? true : false); flag = !flag2; } if (flag) { return null; } ushort num = MemoryHelper.Read((nint)((byte*)agentByInternalId + 46)); return (new QuestReference(new QuestId(num), QuestManager.GetQuestSequence(num), MainScenarioQuestState.Available), "NG+"); } private (QuestReference, string?)? TryResolveFallbackMsq(out QuestId resolvedQuest) { resolvedQuest = new QuestId(0); if (IsMainScenarioQuestComplete()) { return (QuestReference.NoQuest(MainScenarioQuestState.Complete), "Main Scenario is complete"); } if (!_lastLoggedFallbackLookup) { _logger.LogDebug("GetMainScenarioQuest: MSQ from scenario tree is 0, using fallback lookup"); _lastLoggedFallbackLookup = true; } List list = (from q in _questData.MainScenarioQuests where q.StartingCity == 0 || q.StartingCity == PlayerStateHelper.GetStartTown() where IsReadyToAcceptQuest(q.QuestId, ignoreLevel: true) select q).ToList(); _logger.LogTrace("GetMainScenarioQuest: Found {Count} potential quests from fallback", list.Count); if (list.Count == 0) { return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No potential quests found"); } if (list.Count == 1) { resolvedQuest = (QuestId)list[0].QuestId; _logger.LogDebug("GetMainScenarioQuest: Selected quest from fallback = {QuestId}", resolvedQuest); return null; } QuestId questId = ResolveBranchingMsq(list); if ((object)questId == null) { _logger.LogWarning("GetMainScenarioQuest: Multiple potential quests found: {Quests}", string.Join(", ", list.Select((QuestInfo x) => x.QuestId.Value))); return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "Multiple potential quests found: " + string.Join(", ", list.Select((QuestInfo x) => x.QuestId.Value))); } resolvedQuest = questId; _logger.LogDebug("GetMainScenarioQuest: Selected quest from fallback = {QuestId}", resolvedQuest); return null; } private static QuestId? ResolveBranchingMsq(List potentialQuests) { if (potentialQuests.All(delegate(QuestInfo x) { ushort value = x.QuestId.Value; return (uint)(value - 680) <= 2u; })) { return new QuestId(681); } if (potentialQuests.Any((QuestInfo x) => x.QuestId.Value == 1583)) { return new QuestId(1583); } if (potentialQuests.Any((QuestInfo x) => x.QuestId.Value == 2451)) { return new QuestId(2451); } if (potentialQuests.Any((QuestInfo x) => x.QuestId.Value == 3282)) { return new QuestId(3282); } if (potentialQuests.Any((QuestInfo x) => x.QuestId.Value == 4359)) { return new QuestId(4359); } if (potentialQuests.Any((QuestInfo x) => x.QuestId.Value == 4865)) { return new QuestId(4865); } return null; } internal unsafe bool IsOnAlliedSocietyMount() { BattleChara* ptr = (BattleChara*)(_objectTable.LocalPlayer?.Address ?? 0); if (ptr != null && ptr->Mount.MountId != 0) { return _alliedSocietyData.Mounts.ContainsKey(ptr->Mount.MountId); } return false; } internal bool IsInteractSequence(ElementId questId, byte sequenceNo, uint[] dataIds) { if (_questRegistry.TryGetQuest(questId, out Quest quest)) { return quest.FindSequence(sequenceNo)?.Steps.All(delegate(QuestStep x) { if (x == null || x.InteractionType != EInteractionType.WalkTo) { if (x != null && x.InteractionType == EInteractionType.Interact) { uint? dataId = x.DataId; if (dataId.HasValue) { uint valueOrDefault = dataId.GetValueOrDefault(); return ((ReadOnlySpan)dataIds).Contains(valueOrDefault); } } return false; } return true; }) ?? false; } return false; } public unsafe QuestProgressInfo? GetQuestProgressInfo(ElementId elementId) { if (elementId is QuestId questId) { QuestWork* questById = QuestManager.Instance()->GetQuestById(questId.Value); if (questById == null) { return null; } return new QuestProgressInfo(*questById); } return null; } public List GetNextPriorityQuestsThatCanBeAccepted() { return (from x in GetPriorityQuests() where IsReadyToAcceptQuest(x) select x).Select(delegate(ElementId x) { if (!_questRegistry.TryGetQuest(x, out Quest quest)) { return new PriorityQuestInfo(x, "Unknown quest"); } if (quest.FindSequence(0)?.FindStep(0) == null) { return new PriorityQuestInfo(x, "No sequence 0 with steps"); } return ((quest.Info.Level > 5 || !(quest.Info is QuestInfo questInfo) || questInfo.ClassJobs.Count <= 0 || !questInfo.ClassJobs.Any((EClassJob classJob) => !classJob.IsCrafter() && !classJob.IsGatherer())) && !_aetheryteFunctions.IsTeleportUnlocked()) ? new PriorityQuestInfo(x, "Teleport not unlocked") : new PriorityQuestInfo(x); }).ToList(); } public List GetPriorityQuests(bool onlyClassAndRoleQuests = false) { List list = new List(); if (!onlyClassAndRoleQuests) { if (!_configuration.Advanced.SkipARealmRebornHardModePrimals) { list.AddRange(QuestData.HardModePrimals.Skip(1)); } if (!_configuration.Advanced.SkipCrystalTowerRaids) { list.AddRange(QuestData.CrystalTowerQuests); } } if (!_configuration.Advanced.SkipClassJobQuests) { EClassJob valueOrDefault = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault(); if (valueOrDefault != EClassJob.Adventurer) { list.AddRange(from x in _questRegistry.GetKnownClassJobQuests(valueOrDefault, includeRoleQuests: false) select x.QuestId); } } if (!_configuration.Advanced.SkipRoleQuests) { EClassJob valueOrDefault2 = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault(); if (valueOrDefault2 != EClassJob.Adventurer) { ERoleQuestPreference roleQuestPreference = _configuration.Advanced.RoleQuestPreference; if (!QuestData.JobMatchesRolePreference(roleQuestPreference, valueOrDefault2)) { _logger.LogDebug("Role quest preference {Preference} does not match current job {ClassJob}, skipping", roleQuestPreference, valueOrDefault2); } else { IReadOnlyList roleQuestChaptersForPreference = QuestData.GetRoleQuestChaptersForPreference(roleQuestPreference, valueOrDefault2); IReadOnlyList finalRoleQuests = QuestData.GetFinalRoleQuests(roleQuestPreference, valueOrDefault2); if (roleQuestChaptersForPreference.Count > 0 && !IsQuestComplete(finalRoleQuests[0])) { list.AddRange(from x in _questData.GetRoleQuestsByChapter(roleQuestChaptersForPreference[0]) where _questRegistry.IsKnownQuest(x.QuestId) select x.QuestId); } if (_configuration.Advanced.ContinueRoleQuestsAfterShb) { for (int num = 1; num < roleQuestChaptersForPreference.Count && num < finalRoleQuests.Count; num++) { if (!IsQuestComplete(finalRoleQuests[num])) { list.AddRange(from x in _questData.GetRoleQuestsByChapter(roleQuestChaptersForPreference[num]) where _questRegistry.IsKnownQuest(x.QuestId) select x.QuestId); } } } } } } if (!onlyClassAndRoleQuests && _configuration.Advanced.AutoPrioritizeAlliedSocietyRankUp) { list.AddRange(GetAllRankUpQuestsNeeded()); } return list.Where(_questRegistry.IsKnownQuest).ToList(); } public unsafe bool IsReadyToAcceptQuest(ElementId questId, bool ignoreLevel = false) { if (IsQuestBlacklisted(questId)) { return false; } _questRegistry.TryGetQuest(questId, out Quest quest); if (quest != null) { IQuestInfo info = quest.Info; if (info != null && info.IsRepeatable) { if (IsQuestAccepted(questId)) { return false; } if (questId is QuestId questId2 && IsDailyAlliedSocietyQuest(questId2)) { if (QuestManager.Instance()->IsDailyQuestCompleted(questId.Value)) { return false; } if (!IsDailyAlliedSocietyQuestAndAvailableToday(questId2)) { return false; } if (_configuration.Advanced.AutoPrioritizeAlliedSocietyRankUp && WouldOvercapAlliedSocietyReputation(questId2)) { return false; } } else if (IsQuestComplete(questId)) { return false; } goto IL_009f; } } if (IsQuestAcceptedOrComplete(questId)) { return false; } goto IL_009f; IL_009f: if (IsQuestLocked(questId)) { return false; } if (!ignoreLevel) { byte b = _objectTable.LocalPlayer?.Level ?? 0; if (b == 0) { return false; } if (quest != null && quest.Info.Level > b) { if (_alreadyLoggedLevelRequirements.Add(questId.Value)) { _logger.LogDebug("Quest {QuestId} level requirement not met: required {RequiredLevel}, current {CurrentLevel}", questId, quest.Info.Level, b); } return false; } if (quest == null && questId is QuestId questId3 && _questData.TryGetQuestInfo(questId3, out IQuestInfo questInfo) && questInfo is QuestInfo questInfo2 && questInfo2.Level > b) { if (_alreadyLoggedLevelRequirements.Add(questId3.Value)) { _logger.LogDebug("Quest {QuestId} (from data) level requirement not met: required {RequiredLevel}, current {CurrentLevel}", questId3, questInfo2.Level, b); } return false; } } if (quest != null && quest.Info.ClassJobs.Count > 0) { EClassJob valueOrDefault = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault(); if (valueOrDefault.IsLimitedJob() && !quest.Info.ClassJobs.Contains(valueOrDefault)) { return false; } } return true; } public bool IsQuestBlacklisted(ElementId questId) { return _configuration.General.BlacklistedQuests.Contains(questId); } public bool IsQuestAcceptedOrComplete(ElementId elementId) { if (!IsQuestComplete(elementId)) { return IsQuestAccepted(elementId); } return true; } public bool IsQuestAccepted(ElementId elementId) { if (elementId is QuestId questId) { return IsQuestAccepted(questId); } if (elementId is UnlockLinkId) { return false; } throw new ArgumentOutOfRangeException("elementId"); } public unsafe bool IsQuestAccepted(QuestId questId) { return QuestManager.Instance()->IsQuestAccepted(questId.Value); } public unsafe (QuestId Id, byte Sequence)? GetCurrentQuestWithoutPath() { QuestManager* ptr = QuestManager.Instance(); if (ptr == null) { return null; } for (int num = ptr->TrackedQuests.Length - 1; num >= 0; num--) { TrackingWork trackingWork = ptr->TrackedQuests[num]; if (trackingWork.QuestType == 1) { ushort questId = ptr->NormalQuests[trackingWork.Index].QuestId; if (questId != 0 && !HasUsablePath(new QuestId(questId))) { return (new QuestId(questId), QuestManager.GetQuestSequence(questId)); } } } for (int i = 0; i < ptr->NormalQuests.Length; i++) { QuestWork questWork = ptr->NormalQuests[i]; if (questWork.QuestId != 0) { QuestId questId2 = new QuestId(questWork.QuestId); if (!HasUsablePath(questId2)) { return (questId2, questWork.Sequence); } } } return null; } private bool HasUsablePath(QuestId questId) { if (_questRegistry.TryGetQuest(questId, out Quest quest) && !quest.Root.Disabled) { return quest.Root.QuestSequence.Count > 0; } return false; } public bool IsQuestComplete(ElementId elementId) { if (elementId is QuestId questId) { return IsQuestComplete(questId); } if (elementId is UnlockLinkId unlockLinkId) { return IsQuestComplete(unlockLinkId); } throw new ArgumentOutOfRangeException("elementId"); } public bool IsQuestComplete(QuestId questId) { return QuestManager.IsQuestComplete(questId.Value); } public unsafe bool IsQuestComplete(UnlockLinkId unlockLinkId) { return UIState.Instance()->IsUnlockLinkUnlocked(unlockLinkId.Value); } public bool IsQuestLocked(ElementId elementId, ElementId? extraCompletedQuest = null) { if (elementId is QuestId questId) { return IsQuestLocked(questId, extraCompletedQuest); } if (elementId is UnlockLinkId unlockLinkId) { return IsQuestLocked(unlockLinkId); } throw new ArgumentOutOfRangeException("elementId"); } private unsafe bool IsQuestLocked(QuestId questId, ElementId? extraCompletedQuest = null) { if (IsQuestUnobtainable(questId, extraCompletedQuest)) { return true; } QuestInfo questInfo = (QuestInfo)_questData.GetQuestInfo(questId); if (questInfo.GrandCompany != GrandCompany.None) { GrandCompany grandCompany = GetGrandCompany(); byte grandCompanyRank = GetGrandCompanyRank(); byte requiredGCRank = QuestData.GetRequiredGCRank(questId); if (questInfo.GrandCompany != grandCompany || grandCompanyRank < requiredGCRank) { if (_alreadyLoggedGcLockedQuests.Add(questId.Value)) { _logger.LogDebug("Quest {QuestId} locked: required GC {RequiredGc}, required rank {RequiredRank}, player GC {PlayerGc}, player rank {PlayerRank}", questId, questInfo.GrandCompany, requiredGCRank, grandCompany, grandCompanyRank); } return true; } } if (questInfo.AlliedSociety != EAlliedSociety.None) { byte beastTribeRank = PlayerStateHelper.GetBeastTribeRank((byte)questInfo.AlliedSociety); if (beastTribeRank == 0 || beastTribeRank < questInfo.AlliedSocietyRank) { return true; } if (questInfo.IsRepeatable) { return !IsDailyAlliedSocietyQuestAndAvailableToday(questId); } } if (_journalData.MoogleDeliveryGenreId.HasValue && questInfo.JournalGenre == _journalData.MoogleDeliveryGenreId.Value) { byte b = PlayerStateHelper.GetDeliveryLevel(); if (extraCompletedQuest != null && _questData.TryGetQuestInfo(extraCompletedQuest, out IQuestInfo questInfo2) && questInfo2 is QuestInfo questInfo3 && _journalData.MoogleDeliveryGenreId.HasValue && questInfo3.JournalGenre == _journalData.MoogleDeliveryGenreId.Value) { b++; } if (questInfo.MoogleDeliveryLevel > b) { return true; } } ushort value = questId.Value; if ((uint)(value - 1432) <= 2u) { RetainerManager* ptr = RetainerManager.Instance(); if (ptr == null || ptr->MaxRetainerEntitlement == 0) { return true; } } if (HasCompletedPreviousQuests(questInfo, extraCompletedQuest)) { return !HasCompletedPreviousInstances(questInfo); } return true; } private static bool IsQuestLocked(UnlockLinkId unlockLinkId) { return IsQuestUnobtainableStatic(unlockLinkId); } public bool IsDailyAlliedSocietyQuest(QuestId questId) { QuestInfo questInfo = (QuestInfo)_questData.GetQuestInfo(questId); if (questInfo.AlliedSociety != EAlliedSociety.None) { return questInfo.IsRepeatable; } return false; } public bool IsDailyAlliedSocietyQuestAndAvailableToday(QuestId questId) { if (!IsDailyAlliedSocietyQuest(questId)) { return false; } QuestInfo questInfo = (QuestInfo)_questData.GetQuestInfo(questId); return _alliedSocietyQuestFunctions.GetAvailableAlliedSocietyQuests(questInfo.AlliedSociety).Contains(questId); } public bool WouldOvercapAlliedSocietyReputation(QuestId questId) { if (!IsDailyAlliedSocietyQuest(questId)) { return false; } return _alliedSocietyQuestFunctions.WouldOvercapReputation(questId); } public QuestId? GetRankUpQuestIfAtCapacity(EAlliedSociety alliedSociety) { (byte, ushort, ushort, bool, bool)? reputationStatus = _alliedSocietyQuestFunctions.GetReputationStatus(alliedSociety); if (!reputationStatus.HasValue) { return null; } var (currentRank, _, _, flag, flag2) = reputationStatus.Value; if (flag || !flag2) { return null; } return _alliedSocietyQuestFunctions.GetRankUpQuest(alliedSociety, currentRank); } public List GetAllRankUpQuestsNeeded() { List list = new List(); foreach (EAlliedSociety item in from s in Enum.GetValues() where s != EAlliedSociety.None select s) { QuestId rankUpQuestIfAtCapacity = GetRankUpQuestIfAtCapacity(item); if (rankUpQuestIfAtCapacity != null && !IsQuestComplete(rankUpQuestIfAtCapacity) && !IsQuestAccepted(rankUpQuestIfAtCapacity)) { list.Add(rankUpQuestIfAtCapacity); } } return list; } public bool IsQuestUnobtainable(ElementId elementId, ElementId? extraCompletedQuest = null) { if (elementId is QuestId questId) { return IsQuestUnobtainable(questId, extraCompletedQuest); } if (elementId is UnlockLinkId unlockLinkId) { return IsQuestUnobtainableStatic(unlockLinkId); } return false; } private static bool IsQuestUnobtainableStatic(UnlockLinkId unlockLinkId) { if (unlockLinkId.Value == 506) { return !IsFestivalActive(160, (ushort)2); } if (unlockLinkId.Value == 568) { return !IsFestivalActive(160, (ushort)3); } return true; } public bool IsQuestUnobtainable(QuestId questId, ElementId? extraCompletedQuest = null) { IQuestInfo questInfo = _questData.GetQuestInfo(questId); if (questInfo is UnlockLinkQuestInfo { QuestExpiry: { } questExpiry }) { DateTime dateTime = ExpiryUtils.NormalizeExpiry(questExpiry); if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) { _logger.LogDebug("UnlockLink quest {QuestId} expiry raw={ExpiryRaw} normalized={ExpiryUtc:o}", questId, questExpiry.ToString("o"), dateTime); } if (DateTime.UtcNow > dateTime) { return true; } } QuestInfo questInfo2 = (QuestInfo)questInfo; if ((int)questInfo2.Expansion > (int)PlayerStateHelper.GetMaxExpansion()) { return true; } if (questInfo2.JournalGenre.HasValue && _journalData.SeasonalEventGenreIds.Contains(questInfo2.JournalGenre.Value)) { if (_questRegistry.TryGetQuest(questId, out Quest quest)) { List list = quest?.Root?.QuestSequence; if (list != null && list.Count > 0) { goto IL_0188; } } if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value)) { _questData.ApplySeasonalOverride(questId, isSeasonal: true, null); _logger.LogDebug("Quest {QuestId} unobtainable: journal genre {Genre} is 'event (seasonal)' and no quest path", questId, questInfo2.JournalGenre); } return true; } goto IL_0188; IL_0188: if (questInfo2.QuestLocks.Count > 0) { int num = questInfo2.QuestLocks.Count((QuestId x) => IsQuestComplete(x) || x.Equals(extraCompletedQuest)); if (questInfo2.QuestLockJoin == EQuestJoin.All && questInfo2.QuestLocks.Count == num) { return true; } if (questInfo2.QuestLockJoin == EQuestJoin.AtLeastOne && num > 0) { return true; } } DateTime? seasonalQuestExpiry = questInfo2.SeasonalQuestExpiry; if (seasonalQuestExpiry.HasValue) { DateTime valueOrDefault = seasonalQuestExpiry.GetValueOrDefault(); if (DateTime.UtcNow > ExpiryUtils.NormalizeExpiry(valueOrDefault)) { return true; } } if ((questInfo2.IsSeasonalEvent || questInfo2.IsSeasonalQuest) && !questInfo2.SeasonalQuestExpiry.HasValue && !_configuration.General.ShowIncompleteSeasonalEvents) { return true; } if (QuestData.GetLockedClassQuests().Contains(questId)) { return true; } byte startTown = PlayerStateHelper.GetStartTown(); if (questInfo2.StartingCity > 0 && questInfo2.StartingCity != startTown) { return true; } if (questId.Value == 674 && startTown == 3) { return true; } if (questId.Value == 673 && startTown != 3) { return true; } if (CloseToHomeQuests.TryGetValue(questId.Value, out var value) && CloseToHomeQuests.Any((KeyValuePair x) => IsQuestAcceptedOrComplete(new QuestId(x.Key))) && (EClassJob)PlayerStateHelper.GetFirstClass() != value) { return true; } if (IsQuestRemoved(questId)) { return true; } return false; } private unsafe static bool IsFestivalActive(ushort id, ushort? phase = null) { for (int i = 0; i < GameMain.Instance()->ActiveFestivals.Length; i++) { GameMain.Festival festival = GameMain.Instance()->ActiveFestivals[i]; if (festival.Id == id) { if (phase.HasValue) { return festival.Phase == phase; } return true; } } return false; } public bool IsQuestRemoved(ElementId elementId) { if (elementId is QuestId questId) { return RemovedQuestIds.Contains(questId.Value); } return false; } private bool HasCompletedPreviousQuests(QuestInfo questInfo, ElementId? extraCompletedQuest) { if (questInfo.PreviousQuests.Count == 0) { return true; } int num = questInfo.PreviousQuests.Count((PreviousQuestInfo x) => HasEnoughProgressOnPreviousQuest(x) || x.QuestId.Equals(extraCompletedQuest)); if (questInfo.PreviousQuestJoin == EQuestJoin.All && questInfo.PreviousQuests.Count == num) { return true; } if (questInfo.PreviousQuestJoin == EQuestJoin.AtLeastOne && num > 0) { return true; } return false; } private bool HasEnoughProgressOnPreviousQuest(PreviousQuestInfo previousQuestInfo) { if (IsQuestComplete(previousQuestInfo.QuestId)) { return true; } if (previousQuestInfo.Sequence != 0 && IsQuestAccepted(previousQuestInfo.QuestId)) { QuestProgressInfo questProgressInfo = GetQuestProgressInfo(previousQuestInfo.QuestId); if (questProgressInfo != null) { return questProgressInfo.Sequence >= previousQuestInfo.Sequence; } return false; } return false; } private static bool HasCompletedPreviousInstances(QuestInfo questInfo) { if (questInfo.PreviousInstanceContent.Count == 0) { return true; } int num = questInfo.PreviousInstanceContent.Count((ushort x) => UIState.IsInstanceContentCompleted(x)); if (questInfo.PreviousInstanceContentJoin == EQuestJoin.All && questInfo.PreviousInstanceContent.Count == num) { return true; } if (questInfo.PreviousInstanceContentJoin == EQuestJoin.AtLeastOne && num > 0) { return true; } return false; } public bool IsClassJobUnlocked(EClassJob classJob) { return ClassJobHelper.IsClassJobUnlocked(classJob, _dataManager); } public bool IsJobUnlocked(EClassJob classJob) { return ClassJobHelper.IsJobUnlocked(classJob, _dataManager); } public static GrandCompany GetGrandCompany() { return GrandCompanyHelper.GetGrandCompany(); } public static byte GetGrandCompanyRank() { return GrandCompanyHelper.GetGrandCompanyRank(); } public bool IsMainScenarioQuestComplete() { return IsQuestComplete(_questData.LastMainScenarioQuestId); } public (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) GetMsqLevelLockInfo() { byte b = _objectTable.LocalPlayer?.Level ?? 0; if (b == 0) { return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); } QuestReference item = GetMainScenarioQuest().Item1; if (item.CurrentQuest == null) { return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); } if (IsQuestAccepted(item.CurrentQuest)) { return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); } if (_questRegistry.TryGetQuest(item.CurrentQuest, out Quest quest)) { int level = quest.Info.Level; if (level <= b) { return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: level, questName: null); } int item2 = level - b; return (isLevelLocked: true, levelsNeeded: item2, requiredLevel: level, questName: quest.Info.Name); } if (item.CurrentQuest is QuestId elementId && _questData.TryGetQuestInfo(elementId, out IQuestInfo questInfo) && questInfo is QuestInfo questInfo2) { int level2 = questInfo2.Level; if (level2 <= b) { return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: level2, questName: null); } int item3 = level2 - b; return (isLevelLocked: true, levelsNeeded: item3, requiredLevel: level2, questName: questInfo2.Name); } return (isLevelLocked: false, levelsNeeded: 0, requiredLevel: 0, questName: null); } }