qstbak/Questionable/Questionable.Functions/QuestFunctions.cs
2026-08-17 20:29:32 +10:00

995 lines
29 KiB
C#

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<ushort, EClassJob> CloseToHomeQuests = new Dictionary<ushort, EClassJob>
{
{
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<QuestFunctions> _logger;
private readonly HashSet<ushort> _alreadyLoggedUnobtainableQuestsDetailed = new HashSet<ushort>();
private readonly HashSet<ushort> _alreadyLoggedLevelRequirements = new HashSet<ushort>();
private readonly HashSet<ushort> _alreadyLoggedGcLockedQuests = new HashSet<ushort>();
private bool _lastLoggedFallbackLookup;
private ElementId? _lastLoggedNotReadyQuest;
private ElementId? _lastLoggedAcceptedHiddenMsq;
private static readonly HashSet<ushort> RemovedQuestIds = new HashSet<ushort> { 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<QuestFunctions> 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<AtkUnitBase>("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<ushort>((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<QuestInfo> 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<QuestInfo> 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<uint>)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<PriorityQuestInfo> 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<ElementId> GetPriorityQuests(bool onlyClassAndRoleQuests = false)
{
List<ElementId> list = new List<ElementId>();
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<uint> roleQuestChaptersForPreference = QuestData.GetRoleQuestChaptersForPreference(roleQuestPreference, valueOrDefault2);
IReadOnlyList<QuestId> 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<QuestId> GetAllRankUpQuestsNeeded()
{
List<QuestId> list = new List<QuestId>();
foreach (EAlliedSociety item in from s in Enum.GetValues<EAlliedSociety>()
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<QuestSequence> 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<ushort, EClassJob> 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);
}
}