1
0
Fork 0
forked from aly/qstbak

muffin v6.35

This commit is contained in:
alydev 2025-10-29 06:43:45 +10:00
parent a10041e3fd
commit ac85599236
7 changed files with 5916 additions and 5158 deletions

View file

@ -262,16 +262,13 @@
}
}
},
"QuestsAccepted": {
"type": "array",
"items": {
"type": [
"number",
"string"
]
}
"MinimumLevel": {
"type": "integer",
"description": "Skip this step if the player level is greater than or equal to this value. Useful for steps that should only be done once at low levels (e.g., early aetheryte attunements).",
"minimum": 1,
"maximum": 100
},
"QuestsCompleted": {
"QuestsAccepted": {
"type": "array",
"items": {
"type": [

File diff suppressed because it is too large Load diff

View file

@ -38,6 +38,8 @@ public sealed class SkipStepConditions
public EAetheryteLocation? AetheryteUnlocked { get; set; }
public ushort? MinimumLevel { get; set; }
public NearPositionCondition? NearPosition { get; set; }
public NearPositionCondition? NotNearPosition { get; set; }
@ -50,7 +52,7 @@ public sealed class SkipStepConditions
{
return false;
}
if ((CompletionQuestVariablesFlags.Count <= 0 || !CompletionQuestVariablesFlags.Any((QuestWorkValue x) => x != null)) && !Flying.HasValue && !Chocobo.HasValue && !Diving.HasValue && !NotTargetable && InTerritory.Count <= 0 && NotInTerritory.Count <= 0 && Item == null && QuestsAccepted.Count <= 0 && QuestsCompleted.Count <= 0 && NotNamePlateIconId.Count <= 0 && !AetheryteLocked.HasValue && !AetheryteUnlocked.HasValue && NearPosition == null && NotNearPosition == null)
if ((CompletionQuestVariablesFlags.Count <= 0 || !CompletionQuestVariablesFlags.Any((QuestWorkValue x) => x != null)) && !Flying.HasValue && !Chocobo.HasValue && !Diving.HasValue && !NotTargetable && InTerritory.Count <= 0 && NotInTerritory.Count <= 0 && Item == null && QuestsAccepted.Count <= 0 && QuestsCompleted.Count <= 0 && NotNamePlateIconId.Count <= 0 && !AetheryteLocked.HasValue && !AetheryteUnlocked.HasValue && !MinimumLevel.HasValue && NearPosition == null && NotNearPosition == null)
{
return ExtraCondition.HasValue;
}

View file

@ -93,6 +93,10 @@ internal static class SkipCondition
{
return true;
}
if (CheckLevelCondition(skipConditions))
{
return true;
}
if (CheckQuestWorkConditions(elementId, step))
{
return true;
@ -318,6 +322,16 @@ internal static class SkipCondition
return false;
}
private bool CheckLevelCondition(SkipStepConditions skipConditions)
{
if (skipConditions.MinimumLevel.HasValue && clientState.LocalPlayer != null && clientState.LocalPlayer.Level >= skipConditions.MinimumLevel.Value)
{
logger.LogInformation("Skipping step, as player level {CurrentLevel} >= minimum level {MinLevel}", clientState.LocalPlayer.Level, skipConditions.MinimumLevel.Value);
return true;
}
return false;
}
private bool CheckQuestWorkConditions(ElementId elementId, QuestStep step)
{
QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(elementId);

View file

@ -9,9 +9,12 @@ using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Common;
using Questionable.Model.Questing;
using Questionable.Model.Questing.Converter;
using Questionable.Windows;
namespace Questionable.Controller;
@ -52,6 +55,8 @@ internal sealed class CommandHandler : IDisposable
private readonly GameFunctions _gameFunctions;
private readonly AetheryteFunctions _aetheryteFunctions;
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
@ -62,7 +67,7 @@ internal sealed class CommandHandler : IDisposable
private IReadOnlyList<uint> _previouslyUnlockedUnlockLinks = Array.Empty<uint>();
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration)
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration)
{
_commandManager = commandManager;
_chatGui = chatGui;
@ -79,6 +84,7 @@ internal sealed class CommandHandler : IDisposable
_targetManager = targetManager;
_questFunctions = questFunctions;
_gameFunctions = gameFunctions;
_aetheryteFunctions = aetheryteFunctions;
_dataManager = dataManager;
_clientState = clientState;
_objectTable = objectTable;
@ -196,7 +202,7 @@ internal sealed class CommandHandler : IDisposable
{
break;
}
List<string> list = new List<string>();
List<string> list5 = new List<string>();
ExcelSheet<ChocoboTaxiStand> excelSheet = _dataManager.GetExcelSheet<ChocoboTaxiStand>();
UIState* ptr = UIState.Instance();
if (ptr == null)
@ -204,35 +210,35 @@ internal sealed class CommandHandler : IDisposable
_chatGui.PrintError("UIState is null", "Questionable", 576);
break;
}
for (int num8 = 0; num8 < 192; num8++)
for (int num10 = 0; num10 < 192; num10++)
{
uint num9 = (uint)(num8 + 1179648);
uint num11 = (uint)(num10 + 1179648);
try
{
if (excelSheet.HasRow(num9) && ptr->IsChocoboTaxiStandUnlocked(num9))
if (excelSheet.HasRow(num11) && ptr->IsChocoboTaxiStandUnlocked(num11))
{
string value10 = excelSheet.GetRow(num9).PlaceName.ToString();
if (string.IsNullOrEmpty(value10))
string value14 = excelSheet.GetRow(num11).PlaceName.ToString();
if (string.IsNullOrEmpty(value14))
{
value10 = "Unknown";
value14 = "Unknown";
}
list.Add($"{value10} (ID: {num8}, Row: 0x{num9:X})");
list5.Add($"{value14} (ID: {num10}, Row: 0x{num11:X})");
}
}
catch
{
}
}
_chatGui.Print($"Unlocked taxi stands ({list.Count}):", "Questionable", 576);
if (list.Count == 0)
_chatGui.Print($"Unlocked taxi stands ({list5.Count}):", "Questionable", 576);
if (list5.Count == 0)
{
_chatGui.Print(" (No unlocked taxi stands found)", "Questionable", 576);
break;
}
{
foreach (string item3 in list)
foreach (string item5 in list5)
{
_chatGui.Print(" - " + item3, "Questionable", 576);
_chatGui.Print(" - " + item5, "Questionable", 576);
}
break;
}
@ -258,10 +264,10 @@ internal sealed class CommandHandler : IDisposable
if (unlockLinks.Count >= 0)
{
_chatGui.Print($"Saved {unlockLinks.Count} unlock links to log.", "Questionable", 576);
List<uint> list3 = unlockLinks.Except(_previouslyUnlockedUnlockLinks).ToList();
if (_previouslyUnlockedUnlockLinks.Count > 0 && list3.Count > 0)
List<uint> list6 = unlockLinks.Except(_previouslyUnlockedUnlockLinks).ToList();
if (_previouslyUnlockedUnlockLinks.Count > 0 && list6.Count > 0)
{
_chatGui.Print("New unlock links: " + string.Join(", ", list3), "Questionable", 576);
_chatGui.Print("New unlock links: " + string.Join(", ", list6), "Questionable", 576);
}
}
else
@ -273,6 +279,136 @@ internal sealed class CommandHandler : IDisposable
}
}
break;
case 9:
switch (text[0])
{
case 'f':
{
if (!(text == "festivals"))
{
break;
}
List<string> list4 = new List<string>();
for (byte b8 = 0; b8 < 4; b8++)
{
GameMain.Festival festival = GameMain.Instance()->ActiveFestivals[b8];
if (festival.Id == 0)
{
list4.Add($"Slot {b8}: None");
}
else
{
list4.Add($"Slot {b8}: {festival.Id}({festival.Phase})");
}
}
_chatGui.Print("Festival slots:", "Questionable", 576);
{
foreach (string item6 in list4)
{
_chatGui.Print(" " + item6, "Questionable", 576);
}
break;
}
}
case 'a':
{
if (!(text == "aethernet"))
{
break;
}
ushort territoryType = _clientState.TerritoryType;
Dictionary<EAetheryteLocation, string> values = AethernetShardConverter.Values;
AetheryteData aetheryteData = new AetheryteData(_dataManager);
HashSet<string> hashSet2 = new HashSet<string>();
Dictionary<string, List<(EAetheryteLocation, string, bool)>> dictionary = new Dictionary<string, List<(EAetheryteLocation, string, bool)>>();
EAetheryteLocation key;
string value10;
foreach (KeyValuePair<EAetheryteLocation, string> item7 in values)
{
item7.Deconstruct(out key, out value10);
EAetheryteLocation key2 = key;
string text2 = value10;
if (aetheryteData.TerritoryIds.TryGetValue(key2, out var value11) && value11 == territoryType)
{
int num8 = text2.IndexOf(']', StringComparison.Ordinal);
if (num8 > 0)
{
string item3 = text2.Substring(1, num8 - 1);
hashSet2.Add(item3);
}
}
}
if (hashSet2.Count == 0)
{
_chatGui.Print("No aethernet shards found in current zone.", "Questionable", 576);
break;
}
foreach (KeyValuePair<EAetheryteLocation, string> item8 in values)
{
item8.Deconstruct(out key, out value10);
EAetheryteLocation eAetheryteLocation = key;
string text3 = value10;
int num9 = text3.IndexOf(']', StringComparison.Ordinal);
if (num9 <= 0)
{
continue;
}
string text4 = text3.Substring(1, num9 - 1);
if (hashSet2.Contains(text4))
{
if (!dictionary.ContainsKey(text4))
{
dictionary[text4] = new List<(EAetheryteLocation, string, bool)>();
}
bool item4 = _aetheryteFunctions.IsAetheryteUnlocked(eAetheryteLocation);
dictionary[text4].Add((eAetheryteLocation, text3, item4));
}
}
{
foreach (KeyValuePair<string, List<(EAetheryteLocation, string, bool)>> item9 in dictionary.OrderBy<KeyValuePair<string, List<(EAetheryteLocation, string, bool)>>, string>((KeyValuePair<string, List<(EAetheryteLocation Location, string Name, bool Unlocked)>> x) => x.Key))
{
item9.Deconstruct(out value10, out var value12);
string value13 = value10;
List<(EAetheryteLocation, string, bool)> list = value12;
List<(EAetheryteLocation, string, bool)> list2 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Unlocked).ToList();
List<(EAetheryteLocation, string, bool)> list3 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => !x.Unlocked).ToList();
_chatGui.Print($"Aethernet Shards in {value13} ({list.Count} total):", "Questionable", 576);
_chatGui.Print($" Unlocked: {list2.Count}", "Questionable", 576);
_chatGui.Print($" Missing: {list3.Count}", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
if (list3.Count > 0)
{
_chatGui.Print("Missing/Unattuned Aethernet Shards:", "Questionable", 576);
foreach (var item10 in list3.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item10.Item2, "Questionable", 576);
}
_chatGui.Print("", "Questionable", 576);
}
if (list2.Count > 0)
{
_chatGui.Print("Unlocked Aethernet Shards:", "Questionable", 576);
foreach (var item11 in list2.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item11.Item2, "Questionable", 576);
}
}
if (dictionary.Count > 1)
{
_chatGui.Print("", "Questionable", 576);
}
}
break;
}
}
}
break;
case 5:
if (text == "setup")
{
_oneTimeSetupWindow.IsOpenAndUncollapsed = true;
}
break;
case 2:
if (text == "do")
{
@ -291,34 +427,6 @@ internal sealed class CommandHandler : IDisposable
PrintMountId();
}
break;
case 9:
{
if (!(text == "festivals"))
{
break;
}
List<string> list2 = new List<string>();
for (byte b8 = 0; b8 < 4; b8++)
{
GameMain.Festival festival = GameMain.Instance()->ActiveFestivals[b8];
if (festival.Id == 0)
{
list2.Add($"Slot {b8}: None");
}
else
{
list2.Add($"Slot {b8}: {festival.Id}({festival.Phase})");
}
}
_chatGui.Print("Festival slots:", "Questionable", 576);
{
foreach (string item4 in list2)
{
_chatGui.Print(" " + item4, "Questionable", 576);
}
break;
}
}
case 11:
{
if (!(text == "quest-kills"))
@ -369,17 +477,17 @@ internal sealed class CommandHandler : IDisposable
if (hashSet.Count > 0)
{
_chatGui.Print($"All Enemy DataIds Found: {hashSet.Count}", "Questionable", 576);
foreach (uint item5 in hashSet.OrderBy((uint x) => x))
foreach (uint item12 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple = GetEnemyName(item5);
(string Name, bool Found) tuple = GetEnemyName(item12);
var (value, _) = tuple;
if (tuple.Found)
{
_chatGui.Print($" - {value} (DataId: {item5})", "Questionable", 576);
_chatGui.Print($" - {value} (DataId: {item12})", "Questionable", 576);
}
else
{
_chatGui.Print($" - DataId: {item5}", "Questionable", 576);
_chatGui.Print($" - DataId: {item12}", "Questionable", 576);
}
}
_chatGui.Print("", "Questionable", 576);
@ -406,17 +514,17 @@ internal sealed class CommandHandler : IDisposable
byte b2 = (byte)(((num >= 0 && num < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num] : 0) & 0xF);
string value2 = (b.HasValue ? $" {b2}/{b}" : "");
string value3 = ((b.HasValue && b2 >= b) ? "✓" : "○");
foreach (uint item6 in hashSet.OrderBy((uint x) => x))
foreach (uint item13 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple3 = GetEnemyName(item6);
(string Name, bool Found) tuple3 = GetEnemyName(item13);
var (value4, _) = tuple3;
if (tuple3.Found)
{
_chatGui.Print($" {value3} Slay {value4}.{value2} (DataId: {item6})", "Questionable", 576);
_chatGui.Print($" {value3} Slay {value4}.{value2} (DataId: {item13})", "Questionable", 576);
}
else
{
_chatGui.Print($" {value3} Slay enemy.{value2} (DataId: {item6})", "Questionable", 576);
_chatGui.Print($" {value3} Slay enemy.{value2} (DataId: {item13})", "Questionable", 576);
}
}
}
@ -495,7 +603,6 @@ internal sealed class CommandHandler : IDisposable
}
break;
}
case 5:
case 6:
case 8:
case 10:

