qstbak/Questionable/Questionable.Functions/QuestFunctions.cs
2025-10-09 08:41:52 +10:00

1158 lines
36 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using Dalamud.Game.ClientState.Aetherytes;
using Dalamud.Game.Text;
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 Lumina.Excel.Sheets;
using Microsoft.Extensions.Logging;
using Questionable.Controller;
using Questionable.Data;
using Questionable.Model;
using Questionable.Model.Common;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
namespace Questionable.Functions;
internal sealed class QuestFunctions
{
private readonly QuestRegistry _questRegistry;
private readonly QuestData _questData;
private readonly AetheryteFunctions _aetheryteFunctions;
private readonly AlliedSocietyQuestFunctions _alliedSocietyQuestFunctions;
private readonly AlliedSocietyData _alliedSocietyData;
private readonly AetheryteData _aetheryteData;
private readonly Configuration _configuration;
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
private readonly IGameGui _gameGui;
private readonly IAetheryteList _aetheryteList;
private readonly ILogger<QuestFunctions> _logger;
private readonly HashSet<ushort> _alreadyLoggedUnobtainableQuestsDetailed = new HashSet<ushort>();
private readonly HashSet<ushort> _alreadyLoggedLevelRequirements = new HashSet<ushort>();
public QuestFunctions(QuestRegistry questRegistry, QuestData questData, AetheryteFunctions aetheryteFunctions, AlliedSocietyQuestFunctions alliedSocietyQuestFunctions, AlliedSocietyData alliedSocietyData, AetheryteData aetheryteData, Configuration configuration, IDataManager dataManager, IClientState clientState, IGameGui gameGui, IAetheryteList aetheryteList, ILogger<QuestFunctions> logger)
{
_questRegistry = questRegistry;
_questData = questData;
_aetheryteFunctions = aetheryteFunctions;
_alliedSocietyQuestFunctions = alliedSocietyQuestFunctions;
_alliedSocietyData = alliedSocietyData;
_aetheryteData = aetheryteData;
_configuration = configuration;
_dataManager = dataManager;
_clientState = clientState;
_gameGui = gameGui;
_aetheryteList = aetheryteList;
_logger = logger;
}
public unsafe QuestReference GetCurrentQuest(bool allowNewMsq = true)
{
QuestReference currentQuestInternal = GetCurrentQuestInternal(allowNewMsq);
currentQuestInternal.Deconstruct(out ElementId CurrentQuest, out byte Sequence, out MainScenarioQuestState State);
ElementId elementId = CurrentQuest;
byte sequence = Sequence;
MainScenarioQuestState state = State;
PlayerState* ptr = PlayerState.Instance();
if (elementId == null || elementId.Value == 0)
{
if (_clientState.TerritoryType == 181)
{
return new QuestReference(new QuestId(107), 0, MainScenarioQuestState.Available);
}
if (_clientState.TerritoryType == 182)
{
return new QuestReference(new QuestId(594), 0, MainScenarioQuestState.Available);
}
if (_clientState.TerritoryType == 183)
{
return new QuestReference(new QuestId(39), 0, MainScenarioQuestState.Available);
}
return QuestReference.NoQuest(state);
}
if (elementId.Value == 681)
{
if (IsQuestAccepted(elementId) || IsQuestComplete(elementId))
{
return new QuestReference(elementId, sequence, state);
}
return _configuration.General.GrandCompany switch
{
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.TwinAdder => new QuestReference(new QuestId(680), 0, state),
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.Maelstrom => new QuestReference(new QuestId(681), 0, state),
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.ImmortalFlames => new QuestReference(new QuestId(682), 0, state),
_ => QuestReference.NoQuest(MainScenarioQuestState.Unavailable),
};
}
if (elementId.Value == 3856 && !ptr->IsMountUnlocked(1u))
{
ushort num = (FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany)ptr->GrandCompany switch
{
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.TwinAdder => 700,
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.Maelstrom => 701,
FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.ImmortalFlames => 702,
_ => 0,
};
if (num != 0 && !QuestManager.IsQuestComplete(num))
{
return new QuestReference(new QuestId(num), QuestManager.GetQuestSequence(num), state);
}
}
else if (elementId.Value == 801)
{
QuestId questId = new QuestId(802);
if (IsQuestAccepted(questId))
{
return new QuestReference(questId, QuestManager.GetQuestSequence(questId.Value), state);
}
}
return currentQuestInternal;
}
public unsafe QuestReference GetCurrentQuestInternal(bool allowNewMsq)
{
QuestManager* ptr = QuestManager.Instance();
if (ptr == null)
{
return QuestReference.NoQuest(MainScenarioQuestState.Unavailable);
}
QuestReference questReference = GetMainScenarioQuest().Item1;
if (questReference.CurrentQuest != null && !_questRegistry.IsKnownQuest(questReference.CurrentQuest))
{
questReference = QuestReference.NoQuest(questReference.State);
}
if (questReference.CurrentQuest != null && !IsQuestAccepted(questReference.CurrentQuest))
{
if (allowNewMsq)
{
return questReference;
}
questReference = QuestReference.NoQuest(questReference.State);
}
List<(ElementId, byte)> list = new List<(ElementId, byte)>();
for (int num = ptr->TrackedQuests.Length - 1; num >= 0; num--)
{
TrackingWork trackingWork = ptr->TrackedQuests[num];
switch (trackingWork.QuestType)
{
case 1:
{
ElementId elementId = new QuestId(ptr->NormalQuests[trackingWork.Index].QuestId);
if (_questRegistry.IsKnownQuest(elementId))
{
list.Add((elementId, QuestManager.GetQuestSequence(elementId.Value)));
}
break;
}
}
}
if (_configuration.General.SkipLowPriorityDuties && list.Count > 0)
{
IReadOnlyList<(uint ContentFinderConditionId, ElementId QuestId, int Sequence)> lowPriorityQuests = _questRegistry.LowPriorityContentFinderConditionQuests;
list.RemoveAll(((ElementId Quest, byte Sequence) x) => lowPriorityQuests.Any<(uint, ElementId, int)>(((uint ContentFinderConditionId, ElementId QuestId, int Sequence) y) => x.Quest == y.QuestId && x.Sequence == y.Sequence));
}
if (list.Count > 0)
{
(ElementId, byte) tuple = list.First();
ElementId item = tuple.Item1;
byte item2 = tuple.Item2;
EAlliedSociety firstTrackedAlliedSociety = _alliedSocietyData.GetCommonAlliedSocietyTurnIn(item);
if (firstTrackedAlliedSociety != EAlliedSociety.None)
{
List<(ElementId, byte)> list2 = (from quest in list.Skip(1)
where _alliedSocietyData.GetCommonAlliedSocietyTurnIn(quest.Quest) == firstTrackedAlliedSociety
select quest).ToList();
if (list2.Count > 0)
{
if (item2 == byte.MaxValue)
{
foreach (var (currentQuest, b) in list2)
{
if (b != byte.MaxValue)
{
return new QuestReference(currentQuest, b, questReference.State);
}
}
}
else if (!IsOnAlliedSocietyMount())
{
list2.Insert(0, (item, item2));
_alliedSocietyData.GetCommonAlliedSocietyNpcs(firstTrackedAlliedSociety, out uint[] normalNpcs, out uint[] _);
if (normalNpcs.Length != 0)
{
(ElementId, byte)? tuple3 = (from x in list2
where x.Sequence < byte.MaxValue
where IsInteractSequence(x.Quest, x.Sequence, normalNpcs)
select x).Cast<(ElementId, byte)?>().FirstOrDefault();
if (tuple3.HasValue)
{
return new QuestReference(tuple3.Value.Item1, tuple3.Value.Item2, questReference.State);
}
}
}
}
}
return new QuestReference(item, item2, questReference.State);
}
ElementId elementId2 = (from x in GetNextPriorityQuestsThatCanBeAccepted()
where x.IsAvailable
select x.QuestId).FirstOrDefault();
if (elementId2 != null)
{
return new QuestReference(elementId2, QuestManager.GetQuestSequence(elementId2.Value), questReference.State);
}
if (questReference.CurrentQuest != null)
{
return questReference;
}
return QuestReference.NoQuest(questReference.State);
}
public unsafe (QuestReference, string?) GetMainScenarioQuest()
{
if (QuestManager.IsQuestComplete(3759))
{
AgentInterface* agentByInternalId = AgentModule.Instance()->GetAgentByInternalId(AgentId.QuestRedoHud);
if (agentByInternalId != null && agentByInternalId->IsAgentActive())
{
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)
{
ushort num = MemoryHelper.Read<ushort>((nint)((byte*)agentByInternalId + 46));
return (new QuestReference(new QuestId(num), QuestManager.GetQuestSequence(num), MainScenarioQuestState.Available), "NG+");
}
}
}
AgentScenarioTree* ptr = AgentScenarioTree.Instance();
if (ptr == null)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No Scenario Tree");
}
if (ptr->Data == null)
{
return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "Scenario Tree Data is null");
}
QuestId questId = new QuestId(ptr->Data->CurrentScenarioQuest);
string item = $"sq: {questId}";
if (questId.Value == 0)
{
if (IsMainScenarioQuestComplete())
{
return (QuestReference.NoQuest(MainScenarioQuestState.Complete), "Main Scenario is complete");
}
PlayerState* playerState = PlayerState.Instance();
List<QuestInfo> list = (from q in _questData.MainScenarioQuests
where q.StartingCity == 0 || q.StartingCity == playerState->StartTown
where IsReadyToAcceptQuest(q.QuestId, ignoreLevel: true)
select q).ToList();
if (list.Count == 0)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No potential quests found");
}
if (list.Count > 1)
{
if (list.All(delegate(QuestInfo x)
{
ushort value = x.QuestId.Value;
return (uint)(value - 680) <= 2u;
}))
{
questId = new QuestId(681);
}
else if (list.Any((QuestInfo x) => x.QuestId.Value == 1583))
{
questId = new QuestId(1583);
}
else if (list.Any((QuestInfo x) => x.QuestId.Value == 2451))
{
questId = new QuestId(2451);
}
else if (list.Any((QuestInfo x) => x.QuestId.Value == 3282))
{
questId = new QuestId(3282);
}
else if (list.Any((QuestInfo x) => x.QuestId.Value == 4359))
{
questId = new QuestId(4359);
}
else if (list.Any((QuestInfo x) => x.QuestId.Value == 4865))
{
questId = new QuestId(4865);
}
if (list.Count != 1)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "Multiple potential quests found: " + string.Join(", ", list.Select((QuestInfo x) => x.QuestId.Value)));
}
}
else
{
questId = (QuestId)list.Single().QuestId;
}
}
QuestManager* ptr2 = QuestManager.Instance();
if (IsQuestAccepted(questId) && ptr2->GetQuestById(questId.Value)->IsHidden)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Available), "Quest accepted but hidden");
}
if (IsQuestComplete(questId))
{
return (new QuestReference(questId, byte.MaxValue, MainScenarioQuestState.Available), $"Quest {questId.Value} complete");
}
if (!IsReadyToAcceptQuest(questId))
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), $"Not readdy to accept quest {questId.Value}");
}
byte? b = _clientState.LocalPlayer?.Level;
if (!b.HasValue)
{
return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "In loading screen");
}
if (_questRegistry.TryGetQuest(questId, out Questionable.Model.Quest quest) && quest.Info.Level > b)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "Low level");
}
return (new QuestReference(questId, QuestManager.GetQuestSequence(questId.Value), MainScenarioQuestState.Available), item);
}
private unsafe bool IsOnAlliedSocietyMount()
{
BattleChara* ptr = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
if (ptr != null && ptr->Mount.MountId != 0)
{
return _alliedSocietyData.Mounts.ContainsKey(ptr->Mount.MountId);
}
return false;
}
private bool IsInteractSequence(ElementId questId, byte sequenceNo, uint[] dataIds)
{
if (_questRegistry.TryGetQuest(questId, out Questionable.Model.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 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 unsafe List<PriorityQuestInfo> GetNextPriorityQuestsThatCanBeAccepted()
{
InventoryManager* ptr = InventoryManager.Instance();
int gil = ptr->GetItemCountInContainer(1u, InventoryType.Currency, isHq: false, 0);
return (from x in GetPriorityQuests()
where IsReadyToAcceptQuest(x)
select x).Select(delegate(ElementId x)
{
if (!_questRegistry.TryGetQuest(x, out Questionable.Model.Quest quest))
{
return new PriorityQuestInfo(x, "Unknown quest");
}
QuestStep questStep = quest.FindSequence(0)?.FindStep(0);
if (questStep == null)
{
return new PriorityQuestInfo(x, "No sequence 0 with steps");
}
if (!_aetheryteFunctions.IsTeleportUnlocked())
{
return new PriorityQuestInfo(x, "Teleport not unlocked");
}
if (!questStep.IsTeleportableForPriorityQuests())
{
return new PriorityQuestInfo(x, "Can't teleport to start");
}
DefaultInterpolatedStringHandler handler;
if (gil < EstimateTeleportCosts(quest))
{
IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
handler = new DefaultInterpolatedStringHandler(32, 2, invariantCulture);
handler.AppendLiteral("Not enough gil, estimated cost: ");
handler.AppendFormatted(EstimateTeleportCosts(quest), "N0");
handler.AppendFormatted(SeIconChar.Gil.ToIconString());
return new PriorityQuestInfo(x, string.Create(invariantCulture, ref handler));
}
EAetheryteLocation? value = quest.AllSteps().Select<(QuestSequence, int, QuestStep), EAetheryteLocation?>(delegate((QuestSequence Sequence, int StepId, QuestStep Step) y)
{
EAetheryteLocation? aetheryteShortcut = y.Step.AetheryteShortcut;
if (aetheryteShortcut.HasValue)
{
EAetheryteLocation valueOrDefault = aetheryteShortcut.GetValueOrDefault();
if (!_aetheryteFunctions.IsAetheryteUnlocked(valueOrDefault))
{
SkipConditions? skipConditions = y.Step.SkipConditions;
if (skipConditions == null || skipConditions.AetheryteShortcutIf?.AetheryteLocked != valueOrDefault)
{
return valueOrDefault;
}
}
}
AethernetShortcut aethernetShortcut = y.Step.AethernetShortcut;
if (aethernetShortcut != null)
{
if (!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.From))
{
return aethernetShortcut.From;
}
if (!_aetheryteFunctions.IsAetheryteUnlocked(aethernetShortcut.To))
{
return aethernetShortcut.To;
}
}
return (EAetheryteLocation?)null;
}).FirstOrDefault((EAetheryteLocation? y) => y.HasValue);
if (value.HasValue)
{
handler = new DefaultInterpolatedStringHandler(18, 1);
handler.AppendLiteral("Aetheryte locked: ");
handler.AppendFormatted(value);
return new PriorityQuestInfo(x, handler.ToStringAndClear());
}
return new PriorityQuestInfo(x);
}).ToList();
}
private int EstimateTeleportCosts(Questionable.Model.Quest quest)
{
Dictionary<EAetheryteLocation, float> cheapTeleports = _aetheryteList.Where((IAetheryteEntry x) => x.IsFavourite || _aetheryteFunctions.IsFreeAetheryte((EAetheryteLocation)x.AetheryteId)).ToDictionary((IAetheryteEntry x) => (EAetheryteLocation)x.AetheryteId, (IAetheryteEntry x) => _aetheryteFunctions.IsFreeAetheryte((EAetheryteLocation)x.AetheryteId) ? 0f : 0.5f);
int baseCost = ((quest.Info.Expansion == EExpansionVersion.ARealmReborn) ? 300 : 1000);
return (from x in quest.AllSteps()
where x.Step.AetheryteShortcut.HasValue
select x).Sum<(QuestSequence, int, QuestStep)>(((QuestSequence Sequence, int StepId, QuestStep Step) x) => (int)((float)baseCost * cheapTeleports.GetValueOrDefault(x.Step.AetheryteShortcut.Value, 1f)));
}
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?)_clientState.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
uint[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select((IReadOnlyList<uint> x) => x[0]).ToArray();
if (valueOrDefault != EClassJob.Adventurer)
{
list.AddRange(from x in _questRegistry.GetKnownClassJobQuests(valueOrDefault).Where(delegate(QuestInfo x)
{
if (_questRegistry.TryGetQuest(x.QuestId, out Questionable.Model.Quest quest))
{
IQuestInfo info = quest.Info;
QuestInfo questInfo = info as QuestInfo;
if (questInfo != null)
{
if (shadowbringersRoleQuestChapters.Contains(questInfo.NewGamePlusChapter))
{
return !QuestData.FinalShadowbringersRoleQuests.Any(IsQuestComplete);
}
if (QuestData.AllRoleQuestChapters.Any((IReadOnlyList<uint> y) => y.Contains(questInfo.NewGamePlusChapter)))
{
return false;
}
return true;
}
}
return false;
})
select x.QuestId);
}
}
return list.Where(_questRegistry.IsKnownQuest).ToList();
}
public unsafe bool IsReadyToAcceptQuest(ElementId questId, bool ignoreLevel = false)
{
_questRegistry.TryGetQuest(questId, out Questionable.Model.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;
}
}
else if (IsQuestComplete(questId))
{
return false;
}
goto IL_0077;
}
}
if (IsQuestAcceptedOrComplete(questId))
{
return false;
}
goto IL_0077;
IL_0077:
if (IsQuestLocked(questId))
{
return false;
}
if (!ignoreLevel)
{
byte b = _clientState.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;
}
}
return true;
}
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 SatisfactionSupplyNpcId)
{
return false;
}
if (elementId is AlliedSocietyDailyId)
{
return false;
}
if (elementId is UnlockLinkId)
{
return false;
}
if (elementId is AethernetId)
{
return false;
}
if (elementId is AetherCurrentId)
{
return false;
}
throw new ArgumentOutOfRangeException("elementId");
}
public unsafe bool IsQuestAccepted(QuestId questId)
{
return QuestManager.Instance()->IsQuestAccepted(questId.Value);
}
public bool IsQuestComplete(ElementId elementId)
{
if (elementId is QuestId questId)
{
return IsQuestComplete(questId);
}
if (elementId is SatisfactionSupplyNpcId)
{
return false;
}
if (elementId is AlliedSocietyDailyId)
{
return false;
}
if (elementId is UnlockLinkId unlockLinkId)
{
return IsQuestComplete(unlockLinkId);
}
if (elementId is AethernetId aethernetId)
{
return IsQuestComplete(aethernetId);
}
if (elementId is AetherCurrentId aetherCurrentId)
{
return IsQuestComplete(aetherCurrentId);
}
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 IsQuestComplete(AethernetId aethernetId)
{
if (!_questRegistry.TryGetQuest(aethernetId, out Questionable.Model.Quest quest))
{
_logger.LogWarning("Aethernet quest {AethernetId} not found in registry", aethernetId);
return false;
}
List<(QuestSequence, int, QuestStep)> list = (from x in quest.AllSteps()
where x.Step.InteractionType == EInteractionType.AttuneAethernetShard && x.Step.AethernetShard.HasValue
select x).ToList();
if (list.Count == 0)
{
_logger.LogWarning("Aethernet quest {AethernetId} has no aethernet shard attunement steps", aethernetId);
return false;
}
return list.All<(QuestSequence, int, QuestStep)>(((QuestSequence Sequence, int StepId, QuestStep Step) step) => _aetheryteFunctions.IsAetheryteUnlocked(step.Step.AethernetShard.Value));
}
public bool IsQuestComplete(AetherCurrentId aetherCurrentId)
{
return false;
}
public bool IsQuestLocked(ElementId elementId, ElementId? extraCompletedQuest = null)
{
if (elementId is QuestId questId)
{
return IsQuestLocked(questId, extraCompletedQuest);
}
if (elementId is SatisfactionSupplyNpcId satisfactionSupplyNpcId)
{
return IsQuestLocked(satisfactionSupplyNpcId);
}
if (elementId is AlliedSocietyDailyId alliedSocietyDailyId)
{
return IsQuestLocked(alliedSocietyDailyId);
}
if (elementId is UnlockLinkId unlockLinkId)
{
return IsQuestLocked(unlockLinkId);
}
if (elementId is AethernetId aethernetId)
{
return IsQuestLocked(aethernetId);
}
if (elementId is AetherCurrentId aetherCurrentId)
{
return IsQuestLocked(aetherCurrentId);
}
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 != FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany.None && questInfo.GrandCompany != GetGrandCompany())
{
return true;
}
if (questInfo.AlliedSociety != EAlliedSociety.None && questInfo.IsRepeatable)
{
return !IsDailyAlliedSocietyQuestAndAvailableToday(questId);
}
if (questInfo.IsMoogleDeliveryQuest)
{
byte b = PlayerState.Instance()->DeliveryLevel;
if (extraCompletedQuest != null && _questData.TryGetQuestInfo(extraCompletedQuest, out IQuestInfo questInfo2) && questInfo2 is QuestInfo { IsMoogleDeliveryQuest: not false })
{
b++;
}
if (questInfo.MoogleDeliveryLevel > b)
{
return true;
}
}
if (HasCompletedPreviousQuests(questInfo, extraCompletedQuest))
{
return !HasCompletedPreviousInstances(questInfo);
}
return true;
}
private bool IsQuestLocked(SatisfactionSupplyNpcId satisfactionSupplyNpcId)
{
SatisfactionSupplyInfo questInfo = (SatisfactionSupplyInfo)_questData.GetQuestInfo(satisfactionSupplyNpcId);
return !HasCompletedPreviousQuests(questInfo, null);
}
private unsafe bool IsQuestLocked(AlliedSocietyDailyId alliedSocietyDailyId)
{
byte beastTribeRank = PlayerState.Instance()->GetBeastTribeRank(alliedSocietyDailyId.AlliedSociety);
if (beastTribeRank != 0)
{
return beastTribeRank < alliedSocietyDailyId.Rank;
}
return true;
}
private static bool IsQuestLocked(UnlockLinkId unlockLinkId)
{
return IsQuestUnobtainable(unlockLinkId);
}
private bool IsQuestLocked(AethernetId aethernetId)
{
EAetheryteLocation aetheryteLocation = aethernetId.Value switch
{
1 => EAetheryteLocation.Limsa,
2 => EAetheryteLocation.Gridania,
3 => EAetheryteLocation.Uldah,
4 => EAetheryteLocation.GoldSaucer,
5 => EAetheryteLocation.Ishgard,
6 => EAetheryteLocation.Idyllshire,
7 => EAetheryteLocation.RhalgrsReach,
8 => EAetheryteLocation.Kugane,
9 => EAetheryteLocation.DomanEnclave,
10 => EAetheryteLocation.Crystarium,
11 => EAetheryteLocation.Eulmore,
12 => EAetheryteLocation.OldSharlayan,
13 => EAetheryteLocation.RadzAtHan,
14 => EAetheryteLocation.Tuliyollal,
15 => EAetheryteLocation.SolutionNine,
_ => throw new ArgumentOutOfRangeException("aethernetId", $"Unknown AethernetId: {aethernetId.Value}"),
};
if (!_aetheryteFunctions.IsAetheryteUnlocked(aetheryteLocation))
{
return true;
}
return false;
}
private static bool IsQuestLocked(AetherCurrentId aetherCurrentId)
{
return false;
}
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 IsQuestUnobtainable(ElementId elementId, ElementId? extraCompletedQuest = null)
{
if (elementId is QuestId questId)
{
return IsQuestUnobtainable(questId, extraCompletedQuest);
}
if (elementId is UnlockLinkId unlockLinkId)
{
return IsQuestUnobtainable(unlockLinkId);
}
return false;
}
public unsafe bool IsQuestUnobtainable(QuestId questId, ElementId? extraCompletedQuest = null)
{
IQuestInfo questInfo = _questData.GetQuestInfo(questId);
if (questInfo is UnlockLinkQuestInfo { QuestExpiry: { TimeOfDay: var timeOfDay } questExpiry })
{
TimeSpan timeSpan = new TimeSpan(23, 59, 59);
bool flag = false;
DateTime dateTime;
if (timeOfDay == TimeSpan.Zero || timeOfDay == timeSpan)
{
dateTime = EventInfoComponent.AtDailyReset(DateOnly.FromDateTime(questExpiry));
flag = true;
}
else
{
dateTime = ((questExpiry.Kind == DateTimeKind.Utc) ? questExpiry : questExpiry.ToUniversalTime());
}
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_logger.LogDebug("UnlockLink quest {QuestId} expiry raw={ExpiryRaw} Kind={Kind} TimeOfDay={TimeOfDay}", questId, questExpiry.ToString("o"), questExpiry.Kind, questExpiry.TimeOfDay);
_logger.LogDebug("UnlockLink quest {QuestId} normalized expiryUtc={ExpiryUtc:o} treatedAsDailyReset={TreatedAsDailyReset}", questId, dateTime, flag);
}
if (DateTime.UtcNow > dateTime)
{
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_logger.LogDebug("UnlockLink quest {QuestId} unobtainable: expiry {ExpiryUtc} (UTC) is before now {NowUtc}", questId, dateTime.ToString("o"), DateTime.UtcNow.ToString("o"));
}
return true;
}
}
QuestInfo questInfo2 = (QuestInfo)questInfo;
if ((int)questInfo2.Expansion > (int)PlayerState.Instance()->MaxExpansion)
{
return true;
}
if (questInfo2.JournalGenre >= 234 && questInfo2.JournalGenre <= 247)
{
if (_questRegistry.TryGetQuest(questId, out Questionable.Model.Quest quest))
{
List<QuestSequence> list = quest?.Root?.QuestSequence;
if (list != null && list.Count > 0)
{
goto IL_0288;
}
}
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_questData.ApplySeasonalOverride(questId, isSeasonal: true, null);
_logger.LogDebug("Quest {QuestId} unobtainable: journal genre is 'event (seasonal)' and no quest path", questId);
}
return true;
}
goto IL_0288;
IL_0288:
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();
TimeSpan timeOfDay2 = valueOrDefault.TimeOfDay;
TimeSpan timeSpan2 = new TimeSpan(23, 59, 59);
bool flag2 = false;
DateTime dateTime2;
if (timeOfDay2 == TimeSpan.Zero || timeOfDay2 == timeSpan2)
{
dateTime2 = EventInfoComponent.AtDailyReset(DateOnly.FromDateTime(valueOrDefault));
flag2 = true;
}
else
{
dateTime2 = ((valueOrDefault.Kind == DateTimeKind.Utc) ? valueOrDefault : valueOrDefault.ToUniversalTime());
}
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_logger.LogDebug("Quest {QuestId} seasonal expiry raw={ExpiryRaw} Kind={Kind} TimeOfDay={TimeOfDay}", questId, valueOrDefault.ToString("o"), valueOrDefault.Kind, valueOrDefault.TimeOfDay);
_logger.LogDebug("Quest {QuestId} normalized expiryUtc={ExpiryUtc:o} treatedAsDailyReset={TreatedAsDailyReset}", questId, dateTime2, flag2);
_logger.LogTrace("Quest {QuestId} expiry check: nowUtc={Now:o}, expiryUtc={Expiry:o}, expired={Expired}", questId, DateTime.UtcNow, dateTime2, DateTime.UtcNow > dateTime2);
}
if (DateTime.UtcNow > dateTime2)
{
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_logger.LogDebug("Quest {QuestId} unobtainable: seasonal expiry {ExpiryUtc} (UTC) is before now {NowUtc}", questId, dateTime2.ToString("o"), DateTime.UtcNow.ToString("o"));
}
return true;
}
}
if ((questInfo2.IsSeasonalEvent || questInfo2.IsSeasonalQuest) && !(questInfo2.SeasonalQuestExpiry is DateTime))
{
if (_alreadyLoggedUnobtainableQuestsDetailed.Add(questId.Value))
{
_logger.LogDebug("Quest {QuestId} is seasonal/event with no expiry; ShowIncompleteSeasonalEvents={ShowIncomplete}", questId, _configuration.General.ShowIncompleteSeasonalEvents);
}
if (!_configuration.General.ShowIncompleteSeasonalEvents)
{
return true;
}
}
if (_questData.GetLockedClassQuests().Contains(questId))
{
return true;
}
byte startTown = PlayerState.Instance()->StartTown;
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;
}
Dictionary<ushort, EClassJob> dictionary = 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
}
};
if (dictionary.TryGetValue(questId.Value, out var value) && dictionary.Any((KeyValuePair<ushort, EClassJob> x) => IsQuestAcceptedOrComplete(new QuestId(x.Key))) && (EClassJob)PlayerState.Instance()->FirstClass != value)
{
return true;
}
if (IsQuestRemoved(questId))
{
return true;
}
return false;
}
private static bool IsQuestUnobtainable(UnlockLinkId unlockLinkId)
{
if (unlockLinkId.Value == 506)
{
return !IsFestivalActive(160, (ushort)2);
}
if (unlockLinkId.Value == 568)
{
return !IsFestivalActive(160, (ushort)3);
}
return true;
}
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 IsQuestRemoved(questId);
}
return false;
}
private bool IsQuestRemoved(QuestId questId)
{
ushort value = questId.Value;
if (value == 487 || (uint)(value - 1428) <= 1u)
{
return true;
}
return false;
}
private bool HasCompletedPreviousQuests(IQuestInfo 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 unsafe bool IsClassJobUnlocked(EClassJob classJob)
{
ClassJob row = _dataManager.GetExcelSheet<ClassJob>().GetRow((uint)classJob);
ushort num = (ushort)row.UnlockQuest.RowId;
if (num != 0)
{
return IsQuestComplete(new QuestId(num));
}
PlayerState* ptr = PlayerState.Instance();
if (ptr != null)
{
return ptr->ClassJobLevels[row.ExpArrayIndex] > 0;
}
return false;
}
public bool IsJobUnlocked(EClassJob classJob)
{
return IsClassJobUnlocked((EClassJob)_dataManager.GetExcelSheet<ClassJob>().GetRow((uint)classJob).ClassJobParent.RowId);
}
public unsafe FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany GetGrandCompany()
{
return (FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany)PlayerState.Instance()->GrandCompany;
}
public bool IsMainScenarioQuestComplete()
{
return IsQuestComplete(_questData.LastMainScenarioQuestId);
}
}