public QuestReference GetCurrentQuest(bool allowNewMsq = true)
{
var internalQuest = GetCurrentQuestInternal(allowNewMsq);
- var (currentQuest, sequence, msqInformationAvailable) = internalQuest;
+ var (currentQuest, sequence, questState) = internalQuest;
PlayerState* playerState = PlayerState.Instance();
if (currentQuest == null || currentQuest.Value == 0)
{
if (_clientState.TerritoryType == 181) // Starting in Limsa
- return new(new QuestId(107), 0, true);
+ return new(new QuestId(107), 0, MainScenarioQuestState.Available);
if (_clientState.TerritoryType == 182) // Starting in Ul'dah
- return new(new QuestId(594), 0, true);
+ return new(new QuestId(594), 0, MainScenarioQuestState.Available);
if (_clientState.TerritoryType == 183) // Starting in Gridania
- return new(new QuestId(39), 0, true);
- return QuestReference.NoQuest(msqInformationAvailable);
+ return new(new QuestId(39), 0, MainScenarioQuestState.Available);
+ return QuestReference.NoQuest(questState);
}
else if (currentQuest.Value == 681)
{
// if we have already picked up the GC quest, just return the progress for it
if (IsQuestAccepted(currentQuest) || IsQuestComplete(currentQuest))
- return new(currentQuest, sequence, msqInformationAvailable);
+ return new(currentQuest, sequence, questState);
// The company you keep...
return _configuration.General.GrandCompany switch
{
- GrandCompany.TwinAdder => new(new QuestId(680), 0, true),
- GrandCompany.Maelstrom => new(new QuestId(681), 0, true),
- GrandCompany.ImmortalFlames => new(new QuestId(682), 0, true),
- _ => QuestReference.NoQuest(),
+ GrandCompany.TwinAdder => new(new QuestId(680), 0, questState),
+ GrandCompany.Maelstrom => new(new QuestId(681), 0, questState),
+ GrandCompany.ImmortalFlames => new(new QuestId(682), 0, questState),
+ _ => QuestReference.NoQuest(MainScenarioQuestState.Unavailable),
};
}
else if (currentQuest.Value == 3856 && !playerState->IsMountUnlocked(1)) // we come in peace
};
if (chocoboQuest != 0 && !QuestManager.IsQuestComplete(chocoboQuest))
- return new(new QuestId(chocoboQuest), QuestManager.GetQuestSequence(chocoboQuest), true);
+ return new(new QuestId(chocoboQuest), QuestManager.GetQuestSequence(chocoboQuest), questState);
}
else if (currentQuest.Value == 801)
{
// skeletons in her closet, finish 'broadening horizons' to unlock the white wolf gate
QuestId broadeningHorizons = new QuestId(802);
if (IsQuestAccepted(broadeningHorizons))
- return new(broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons.Value), true);
+ return new(broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons.Value), questState);
}
return internalQuest;
{
var questManager = QuestManager.Instance();
if (questManager == null)
- return QuestReference.NoQuest(false);
+ return QuestReference.NoQuest(MainScenarioQuestState.Unavailable);
// always prioritize accepting MSQ quests, to make sure we don't turn in one MSQ quest and then go off to do
// side quests until the end of time.
- QuestReference msqQuest = GetMainScenarioQuest();
+ (QuestReference msqQuest, _) = GetMainScenarioQuest();
if (msqQuest.CurrentQuest != null && !_questRegistry.IsKnownQuest(msqQuest.CurrentQuest))
- msqQuest = QuestReference.NoQuest(msqQuest.MsqInformationAvailable);
+ msqQuest = QuestReference.NoQuest(msqQuest.State);
if (msqQuest.CurrentQuest != null && !IsQuestAccepted(msqQuest.CurrentQuest))
{
if (allowNewMsq)
return msqQuest;
else
- msqQuest = QuestReference.NoQuest(msqQuest.MsqInformationAvailable);
+ msqQuest = QuestReference.NoQuest(msqQuest.State);
}
// Use the quests in the same order as they're shown in the to-do list, e.g. if the MSQ is the first item,
{
// only if the other quest isn't ready to be turned in
if (sequence != 255)
- return new(quest, sequence, msqQuest.MsqInformationAvailable);
+ return new(quest, sequence, msqQuest.State);
}
}
else if (!IsOnAlliedSocietyMount())
.Cast<(ElementId, byte)?>()
.FirstOrDefault();
if (talkToNormalNpcs != null)
- return new(talkToNormalNpcs.Value.Item1, talkToNormalNpcs.Value.Item2, msqQuest.MsqInformationAvailable);
+ return new(talkToNormalNpcs.Value.Item1, talkToNormalNpcs.Value.Item2, msqQuest.State);
}
/*
}
}
- return new(firstTrackedQuest, firstTrackedSequence, msqQuest.MsqInformationAvailable);
+ return new(firstTrackedQuest, firstTrackedSequence, msqQuest.State);
}
ElementId? priorityQuest = GetNextPriorityQuestsThatCanBeAccepted().FirstOrDefault();
{
// if we have an accepted msq quest, and know of no quest of those currently in the to-do list...
// (1) try and find a priority quest to do
- return new(priorityQuest, QuestManager.GetQuestSequence(priorityQuest.Value), msqQuest.MsqInformationAvailable);
+ return new(priorityQuest, QuestManager.GetQuestSequence(priorityQuest.Value), msqQuest.State);
}
else if (msqQuest.CurrentQuest != null)
{
return msqQuest;
}
- return QuestReference.NoQuest(msqQuest.MsqInformationAvailable);
+ return QuestReference.NoQuest(msqQuest.State);
}
- private QuestReference GetMainScenarioQuest()
+ public (QuestReference, string?) GetMainScenarioQuest()
{
if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
{
// redoHud+44 is chapter
// redoHud+46 is quest
ushort questId = MemoryHelper.Read<ushort>((nint)questRedoHud + 46);
- return new(new QuestId(questId), QuestManager.GetQuestSequence(questId), true);
+ return (new(new QuestId(questId), QuestManager.GetQuestSequence(questId), MainScenarioQuestState.Available), "NG+");
}
}
}
var scenarioTree = AgentScenarioTree.Instance();
if (scenarioTree == null)
- return QuestReference.NoQuest(false);
+ return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No Scenario Tree");
if (scenarioTree->Data == null)
- return QuestReference.NoQuest(false);
+ return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "Scenario Tree Data is null");
QuestId currentQuest = new QuestId(scenarioTree->Data->CurrentScenarioQuest);
+ string extraData = $"sq: {currentQuest}";
if (currentQuest.Value == 0)
{
if (IsMainScenarioQuestComplete())
- return QuestReference.NoQuest();
+ return (QuestReference.NoQuest(MainScenarioQuestState.Complete), "Main Scenario is complete");
// fallback lookup; find a quest which isn't completed but where all prequisites are met
// excluding branching quests
.Where(q => IsReadyToAcceptQuest(q.QuestId, true))
.ToList();
if (potentialQuests.Count == 0)
- return QuestReference.NoQuest(false);
+ return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No potential quests found");
else if (potentialQuests.Count > 1)
{
// for all of these (except the GC quests), questionable normally auto-picks the next quest based on the
else if (potentialQuests.Any(x => x.QuestId.Value == 4865))
currentQuest = new QuestId(4865); // DT: To Kozama'uk vs. To Urqopacha
if (potentialQuests.Count != 1)
- return QuestReference.NoQuest(false);
+ return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "Multiple potential quests found: " +
+ string.Join(", ", potentialQuests.Select(x => x.QuestId.Value)));
}
else
currentQuest = (QuestId)potentialQuests.Single().QuestId;
// if the MSQ is hidden, we generally ignore it
QuestManager* questManager = QuestManager.Instance();
if (IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest.Value)->IsHidden)
- return QuestReference.NoQuest();
+ return (QuestReference.NoQuest(MainScenarioQuestState.Available), "Quest accepted but hidden");
// it can sometimes happen (although this isn't reliably reproducible) that the quest returned here
// is one you've just completed. We return 255 as sequence here, since that is the end of said quest;
// but this is just really hoping that this breaks nothing.
if (IsQuestComplete(currentQuest))
- return new(currentQuest, 255, true);
+ return (new(currentQuest, 255, MainScenarioQuestState.Available), $"Quest {currentQuest.Value} complete");
else if (!IsReadyToAcceptQuest(currentQuest))
- return QuestReference.NoQuest();
+ return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), $"Not readdy to accept quest {currentQuest.Value}");
var currentLevel = _clientState.LocalPlayer?.Level;
// are we in a loading screen?
if (currentLevel == null)
- return QuestReference.NoQuest();
+ return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "In loading screen");
// if we're not at a high enough level to continue, we also ignore it
if (_questRegistry.TryGetQuest(currentQuest, out Quest? quest)
&& quest.Info.Level > currentLevel)
- return QuestReference.NoQuest();
+ return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "Low level");
- return new(currentQuest, QuestManager.GetQuestSequence(currentQuest.Value), true);
+ return (new(currentQuest, QuestManager.GetQuestSequence(currentQuest.Value), MainScenarioQuestState.Available), extraData);
}
private bool IsOnAlliedSocietyMount()
}
}
-public sealed record QuestReference(ElementId? CurrentQuest, byte Sequence, bool MsqInformationAvailable)
+public sealed record QuestReference(ElementId? CurrentQuest, byte Sequence, MainScenarioQuestState State)
{
- public static QuestReference NoQuest(bool msqInformationAvailable = true) => new(null, 0, msqInformationAvailable);
+ public static QuestReference NoQuest(MainScenarioQuestState state) => new(null, 0, state);
+}
+
+public enum MainScenarioQuestState
+{
+ Unavailable,
+ Available,
+ Complete,
+ LoadingScreen,
}