View file

@ -56,6 +56,14 @@ internal sealed class QuestFunctions
private readonly HashSet<ushort> _alreadyLoggedLevelRequirements = new HashSet<ushort>();
private ElementId? _lastLoggedLevelLockedMsq;
private ElementId? _lastLoggedForcedClassQuest;
private bool _lastLoggedFallbackLookup;
private ElementId? _lastLoggedNotReadyQuest;
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;
@ -145,8 +153,99 @@ internal sealed class QuestFunctions
QuestReference questReference = GetMainScenarioQuest().Item1;
if (questReference.CurrentQuest != null && !_questRegistry.IsKnownQuest(questReference.CurrentQuest))
{
_logger.LogWarning("MSQ {MsqQuestId} is not in the quest registry, resetting to NoQuest", questReference.CurrentQuest);
questReference = QuestReference.NoQuest(questReference.State);
}
byte currentLevel = _clientState.LocalPlayer?.Level ?? 0;
if (questReference.CurrentQuest != null)
{
Questionable.Model.Quest quest;
bool flag = _questRegistry.TryGetQuest(questReference.CurrentQuest, out quest);
bool flag2 = IsQuestAccepted(questReference.CurrentQuest);
_logger.LogTrace("MSQ check: QuestId={QuestId}, InRegistry={InRegistry}, Level={QuestLevel}, CurrentLevel={CurrentLevel}, IsAccepted={IsAccepted}", questReference.CurrentQuest, flag, ((int?)quest?.Info.Level) ?? (-1), currentLevel, flag2);
EClassJob valueOrDefault = ((EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
if (valueOrDefault != EClassJob.Adventurer)
{
QuestInfo questInfo = (from x in _questData.GetClassJobQuests(valueOrDefault)
where x.Level <= 5 && IsQuestAccepted(x.QuestId) && !IsQuestComplete(x.QuestId)
orderby x.Level
select x).FirstOrDefault();
if (questInfo != null)
{
_logger.LogTrace("Found in-progress class quest {QuestId}, continuing it", questInfo.QuestId);
return new QuestReference(questInfo.QuestId, QuestManager.GetQuestSequence(questInfo.QuestId.Value), questReference.State);
}
}
if (flag && quest.Info.Level > currentLevel && !flag2)
{
if (_lastLoggedLevelLockedMsq != questReference.CurrentQuest)
{
_logger.LogInformation("MSQ {MsqId} requires level {RequiredLevel}, current level {CurrentLevel}. Checking for early class quests to level up.", questReference.CurrentQuest, quest.Info.Level, currentLevel);
_lastLoggedLevelLockedMsq = questReference.CurrentQuest;
}
if (valueOrDefault != EClassJob.Adventurer)
{
List<QuestInfo> classJobQuests = _questData.GetClassJobQuests(valueOrDefault);
List<QuestId> lockedQuests = _questData.GetLockedClassQuests();
List<QuestInfo> list = (from x in classJobQuests.Where(delegate(QuestInfo x)
{
bool num2 = x.Level <= currentLevel && x.Level <= 5;
bool flag3 = !IsQuestComplete(x.QuestId);
bool flag4 = !lockedQuests.Contains(x.QuestId);
return num2 && flag3 && flag4;
})
orderby x.Level
select x).ToList();
if (list.Count > 0)
{
ElementId questId = list.First().QuestId;
if (!IsQuestComplete(questId))
{
if (_lastLoggedForcedClassQuest != questId)
{
_logger.LogInformation("MSQ level locked. Forcing class quest {ClassQuestId} for {ClassJob} (level {QuestLevel})", questId, valueOrDefault, list.First().Level);
_lastLoggedForcedClassQuest = questId;
}
return new QuestReference(questId, QuestManager.GetQuestSequence(questId.Value), questReference.State);
}
}
else if (_lastLoggedLevelLockedMsq == questReference.CurrentQuest && _lastLoggedForcedClassQuest == null)
{
_logger.LogWarning("No class quests passed the filter for {ClassJob} at level {CurrentLevel}", valueOrDefault, currentLevel);
_lastLoggedLevelLockedMsq = null;
}
List<QuestInfo> list2 = (from x in _questRegistry.GetKnownClassJobQuests(valueOrDefault, includeRoleQuests: false)
where x.Level <= currentLevel && x.Level <= 5 && !IsQuestAcceptedOrComplete(x.QuestId) && IsReadyToAcceptQuest(x.QuestId)
orderby x.Level
select x).ToList();
if (list2.Count > 0)
{
ElementId questId2 = list2.First().QuestId;
_logger.LogInformation("MSQ level locked. Prioritizing class quest {ClassQuestId} for {ClassJob} (level {QuestLevel}) from registry", questId2, valueOrDefault, list2.First().Level);
return new QuestReference(questId2, QuestManager.GetQuestSequence(questId2.Value), questReference.State);
}
_logger.LogWarning("MSQ level locked but no available early class quests found for {ClassJob} at level {CurrentLevel}", valueOrDefault, currentLevel);
}
else
{
_logger.LogWarning("Current class is Adventurer, cannot find class quests");
}
Questionable.Model.Quest quest2;
ElementId elementId = (from x in GetNextPriorityQuestsThatCanBeAccepted()
where x.IsAvailable
where _questRegistry.TryGetQuest(x.QuestId, out quest2) && quest2.Info.Level <= currentLevel && quest2.Info.Level <= 5 && quest2.Info is QuestInfo questInfo2 && questInfo2.ClassJobs.Count > 0 && questInfo2.ClassJobs.Any((EClassJob job) => !job.IsCrafter() && !job.IsGatherer())
select x.QuestId).FirstOrDefault();
if (elementId != null)
{
_logger.LogInformation("MSQ {MsqId} requires level {RequiredLevel}, current level {CurrentLevel}. Prioritizing early class quest {ClassQuestId} (from priority list)", questReference.CurrentQuest, quest.Info.Level, currentLevel, elementId);
return new QuestReference(elementId, QuestManager.GetQuestSequence(elementId.Value), questReference.State);
}
_logger.LogWarning("MSQ {MsqId} is level locked (requires {RequiredLevel}, current {CurrentLevel}) and no early class quests available. Cannot proceed.", questReference.CurrentQuest, quest.Info.Level, currentLevel);
return QuestReference.NoQuest(MainScenarioQuestState.Unavailable);
}
_lastLoggedLevelLockedMsq = null;
_lastLoggedForcedClassQuest = null;
}
if (questReference.CurrentQuest != null && !IsQuestAccepted(questReference.CurrentQuest))
{
if (allowNewMsq)
@ -155,7 +254,7 @@ internal sealed class QuestFunctions
}
questReference = QuestReference.NoQuest(questReference.State);
}
List<(ElementId, byte)> list = new List<(ElementId, byte)>();
List<(ElementId, byte)> list3 = new List<(ElementId, byte)>();
for (int num = ptr->TrackedQuests.Length - 1; num >= 0; num--)
{
TrackingWork trackingWork = ptr->TrackedQuests[num];
@ -163,36 +262,36 @@ internal sealed class QuestFunctions
{
case 1:
{
ElementId elementId = new QuestId(ptr->NormalQuests[trackingWork.Index].QuestId);
if (_questRegistry.IsKnownQuest(elementId))
ElementId elementId2 = new QuestId(ptr->NormalQuests[trackingWork.Index].QuestId);
if (_questRegistry.IsKnownQuest(elementId2))
{
list.Add((elementId, QuestManager.GetQuestSequence(elementId.Value)));
list3.Add((elementId2, QuestManager.GetQuestSequence(elementId2.Value)));
}
break;
}
}
}
if (_configuration.General.SkipLowPriorityDuties && list.Count > 0)
if (_configuration.General.SkipLowPriorityDuties && list3.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));
list3.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)
if (list3.Count > 0)
{
(ElementId, byte) tuple = list.First();
(ElementId, byte) tuple = list3.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)
List<(ElementId, byte)> list4 = (from tuple4 in list3.Skip(1)
where _alliedSocietyData.GetCommonAlliedSocietyTurnIn(tuple4.Quest) == firstTrackedAlliedSociety
select tuple4).ToList();
if (list4.Count > 0)
{
if (item2 == byte.MaxValue)
{
foreach (var (currentQuest, b) in list2)
foreach (var (currentQuest, b) in list4)
{
if (b != byte.MaxValue)
{
@ -202,11 +301,11 @@ internal sealed class QuestFunctions
}
else if (!IsOnAlliedSocietyMount())
{
list2.Insert(0, (item, item2));
list4.Insert(0, (item, item2));
_alliedSocietyData.GetCommonAlliedSocietyNpcs(firstTrackedAlliedSociety, out uint[] normalNpcs, out uint[] _);
if (normalNpcs.Length != 0)
{
(ElementId, byte)? tuple3 = (from x in list2
(ElementId, byte)? tuple3 = (from x in list4
where x.Sequence < byte.MaxValue
where IsInteractSequence(x.Quest, x.Sequence, normalNpcs)
select x).Cast<(ElementId, byte)?>().FirstOrDefault();
@ -220,12 +319,12 @@ internal sealed class QuestFunctions
}
return new QuestReference(item, item2, questReference.State);
}
ElementId elementId2 = (from x in GetNextPriorityQuestsThatCanBeAccepted()
ElementId elementId3 = (from x in GetNextPriorityQuestsThatCanBeAccepted()
where x.IsAvailable
select x.QuestId).FirstOrDefault();
if (elementId2 != null)
if (elementId3 != null)
{
return new QuestReference(elementId2, QuestManager.GetQuestSequence(elementId2.Value), questReference.State);
return new QuestReference(elementId3, QuestManager.GetQuestSequence(elementId3.Value), questReference.State);
}
if (questReference.CurrentQuest != null)
{
@ -259,10 +358,12 @@ internal sealed class QuestFunctions
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 questId = new QuestId(ptr->Data->CurrentScenarioQuest);
@ -273,11 +374,20 @@ internal sealed class QuestFunctions
{
return (QuestReference.NoQuest(MainScenarioQuestState.Complete), "Main Scenario is complete");
}
if (!_lastLoggedFallbackLookup)
{
_logger.LogInformation("GetMainScenarioQuest: MSQ from scenario tree is 0, using fallback lookup");
_lastLoggedFallbackLookup = true;
}
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 (!_lastLoggedFallbackLookup || list.Count > 0)
{
_logger.LogTrace("GetMainScenarioQuest: Found {Count} potential quests from fallback", list.Count);
}
if (list.Count == 0)
{
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), "No potential quests found");
@ -314,6 +424,7 @@ internal sealed class QuestFunctions
}
if (list.Count != 1)
{
_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)));
}
}
@ -321,29 +432,35 @@ internal sealed class QuestFunctions
{
questId = (QuestId)list.Single().QuestId;
}
_logger.LogInformation("GetMainScenarioQuest: Selected quest from fallback = {QuestId}", questId);
_lastLoggedFallbackLookup = false;
}
QuestManager* ptr2 = QuestManager.Instance();
if (IsQuestAccepted(questId) && ptr2->GetQuestById(questId.Value)->IsHidden)
{
_logger.LogInformation("GetMainScenarioQuest: Quest {QuestId} is accepted but hidden", questId);
return (QuestReference.NoQuest(MainScenarioQuestState.Available), "Quest accepted but hidden");
}
if (IsQuestComplete(questId))
{
_logger.LogInformation("GetMainScenarioQuest: Quest {QuestId} is already complete", questId);
return (new QuestReference(questId, byte.MaxValue, MainScenarioQuestState.Available), $"Quest {questId.Value} complete");
}
if (!IsReadyToAcceptQuest(questId))
if (!IsReadyToAcceptQuest(questId, ignoreLevel: true))
{
if (_lastLoggedNotReadyQuest != questId)
{
_logger.LogInformation("GetMainScenarioQuest: Quest {QuestId} is not ready to accept (ignoring level)", questId);
_lastLoggedNotReadyQuest = questId;
}
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), $"Not readdy to accept quest {questId.Value}");
}
byte? b = _clientState.LocalPlayer?.Level;
if (!b.HasValue)
_lastLoggedNotReadyQuest = null;
if (!(_clientState.LocalPlayer?.Level).HasValue)
{
_logger.LogTrace("GetMainScenarioQuest: In loading screen");
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);
}
@ -413,7 +530,7 @@ internal sealed class QuestFunctions
{
return new PriorityQuestInfo(x, "No sequence 0 with steps");
}
if (!_aetheryteFunctions.IsTeleportUnlocked())
if ((quest.Info.Level > 5 || !(quest.Info is QuestInfo questInfo) || questInfo.ClassJobs.Count <= 0 || !questInfo.ClassJobs.Any((EClassJob classJob) => !classJob.IsCrafter() && !classJob.IsGatherer())) && !_aetheryteFunctions.IsTeleportUnlocked())
{
return new PriorityQuestInfo(x, "Teleport not unlocked");
}

View file

@ -85,7 +85,7 @@ internal sealed class DebugConfigComponent : ConfigComponent
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("Class and job skills for A Realm Reborn, Heavensward and (for the Lv70 skills) Stormblood are locked behind quests. Not recommended if you plan on queueing for instances with duty finder/party finder.");
ImGuiComponents.HelpMarker("Class and job skills for A Realm Reborn, Heavensward and (for the Lv70 skills) Stormblood are locked behind quests. Not recommended if you plan on queueing for instances with duty finder/party finder.\n\nNote: This setting is ignored for the first class/job quest if your character is not high enough level to start the level 4 MSQ.");
bool v8 = base.Configuration.Advanced.SkipARealmRebornHardModePrimals;
if (ImGui.Checkbox("Don't pick up ARR hard mode primal quests", ref v8))
{