diff --git a/FatePaths/Questionable.FatePaths.FateBundle b/FatePaths/Questionable.FatePaths.FateBundle index 88a94cc..570b2f6 100644 Binary files a/FatePaths/Questionable.FatePaths.FateBundle and b/FatePaths/Questionable.FatePaths.FateBundle differ diff --git a/FatePaths/Questionable.FatePaths/PathBundleSecret.cs b/FatePaths/Questionable.FatePaths/PathBundleSecret.cs index 37ddf70..b2ec572 100644 --- a/FatePaths/Questionable.FatePaths/PathBundleSecret.cs +++ b/FatePaths/Questionable.FatePaths/PathBundleSecret.cs @@ -4,18 +4,18 @@ internal static class PathBundleSecret { private static readonly byte[] A = new byte[32] { - 184, 146, 158, 4, 12, 188, 14, 129, 154, 207, - 124, 249, 11, 86, 207, 158, 174, 9, 114, 165, - 243, 163, 70, 242, 33, 231, 45, 140, 119, 185, - 162, 26 + 48, 212, 251, 149, 233, 68, 48, 175, 219, 236, + 217, 100, 130, 200, 255, 152, 233, 112, 177, 94, + 93, 198, 133, 46, 110, 244, 154, 244, 247, 13, + 145, 158 }; private static readonly byte[] B = new byte[32] { - 150, 86, 230, 115, 69, 121, 30, 79, 176, 162, - 216, 139, 248, 83, 68, 111, 173, 223, 203, 79, - 42, 143, 208, 219, 192, 216, 102, 249, 57, 147, - 95, 21 + 79, 60, 140, 255, 167, 84, 206, 132, 105, 228, + 71, 209, 251, 6, 250, 62, 38, 86, 249, 66, + 102, 234, 109, 58, 57, 229, 131, 131, 42, 66, + 172, 97 }; internal static byte[] Key() diff --git a/GatheringPaths/Questionable.GatheringPaths.GatheringBundle b/GatheringPaths/Questionable.GatheringPaths.GatheringBundle index 6eae81e..cde9fd5 100644 Binary files a/GatheringPaths/Questionable.GatheringPaths.GatheringBundle and b/GatheringPaths/Questionable.GatheringPaths.GatheringBundle differ diff --git a/GatheringPaths/Questionable.GatheringPaths/PathBundleSecret.cs b/GatheringPaths/Questionable.GatheringPaths/PathBundleSecret.cs index 6e96944..4c32249 100644 --- a/GatheringPaths/Questionable.GatheringPaths/PathBundleSecret.cs +++ b/GatheringPaths/Questionable.GatheringPaths/PathBundleSecret.cs @@ -4,18 +4,18 @@ internal static class PathBundleSecret { private static readonly byte[] A = new byte[32] { - 145, 72, 249, 36, 189, 185, 130, 238, 193, 10, - 202, 232, 4, 205, 253, 37, 116, 120, 105, 80, - 155, 225, 237, 122, 124, 183, 185, 180, 222, 128, - 163, 58 + 83, 53, 162, 177, 116, 210, 154, 147, 32, 246, + 109, 49, 21, 10, 185, 40, 3, 223, 158, 234, + 82, 124, 250, 209, 185, 174, 77, 93, 58, 15, + 16, 246 }; private static readonly byte[] B = new byte[32] { - 38, 241, 153, 134, 236, 129, 153, 181, 53, 177, - 92, 29, 47, 248, 180, 1, 80, 8, 68, 19, - 82, 214, 85, 67, 101, 195, 46, 138, 196, 69, - 206, 117 + 11, 0, 215, 132, 153, 74, 202, 77, 15, 86, + 243, 43, 187, 26, 168, 53, 42, 2, 79, 60, + 154, 215, 216, 52, 147, 70, 38, 49, 232, 46, + 225, 130 }; internal static byte[] Key() diff --git a/LLib/LLib.Shop/NpcPositionCache.cs b/LLib/LLib.Shop/NpcPositionCache.cs index 3eae4b7..3f327c0 100644 --- a/LLib/LLib.Shop/NpcPositionCache.cs +++ b/LLib/LLib.Shop/NpcPositionCache.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Numerics; +using System.Text; using System.Threading; using Dalamud.Plugin.Services; using Lumina.Data.Files; @@ -22,6 +23,8 @@ public sealed class NpcPositionCache private const string FileName = "npc-position-cache.bin"; + private const int MaxPreallocatedEntries = 131072; + private readonly IDataManager _dataManager; private readonly ILogger _logger; @@ -89,6 +92,23 @@ public sealed class NpcPositionCache } } + public bool TryLoadFromStream(Stream stream, bool acceptStaleVersion) + { + ArgumentNullException.ThrowIfNull(stream, "stream"); + if (_cache != null) + { + return true; + } + lock (_buildLock) + { + if (_cache == null) + { + _cache = ReadCache(stream, acceptStaleVersion, "stream"); + } + return _cache != null; + } + } + public void WarmUp(CancellationToken cancellationToken = default(CancellationToken)) { EnsureBuilt(cancellationToken); @@ -149,36 +169,20 @@ public sealed class NpcPositionCache { return null; } + Dictionary dictionary; try { - using BinaryReader binaryReader = new BinaryReader(File.OpenRead(cacheFilePath)); - if (binaryReader.ReadUInt32() != 1129336401 || binaryReader.ReadInt32() != 1) - { - _logger.LogDebug("NPC position cache file has an unknown header, discarding"); - } - else - { - if (!(binaryReader.ReadString() != _options.GameVersion)) - { - int num = binaryReader.ReadInt32(); - Dictionary dictionary = new Dictionary(num); - for (int i = 0; i < num; i++) - { - uint key = binaryReader.ReadUInt32(); - ushort item = binaryReader.ReadUInt16(); - Vector3 item2 = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); - bool item3 = binaryReader.ReadBoolean(); - dictionary[key] = (item, item2, item3); - } - _logger.LogDebug("NPC position cache loaded from disk with {Count} entries", dictionary.Count); - return dictionary; - } - _logger.LogDebug("NPC position cache file is for a different game version, discarding"); - } + using FileStream stream = File.OpenRead(cacheFilePath); + dictionary = ReadCache(stream, acceptStaleVersion: false, "disk"); } catch (Exception exception) { - _logger.LogWarning(exception, "Failed to load NPC position cache file, discarding"); + _logger.LogWarning(exception, "Failed to open NPC position cache file, discarding"); + dictionary = null; + } + if (dictionary != null) + { + return dictionary; } try { @@ -190,6 +194,51 @@ public sealed class NpcPositionCache return null; } + private Dictionary? ReadCache(Stream stream, bool acceptStaleVersion, string source) + { + try + { + using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); + if (binaryReader.ReadUInt32() != 1129336401 || binaryReader.ReadInt32() != 1) + { + _logger.LogDebug("NPC position cache data has an unknown header, discarding"); + return null; + } + string text = binaryReader.ReadString(); + if (text != _options.GameVersion) + { + if (!acceptStaleVersion) + { + _logger.LogDebug("NPC position cache data is for a different game version, discarding"); + return null; + } + _logger.LogInformation("NPC position cache data was built for game version {CachedVersion} instead of {ExpectedVersion}, using it anyway", text, _options.GameVersion); + } + int num = binaryReader.ReadInt32(); + if (num < 0) + { + _logger.LogDebug("NPC position cache data has a negative entry count, discarding"); + return null; + } + Dictionary dictionary = new Dictionary(Math.Min(num, 131072)); + for (int i = 0; i < num; i++) + { + uint key = binaryReader.ReadUInt32(); + ushort item = binaryReader.ReadUInt16(); + Vector3 item2 = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); + bool item3 = binaryReader.ReadBoolean(); + dictionary[key] = (item, item2, item3); + } + _logger.LogDebug("NPC position cache loaded from {Source} with {Count} entries", source, dictionary.Count); + return dictionary; + } + catch (Exception exception) + { + _logger.LogWarning(exception, "Failed to read NPC position cache data, discarding"); + return null; + } + } + private void SaveToDisk(Dictionary cache) { string cacheFilePath = CacheFilePath; diff --git a/QuestPaths/Questionable.QuestPaths.QuestBundle b/QuestPaths/Questionable.QuestPaths.QuestBundle index d61e136..d5cde1a 100644 Binary files a/QuestPaths/Questionable.QuestPaths.QuestBundle and b/QuestPaths/Questionable.QuestPaths.QuestBundle differ diff --git a/QuestPaths/Questionable.QuestPaths/PathBundleSecret.cs b/QuestPaths/Questionable.QuestPaths/PathBundleSecret.cs index c6a5d56..bf8d03b 100644 --- a/QuestPaths/Questionable.QuestPaths/PathBundleSecret.cs +++ b/QuestPaths/Questionable.QuestPaths/PathBundleSecret.cs @@ -4,18 +4,18 @@ internal static class PathBundleSecret { private static readonly byte[] A = new byte[32] { - 14, 205, 205, 220, 252, 13, 221, 232, 100, 189, - 37, 224, 91, 77, 175, 255, 30, 195, 23, 136, - 234, 230, 125, 209, 122, 165, 20, 174, 100, 226, - 27, 222 + 73, 4, 92, 189, 214, 0, 53, 235, 105, 164, + 216, 243, 244, 200, 198, 26, 227, 227, 153, 121, + 241, 147, 166, 171, 63, 63, 121, 152, 52, 115, + 89, 201 }; private static readonly byte[] B = new byte[32] { - 241, 56, 43, 159, 122, 127, 111, 25, 223, 150, - 24, 59, 99, 24, 253, 141, 217, 107, 154, 16, - 247, 125, 175, 254, 169, 41, 27, 2, 18, 101, - 46, 175 + 243, 132, 180, 1, 83, 89, 202, 66, 33, 20, + 242, 184, 99, 15, 215, 2, 4, 125, 221, 134, + 186, 51, 132, 151, 141, 220, 230, 57, 252, 77, + 103, 57 }; internal static byte[] Key() diff --git a/Questionable.Model/Questionable.Model.Questing.Converter/AethernetShardConverter.cs b/Questionable.Model/Questionable.Model.Questing.Converter/AethernetShardConverter.cs index 1409807..521a07e 100644 --- a/Questionable.Model/Questionable.Model.Questing.Converter/AethernetShardConverter.cs +++ b/Questionable.Model/Questionable.Model.Questing.Converter/AethernetShardConverter.cs @@ -548,6 +548,10 @@ public sealed class AethernetShardConverter : EnumConverter EAetheryteLocation.TuliyollalXakTuralSkygate, "[Tuliyollal] Xak Tural Skygate (Shaaloani)" }, + { + EAetheryteLocation.TuliyollalPhantomVillage, + "[Tuliyollal] Phantom Village" + }, { EAetheryteLocation.SolutionNine, "[Solution Nine] Aetheryte Plaza" diff --git a/Questionable/Questionable.Controller.CombatModules/RotationSolverRebornModule.cs b/Questionable/Questionable.Controller.CombatModules/RotationSolverRebornModule.cs index 3805a4f..6a90d71 100644 --- a/Questionable/Questionable.Controller.CombatModules/RotationSolverRebornModule.cs +++ b/Questionable/Questionable.Controller.CombatModules/RotationSolverRebornModule.cs @@ -11,6 +11,8 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable { private const byte ModeOff = 0; + private const byte ModeManual = 3; + private const byte ModeHenched = 4; private readonly ILogger _logger; @@ -21,15 +23,12 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable private readonly ICallGateSubscriber _changeOperationMode; - private readonly ICallGateSubscriber _setConfig; - public RotationSolverRebornModule(ILogger logger, IDalamudPluginInterface pluginInterface, Configuration configuration) { _logger = logger; _configuration = configuration; _test = pluginInterface.GetIpcSubscriber("RotationSolverReborn.Test"); _changeOperationMode = pluginInterface.GetIpcSubscriber("RotationSolverReborn.ChangeOperatingMode"); - _setConfig = pluginInterface.GetIpcSubscriber("RotationSolverReborn.SetConfig"); } public bool IsAvailable() @@ -55,13 +54,20 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable } public bool Start(CombatController.CombatData combatData) + { + return Start(3); + } + + public bool StartForDuty() + { + return Start(4); + } + + private bool Start(byte mode) { try { - _changeOperationMode.InvokeAction(4); - SetConfig("AutoOffAfterCombat", false); - SetConfig("HealPartyMembers", true); - SetConfig("HostileType", 1); + _changeOperationMode.InvokeAction(mode); return true; } catch (IpcError exception) @@ -101,16 +107,4 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable public void Dispose() { } - - private void SetConfig(string key, object value) - { - try - { - _setConfig.InvokeAction(key, value); - } - catch (IpcError exception) - { - _logger.LogDebug(exception, "RSR SetConfig {Key} failed", key); - } - } } diff --git a/Questionable/Questionable.Controller.CustomDelivery/DeliveryNpcApproachExecutor.cs b/Questionable/Questionable.Controller.CustomDelivery/DeliveryNpcApproachExecutor.cs index 67c1dd6..7cb64d4 100644 --- a/Questionable/Questionable.Controller.CustomDelivery/DeliveryNpcApproachExecutor.cs +++ b/Questionable/Questionable.Controller.CustomDelivery/DeliveryNpcApproachExecutor.cs @@ -47,6 +47,10 @@ internal sealed class DeliveryNpcApproachExecutor(NavmeshIpc navmeshIpc, Movemen { return true; } + if (Vector3.Distance(localPlayer.Position, _npcPosition) <= _interactionDistance + 0.5f) + { + return false; + } float num = MathF.Atan2(localPlayer.Position.X - _npcPosition.X, localPlayer.Position.Z - _npcPosition.Z); float num2 = MathF.Max(1f, _interactionDistance - 0.5f); for (int i = 0; i < 24; i++) diff --git a/Questionable/Questionable.Controller.CustomDelivery/DeliveryPlannerService.cs b/Questionable/Questionable.Controller.CustomDelivery/DeliveryPlannerService.cs index 5d1f65c..67fefa3 100644 --- a/Questionable/Questionable.Controller.CustomDelivery/DeliveryPlannerService.cs +++ b/Questionable/Questionable.Controller.CustomDelivery/DeliveryPlannerService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using LLib.GameData; using LLib.Inventory; using LLib.Shop; @@ -73,27 +74,39 @@ internal sealed class DeliveryPlannerService return null; } uint supplyIndex = (uint)row.SatisfactionNpcParams[npcState.Rank].SupplyIndex; - uint num = _supplyHelper.CalculateRequestedItems(supplyIndex, supplySeed)[(int)slot]; - if (num == uint.MaxValue) + SubrowExcelSheet subrowExcelSheet = _dataManager.GetSubrowExcelSheet(); + if (TryGetLiveSupplyRow(npcIndex, slot, supplyIndex, out var supplyRow, out var hasLiveRequest)) { - _logger.LogDebug("No item predicted for NPC {NpcIndex} slot {Slot}", npcIndex, slot); - return null; + _logger.LogDebug("Using live requested item {ItemId} for NPC {NpcIndex} slot {Slot}", supplyRow.Item.RowId, npcIndex, slot); } - if (!_dataManager.GetSubrowExcelSheet().TryGetSubrow(supplyIndex, (ushort)num, out var subrow)) + else { - _logger.LogWarning("SatisfactionSupply subrow ({SupplyIndex}, {SubrowIndex}) not found", supplyIndex, num); - return null; + if (hasLiveRequest) + { + return null; + } + uint num = _supplyHelper.CalculateRequestedItems(supplyIndex, supplySeed)[(int)slot]; + if (num == uint.MaxValue) + { + _logger.LogDebug("No item predicted for NPC {NpcIndex} slot {Slot}", npcIndex, slot); + return null; + } + if (!subrowExcelSheet.TryGetSubrow(supplyIndex, (ushort)num, out supplyRow)) + { + _logger.LogWarning("SatisfactionSupply subrow ({SupplyIndex}, {SubrowIndex}) not found", supplyIndex, num); + return null; + } } - uint rowId = subrow.Item.RowId; + uint rowId = supplyRow.Item.RowId; ushort num2 = _configuration.CustomDeliveries.CollectabilityTier switch { - Configuration.CustomDeliveryConfiguration.ECollectabilityTier.Low => subrow.CollectabilityLow, - Configuration.CustomDeliveryConfiguration.ECollectabilityTier.Mid => subrow.CollectabilityMid, - _ => subrow.CollectabilityHigh, + Configuration.CustomDeliveryConfiguration.ECollectabilityTier.Low => supplyRow.CollectabilityLow, + Configuration.CustomDeliveryConfiguration.ECollectabilityTier.Mid => supplyRow.CollectabilityMid, + _ => supplyRow.CollectabilityHigh, }; if (rowId == 0) { - _logger.LogWarning("SatisfactionSupply subrow ({SupplyIndex}, {SubrowIndex}) has no item", supplyIndex, num); + _logger.LogWarning("SatisfactionSupply row {SupplyIndex} has no item for slot {Slot}", supplyIndex, slot); return null; } int num3 = row.DeliveriesPerWeek - npcState.UsedDeliveries; @@ -107,7 +120,7 @@ internal sealed class DeliveryPlannerService if (satisfactionRequired > 0) { Configuration.CustomDeliveryConfiguration.ECollectabilityTier collectabilityTier = _configuration.CustomDeliveries.CollectabilityTier; - int num5 = _rewardCalculator.CalculateSatisfactionPerDelivery(subrow.Reward.RowId, subrow.IsBonus, collectabilityTier); + int num5 = _rewardCalculator.CalculateSatisfactionPerDelivery(supplyRow.Reward.RowId, supplyRow.IsBonus, collectabilityTier); if (num5 > 0) { int num6 = (satisfactionRequired - npcState.SatisfactionCurrent + num5 - 1) / num5; @@ -131,8 +144,8 @@ internal sealed class DeliveryPlannerService } if (_configuration.CustomDeliveries.ScripOvercapMode != Configuration.CustomDeliveryConfiguration.EScripOvercapMode.Ignore) { - uint rowId2 = subrow.Reward.RowId; - bool isBonus = subrow.IsBonus; + uint rowId2 = supplyRow.Reward.RowId; + bool isBonus = supplyRow.IsBonus; int num8 = _rewardCalculator.MaxDeliveriesBeforeOvercap(rowId2, isBonus, _configuration.CustomDeliveries.CollectabilityTier); if (num8 <= 0) { @@ -201,6 +214,31 @@ internal sealed class DeliveryPlannerService return result; } + private unsafe bool TryGetLiveSupplyRow(int npcIndex, EDeliverySlot slot, uint supplyIndex, out SatisfactionSupply supplyRow, out bool hasLiveRequest) + { + hasLiveRequest = false; + AgentSatisfactionSupply* ptr = AgentSatisfactionSupply.Instance(); + if (ptr != null && ptr->IsAgentActive() && ptr->NpcInfo.Valid && ptr->NpcInfo.Initialized && ptr->NpcInfo.Id == (uint)(npcIndex + 1)) + { + uint id = ptr->Items[(int)slot].Id; + if (id != 0) + { + hasLiveRequest = true; + foreach (SatisfactionSupply item in _dataManager.GetSubrowExcelSheet().Flatten()) + { + if (item.RowId == supplyIndex && item.Item.RowId == id) + { + supplyRow = item; + return true; + } + } + _logger.LogWarning("Live requested item {ItemId} was not found in SatisfactionSupply row {SupplyIndex}", id, supplyIndex); + } + } + supplyRow = default(SatisfactionSupply); + return false; + } + public bool IsNpcUnlocked(int npcIndex) { if (!_dataManager.GetExcelSheet().TryGetRow((uint)(npcIndex + 1), out var row)) diff --git a/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInExecutor.cs b/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInExecutor.cs index 4a584dc..90ec6fd 100644 --- a/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInExecutor.cs +++ b/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInExecutor.cs @@ -1,6 +1,8 @@ using System; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using LLib; using LLib.Shop; using Microsoft.Extensions.Logging; @@ -89,6 +91,10 @@ internal sealed class SatisfactionSupplyTurnInExecutor(GameFunctions gameFunctio case EPhase.SelectSlot: if (_throttle.TryReset(0.5)) { + if (!PlanStillMatchesRequest()) + { + return ETaskResult.RetryStep; + } if (SatisfactionSupplyActions.IsNpcTradeReady()) { logger.LogDebug("NpcTrade agent ready"); @@ -106,10 +112,18 @@ internal sealed class SatisfactionSupplyTurnInExecutor(GameFunctions gameFunctio } break; case EPhase.ConfirmTrade: - if (_throttle.TryReset(0.5) && SatisfactionSupplyActions.TryConfirmTrade()) + if (_throttle.TryReset(0.5)) { - logger.LogDebug("Confirmed trade for slot {Slot}", base.Task.Slot); - SetPhase(EPhase.WaitForCutsceneStart); + if (!HasRemainingItems()) + { + logger.LogWarning("Planned item {ItemId} is no longer available for turn-in", base.Task.ItemId); + return ETaskResult.RetryStep; + } + if (SatisfactionSupplyActions.TryConfirmTrade()) + { + logger.LogDebug("Confirmed trade for slot {Slot}", base.Task.Slot); + SetPhase(EPhase.WaitForCutsceneStart); + } } break; case EPhase.WaitForCutsceneStart: @@ -192,6 +206,32 @@ internal sealed class SatisfactionSupplyTurnInExecutor(GameFunctions gameFunctio return SatisfactionSupplyActions.GetNpcState(base.Task.NpcIndex).UsedDeliveries > _lastUsedDeliveries; } + private unsafe bool PlanStillMatchesRequest() + { + AgentSatisfactionSupply* ptr = AgentSatisfactionSupply.Instance(); + if (ptr == null || !ptr->IsAgentActive() || !ptr->NpcInfo.Valid || !ptr->NpcInfo.Initialized) + { + return true; + } + uint id = ptr->Items[(int)base.Task.Slot].Id; + if (ptr->NpcInfo.Id == (uint)(base.Task.NpcIndex + 1) && id == base.Task.ItemId && HasRemainingItems()) + { + return true; + } + logger.LogWarning("Turn-in plan no longer matches live request: NPC {NpcIndex}, slot {Slot}, planned item {PlannedItemId}, live item {LiveItemId}", base.Task.NpcIndex, base.Task.Slot, base.Task.ItemId, id); + return false; + } + + private unsafe bool HasRemainingItems() + { + InventoryManager* ptr = InventoryManager.Instance(); + if (ptr != null) + { + return ptr->GetInventoryItemCount(base.Task.ItemId, isHq: false, checkEquipped: true, checkArmory: true, (short)base.Task.Collectability) >= _remaining; + } + return false; + } + public override bool ShouldInterruptOnDamage() { return false; diff --git a/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInTask.cs b/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInTask.cs index f19fc8b..22902b5 100644 --- a/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInTask.cs +++ b/Questionable/Questionable.Controller.CustomDelivery/SatisfactionSupplyTurnInTask.cs @@ -2,7 +2,7 @@ using Questionable.Controller.Steps; namespace Questionable.Controller.CustomDelivery; -internal sealed record SatisfactionSupplyTurnInTask(int NpcIndex, EDeliverySlot Slot, int DeliveryCount, uint NpcDataId) : ITask +internal sealed record SatisfactionSupplyTurnInTask(int NpcIndex, EDeliverySlot Slot, int DeliveryCount, uint NpcDataId, uint ItemId, ushort Collectability) : ITask { public override string ToString() { diff --git a/Questionable/Questionable.Controller.GameUi/InteractionUiController.cs b/Questionable/Questionable.Controller.GameUi/InteractionUiController.cs index d3baaf0..5dcdf73 100644 --- a/Questionable/Questionable.Controller.GameUi/InteractionUiController.cs +++ b/Questionable/Questionable.Controller.GameUi/InteractionUiController.cs @@ -97,7 +97,7 @@ internal sealed class InteractionUiController : IDisposable { get { - if (!_questController.IsRunning && !_fateController.IsRunning && !_seasonalDutyController.IsRunning && !_attunementController.IsRunning) + if (!SmartNavWarpRowId.HasValue && !SmartNavDestTerritoryId.HasValue && SmartNavTaxiDestPlaceName == null && AethernetDestinationName == null && !_questController.IsRunning && !_fateController.IsRunning && !_seasonalDutyController.IsRunning && !_attunementController.IsRunning) { return _territoryData.IsQuestBattleInstance(_clientState.TerritoryType); } diff --git a/Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs b/Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs index 3e7972e..829bd44 100644 --- a/Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs +++ b/Questionable/Questionable.Controller.Steps.Gathering/DoGather.cs @@ -106,9 +106,19 @@ internal static class DoGather else { List list = ReadSlots(addonPtr2); + if (list.Count == 0) + { + return ETaskResult.StillRunning; + } if (base.Task.Request.Collectability > 0) { - SlotInfo slotInfo = list.Single((SlotInfo x) => x.ItemId == base.Task.Request.ItemId); + SlotInfo slotInfo = list.FirstOrDefault((SlotInfo x) => x.ItemId == base.Task.Request.ItemId); + if (!(slotInfo != null)) + { + logger.LogDebug("Requested collectable {ItemId} is not available at this node", base.Task.Request.ItemId); + addonPtr2->FireCallbackInt(-1); + return ETaskResult.TaskComplete; + } addonPtr2->FireCallbackInt(slotInfo.Index); } else diff --git a/Questionable/Questionable.Controller.Steps.Interactions/Action.cs b/Questionable/Questionable.Controller.Steps.Interactions/Action.cs index 87e7f7a..1f42f79 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/Action.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/Action.cs @@ -86,6 +86,12 @@ internal static class Action return Environment.TickCount64 - _questFlagWaitStarted > (long)(timeoutSeconds * 1000f); } + public override void ResetTimeout() + { + base.ResetTimeout(); + _questFlagWaitStarted = Environment.TickCount64; + } + protected override bool Start() { if (base.Task.DataId.HasValue) @@ -283,6 +289,12 @@ internal static class Action return Environment.TickCount64 - _startedAt > (long)(timeoutSeconds * 1000f); } + public override void ResetTimeout() + { + base.ResetTimeout(); + _startedAt = Environment.TickCount64; + } + protected override bool Start() { if (gameFunctions.HasStatus(base.Task.Status)) diff --git a/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs b/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs index a852306..0aa29fb 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/Duty.cs @@ -209,6 +209,8 @@ internal static class Duty { private long _startMs; + private bool _runAccepted; + protected unsafe override bool Start() { if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData)) @@ -240,6 +242,7 @@ internal static class Duty } autoDutyIpc.StartInstance(base.Task.ContentFinderConditionId, base.Task.DutyMode); _startMs = Environment.TickCount64; + _runAccepted = false; return true; } @@ -255,8 +258,15 @@ internal static class Duty } if (!autoDutyIpc.IsStopped()) { + _runAccepted = true; return ETaskResult.StillRunning; } + if (!_runAccepted) + { + logger.LogError("AutoDuty did not start duty {CfcId} (territory {TerritoryId}) - Run was rejected, check the AutoDuty log [{Installation}]", base.Task.ContentFinderConditionId, contentFinderConditionData.TerritoryId, autoDutyIpc.DescribeInstallation()); + chatGui.PrintError("AutoDuty did not start the duty (rejected the run), check the AutoDuty log.", "Questionable", 576); + return ETaskResult.End; + } if (Environment.TickCount64 - _startMs < 10000) { return ETaskResult.StillRunning; @@ -277,7 +287,7 @@ internal static class Duty } } - internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask + internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : IDutyTask, ITask { public override string ToString() { @@ -433,17 +443,7 @@ internal static class Duty { protected override bool Start() { - CombatController.CombatData combatData = new CombatController.CombatData - { - ElementId = null, - Sequence = 0, - CompletionQuestVariablesFlags = new List(), - SpawnType = EEnemySpawnType.None, - KillEnemyDataIds = new List(), - ComplexCombatDatas = new List(), - CombatItemUse = null - }; - if (rsrModule.Start(combatData)) + if (rsrModule.StartForDuty()) { logger.LogDebug("Enabled RSR Henched mode for AutoDuty run"); return true; diff --git a/Questionable/Questionable.Controller.Steps.Interactions/EquipRecommended.cs b/Questionable/Questionable.Controller.Steps.Interactions/EquipRecommended.cs index e923791..6f8029b 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/EquipRecommended.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/EquipRecommended.cs @@ -92,6 +92,10 @@ internal static class EquipRecommended private bool _smartApplied; + private bool _useDirectEquip; + + private uint _smartClassJobId; + private int _directEquipCursor; private bool _directEquipStarted; @@ -150,8 +154,9 @@ internal static class EquipRecommended EClassJob job = (EClassJob)rowId; StatPriority priority = StatPriority.ForJob(job); byte playerLevel = GetPlayerLevel(job); - RaptureGearsetModule.GearsetEntry* entry = (RaptureGearsetModule.GearsetEntry*)Unsafe.AsPointer(in ptr->Entries[currentGearsetIndex]); - if (!TryComputeUpgradesAndMoves(job, priority, playerLevel, entry, out _smartUpgrades, out _smartMoves)) + _smartClassJobId = rowId; + _useDirectEquip = configuration.General.PreserveGearset || !CurrentGearsetMatchesEquippedItems(); + if (!TryComputeUpgradesAndMoves(job, priority, playerLevel, out _smartUpgrades, out _smartMoves)) { _smartUpgrades = null; _smartMoves = null; @@ -196,7 +201,11 @@ internal static class EquipRecommended } return ETaskResult.StillRunning; } - if (configuration.General.PreserveGearset) + if (!_useDirectEquip && !CurrentGearsetMatchesEquippedItems()) + { + _useDirectEquip = true; + } + if (_useDirectEquip) { return DirectEquipPhase(); } @@ -403,10 +412,20 @@ internal static class EquipRecommended return true; } - private unsafe bool TryComputeUpgradesAndMoves(EClassJob job, StatPriority priority, byte playerLevel, RaptureGearsetModule.GearsetEntry* entry, out List<(int Slot, BestItemRef Item)> upgrades, out List moves) + private unsafe bool TryComputeUpgradesAndMoves(EClassJob job, StatPriority priority, byte playerLevel, out List<(int Slot, BestItemRef Item)> upgrades, out List moves) { upgrades = new List<(int, BestItemRef)>(); moves = new List(); + InventoryManager* ptr = InventoryManager.Instance(); + if (ptr == null) + { + return false; + } + InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(InventoryType.EquippedItems); + if (inventoryContainer == null) + { + return false; + } BestItemRef? bestItemRef = null; bool flag = job.IsCrafter() || job.IsGatherer(); Dictionary cursors = new Dictionary(); @@ -434,7 +453,8 @@ internal static class EquipRecommended { bestItemRef = bestItemRef2; } - if (((ItemHandle)entry->GetItem((RaptureGearsetModule.GearsetItemIndex)i).ItemId).Id == ((ItemHandle)bestItemRef2.Value.ItemId).Id) + InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i); + if (((inventorySlot != null) ? ItemHandle.FromInventorySlot(inventorySlot).Id : 0) == ((ItemHandle)bestItemRef2.Value.ItemId).Id) { continue; } @@ -457,6 +477,48 @@ internal static class EquipRecommended return true; } + private unsafe bool CurrentGearsetMatchesEquippedItems() + { + RaptureGearsetModule* ptr = RaptureGearsetModule.Instance(); + if (ptr == null) + { + return false; + } + int currentGearsetIndex = ptr->CurrentGearsetIndex; + if (currentGearsetIndex < 0 || !ptr->IsValidGearset(currentGearsetIndex)) + { + return false; + } + RaptureGearsetModule.GearsetEntry* ptr2 = (RaptureGearsetModule.GearsetEntry*)Unsafe.AsPointer(in ptr->Entries[currentGearsetIndex]); + if (ptr2->ClassJob != _smartClassJobId) + { + return false; + } + InventoryManager* ptr3 = InventoryManager.Instance(); + if (ptr3 == null) + { + return false; + } + InventoryContainer* inventoryContainer = ptr3->GetInventoryContainer(InventoryType.EquippedItems); + if (inventoryContainer == null) + { + return false; + } + for (int i = 0; i <= 13; i++) + { + if (i != 5) + { + InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i); + uint num = ((inventorySlot != null) ? ItemHandle.FromInventorySlot(inventorySlot).Id : 0u); + if (((ItemHandle)ptr2->GetItem((RaptureGearsetModule.GearsetItemIndex)i).ItemId).Id != num) + { + return false; + } + } + } + return true; + } + private static bool IsBagContainer(InventoryType type) { if (type <= InventoryType.Inventory4) diff --git a/Questionable/Questionable.Controller.Steps.Interactions/MeldMateria.cs b/Questionable/Questionable.Controller.Steps.Interactions/MeldMateria.cs index 8c0191f..a93368f 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/MeldMateria.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/MeldMateria.cs @@ -66,6 +66,12 @@ internal static class MeldMateria return Environment.TickCount64 - _stateChangedAt > (long)(timeoutSeconds * 1000f); } + public override void ResetTimeout() + { + base.ResetTimeout(); + _stateChangedAt = Environment.TickCount64; + } + private void SetState(EMeldState state) { _state = state; diff --git a/Questionable/Questionable.Controller.Steps.Interactions/SinglePlayerDuty.cs b/Questionable/Questionable.Controller.Steps.Interactions/SinglePlayerDuty.cs index 50fd8bf..cb28962 100644 --- a/Questionable/Questionable.Controller.Steps.Interactions/SinglePlayerDuty.cs +++ b/Questionable/Questionable.Controller.Steps.Interactions/SinglePlayerDuty.cs @@ -193,7 +193,7 @@ internal static class SinglePlayerDuty } } - internal sealed record WaitSinglePlayerDuty(uint ContentFinderConditionId) : ITask + internal sealed record WaitSinglePlayerDuty(uint ContentFinderConditionId) : IDutyTask, ITask { public override string ToString() { @@ -302,7 +302,7 @@ internal static class SinglePlayerDuty } } - internal sealed record CheckDutyOutcome(ElementId QuestId, byte SequenceBeforeEntering, IReadOnlyList? VariablesBeforeEntering, IList CompletionQuestVariablesFlags, IReadOnlyList DutyTasks) : ITask + internal sealed record CheckDutyOutcome(ElementId QuestId, byte SequenceBeforeEntering, IReadOnlyList? VariablesBeforeEntering, IList CompletionQuestVariablesFlags, IReadOnlyList DutyTasks) : IDutyTask, ITask { public override string ToString() { diff --git a/Questionable/Questionable.Controller.Steps.Shared/AethernetRide.cs b/Questionable/Questionable.Controller.Steps.Shared/AethernetRide.cs index d39a501..b99bc37 100644 --- a/Questionable/Questionable.Controller.Steps.Shared/AethernetRide.cs +++ b/Questionable/Questionable.Controller.Steps.Shared/AethernetRide.cs @@ -32,6 +32,7 @@ internal static class AethernetRide private enum EAethernetPhase { None, + WaitingForPlayer, Mounting, Moving, Unmounting, @@ -72,6 +73,12 @@ internal static class AethernetRide return Environment.TickCount64 - _phaseStartedAt > (long)(timeoutSeconds * 1000f); } + public override void ResetTimeout() + { + base.ResetTimeout(); + _phaseStartedAt = Environment.TickCount64; + } + protected override bool Start() { SetPhase(EAethernetPhase.None); @@ -79,56 +86,60 @@ internal static class AethernetRide aethernetTeleportService.Reset(); if (aetheryteFunctions.IsAetheryteUnlocked(base.Task.From) && aetheryteFunctions.IsAetheryteUnlocked(base.Task.To)) { - uint territoryType = clientState.TerritoryType; IPlayerCharacter localPlayer = objectTable.LocalPlayer; if (localPlayer == null) { - return false; - } - Vector3 playerPosition = localPlayer.Position; - if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.To)) - { - float num = (base.Task.From.IsFirmamentAetheryte() ? 11f : (AetheryteConverter.IsLargeAetheryte(base.Task.From) ? 11f : 4f)); - if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < num) - { - BeginTeleport(); - return true; - } - if (base.Task.From == EAetheryteLocation.SolutionNine) - { - logger.LogDebug("Moving to S9 aetheryte"); - int num2 = 4; - List list = new List(num2); - CollectionsMarshal.SetCount(list, num2); - Span span = CollectionsMarshal.AsSpan(list); - span[0] = new Vector3(0f, 8.442986f, 9f); - span[1] = new Vector3(9f, 8.442986f, 0f); - span[2] = new Vector3(-9f, 8.442986f, 0f); - span[3] = new Vector3(0f, 8.442986f, -9f); - Vector3 to = list.MinBy((Vector3 x) => Vector3.Distance(playerPosition, x)); - SetPhase(EAethernetPhase.Moving); - movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, to, fly: false, sprint: true, 0.25f); - return true; - } - if (territoryData.CanUseMount(territoryType) && aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) > 30f && !gameFunctions.HasStatusPreventingMount() && gameFunctions.Mount()) - { - SetPhase(EAethernetPhase.Mounting); - _continueAt = Environment.TickCount64 + 500; - return true; - } - StartMoving(); + SetPhase(EAethernetPhase.WaitingForPlayer); return true; } + Vector3 position = localPlayer.Position; + if (HasArrived(position)) + { + return false; + } + StartFromCurrentPosition(position); + return true; + } + if (clientState.TerritoryType == aetheryteData.TerritoryIds[base.Task.To]) + { + logger.LogWarning("Aethernet ride not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), skipping as we are already in the destination territory", base.Task.From, base.Task.To); + return false; + } + throw new TaskException($"Aethernet ride not unlocked (from: {base.Task.From}, to: {base.Task.To})"); + } + + private void StartFromCurrentPosition(Vector3 playerPosition) + { + uint territoryType = clientState.TerritoryType; + float num = (base.Task.From.IsFirmamentAetheryte() ? 11f : (AetheryteConverter.IsLargeAetheryte(base.Task.From) ? 11f : 4f)); + if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < num) + { + BeginTeleport(); + } + else if (base.Task.From == EAetheryteLocation.SolutionNine) + { + logger.LogDebug("Moving to S9 aetheryte"); + int num2 = 4; + List list = new List(num2); + CollectionsMarshal.SetCount(list, num2); + Span span = CollectionsMarshal.AsSpan(list); + span[0] = new Vector3(0f, 8.442986f, 9f); + span[1] = new Vector3(9f, 8.442986f, 0f); + span[2] = new Vector3(-9f, 8.442986f, 0f); + span[3] = new Vector3(0f, 8.442986f, -9f); + Vector3 to = list.MinBy((Vector3 x) => Vector3.Distance(playerPosition, x)); + SetPhase(EAethernetPhase.Moving); + movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, to, fly: false, sprint: true, 0.25f); + } + else if (territoryData.CanUseMount(territoryType) && aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) > 30f && !gameFunctions.HasStatusPreventingMount() && gameFunctions.Mount()) + { + SetPhase(EAethernetPhase.Mounting); + _continueAt = Environment.TickCount64 + 500; } else { - if (clientState.TerritoryType != aetheryteData.TerritoryIds[base.Task.To]) - { - throw new TaskException($"Aethernet ride not unlocked (from: {base.Task.From}, to: {base.Task.To})"); - } - logger.LogWarning("Aethernet ride not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), skipping as we are already in the destination territory", base.Task.From, base.Task.To); + StartMoving(); } - return false; } private void StartMoving() @@ -178,6 +189,20 @@ internal static class AethernetRide } switch (_phase) { + case EAethernetPhase.WaitingForPlayer: + { + Vector3? vector2 = objectTable.LocalPlayer?.Position; + if (!vector2.HasValue) + { + return ETaskResult.StillRunning; + } + if (HasArrived(vector2.Value)) + { + return ETaskResult.TaskComplete; + } + StartFromCurrentPosition(vector2.Value); + return ETaskResult.StillRunning; + } case EAethernetPhase.Mounting: if (condition[ConditionFlag.Mounted]) { @@ -228,21 +253,7 @@ internal static class AethernetRide { return ETaskResult.StillRunning; } - if (aetheryteData.IsAirshipLanding(base.Task.To)) - { - if (aetheryteData.CalculateAirshipLandingDistance(vector.Value, clientState.TerritoryType, base.Task.To) > 5f) - { - return ETaskResult.StillRunning; - } - } - else if (aetheryteData.IsCityAetheryte(base.Task.To) || aetheryteData.IsGoldSaucerAetheryte(base.Task.To)) - { - if (aetheryteData.CalculateDistance(vector.Value, clientState.TerritoryType, base.Task.To) > 20f) - { - return ETaskResult.StillRunning; - } - } - else if (clientState.TerritoryType != aetheryteData.TerritoryIds[base.Task.To]) + if (!HasArrived(vector.Value)) { return ETaskResult.StillRunning; } @@ -254,6 +265,15 @@ internal static class AethernetRide } } + private bool HasArrived(Vector3 position) + { + if (!aetheryteData.IsAirshipLanding(base.Task.To)) + { + return aetheryteData.CalculateDistance(position, clientState.TerritoryType, base.Task.To) <= 20f; + } + return aetheryteData.CalculateAirshipLandingDistance(position, clientState.TerritoryType, base.Task.To) <= 5f; + } + public override bool ShouldInterruptOnDamage() { return true; diff --git a/Questionable/Questionable.Controller.Steps.Shared/RedeemRewardItems.cs b/Questionable/Questionable.Controller.Steps.Shared/RedeemRewardItems.cs index 8140d2e..12a69fc 100644 --- a/Questionable/Questionable.Controller.Steps.Shared/RedeemRewardItems.cs +++ b/Questionable/Questionable.Controller.Steps.Shared/RedeemRewardItems.cs @@ -16,33 +16,85 @@ namespace Questionable.Controller.Steps.Shared; internal static class RedeemRewardItems { - internal sealed class Factory(QuestData questData, Configuration configuration) : ITaskFactory + internal sealed class Factory(QuestData questData, Configuration configuration, ILogger logger) : ITaskFactory { - public unsafe IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) + public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.InteractionType != EInteractionType.AcceptQuest) { return Array.Empty(); } - List list = new List(); - InventoryManager* ptr = InventoryManager.Instance(); - if (ptr == null) + return CreateTasks(questData, configuration, logger); + } + } + + internal sealed class CompletionFactory : ITaskFactory + { + public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) + { + if (step.InteractionType != EInteractionType.CompleteQuest) { - return list; + return Array.Empty(); } - bool hasFreeInventorySlot = InventoryHelper.HasFreeInventorySlot(); - foreach (ItemReward redeemableItem in questData.RedeemableItems) + return new global::_003C_003Ez__ReadOnlySingleElementList(new ScanAfterQuestCompletion(step.TurnInQuestId ?? quest.Id)); + } + } + + internal sealed record ScanAfterQuestCompletion(ElementId ElementId) : ITask + { + public override string ToString() + { + return $"ScanRewardsAfterQuestCompletion({ElementId})"; + } + } + + internal sealed class ScanAfterQuestCompletionExecutor(QuestFunctions questFunctions, QuestData questData, Configuration configuration, ILogger logger) : TaskExecutor(), IExtraTaskCreator, ITaskExecutor + { + private const long InventorySettleDelayMs = 1000L; + + private long? _scanAt; + + private List _tasks = new List(); + + protected override bool Start() + { + _scanAt = null; + _tasks = new List(); + return true; + } + + public override ETaskResult Update() + { + if (!questFunctions.IsQuestComplete(base.Task.ElementId)) { - if (ptr->GetInventoryItemCount(redeemableItem.ItemId, isHq: false, checkEquipped: true, checkArmory: true, 0) != 0 && !redeemableItem.IsUnlocked() && PassesRedemptionFilters(redeemableItem.Type, redeemableItem.IsUntradable, configuration.General, hasFreeInventorySlot)) - { - list.Add(new Task(redeemableItem)); - } + return ETaskResult.StillRunning; } - if (list.Count > 0) + long tickCount = Environment.TickCount64; + if (!_scanAt.HasValue) { - list.Insert(0, new Mount.UnmountTask()); + _scanAt = tickCount + 1000; + return ETaskResult.StillRunning; } - return list; + if (tickCount < _scanAt) + { + return ETaskResult.StillRunning; + } + _tasks = CreateTasks(questData, configuration, logger); + if (_tasks.Count <= 0) + { + return ETaskResult.TaskComplete; + } + return ETaskResult.CreateNewTasks; + } + + public IEnumerable CreateExtraTasks() + { + return _tasks; + } + + public override bool ShouldInterruptOnDamage() + { + return false; } } @@ -182,6 +234,36 @@ internal static class RedeemRewardItems } } + private unsafe static List CreateTasks(QuestData questData, Configuration configuration, ILogger logger) + { + List list = new List(); + InventoryManager* ptr = InventoryManager.Instance(); + if (ptr == null) + { + return list; + } + bool flag = InventoryHelper.HasFreeInventorySlot(); + foreach (ItemReward redeemableItem in questData.RedeemableItems) + { + if (ptr->GetInventoryItemCount(redeemableItem.ItemId, isHq: false, checkEquipped: true, checkArmory: true, 0) != 0 && !redeemableItem.IsUnlocked()) + { + if (!PassesRedemptionFilters(redeemableItem.Type, redeemableItem.IsUntradable, configuration.General, flag)) + { + logger.LogDebug("Skipping quest reward {ItemName}: type={RewardType}, untradable={IsUntradable}, freeInventorySlot={HasFreeInventorySlot}", redeemableItem.Name, redeemableItem.Type, redeemableItem.IsUntradable, flag); + } + else + { + list.Add(new Task(redeemableItem)); + } + } + } + if (list.Count > 0) + { + list.Insert(0, new Mount.UnmountTask()); + } + return list; + } + internal static bool PassesRedemptionFilters(EItemRewardType type, bool isUntradable, Configuration.GeneralConfiguration general, bool hasFreeInventorySlot) { if (general.DisabledRewardTypes.Contains(type)) diff --git a/Questionable/Questionable.Controller.Steps.Shared/WaitAtEnd.cs b/Questionable/Questionable.Controller.Steps.Shared/WaitAtEnd.cs index 907b4d6..2f88f96 100644 --- a/Questionable/Questionable.Controller.Steps.Shared/WaitAtEnd.cs +++ b/Questionable/Questionable.Controller.Steps.Shared/WaitAtEnd.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Numerics; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; +using LLib.GameData; using Questionable.Controller.Steps.Common; using Questionable.Controller.Utils; using Questionable.Data; @@ -61,19 +62,19 @@ internal static class WaitAtEnd { break; } - goto IL_01bf; + goto IL_01ad; case EInteractionType.SinglePlayerDuty: if (bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions)) { break; } - goto IL_01bf; + goto IL_01ad; case EInteractionType.Fish: - if (autoHookIpc.IsAvailable()) + if (!autoHookIpc.IsAvailable()) { - break; + return new global::_003C_003Ez__ReadOnlySingleElementList(new EndAutomation()); } - goto IL_01bf; + break; case EInteractionType.WalkTo: case EInteractionType.Jump: return new global::_003C_003Ez__ReadOnlySingleElementList(Next(quest, sequence)); @@ -91,13 +92,13 @@ internal static class WaitAtEnd { break; } - goto IL_02a6; + goto IL_02ca; case EInteractionType.UseItem: if (!step.TargetTerritoryId.HasValue) { break; } - goto IL_02a6; + goto IL_02ca; case EInteractionType.AcceptQuest: { WaitQuestAccepted waitQuestAccepted = new WaitQuestAccepted(step.PickUpQuestId ?? quest.Id); @@ -128,9 +129,14 @@ internal static class WaitAtEnd } return new global::_003C_003Ez__ReadOnlyArray(new ITask[2] { waitQuestCompleted, waitDelay2 }); } - IL_01bf: - return new global::_003C_003Ez__ReadOnlySingleElementList(new EndAutomation()); - IL_02a6: + IL_01ad: + return new global::_003C_003Ez__ReadOnlyArray(new ITask[3] + { + new WaitManualDuty(), + new WaitDelay(), + Next(quest, sequence) + }); + IL_02ca: if (step.TerritoryId != step.TargetTerritoryId) { task2 = new WaitCondition.Task(() => clientState.TerritoryType == step.TargetTerritoryId, "Wait(tp to territory: " + territoryData.GetNameAndId(step.TargetTerritoryId.Value) + ")"); @@ -369,6 +375,58 @@ internal static class WaitAtEnd } } + internal sealed record WaitManualDuty : IDutyTask, ITask + { + public override string ToString() + { + return "Wait(manual duty)"; + } + } + + internal sealed class WaitManualDutyExecutor(IClientState clientState, ICondition condition, TerritoryData territoryData) : TaskExecutor(), IDebugStateProvider, ITaskExecutor + { + private bool _enteredDuty; + + protected override bool Start() + { + _enteredDuty = false; + return true; + } + + public override ETaskResult Update() + { + uint territoryType = clientState.TerritoryType; + bool flag = territoryData.IsDutyInstance(territoryType) && !territoryData.IsFieldOperation(territoryType); + if (!_enteredDuty) + { + if (flag) + { + _enteredDuty = true; + } + return ETaskResult.StillRunning; + } + if (flag || ConditionHelper.IsBetweenAreas(condition)) + { + return ETaskResult.StillRunning; + } + return ETaskResult.TaskComplete; + } + + public override bool ShouldInterruptOnDamage() + { + return false; + } + + public string? GetDebugState() + { + if (!_enteredDuty) + { + return "Waiting for you to enter the duty"; + } + return "Waiting for the duty to finish"; + } + } + internal sealed record NextStep(ElementId ElementId, int Sequence) : ILastTask, ITask { public override string ToString() diff --git a/Questionable/Questionable.Controller.Steps/IDutyTask.cs b/Questionable/Questionable.Controller.Steps/IDutyTask.cs new file mode 100644 index 0000000..f000db0 --- /dev/null +++ b/Questionable/Questionable.Controller.Steps/IDutyTask.cs @@ -0,0 +1,5 @@ +namespace Questionable.Controller.Steps; + +internal interface IDutyTask : ITask +{ +} diff --git a/Questionable/Questionable.Controller.Steps/ITaskExecutor.cs b/Questionable/Questionable.Controller.Steps/ITaskExecutor.cs index 23afa06..d48d3ce 100644 --- a/Questionable/Questionable.Controller.Steps/ITaskExecutor.cs +++ b/Questionable/Questionable.Controller.Steps/ITaskExecutor.cs @@ -16,6 +16,8 @@ internal interface ITaskExecutor bool WasInterrupted(); + void ResetTimeout(); + bool HasTimedOut(float timeoutSeconds); ETaskResult Update(); diff --git a/Questionable/Questionable.Controller.Steps/TaskExecutor.cs b/Questionable/Questionable.Controller.Steps/TaskExecutor.cs index 2f87093..da92ecb 100644 --- a/Questionable/Questionable.Controller.Steps/TaskExecutor.cs +++ b/Questionable/Questionable.Controller.Steps/TaskExecutor.cs @@ -32,6 +32,11 @@ internal abstract class TaskExecutor : ITaskExecutor where T : class, ITask return Environment.TickCount64 - _lastProgressAt > (long)(timeoutSeconds * 1000f); } + public virtual void ResetTimeout() + { + ResetProgressTimer(); + } + protected void ResetProgressTimer() { _lastProgressAt = Environment.TickCount64; diff --git a/Questionable/Questionable.Controller/CustomDeliveryController.cs b/Questionable/Questionable.Controller/CustomDeliveryController.cs index f293db1..20f869b 100644 --- a/Questionable/Questionable.Controller/CustomDeliveryController.cs +++ b/Questionable/Questionable.Controller/CustomDeliveryController.cs @@ -786,18 +786,18 @@ internal sealed class CustomDeliveryController : MiniTaskController : IDisposable InterruptQueueWithCombat(); return; } - if (currentTaskExecutor.HasTimedOut(_configuration.Advanced.InteractionTimeoutSeconds)) + if (ConditionHelper.IsInCutscene(_condition)) + { + currentTaskExecutor.ResetTimeout(); + } + else if (currentTaskExecutor.HasTimedOut(_configuration.Advanced.InteractionTimeoutSeconds)) { _logger.LogWarning("Task {TaskName} timed out after {Timeout}s", currentTask, _configuration.Advanced.InteractionTimeoutSeconds); if (_condition[ConditionFlag.InCombat]) diff --git a/Questionable/Questionable.Controller/QuestController.cs b/Questionable/Questionable.Controller/QuestController.cs index 3b3694c..47d0900 100644 --- a/Questionable/Questionable.Controller/QuestController.cs +++ b/Questionable/Questionable.Controller/QuestController.cs @@ -61,6 +61,11 @@ internal sealed class QuestController : MiniTaskController } } + internal void ResetInitialQuestWork(QuestProgressInfo? questWork) + { + InitialQuestWork = questWork; + } + public void SetSequence(byte sequence, int step = 0) { Sequence = sequence; @@ -180,6 +185,8 @@ internal sealed class QuestController : MiniTaskController private QuestProgress? _pendingQuest; + private (ElementId QuestId, QuestProgressInfo QuestWork)? _initialQuestWorkPreservedForReload; + private EAutomationType _automationType; private bool _commandAfterStopFired; @@ -210,6 +217,10 @@ internal sealed class QuestController : MiniTaskController private bool _resumeAfterSideTasks; + private bool _resumeAfterDutyExit; + + private bool _resumeAfterOccupiedQuestTransition; + private bool _runningDebugTasks; private const char ClipboardSeparator = ';'; @@ -360,6 +371,13 @@ internal sealed class QuestController : MiniTaskController Dictionary gatheringPoints = _gatheringPointRegistry.Build(); _framework.RunOnFrameworkThread(delegate { + QuestProgress questProgress = CurrentQuestDetails?.Progress; + QuestProgressInfo questProgressInfo = questProgress?.InitialQuestWork; + if (questProgress != null && questProgressInfo == null) + { + questProgressInfo = _questFunctions.GetQuestProgressInfo(questProgress.Quest.Id); + } + _initialQuestWorkPreservedForReload = ((questProgress != null && questProgressInfo != null) ? new(ElementId, QuestProgressInfo)?((questProgress.Quest.Id, questProgressInfo)) : (((ElementId, QuestProgressInfo)?)null)); ResetInternalState(); _gatheringPointRegistry.Publish(gatheringPoints); _questRegistry.Publish(questSnapshot); @@ -373,6 +391,8 @@ internal sealed class QuestController : MiniTaskController _pendingQuest = null; _simulatedQuest = null; _safeAnimationEnd = 0L; + _resumeAfterDutyExit = false; + _resumeAfterOccupiedQuestTransition = false; DebugState = null; } @@ -501,6 +521,28 @@ internal sealed class QuestController : MiniTaskController DebugState = "Not logged in"; return; } + if (_resumeAfterDutyExit) + { + if (ConditionHelper.IsBetweenAreas(_condition) || IsInsideDutyInstance()) + { + DebugState = "Waiting for duty exit"; + return; + } + _resumeAfterDutyExit = false; + CheckNextTasks("Resume after duty exit"); + return; + } + if (_resumeAfterOccupiedQuestTransition) + { + if (_gameFunctions.IsOccupied()) + { + DebugState = "Waiting for quest transition to finish"; + return; + } + _resumeAfterOccupiedQuestTransition = false; + CheckNextTasks("Resume after occupied quest transition"); + return; + } if (_runningDebugTasks) { if (!_taskQueue.AllTasksComplete) @@ -638,8 +680,32 @@ internal sealed class QuestController : MiniTaskController } else if (questProgress.Sequence != b) { + if (IsDutyChainActive()) + { + DebugState = "Waiting for duty to finish"; + return; + } questProgress.SetSequence(b); - CheckNextTasks($"New sequence {questProgress == _startedQuest}"); + QuestProgressInfo questProgressInfo = _questFunctions.GetQuestProgressInfo(questProgress.Quest.Id); + questProgress.ResetInitialQuestWork(questProgressInfo); + _logger.LogDebug("Captured initial quest variables for {QuestId} sequence {Sequence}: {QuestWork}", questProgress.Quest.Id, b, questProgressInfo); + bool flag = _gameFunctions.IsOccupied(); + if (flag) + { + EAutomationType automationType = AutomationType; + bool flag2 = (uint)(automationType - 1) <= 2u; + flag = flag2; + } + if (flag) + { + ClearTasksInternal(); + _resumeAfterOccupiedQuestTransition = true; + DebugState = "Waiting for quest transition to finish"; + } + else + { + CheckNextTasks($"New sequence {questProgress == _startedQuest}"); + } } else if (questProgress.Step == 255) { @@ -770,6 +836,25 @@ internal sealed class QuestController : MiniTaskController return (null, 0); } + private bool IsInsideDutyInstance() + { + uint territoryType = _clientState.TerritoryType; + if (_territoryData.IsDutyInstance(territoryType)) + { + return !_territoryData.IsFieldOperation(territoryType); + } + return false; + } + + private bool IsDutyChainActive() + { + if (!(_taskQueue.CurrentTaskExecutor?.CurrentTask is IDutyTask)) + { + return _taskQueue.RemainingTasks.Any((ITask x) => x is IDutyTask); + } + return true; + } + private bool IsLevelingModeActive() { ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask; @@ -890,6 +975,8 @@ internal sealed class QuestController : MiniTaskController _deathCount = 0; _deathRecoveryPending = false; _resumeAfterSideTasks = false; + _resumeAfterDutyExit = false; + _resumeAfterOccupiedQuestTransition = false; SessionConditions.Clear(); _conditionsMetAtStart.Clear(); } @@ -1179,12 +1266,29 @@ internal sealed class QuestController : MiniTaskController { return null; } + questProgress.CaptureInitialQuestWork(_questFunctions.GetQuestProgressInfo(questId)); return questProgress.InitialQuestWork; } private QuestProgress CreateQuestProgress(Quest quest, byte sequence = 0, int step = 0) { - return new QuestProgress(quest, sequence, step, _questFunctions.GetQuestProgressInfo(quest.Id)); + (ElementId, QuestProgressInfo)? initialQuestWorkPreservedForReload = _initialQuestWorkPreservedForReload; + QuestProgressInfo questProgressInfo; + if (initialQuestWorkPreservedForReload.HasValue) + { + (ElementId, QuestProgressInfo) valueOrDefault = initialQuestWorkPreservedForReload.GetValueOrDefault(); + if (valueOrDefault.Item1.Equals(quest.Id)) + { + questProgressInfo = valueOrDefault.Item2; + _initialQuestWorkPreservedForReload = null; + _logger.LogDebug("Restored initial quest variables for {QuestId} after data reload: {QuestWork}", quest.Id, questProgressInfo); + goto IL_0075; + } + } + questProgressInfo = _questFunctions.GetQuestProgressInfo(quest.Id); + goto IL_0075; + IL_0075: + return new QuestProgress(quest, sequence, step, questProgressInfo); } private void CaptureInitialQuestWork(QuestProgress? progress, ElementId questId) @@ -1257,8 +1361,9 @@ internal sealed class QuestController : MiniTaskController return; } } - if (questStep != null && questStep.TerritoryId != _clientState.TerritoryType && (_territoryData.IsDutyInstance(_clientState.TerritoryType) || _territoryData.IsQuestBattleInstance(_clientState.TerritoryType))) + if (questStep != null && questStep.TerritoryId != _clientState.TerritoryType && IsInsideDutyInstance()) { + _resumeAfterDutyExit = true; _logger.LogDebug("Deferring next step: step territory {StepTerritory} != current instance territory {CurrentTerritory}, waiting for duty exit", questStep.TerritoryId, _clientState.TerritoryType); DebugState = "Waiting for duty exit"; return; diff --git a/Questionable/Questionable.Controller/QuestPriorityResolver.cs b/Questionable/Questionable.Controller/QuestPriorityResolver.cs index ab6d83f..ab95243 100644 --- a/Questionable/Questionable.Controller/QuestPriorityResolver.cs +++ b/Questionable/Questionable.Controller/QuestPriorityResolver.cs @@ -84,7 +84,7 @@ internal sealed class QuestPriorityResolver { return QuestResolution.Slot(EQuestResolutionType.Simulated, slots.Simulated, "Simulated quest"); } - if (slots.Next != null) + if (slots.Next != null && !_questFunctions.IsQuestBlacklisted(slots.Next.Quest.Id)) { return QuestResolution.Slot(EQuestResolutionType.NextQuest, slots.Next, $"Next quest {slots.Next.Quest.Id}"); } @@ -93,7 +93,7 @@ internal sealed class QuestPriorityResolver private QuestResolution ResolveFromGameState(List manualPriorityQuests, QuestController.EAutomationType automationType) { - bool allowNewMsq = automationType != QuestController.EAutomationType.SingleQuestB; + bool flag = automationType != QuestController.EAutomationType.SingleQuestB; QuestReference resolvedMsqQuest = GetResolvedMsqQuest(); int num = _objectTable.LocalPlayer?.Level ?? 0; EClassJob valueOrDefault = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault(); @@ -103,6 +103,16 @@ internal sealed class QuestPriorityResolver QuestResolution valueOrDefault2 = questResolution.GetValueOrDefault(); return ApplyQuestRedirects(valueOrDefault2); } + uint territoryType = _clientState.TerritoryType; + bool flag2 = territoryType - 181 <= 2; + if (flag2 && flag && _configuration.General.MsqPriority != Configuration.EMsqPriorityMode.Manual) + { + ElementId currentQuest = resolvedMsqQuest.CurrentQuest; + if ((object)currentQuest != null && currentQuest.Value > 0 && !_questFunctions.IsQuestAccepted(currentQuest)) + { + return QuestResolution.ForQuest(EQuestResolutionType.MsqImmediate, currentQuest, resolvedMsqQuest.Sequence, resolvedMsqQuest.State, $"Starting city opening MSQ {currentQuest}"); + } + } questResolution = TryResolveInProgressClassQuest(valueOrDefault, resolvedMsqQuest.State); if (questResolution.HasValue) { @@ -130,7 +140,7 @@ internal sealed class QuestPriorityResolver _lastLoggedPriorityClassQuest = null; _loggedNoClassQuestsAvailable = false; _loggedAdventurerClass = false; - questResolution = TryResolveMsqImmediate(resolvedMsqQuest, allowNewMsq); + questResolution = TryResolveMsqImmediate(resolvedMsqQuest, flag); if (questResolution.HasValue) { return questResolution.GetValueOrDefault(); @@ -147,7 +157,7 @@ internal sealed class QuestPriorityResolver QuestResolution valueOrDefault4 = questResolution.GetValueOrDefault(); return ApplyQuestRedirects(valueOrDefault4); } - questResolution = TryResolveMsqFallback(resolvedMsqQuest, allowNewMsq); + questResolution = TryResolveMsqFallback(resolvedMsqQuest, flag); if (questResolution.HasValue) { return questResolution.GetValueOrDefault(); @@ -259,8 +269,14 @@ internal sealed class QuestPriorityResolver { return null; } - (ElementId, byte) tuple = (from x in manualPriorityQuests - where _questFunctions.IsQuestAccepted(x.Id) ? ((!_configuration.Advanced.AutoPrioritizeAlliedSocietyRankUp || !(x.Id is QuestId questId) || !_questFunctions.WouldOvercapAlliedSocietyReputation(questId)) ? true : false) : _questFunctions.IsReadyToAcceptQuest(x.Id) + (ElementId, byte) tuple = (from x in manualPriorityQuests.Where(delegate(Quest x) + { + if (_questFunctions.IsQuestBlacklisted(x.Id)) + { + return false; + } + return _questFunctions.IsQuestAccepted(x.Id) ? ((!_configuration.Advanced.AutoPrioritizeAlliedSocietyRankUp || !(x.Id is QuestId questId) || !_questFunctions.WouldOvercapAlliedSocietyReputation(questId)) ? true : false) : _questFunctions.IsReadyToAcceptQuest(x.Id); + }) select (QuestId: x.Id, Sequence: _questFunctions.GetQuestProgressInfo(x.Id)?.Sequence ?? 0)).FirstOrDefault(); if ((object)tuple.Item1 != null) { @@ -271,12 +287,12 @@ internal sealed class QuestPriorityResolver private QuestResolution? TryResolveInProgressClassQuest(EClassJob currentClassJob, MainScenarioQuestState msqState) { - if (currentClassJob == EClassJob.Adventurer) + if (_configuration.Advanced.SkipClassJobQuests || currentClassJob == EClassJob.Adventurer) { return null; } QuestInfo questInfo = (from x in _questData.GetClassJobQuests(currentClassJob) - where (x.Level <= 5 || !_configuration.Advanced.SkipClassJobQuests) && _questFunctions.IsQuestAccepted(x.QuestId) && !_questFunctions.IsQuestComplete(x.QuestId) + where !_questFunctions.IsQuestBlacklisted(x.QuestId) && _questFunctions.IsQuestAccepted(x.QuestId) && !_questFunctions.IsQuestComplete(x.QuestId) orderby x.Level select x).FirstOrDefault(); if (questInfo == null) @@ -422,10 +438,14 @@ internal sealed class QuestPriorityResolver { case 1: { - ElementId elementId = new QuestId(ptr->NormalQuests[trackingWork.Index].QuestId); - if (_questRegistry.IsKnownQuest(elementId) && !_questFunctions.IsQuestBlacklisted(elementId)) + QuestWork questWork = ptr->NormalQuests[trackingWork.Index]; + if (!questWork.IsHidden) { - list.Add((elementId, QuestManager.GetQuestSequence(elementId.Value))); + ElementId elementId = new QuestId(questWork.QuestId); + if (_questRegistry.IsKnownQuest(elementId) && !_questFunctions.IsQuestBlacklisted(elementId)) + { + list.Add((elementId, QuestManager.GetQuestSequence(elementId.Value))); + } } break; } diff --git a/Questionable/Questionable.Data/ChangelogData.cs b/Questionable/Questionable.Data/ChangelogData.cs index 34aa9a5..b76a110 100644 --- a/Questionable/Questionable.Data/ChangelogData.cs +++ b/Questionable/Questionable.Data/ChangelogData.cs @@ -11,328 +11,357 @@ internal static class ChangelogData static ChangelogData() { - int num = 74; + int num = 77; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); ref ChangelogEntry reference = ref span[0]; - DateOnly releaseDate = new DateOnly(2026, 8, 13); - int num2 = 3; + DateOnly releaseDate = new DateOnly(2026, 8, 17); + int num2 = 5; List list2 = new List(num2); CollectionsMarshal.SetCount(list2, num2); Span span2 = CollectionsMarshal.AsSpan(list2); ref ChangeEntry reference2 = ref span2[0]; - int num3 = 1; + int num3 = 5; List list3 = new List(num3); CollectionsMarshal.SetCount(list3, num3); - CollectionsMarshal.AsSpan(list3)[0] = "Added autodetection of your Grand Company based on player's starting city."; - reference2 = new ChangeEntry(EChangeCategory.Added, "Added", list3); + Span span3 = CollectionsMarshal.AsSpan(list3); + span3[0] = "Added a fixed-height, scrollable list to Queue -> Saved Presets."; + span3[1] = "Added an always-on selected-step world preview to the debug Quest Editor, including targetability and required quest variables."; + span3[2] = "Added step duplication to the debug Quest Editor."; + span3[3] = "Added an option to abandon the active quest from the main Questionable window."; + span3[4] = "Added automatic continuation after manually completed duties."; + reference2 = new ChangeEntry(EChangeCategory.Added, "Additions", list3); ref ChangeEntry reference3 = ref span2[1]; - num3 = 1; + num3 = 5; List list4 = new List(num3); CollectionsMarshal.SetCount(list4, num3); - CollectionsMarshal.AsSpan(list4)[0] = "Updated Moonfire Faire (2026) event paths to go around the pool."; - reference3 = new ChangeEntry(EChangeCategory.Changed, "Changed", list4); + Span span4 = CollectionsMarshal.AsSpan(list4); + span4[0] = "LGB-derived data (NPC positions and zone boundaries) now ships with the plugin."; + span4[1] = "Automatically use reward items after quest complete as well as on quest accept."; + span4[2] = "Added copy and paste for stop conditions and blacklisted quests, and restored drag reordering for stop conditions."; + span4[3] = "Prioritized starting city-state quests needed to unlock the full city over class and job quests."; + span4[4] = "Improved Rotation Solver Reborn target handling to avoid unnecessary target changes."; + reference3 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list4); ref ChangeEntry reference4 = ref span2[2]; - num3 = 3; + num3 = 16; List list5 = new List(num3); CollectionsMarshal.SetCount(list5, num3); - Span span3 = CollectionsMarshal.AsSpan(list5); - span3[0] = "Fix an issue where Custom Deliveries were not working due to Questionable attempting to use old quest presets."; - span3[1] = "Fix race condition where Questionable is attempting to call Bossmod IPC before Bossmod had finished loading."; - span3[2] = "Fix Bossmod AI not working due to Questionable intializing an empty preset."; - reference4 = new ChangeEntry(EChangeCategory.Fixed, "Fixed", list5); - reference = new ChangelogEntry("7.5.11", releaseDate, list2); - ref ChangelogEntry reference5 = ref span[1]; - DateOnly releaseDate2 = new DateOnly(2026, 8, 12); - num2 = 1; - List list6 = new List(num2); - CollectionsMarshal.SetCount(list6, num2); - ref ChangeEntry reference6 = ref CollectionsMarshal.AsSpan(list6)[0]; - num3 = 1; + Span span5 = CollectionsMarshal.AsSpan(list5); + span5[0] = "Fixed timeout state being triggered if character status is 'Occupied'."; + span5[1] = "Prevented DoGather exceptions when gathering slots are empty or the requested collectable is unavailable."; + span5[2] = "Fixed Custom Deliveries selecting or attempting to turn in an item that is no longer requested."; + span5[3] = "Fixed Custom Delivery travel into the Firmament getting stuck at the gatekeeper."; + span5[4] = "Fixed Custom Delivery navigation not recovering when pathfinding to the delivery NPC fails after gathering."; + span5[5] = "Fixed Smart Gear Upgrades unequipping gear when the current gearset contains stale or mismatched data."; + span5[6] = "Fixed Questionable selecting quests that were hidden in the in-game Journal."; + span5[7] = "Fixed 'Don't pick up class/job quests' preventing Questionable from continuing with an available MSQ."; + span5[8] = "Fixed required quest-variable baselines being lost on data reload or carried into a later quest sequence."; + span5[9] = "Fixed quest and event items appearing as unknown items in the debug Quest Editor."; + span5[10] = "Fixed rejected AutoDuty runs hanging instead of failing promptly."; + span5[11] = "Fixed aethernet travel failing when the local player was temporarily unavailable at startup."; + span5[12] = "Fixed Custom Deliveries unnecessarily repositioning near delivery NPCs or failing to unmount on direct approaches."; + span5[13] = "Fixed ignored quests being selected from the priority queue."; + span5[14] = "Fixed questing not resuming correctly after duties, including premature sequence transitions and Field Operations being treated as duties."; + span5[15] = "Fixed Fishing navigation around unlandable fishing markers and the initial cast after AutoHook setup."; + reference4 = new ChangeEntry(EChangeCategory.Fixed, "Resolved", list5); + ref ChangeEntry reference5 = ref span2[3]; + num3 = 2; + List list6 = new List(num3); + CollectionsMarshal.SetCount(list6, num3); + Span span6 = CollectionsMarshal.AsSpan(list6); + span6[0] = "Removed the background LGB scan and its out-of-process worker - there is no first-load disk or memory spike after a game patch anymore."; + span6[1] = "Removed the \"Clear LGB Caches\" button, as there are no LGB caches left to clear."; + reference5 = new ChangeEntry(EChangeCategory.Removed, "Retired", list6); + ref ChangeEntry reference6 = ref span2[4]; + num3 = 3; List list7 = new List(num3); CollectionsMarshal.SetCount(list7, num3); - CollectionsMarshal.AsSpan(list7)[0] = "Fixed an issue which causes the plugin to fail to load and Dalamud to crash in rare occasions."; - reference6 = new ChangeEntry(EChangeCategory.Fixed, "Fixed", list7); - reference5 = new ChangelogEntry("7.5.9", releaseDate2, list6); - ref ChangelogEntry reference7 = ref span[2]; - DateOnly releaseDate3 = new DateOnly(2026, 8, 12); - num2 = 1; + Span span7 = CollectionsMarshal.AsSpan(list7); + span7[0] = "Updated Moonfire Faire (2026) quest 'Earning Your Water Wings' to pick up the subsequent event quest, 'Whoever Floats Your Raft'."; + span7[1] = "Added the Phantom Village aethernet destination and the final relic quest, 'All Too Fleeting'."; + span7[2] = "Added paths for the Amalj'aa daily quests 'Smothering Instinct' and 'Ravage the Ravagers'."; + reference6 = new ChangeEntry(EChangeCategory.QuestUpdates, "Quest Path Updates", list7); + reference = new ChangelogEntry("7.5.12", releaseDate, list2); + ref ChangelogEntry reference7 = ref span[1]; + DateOnly releaseDate2 = new DateOnly(2026, 8, 13); + num2 = 3; List list8 = new List(num2); CollectionsMarshal.SetCount(list8, num2); - ref ChangeEntry reference8 = ref CollectionsMarshal.AsSpan(list8)[0]; - num3 = 2; + Span span8 = CollectionsMarshal.AsSpan(list8); + ref ChangeEntry reference8 = ref span8[0]; + num3 = 1; List list9 = new List(num3); CollectionsMarshal.SetCount(list9, num3); - Span span4 = CollectionsMarshal.AsSpan(list9); - span4[0] = "Move LGB scanning (NPC positions and zone boundaries) to an out-of-process worker to eliminate memory spikes on first load and after game patches"; - span4[1] = "Add Clear LGB Caches button in Advanced settings with cache size tooltip"; - reference8 = new ChangeEntry(EChangeCategory.Changed, "Changed", list9); - reference7 = new ChangelogEntry("7.5.7", releaseDate3, list8); - ref ChangelogEntry reference9 = ref span[3]; - DateOnly releaseDate4 = new DateOnly(2026, 8, 12); - num2 = 4; - List list10 = new List(num2); - CollectionsMarshal.SetCount(list10, num2); - Span span5 = CollectionsMarshal.AsSpan(list10); - ref ChangeEntry reference10 = ref span5[0]; - num3 = 35; + CollectionsMarshal.AsSpan(list9)[0] = "Added autodetection of your Grand Company based on player's starting city."; + reference8 = new ChangeEntry(EChangeCategory.Added, "Added", list9); + ref ChangeEntry reference9 = ref span8[1]; + num3 = 1; + List list10 = new List(num3); + CollectionsMarshal.SetCount(list10, num3); + CollectionsMarshal.AsSpan(list10)[0] = "Updated Moonfire Faire (2026) event paths to go around the pool."; + reference9 = new ChangeEntry(EChangeCategory.Changed, "Changed", list10); + ref ChangeEntry reference10 = ref span8[2]; + num3 = 3; List list11 = new List(num3); CollectionsMarshal.SetCount(list11, num3); - Span span6 = CollectionsMarshal.AsSpan(list11); - span6[0] = "Add SmartNav automatic cross-zone routing with real-time gil cost awareness"; - span6[1] = "Add WigglyNav as an alternative navigation provider with provider-aware navmesh rebuild"; - span6[2] = "Add Custom Delivery automation with crafting, gathering, fishing, vendor purchases, and job selection"; - span6[3] = "Add Attunement tab to Journal with aetheryte/aethernet/aether current status and batch attune"; - span6[4] = "Add Fishing automation with AutoHook preset management and bait vendor resolution"; - span6[5] = "Add allied society overcap protection with reputation-aware turn-in ordering"; - span6[6] = "Add quest chain graph with queue management in Journal"; - span6[7] = "Add Gathering Editor as a sub-panel to Quest Editor Window"; - span6[8] = "Add smart equip with three modes: Game Default, Smart (stat-aware), and None"; - span6[9] = "Add gear coffer opening from quest rewards with gearset switching"; - span6[10] = "Add automatic repair at quest boundaries and duty gates via self-repair and menders"; - span6[11] = "Add out-of-game notifications with per-event alert toggles and sound selection"; - span6[12] = "Add run-command-after-stop for executing a command when a stop condition fires"; - span6[13] = "Add reward redemption settings with per-type filtering and skip-tradable option"; - span6[14] = "Add UnlockLink reward type for unlock-link quest rewards"; - span6[15] = "Add teleport ticket support for cheaper cross-zone travel"; - span6[16] = "Add chocobo taxi routing for zones with taxi stands"; - span6[17] = "Add warp-based travel (elevators, ferries, zone transitions)"; - span6[18] = "Add Critical Encounter and Fishing quest step types"; - span6[19] = "Add auto-unsync for 4-man dungeons when safely overleveled"; - span6[20] = "Add death recovery system that auto-respawns and re-routes"; - span6[21] = "Add RetryStep system that re-navigates on interaction failures"; - span6[22] = "Add UnequipItem step executor"; - span6[23] = "Add unified stop condition system with QuestCount, Level, and Sequence conditions"; - span6[24] = "Add setup wizard with guided steps, re-openable via command"; - span6[25] = "Add BossModReborn support alongside BossMod"; - span6[26] = "Add WrathCombo integration with explicit combo state management"; - span6[27] = "Add per-feature control for external plugin pausing"; - span6[28] = "Add hot-reloading and in-memory quest testing to Quest Editor"; - span6[29] = "Add Navigation config tab with mount, flying, and gil reserve settings"; - span6[30] = "Add config backup before version migrations"; - span6[31] = "Add option to disable coffer opening (None mode)"; - span6[32] = "Add detection of installed-but-disabled plugins with Enable button"; - span6[33] = "Add plugin alternative listing (vnavmesh-cn, BossModReborn) with conflict warnings"; - span6[34] = "Add accepted quests with no path shown with live game sequence"; - reference10 = new ChangeEntry(EChangeCategory.Added, "Added", list11); - ref ChangeEntry reference11 = ref span5[1]; - num3 = 17; - List list12 = new List(num3); - CollectionsMarshal.SetCount(list12, num3); - Span span7 = CollectionsMarshal.AsSpan(list12); - span7[0] = "Redesign Configuration window with grouped sidebar navigation and themed cards"; - span7[1] = "Redesign Journal with sidebar navigation, accordion layouts, and tracker tables"; - span7[2] = "Redesign main quest window with themed banners, titles, and stop buttons"; - span7[3] = "Redesign FATE and Seasonal Duty selection windows with search and shared cycle UI"; - span7[4] = "Replace quest-path-based custom deliveries with dynamic automation controller"; - span7[5] = "Move path data to external repository for community contributions"; - span7[6] = "Install and enable required plugins directly from the Dependencies tab"; - span7[7] = "Faster startup with parallel data loading"; - span7[8] = "SmartNav automatically determines mount, fly, and teleport decisions (replaces manual shortcuts)"; - span7[9] = "Replace native aethernet teleportation with direct implementation (removes Lifestream dependency)"; - span7[10] = "Remove aether current and city aethernet quest presets (replaced by Attunement tab)"; - span7[11] = "Remove 'Stop after current quest' and quest-count/timer stop buttons from main window"; - span7[12] = "Remove 'View All Quest Paths Online' button from journal"; - span7[13] = "Remove Explanation dropdown from Quests and Duties tabs"; - span7[14] = "Virtualize quest, duty, and gathering journals for better performance"; - span7[15] = "Default custom delivery scrip overcap mode to Ignore"; - span7[16] = "Default quest journal groups to collapsed"; - reference11 = new ChangeEntry(EChangeCategory.Changed, "Changed", list12); - ref ChangeEntry reference12 = ref span5[2]; - num3 = 41; + Span span9 = CollectionsMarshal.AsSpan(list11); + span9[0] = "Fix an issue where Custom Deliveries were not working due to Questionable attempting to use old quest presets."; + span9[1] = "Fix race condition where Questionable is attempting to call Bossmod IPC before Bossmod had finished loading."; + span9[2] = "Fix Bossmod AI not working due to Questionable intializing an empty preset."; + reference10 = new ChangeEntry(EChangeCategory.Fixed, "Fixed", list11); + reference7 = new ChangelogEntry("7.5.11", releaseDate2, list8); + ref ChangelogEntry reference11 = ref span[2]; + DateOnly releaseDate3 = new DateOnly(2026, 8, 12); + num2 = 1; + List list12 = new List(num2); + CollectionsMarshal.SetCount(list12, num2); + ref ChangeEntry reference12 = ref CollectionsMarshal.AsSpan(list12)[0]; + num3 = 1; List list13 = new List(num3); CollectionsMarshal.SetCount(list13, num3); - Span span8 = CollectionsMarshal.AsSpan(list13); - span8[0] = "Fix zone transition hangs and re-routing after zone changes"; - span8[1] = "Fix flying route decisions based on mount ownership and flying unlock state"; - span8[2] = "Fix warp routing filtering by quest requirements and class level"; - span8[3] = "Fix zone boundary access respecting per-boundary quest requirements"; - span8[4] = "Fix custom delivery craft purchases, turn-in timeouts, and rank-up resume"; - span8[5] = "Fix custom delivery keeping queued deliveries when inventory is full"; - span8[6] = "Fix fishing approach rebuild on retry and no-progress timeout"; - span8[7] = "Fix smart equip ranking weapons by weapon damage and job-usable stats"; - span8[8] = "Fix repair retry when gear condition did not improve"; - span8[9] = "Fix gathering retry counter reset and node integrity checks"; - span8[10] = "Fix reward redemption for tradeable items and stale reward task completion"; - span8[11] = "Fix gear coffer gearset restore across interrupts and retries"; - span8[12] = "Fix AutoDuty leveling loop not continuing until target level reached"; - span8[13] = "Fix AutoDuty not persisting excessive LoopTimes values"; - span8[14] = "Fix quest editor schema validation, dirty tracking, and atomic save"; - span8[15] = "Fix death recovery respawn prompt clicking timing"; - span8[16] = "Fix same-zone teleport skipping when it gains no distance"; - span8[17] = "Fix stop condition removal and level-15 floor in StartLevelingMode IPC"; - span8[18] = "Fix stop conditions not firing during leveling mode"; - span8[19] = "Fix command-after-stop firing once per run instead of per frame"; - span8[20] = "Fix soft hyphens and escaped macros in game string/dialogue comparisons"; - span8[21] = "Fix skip aether current quest pickups once flying is unlocked"; - span8[22] = "Fix class quest prioritization and level-lock pickup logic"; - span8[23] = "Fix respecting skip setting for in-progress class quests over level 5"; - span8[24] = "Fix repeatable quest completion flag being checked while quest is accepted"; - span8[25] = "Fix priority quest pickup after shortcut-key strip"; - span8[26] = "Fix duty category classification"; - span8[27] = "Fix loading crash when InterruptHandler hook fails"; - span8[28] = "Fix shop opening on wrong tab during vendor purchases"; - span8[29] = "Fix aethernet destination selection and teleport retry on transient failures"; - span8[30] = "Fix combat targeting overpull and self-defense getting stuck on evading mobs"; - span8[31] = "Fix BossMod preset management during solo duties and between combat phases"; - span8[32] = "Fix WrathCombo rotation mode enum values"; - span8[33] = "Fix startup crash from Pictomancy initialization"; - span8[34] = "Fix emote retry when target NPC not found"; - span8[35] = "Fix interaction wait for missing objects instead of silently skipping"; - span8[36] = "Fix PurchaseItem vendor interaction retry when shop not open"; - span8[37] = "Fix GC shop cancel on handler reset"; - span8[38] = "Fix quest sequence window latching closed after first close"; - span8[39] = "Fix An Ill-conceived Venture locked without retainer entitlement"; - span8[40] = "Fix various UI text clipping and wrapping issues"; - reference12 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list13); - ref ChangeEntry reference13 = ref span5[3]; - num3 = 6; - List list14 = new List(num3); - CollectionsMarshal.SetCount(list14, num3); - Span span9 = CollectionsMarshal.AsSpan(list14); - span9[0] = "Add Moonfire Faire (2026) quests"; - span9[1] = "Add 88 community-contributed quest ports"; - span9[2] = "Add all missing gathering paths"; - span9[3] = "Add AutoHook presets for ARR fishing quests"; - span9[4] = "Add Shadow of the First missing sequence"; - span9[5] = "Add Amaljaa, Sylph, and Ixal allied society NPC data"; - reference13 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list14); - reference9 = new ChangelogEntry("7.5.6", releaseDate4, list10); - ref ChangelogEntry reference14 = ref span[4]; - DateOnly releaseDate5 = new DateOnly(2026, 6, 25); + CollectionsMarshal.AsSpan(list13)[0] = "This version was intended to roll back early changes made to 7.5.9 before it was ready for publishing."; + reference12 = new ChangeEntry(EChangeCategory.Removed, "Rollback", list13); + reference11 = new ChangelogEntry("7.5.10", releaseDate3, list12); + ref ChangelogEntry reference13 = ref span[3]; + DateOnly releaseDate4 = new DateOnly(2026, 8, 12); num2 = 1; - List list15 = new List(num2); - CollectionsMarshal.SetCount(list15, num2); - ref ChangeEntry reference15 = ref CollectionsMarshal.AsSpan(list15)[0]; + List list14 = new List(num2); + CollectionsMarshal.SetCount(list14, num2); + ref ChangeEntry reference14 = ref CollectionsMarshal.AsSpan(list14)[0]; num3 = 1; - List list16 = new List(num3); - CollectionsMarshal.SetCount(list16, num3); - CollectionsMarshal.AsSpan(list16)[0] = "Add Breaking Brick Mountains (2026) event quest"; - reference15 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list16); - reference14 = new ChangelogEntry("7.5.5", releaseDate5, list15); - ref ChangelogEntry reference16 = ref span[5]; - DateOnly releaseDate6 = new DateOnly(2026, 6, 6); + List list15 = new List(num3); + CollectionsMarshal.SetCount(list15, num3); + CollectionsMarshal.AsSpan(list15)[0] = "Fixed an issue which causes the plugin to fail to load and Dalamud to crash in rare occasions."; + reference14 = new ChangeEntry(EChangeCategory.Fixed, "Fixed", list15); + reference13 = new ChangelogEntry("7.5.9", releaseDate4, list14); + ref ChangelogEntry reference15 = ref span[4]; + DateOnly releaseDate5 = new DateOnly(2026, 8, 12); num2 = 1; - List list17 = new List(num2); - CollectionsMarshal.SetCount(list17, num2); - ref ChangeEntry reference17 = ref CollectionsMarshal.AsSpan(list17)[0]; + List list16 = new List(num2); + CollectionsMarshal.SetCount(list16, num2); + ref ChangeEntry reference16 = ref CollectionsMarshal.AsSpan(list16)[0]; num3 = 1; - List list18 = new List(num3); - CollectionsMarshal.SetCount(list18, num3); - CollectionsMarshal.AsSpan(list18)[0] = "Add Make It Rain (2026) seasonal quest"; - reference17 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list18); - reference16 = new ChangelogEntry("7.5.4", releaseDate6, list17); - ref ChangelogEntry reference18 = ref span[6]; - DateOnly releaseDate7 = new DateOnly(2026, 5, 12); + List list17 = new List(num3); + CollectionsMarshal.SetCount(list17, num3); + CollectionsMarshal.AsSpan(list17)[0] = "This version was intended to roll back early changes made to 7.5.9 before it was ready for publishing."; + reference16 = new ChangeEntry(EChangeCategory.Removed, "Rollback", list17); + reference15 = new ChangelogEntry("7.5.10", releaseDate5, list16); + ref ChangelogEntry reference17 = ref span[5]; + DateOnly releaseDate6 = new DateOnly(2026, 8, 12); num2 = 1; - List list19 = new List(num2); - CollectionsMarshal.SetCount(list19, num2); - ref ChangeEntry reference19 = ref CollectionsMarshal.AsSpan(list19)[0]; - num3 = 1; - List list20 = new List(num3); - CollectionsMarshal.SetCount(list20, num3); - CollectionsMarshal.AsSpan(list20)[0] = "Add The Maiden's Rhapsody (2026) event quest"; - reference19 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list20); - reference18 = new ChangelogEntry("7.5.3", releaseDate7, list19); - ref ChangelogEntry reference20 = ref span[7]; - DateOnly releaseDate8 = new DateOnly(2026, 5, 1); - num2 = 1; - List list21 = new List(num2); - CollectionsMarshal.SetCount(list21, num2); - ref ChangeEntry reference21 = ref CollectionsMarshal.AsSpan(list21)[0]; - num3 = 1; + List list18 = new List(num2); + CollectionsMarshal.SetCount(list18, num2); + ref ChangeEntry reference18 = ref CollectionsMarshal.AsSpan(list18)[0]; + num3 = 2; + List list19 = new List(num3); + CollectionsMarshal.SetCount(list19, num3); + Span span10 = CollectionsMarshal.AsSpan(list19); + span10[0] = "Move LGB scanning (NPC positions and zone boundaries) to an out-of-process worker to eliminate memory spikes on first load and after game patches"; + span10[1] = "Add Clear LGB Caches button in Advanced settings with cache size tooltip"; + reference18 = new ChangeEntry(EChangeCategory.Changed, "Changed", list19); + reference17 = new ChangelogEntry("7.5.7", releaseDate6, list18); + ref ChangelogEntry reference19 = ref span[6]; + DateOnly releaseDate7 = new DateOnly(2026, 8, 12); + num2 = 4; + List list20 = new List(num2); + CollectionsMarshal.SetCount(list20, num2); + Span span11 = CollectionsMarshal.AsSpan(list20); + ref ChangeEntry reference20 = ref span11[0]; + num3 = 35; + List list21 = new List(num3); + CollectionsMarshal.SetCount(list21, num3); + Span span12 = CollectionsMarshal.AsSpan(list21); + span12[0] = "Add SmartNav automatic cross-zone routing with real-time gil cost awareness"; + span12[1] = "Add WigglyNav as an alternative navigation provider with provider-aware navmesh rebuild"; + span12[2] = "Add Custom Delivery automation with crafting, gathering, fishing, vendor purchases, and job selection"; + span12[3] = "Add Attunement tab to Journal with aetheryte/aethernet/aether current status and batch attune"; + span12[4] = "Add Fishing automation with AutoHook preset management and bait vendor resolution"; + span12[5] = "Add allied society overcap protection with reputation-aware turn-in ordering"; + span12[6] = "Add quest chain graph with queue management in Journal"; + span12[7] = "Add Gathering Editor as a sub-panel to Quest Editor Window"; + span12[8] = "Add smart equip with three modes: Game Default, Smart (stat-aware), and None"; + span12[9] = "Add gear coffer opening from quest rewards with gearset switching"; + span12[10] = "Add automatic repair at quest boundaries and duty gates via self-repair and menders"; + span12[11] = "Add out-of-game notifications with per-event alert toggles and sound selection"; + span12[12] = "Add run-command-after-stop for executing a command when a stop condition fires"; + span12[13] = "Add reward redemption settings with per-type filtering and skip-tradable option"; + span12[14] = "Add UnlockLink reward type for unlock-link quest rewards"; + span12[15] = "Add teleport ticket support for cheaper cross-zone travel"; + span12[16] = "Add chocobo taxi routing for zones with taxi stands"; + span12[17] = "Add warp-based travel (elevators, ferries, zone transitions)"; + span12[18] = "Add Critical Encounter and Fishing quest step types"; + span12[19] = "Add auto-unsync for 4-man dungeons when safely overleveled"; + span12[20] = "Add death recovery system that auto-respawns and re-routes"; + span12[21] = "Add RetryStep system that re-navigates on interaction failures"; + span12[22] = "Add UnequipItem step executor"; + span12[23] = "Add unified stop condition system with QuestCount, Level, and Sequence conditions"; + span12[24] = "Add setup wizard with guided steps, re-openable via command"; + span12[25] = "Add BossModReborn support alongside BossMod"; + span12[26] = "Add WrathCombo integration with explicit combo state management"; + span12[27] = "Add per-feature control for external plugin pausing"; + span12[28] = "Add hot-reloading and in-memory quest testing to Quest Editor"; + span12[29] = "Add Navigation config tab with mount, flying, and gil reserve settings"; + span12[30] = "Add config backup before version migrations"; + span12[31] = "Add option to disable coffer opening (None mode)"; + span12[32] = "Add detection of installed-but-disabled plugins with Enable button"; + span12[33] = "Add plugin alternative listing (vnavmesh-cn, BossModReborn) with conflict warnings"; + span12[34] = "Add accepted quests with no path shown with live game sequence"; + reference20 = new ChangeEntry(EChangeCategory.Added, "Added", list21); + ref ChangeEntry reference21 = ref span11[1]; + num3 = 17; List list22 = new List(num3); CollectionsMarshal.SetCount(list22, num3); - CollectionsMarshal.AsSpan(list22)[0] = "Revert vnavmesh SimpleMove IPC temporary fix"; - reference21 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list22); - reference20 = new ChangelogEntry("7.5.2", releaseDate8, list21); - ref ChangelogEntry reference22 = ref span[8]; - DateOnly releaseDate9 = new DateOnly(2026, 4, 30); - num2 = 2; - List list23 = new List(num2); - CollectionsMarshal.SetCount(list23, num2); - Span span10 = CollectionsMarshal.AsSpan(list23); - ref ChangeEntry reference23 = ref span10[0]; - num3 = 2; + Span span13 = CollectionsMarshal.AsSpan(list22); + span13[0] = "Redesign Configuration window with grouped sidebar navigation and themed cards"; + span13[1] = "Redesign Journal with sidebar navigation, accordion layouts, and tracker tables"; + span13[2] = "Redesign main quest window with themed banners, titles, and stop buttons"; + span13[3] = "Redesign FATE and Seasonal Duty selection windows with search and shared cycle UI"; + span13[4] = "Replace quest-path-based custom deliveries with dynamic automation controller"; + span13[5] = "Move path data to external repository for community contributions"; + span13[6] = "Install and enable required plugins directly from the Dependencies tab"; + span13[7] = "Faster startup with parallel data loading"; + span13[8] = "SmartNav automatically determines mount, fly, and teleport decisions (replaces manual shortcuts)"; + span13[9] = "Replace native aethernet teleportation with direct implementation (removes Lifestream dependency)"; + span13[10] = "Remove aether current and city aethernet quest presets (replaced by Attunement tab)"; + span13[11] = "Remove 'Stop after current quest' and quest-count/timer stop buttons from main window"; + span13[12] = "Remove 'View All Quest Paths Online' button from journal"; + span13[13] = "Remove Explanation dropdown from Quests and Duties tabs"; + span13[14] = "Virtualize quest, duty, and gathering journals for better performance"; + span13[15] = "Default custom delivery scrip overcap mode to Ignore"; + span13[16] = "Default quest journal groups to collapsed"; + reference21 = new ChangeEntry(EChangeCategory.Changed, "Changed", list22); + ref ChangeEntry reference22 = ref span11[2]; + num3 = 41; + List list23 = new List(num3); + CollectionsMarshal.SetCount(list23, num3); + Span span14 = CollectionsMarshal.AsSpan(list23); + span14[0] = "Fix zone transition hangs and re-routing after zone changes"; + span14[1] = "Fix flying route decisions based on mount ownership and flying unlock state"; + span14[2] = "Fix warp routing filtering by quest requirements and class level"; + span14[3] = "Fix zone boundary access respecting per-boundary quest requirements"; + span14[4] = "Fix custom delivery craft purchases, turn-in timeouts, and rank-up resume"; + span14[5] = "Fix custom delivery keeping queued deliveries when inventory is full"; + span14[6] = "Fix fishing approach rebuild on retry and no-progress timeout"; + span14[7] = "Fix smart equip ranking weapons by weapon damage and job-usable stats"; + span14[8] = "Fix repair retry when gear condition did not improve"; + span14[9] = "Fix gathering retry counter reset and node integrity checks"; + span14[10] = "Fix reward redemption for tradeable items and stale reward task completion"; + span14[11] = "Fix gear coffer gearset restore across interrupts and retries"; + span14[12] = "Fix AutoDuty leveling loop not continuing until target level reached"; + span14[13] = "Fix AutoDuty not persisting excessive LoopTimes values"; + span14[14] = "Fix quest editor schema validation, dirty tracking, and atomic save"; + span14[15] = "Fix death recovery respawn prompt clicking timing"; + span14[16] = "Fix same-zone teleport skipping when it gains no distance"; + span14[17] = "Fix stop condition removal and level-15 floor in StartLevelingMode IPC"; + span14[18] = "Fix stop conditions not firing during leveling mode"; + span14[19] = "Fix command-after-stop firing once per run instead of per frame"; + span14[20] = "Fix soft hyphens and escaped macros in game string/dialogue comparisons"; + span14[21] = "Fix skip aether current quest pickups once flying is unlocked"; + span14[22] = "Fix class quest prioritization and level-lock pickup logic"; + span14[23] = "Fix respecting skip setting for in-progress class quests over level 5"; + span14[24] = "Fix repeatable quest completion flag being checked while quest is accepted"; + span14[25] = "Fix priority quest pickup after shortcut-key strip"; + span14[26] = "Fix duty category classification"; + span14[27] = "Fix loading crash when InterruptHandler hook fails"; + span14[28] = "Fix shop opening on wrong tab during vendor purchases"; + span14[29] = "Fix aethernet destination selection and teleport retry on transient failures"; + span14[30] = "Fix combat targeting overpull and self-defense getting stuck on evading mobs"; + span14[31] = "Fix BossMod preset management during solo duties and between combat phases"; + span14[32] = "Fix WrathCombo rotation mode enum values"; + span14[33] = "Fix startup crash from Pictomancy initialization"; + span14[34] = "Fix emote retry when target NPC not found"; + span14[35] = "Fix interaction wait for missing objects instead of silently skipping"; + span14[36] = "Fix PurchaseItem vendor interaction retry when shop not open"; + span14[37] = "Fix GC shop cancel on handler reset"; + span14[38] = "Fix quest sequence window latching closed after first close"; + span14[39] = "Fix An Ill-conceived Venture locked without retainer entitlement"; + span14[40] = "Fix various UI text clipping and wrapping issues"; + reference22 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list23); + ref ChangeEntry reference23 = ref span11[3]; + num3 = 6; List list24 = new List(num3); CollectionsMarshal.SetCount(list24, num3); - Span span11 = CollectionsMarshal.AsSpan(list24); - span11[0] = "Add Windurst: The Third Walk quests"; - span11[1] = "Add 7.5 MSQ"; + Span span15 = CollectionsMarshal.AsSpan(list24); + span15[0] = "Add Moonfire Faire (2026) quests"; + span15[1] = "Add 88 community-contributed quest ports"; + span15[2] = "Add all missing gathering paths"; + span15[3] = "Add AutoHook presets for ARR fishing quests"; + span15[4] = "Add Shadow of the First missing sequence"; + span15[5] = "Add Amaljaa, Sylph, and Ixal allied society NPC data"; reference23 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list24); - ref ChangeEntry reference24 = ref span10[1]; - num3 = 1; - List list25 = new List(num3); - CollectionsMarshal.SetCount(list25, num3); - CollectionsMarshal.AsSpan(list25)[0] = "Use vnavmesh SimpleMove IPC to fix navmesh hanging the game (temporary fix)"; - reference24 = new ChangeEntry(EChangeCategory.Changed, "Fixes", list25); - reference22 = new ChangelogEntry("7.5.1", releaseDate9, list23); - ref ChangelogEntry reference25 = ref span[9]; - DateOnly releaseDate10 = new DateOnly(2026, 4, 29); + reference19 = new ChangelogEntry("7.5.6", releaseDate7, list20); + ref ChangelogEntry reference24 = ref span[7]; + DateOnly releaseDate8 = new DateOnly(2026, 6, 25); num2 = 1; - List list26 = new List(num2); - CollectionsMarshal.SetCount(list26, num2); - ref ChangeEntry reference26 = ref CollectionsMarshal.AsSpan(list26)[0]; + List list25 = new List(num2); + CollectionsMarshal.SetCount(list25, num2); + ref ChangeEntry reference25 = ref CollectionsMarshal.AsSpan(list25)[0]; num3 = 1; - List list27 = new List(num3); - CollectionsMarshal.SetCount(list27, num3); - CollectionsMarshal.AsSpan(list27)[0] = "Api15"; - reference26 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list27); - reference25 = new ChangelogEntry("7.5.0", releaseDate10, list26); - ref ChangelogEntry reference27 = ref span[10]; - DateOnly releaseDate11 = new DateOnly(2026, 3, 24); + List list26 = new List(num3); + CollectionsMarshal.SetCount(list26, num3); + CollectionsMarshal.AsSpan(list26)[0] = "Add Breaking Brick Mountains (2026) event quest"; + reference25 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list26); + reference24 = new ChangelogEntry("7.5.5", releaseDate8, list25); + ref ChangelogEntry reference26 = ref span[8]; + DateOnly releaseDate9 = new DateOnly(2026, 6, 6); num2 = 1; - List list28 = new List(num2); - CollectionsMarshal.SetCount(list28, num2); - ref ChangeEntry reference28 = ref CollectionsMarshal.AsSpan(list28)[0]; - num3 = 2; - List list29 = new List(num3); - CollectionsMarshal.SetCount(list29, num3); - Span span12 = CollectionsMarshal.AsSpan(list29); - span12[0] = "Add duty handling to external and qst"; - span12[1] = "Fix duty farming NPC interact getting stuck after zone transition into duty"; - reference28 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list29); - reference27 = new ChangelogEntry("7.4.19", releaseDate11, list28); - ref ChangelogEntry reference29 = ref span[11]; - DateOnly releaseDate12 = new DateOnly(2026, 3, 24); - num2 = 3; - List list30 = new List(num2); - CollectionsMarshal.SetCount(list30, num2); - Span span13 = CollectionsMarshal.AsSpan(list30); - ref ChangeEntry reference30 = ref span13[0]; + List list27 = new List(num2); + CollectionsMarshal.SetCount(list27, num2); + ref ChangeEntry reference27 = ref CollectionsMarshal.AsSpan(list27)[0]; num3 = 1; - List list31 = new List(num3); - CollectionsMarshal.SetCount(list31, num3); - CollectionsMarshal.AsSpan(list31)[0] = "Add seasonal duty farming system (/qst duty)"; - reference30 = new ChangeEntry(EChangeCategory.Added, "New Features", list31); - ref ChangeEntry reference31 = ref span13[1]; + List list28 = new List(num3); + CollectionsMarshal.SetCount(list28, num3); + CollectionsMarshal.AsSpan(list28)[0] = "Add Make It Rain (2026) seasonal quest"; + reference27 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list28); + reference26 = new ChangelogEntry("7.5.4", releaseDate9, list27); + ref ChangelogEntry reference28 = ref span[9]; + DateOnly releaseDate10 = new DateOnly(2026, 5, 12); + num2 = 1; + List list29 = new List(num2); + CollectionsMarshal.SetCount(list29, num2); + ref ChangeEntry reference29 = ref CollectionsMarshal.AsSpan(list29)[0]; + num3 = 1; + List list30 = new List(num3); + CollectionsMarshal.SetCount(list30, num3); + CollectionsMarshal.AsSpan(list30)[0] = "Add The Maiden's Rhapsody (2026) event quest"; + reference29 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list30); + reference28 = new ChangelogEntry("7.5.3", releaseDate10, list29); + ref ChangelogEntry reference30 = ref span[10]; + DateOnly releaseDate11 = new DateOnly(2026, 5, 1); + num2 = 1; + List list31 = new List(num2); + CollectionsMarshal.SetCount(list31, num2); + ref ChangeEntry reference31 = ref CollectionsMarshal.AsSpan(list31)[0]; num3 = 1; List list32 = new List(num3); CollectionsMarshal.SetCount(list32, num3); - CollectionsMarshal.AsSpan(list32)[0] = "Add Hatching-tide 2026 quest"; - reference31 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list32); - ref ChangeEntry reference32 = ref span13[2]; - num3 = 1; - List list33 = new List(num3); - CollectionsMarshal.SetCount(list33, num3); - CollectionsMarshal.AsSpan(list33)[0] = "Quest path fixes"; - reference32 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list33); - reference29 = new ChangelogEntry("7.4.18", releaseDate12, list30); - ref ChangelogEntry reference33 = ref span[12]; - DateOnly releaseDate13 = new DateOnly(2026, 3, 1); - num2 = 1; - List list34 = new List(num2); - CollectionsMarshal.SetCount(list34, num2); - ref ChangeEntry reference34 = ref CollectionsMarshal.AsSpan(list34)[0]; + CollectionsMarshal.AsSpan(list32)[0] = "Revert vnavmesh SimpleMove IPC temporary fix"; + reference31 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list32); + reference30 = new ChangelogEntry("7.5.2", releaseDate11, list31); + ref ChangelogEntry reference32 = ref span[11]; + DateOnly releaseDate12 = new DateOnly(2026, 4, 30); + num2 = 2; + List list33 = new List(num2); + CollectionsMarshal.SetCount(list33, num2); + Span span16 = CollectionsMarshal.AsSpan(list33); + ref ChangeEntry reference33 = ref span16[0]; + num3 = 2; + List list34 = new List(num3); + CollectionsMarshal.SetCount(list34, num3); + Span span17 = CollectionsMarshal.AsSpan(list34); + span17[0] = "Add Windurst: The Third Walk quests"; + span17[1] = "Add 7.5 MSQ"; + reference33 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list34); + ref ChangeEntry reference34 = ref span16[1]; num3 = 1; List list35 = new List(num3); CollectionsMarshal.SetCount(list35, num3); - CollectionsMarshal.AsSpan(list35)[0] = "Quest path fixes"; - reference34 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list35); - reference33 = new ChangelogEntry("7.4.17", releaseDate13, list34); - ref ChangelogEntry reference35 = ref span[13]; - DateOnly releaseDate14 = new DateOnly(2026, 3, 1); + CollectionsMarshal.AsSpan(list35)[0] = "Use vnavmesh SimpleMove IPC to fix navmesh hanging the game (temporary fix)"; + reference34 = new ChangeEntry(EChangeCategory.Changed, "Fixes", list35); + reference32 = new ChangelogEntry("7.5.1", releaseDate12, list33); + ref ChangelogEntry reference35 = ref span[12]; + DateOnly releaseDate13 = new DateOnly(2026, 4, 29); num2 = 1; List list36 = new List(num2); CollectionsMarshal.SetCount(list36, num2); @@ -340,11 +369,11 @@ internal static class ChangelogData num3 = 1; List list37 = new List(num3); CollectionsMarshal.SetCount(list37, num3); - CollectionsMarshal.AsSpan(list37)[0] = "Enable TextAdvance, PandorasBox, and Automaton IPC integration during FATE farming"; + CollectionsMarshal.AsSpan(list37)[0] = "Api15"; reference36 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list37); - reference35 = new ChangelogEntry("7.4.16", releaseDate14, list36); - ref ChangelogEntry reference37 = ref span[14]; - DateOnly releaseDate15 = new DateOnly(2026, 2, 28); + reference35 = new ChangelogEntry("7.5.0", releaseDate13, list36); + ref ChangelogEntry reference37 = ref span[13]; + DateOnly releaseDate14 = new DateOnly(2026, 3, 24); num2 = 1; List list38 = new List(num2); CollectionsMarshal.SetCount(list38, num2); @@ -352,218 +381,212 @@ internal static class ChangelogData num3 = 2; List list39 = new List(num3); CollectionsMarshal.SetCount(list39, num3); - Span span14 = CollectionsMarshal.AsSpan(list39); - span14[0] = "Fix FATE transform interact task never completing after NPC dialogue"; - span14[1] = "Fix FATE farming loop not detecting completion when NPCs persist after FATE ends"; + Span span18 = CollectionsMarshal.AsSpan(list39); + span18[0] = "Add duty handling to external and qst"; + span18[1] = "Fix duty farming NPC interact getting stuck after zone transition into duty"; reference38 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list39); - reference37 = new ChangelogEntry("7.4.15", releaseDate15, list38); - ref ChangelogEntry reference39 = ref span[15]; - DateOnly releaseDate16 = new DateOnly(2026, 2, 27); - num2 = 2; + reference37 = new ChangelogEntry("7.4.19", releaseDate14, list38); + ref ChangelogEntry reference39 = ref span[14]; + DateOnly releaseDate15 = new DateOnly(2026, 3, 24); + num2 = 3; List list40 = new List(num2); CollectionsMarshal.SetCount(list40, num2); - Span span15 = CollectionsMarshal.AsSpan(list40); - ref ChangeEntry reference40 = ref span15[0]; - num3 = 2; + Span span19 = CollectionsMarshal.AsSpan(list40); + ref ChangeEntry reference40 = ref span19[0]; + num3 = 1; List list41 = new List(num3); CollectionsMarshal.SetCount(list41, num3); - Span span16 = CollectionsMarshal.AsSpan(list41); - span16[0] = "Added prerequisite quest requirement to FATE farming definitions"; - span16[1] = "Show active FATE in main quest UI and disable quest start during FATE farming"; - reference40 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list41); - ref ChangeEntry reference41 = ref span15[1]; + CollectionsMarshal.AsSpan(list41)[0] = "Add seasonal duty farming system (/qst duty)"; + reference40 = new ChangeEntry(EChangeCategory.Added, "New Features", list41); + ref ChangeEntry reference41 = ref span19[1]; num3 = 1; List list42 = new List(num3); CollectionsMarshal.SetCount(list42, num3); - CollectionsMarshal.AsSpan(list42)[0] = "Fixed FATE farming buff expiry and added Curtain Call cleanup on stop"; - reference41 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list42); - reference39 = new ChangelogEntry("7.4.14", releaseDate16, list40); - ref ChangelogEntry reference42 = ref span[16]; - DateOnly releaseDate17 = new DateOnly(2026, 2, 27); - num2 = 1; - List list43 = new List(num2); - CollectionsMarshal.SetCount(list43, num2); - ref ChangeEntry reference43 = ref CollectionsMarshal.AsSpan(list43)[0]; + CollectionsMarshal.AsSpan(list42)[0] = "Add Hatching-tide 2026 quest"; + reference41 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list42); + ref ChangeEntry reference42 = ref span19[2]; num3 = 1; - List list44 = new List(num3); - CollectionsMarshal.SetCount(list44, num3); - CollectionsMarshal.AsSpan(list44)[0] = "Fixed an issue where FATE actions were not performed during the Little Ladies Day (2026) FATE event"; - reference43 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list44); - reference42 = new ChangelogEntry("7.4.13", releaseDate17, list43); - ref ChangelogEntry reference44 = ref span[17]; - DateOnly releaseDate18 = new DateOnly(2026, 2, 26); - num2 = 3; - List list45 = new List(num2); - CollectionsMarshal.SetCount(list45, num2); - Span span17 = CollectionsMarshal.AsSpan(list45); - ref ChangeEntry reference45 = ref span17[0]; - num3 = 3; - List list46 = new List(num3); - CollectionsMarshal.SetCount(list46, num3); - Span span18 = CollectionsMarshal.AsSpan(list46); - span18[0] = "Add Saved Presets tab to quest priority window"; - span18[1] = "Add passive FATE handling and per npc ability handling"; - span18[2] = "Add FATE farming support"; - reference45 = new ChangeEntry(EChangeCategory.Added, "New Features", list46); - ref ChangeEntry reference46 = ref span17[1]; + List list43 = new List(num3); + CollectionsMarshal.SetCount(list43, num3); + CollectionsMarshal.AsSpan(list43)[0] = "Quest path fixes"; + reference42 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list43); + reference39 = new ChangelogEntry("7.4.18", releaseDate15, list40); + ref ChangelogEntry reference43 = ref span[15]; + DateOnly releaseDate16 = new DateOnly(2026, 3, 1); + num2 = 1; + List list44 = new List(num2); + CollectionsMarshal.SetCount(list44, num2); + ref ChangeEntry reference44 = ref CollectionsMarshal.AsSpan(list44)[0]; + num3 = 1; + List list45 = new List(num3); + CollectionsMarshal.SetCount(list45, num3); + CollectionsMarshal.AsSpan(list45)[0] = "Quest path fixes"; + reference44 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list45); + reference43 = new ChangelogEntry("7.4.17", releaseDate16, list44); + ref ChangelogEntry reference45 = ref span[16]; + DateOnly releaseDate17 = new DateOnly(2026, 3, 1); + num2 = 1; + List list46 = new List(num2); + CollectionsMarshal.SetCount(list46, num2); + ref ChangeEntry reference46 = ref CollectionsMarshal.AsSpan(list46)[0]; num3 = 1; List list47 = new List(num3); CollectionsMarshal.SetCount(list47, num3); - CollectionsMarshal.AsSpan(list47)[0] = "Add Little Ladies' Day 2026 quests"; - reference46 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list47); - ref ChangeEntry reference47 = ref span17[2]; - num3 = 1; - List list48 = new List(num3); - CollectionsMarshal.SetCount(list48, num3); - CollectionsMarshal.AsSpan(list48)[0] = "Quest path fixes"; - reference47 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list48); - reference44 = new ChangelogEntry("7.4.12", releaseDate18, list45); - ref ChangelogEntry reference48 = ref span[18]; - DateOnly releaseDate19 = new DateOnly(2026, 2, 2); + CollectionsMarshal.AsSpan(list47)[0] = "Enable TextAdvance, PandorasBox, and Automaton IPC integration during FATE farming"; + reference46 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list47); + reference45 = new ChangelogEntry("7.4.16", releaseDate17, list46); + ref ChangelogEntry reference47 = ref span[17]; + DateOnly releaseDate18 = new DateOnly(2026, 2, 28); + num2 = 1; + List list48 = new List(num2); + CollectionsMarshal.SetCount(list48, num2); + ref ChangeEntry reference48 = ref CollectionsMarshal.AsSpan(list48)[0]; + num3 = 2; + List list49 = new List(num3); + CollectionsMarshal.SetCount(list49, num3); + Span span20 = CollectionsMarshal.AsSpan(list49); + span20[0] = "Fix FATE transform interact task never completing after NPC dialogue"; + span20[1] = "Fix FATE farming loop not detecting completion when NPCs persist after FATE ends"; + reference48 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list49); + reference47 = new ChangelogEntry("7.4.15", releaseDate18, list48); + ref ChangelogEntry reference49 = ref span[18]; + DateOnly releaseDate19 = new DateOnly(2026, 2, 27); num2 = 2; - List list49 = new List(num2); - CollectionsMarshal.SetCount(list49, num2); - Span span19 = CollectionsMarshal.AsSpan(list49); - ref ChangeEntry reference49 = ref span19[0]; - num3 = 1; - List list50 = new List(num3); - CollectionsMarshal.SetCount(list50, num3); - CollectionsMarshal.AsSpan(list50)[0] = "Add Valentione's Day 2026 quest (The Icing on the Cake)"; - reference49 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list50); - ref ChangeEntry reference50 = ref span19[1]; + List list50 = new List(num2); + CollectionsMarshal.SetCount(list50, num2); + Span span21 = CollectionsMarshal.AsSpan(list50); + ref ChangeEntry reference50 = ref span21[0]; num3 = 2; List list51 = new List(num3); CollectionsMarshal.SetCount(list51, num3); - Span span20 = CollectionsMarshal.AsSpan(list51); - span20[0] = "Updated Duty journal to include missing duty types"; - span20[1] = "Added various missing sequences to quests"; + Span span22 = CollectionsMarshal.AsSpan(list51); + span22[0] = "Added prerequisite quest requirement to FATE farming definitions"; + span22[1] = "Show active FATE in main quest UI and disable quest start during FATE farming"; reference50 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list51); - reference48 = new ChangelogEntry("7.4.11", releaseDate19, list49); - ref ChangelogEntry reference51 = ref span[19]; - DateOnly releaseDate20 = new DateOnly(2026, 1, 18); - num2 = 2; - List list52 = new List(num2); - CollectionsMarshal.SetCount(list52, num2); - Span span21 = CollectionsMarshal.AsSpan(list52); - ref ChangeEntry reference52 = ref span21[0]; - num3 = 5; - List list53 = new List(num3); - CollectionsMarshal.SetCount(list53, num3); - Span span22 = CollectionsMarshal.AsSpan(list53); - span22[0] = "Added quest blacklisting"; - span22[1] = "Added MSQ Priority config"; - span22[2] = "Added Quest priority persistence config"; - span22[3] = "Added Duties tab to Journal"; - span22[4] = "Added GC shop handling and chocobo naming for chocobo quests"; - reference52 = new ChangeEntry(EChangeCategory.Added, "Major Features", list53); - ref ChangeEntry reference53 = ref span21[1]; - num3 = 5; + ref ChangeEntry reference51 = ref span21[1]; + num3 = 1; + List list52 = new List(num3); + CollectionsMarshal.SetCount(list52, num3); + CollectionsMarshal.AsSpan(list52)[0] = "Fixed FATE farming buff expiry and added Curtain Call cleanup on stop"; + reference51 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list52); + reference49 = new ChangelogEntry("7.4.14", releaseDate19, list50); + ref ChangelogEntry reference52 = ref span[19]; + DateOnly releaseDate20 = new DateOnly(2026, 2, 27); + num2 = 1; + List list53 = new List(num2); + CollectionsMarshal.SetCount(list53, num2); + ref ChangeEntry reference53 = ref CollectionsMarshal.AsSpan(list53)[0]; + num3 = 1; List list54 = new List(num3); CollectionsMarshal.SetCount(list54, num3); - Span span23 = CollectionsMarshal.AsSpan(list54); - span23[0] = "Removed PandorasBox dependency and added QTE handling"; - span23[1] = "Removed CBT dependency and added Snipe handling"; - span23[2] = "Added drag reordering to Stop condition quests"; - span23[3] = "Ignore item level requirements if using Unsync config"; - span23[4] = "Setting a Stop quest to Off no longer removes it from the list"; - reference53 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list54); - reference51 = new ChangelogEntry("7.4.10", releaseDate20, list52); + CollectionsMarshal.AsSpan(list54)[0] = "Fixed an issue where FATE actions were not performed during the Little Ladies Day (2026) FATE event"; + reference53 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list54); + reference52 = new ChangelogEntry("7.4.13", releaseDate20, list53); ref ChangelogEntry reference54 = ref span[20]; - DateOnly releaseDate21 = new DateOnly(2025, 12, 31); + DateOnly releaseDate21 = new DateOnly(2026, 2, 26); num2 = 3; List list55 = new List(num2); CollectionsMarshal.SetCount(list55, num2); - Span span24 = CollectionsMarshal.AsSpan(list55); - ref ChangeEntry reference55 = ref span24[0]; - num3 = 1; + Span span23 = CollectionsMarshal.AsSpan(list55); + ref ChangeEntry reference55 = ref span23[0]; + num3 = 3; List list56 = new List(num3); CollectionsMarshal.SetCount(list56, num3); - CollectionsMarshal.AsSpan(list56)[0] = "Add Heavensturn (2026) quests"; - reference55 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list56); - ref ChangeEntry reference56 = ref span24[1]; + Span span24 = CollectionsMarshal.AsSpan(list56); + span24[0] = "Add Saved Presets tab to quest priority window"; + span24[1] = "Add passive FATE handling and per npc ability handling"; + span24[2] = "Add FATE farming support"; + reference55 = new ChangeEntry(EChangeCategory.Added, "New Features", list56); + ref ChangeEntry reference56 = ref span23[1]; num3 = 1; List list57 = new List(num3); CollectionsMarshal.SetCount(list57, num3); - CollectionsMarshal.AsSpan(list57)[0] = "Added missing quest sequences"; - reference56 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list57); - ref ChangeEntry reference57 = ref span24[2]; + CollectionsMarshal.AsSpan(list57)[0] = "Add Little Ladies' Day 2026 quests"; + reference56 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list57); + ref ChangeEntry reference57 = ref span23[2]; num3 = 1; List list58 = new List(num3); CollectionsMarshal.SetCount(list58, num3); - CollectionsMarshal.AsSpan(list58)[0] = "Fixed leveling mode not restarting properly"; + CollectionsMarshal.AsSpan(list58)[0] = "Quest path fixes"; reference57 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list58); - reference54 = new ChangelogEntry("7.4.9", releaseDate21, list55); + reference54 = new ChangelogEntry("7.4.12", releaseDate21, list55); ref ChangelogEntry reference58 = ref span[21]; - DateOnly releaseDate22 = new DateOnly(2025, 12, 29); - num2 = 1; + DateOnly releaseDate22 = new DateOnly(2026, 2, 2); + num2 = 2; List list59 = new List(num2); CollectionsMarshal.SetCount(list59, num2); - ref ChangeEntry reference59 = ref CollectionsMarshal.AsSpan(list59)[0]; - num3 = 2; + Span span25 = CollectionsMarshal.AsSpan(list59); + ref ChangeEntry reference59 = ref span25[0]; + num3 = 1; List list60 = new List(num3); CollectionsMarshal.SetCount(list60, num3); - Span span25 = CollectionsMarshal.AsSpan(list60); - span25[0] = "Fixed infinite teleport loop when multiple quests compete for priority"; - span25[1] = "Fixed leveling mode enabling for quest duties"; - reference59 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list60); - reference58 = new ChangelogEntry("7.4.8", releaseDate22, list59); - ref ChangelogEntry reference60 = ref span[22]; - DateOnly releaseDate23 = new DateOnly(2025, 12, 28); - num2 = 4; - List list61 = new List(num2); - CollectionsMarshal.SetCount(list61, num2); - Span span26 = CollectionsMarshal.AsSpan(list61); - ref ChangeEntry reference61 = ref span26[0]; - num3 = 1; - List list62 = new List(num3); - CollectionsMarshal.SetCount(list62, num3); - CollectionsMarshal.AsSpan(list62)[0] = "Added leveling mode when underleveled for MSQ"; - reference61 = new ChangeEntry(EChangeCategory.Added, "Major Features", list62); - ref ChangeEntry reference62 = ref span26[1]; - num3 = 3; + CollectionsMarshal.AsSpan(list60)[0] = "Add Valentione's Day 2026 quest (The Icing on the Cake)"; + reference59 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list60); + ref ChangeEntry reference60 = ref span25[1]; + num3 = 2; + List list61 = new List(num3); + CollectionsMarshal.SetCount(list61, num3); + Span span26 = CollectionsMarshal.AsSpan(list61); + span26[0] = "Updated Duty journal to include missing duty types"; + span26[1] = "Added various missing sequences to quests"; + reference60 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list61); + reference58 = new ChangelogEntry("7.4.11", releaseDate22, list59); + ref ChangelogEntry reference61 = ref span[22]; + DateOnly releaseDate23 = new DateOnly(2026, 1, 18); + num2 = 2; + List list62 = new List(num2); + CollectionsMarshal.SetCount(list62, num2); + Span span27 = CollectionsMarshal.AsSpan(list62); + ref ChangeEntry reference62 = ref span27[0]; + num3 = 5; List list63 = new List(num3); CollectionsMarshal.SetCount(list63, num3); - Span span27 = CollectionsMarshal.AsSpan(list63); - span27[0] = "Added missing dungeons to Duties"; - span27[1] = "Added Normal Raids and Alliance Raids to duties tab"; - span27[2] = "Added Pause/Stop modes to stop conditions"; - reference62 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list63); - ref ChangeEntry reference63 = ref span26[2]; - num3 = 1; + Span span28 = CollectionsMarshal.AsSpan(list63); + span28[0] = "Added quest blacklisting"; + span28[1] = "Added MSQ Priority config"; + span28[2] = "Added Quest priority persistence config"; + span28[3] = "Added Duties tab to Journal"; + span28[4] = "Added GC shop handling and chocobo naming for chocobo quests"; + reference62 = new ChangeEntry(EChangeCategory.Added, "Major Features", list63); + ref ChangeEntry reference63 = ref span27[1]; + num3 = 5; List list64 = new List(num3); CollectionsMarshal.SetCount(list64, num3); - CollectionsMarshal.AsSpan(list64)[0] = "Added leveling mode IPC: IsLevelingModeEnabled, SetLevelingModeEnabled, GetMsqLevelLockInfo, StartLevelingMode, StopLevelingMode"; - reference63 = new ChangeEntry(EChangeCategory.Added, "IPC Changes", list64); - ref ChangeEntry reference64 = ref span26[3]; + Span span29 = CollectionsMarshal.AsSpan(list64); + span29[0] = "Removed PandorasBox dependency and added QTE handling"; + span29[1] = "Removed CBT dependency and added Snipe handling"; + span29[2] = "Added drag reordering to Stop condition quests"; + span29[3] = "Ignore item level requirements if using Unsync config"; + span29[4] = "Setting a Stop quest to Off no longer removes it from the list"; + reference63 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list64); + reference61 = new ChangelogEntry("7.4.10", releaseDate23, list62); + ref ChangelogEntry reference64 = ref span[23]; + DateOnly releaseDate24 = new DateOnly(2025, 12, 31); + num2 = 3; + List list65 = new List(num2); + CollectionsMarshal.SetCount(list65, num2); + Span span30 = CollectionsMarshal.AsSpan(list65); + ref ChangeEntry reference65 = ref span30[0]; num3 = 1; - List list65 = new List(num3); - CollectionsMarshal.SetCount(list65, num3); - CollectionsMarshal.AsSpan(list65)[0] = "Fixed UI appearing in duties when debuff or interrupts occur"; - reference64 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list65); - reference60 = new ChangelogEntry("7.4.7", releaseDate23, list61); - ref ChangelogEntry reference65 = ref span[23]; - DateOnly releaseDate24 = new DateOnly(2025, 12, 23); - num2 = 2; - List list66 = new List(num2); - CollectionsMarshal.SetCount(list66, num2); - Span span28 = CollectionsMarshal.AsSpan(list66); - ref ChangeEntry reference66 = ref span28[0]; + List list66 = new List(num3); + CollectionsMarshal.SetCount(list66, num3); + CollectionsMarshal.AsSpan(list66)[0] = "Add Heavensturn (2026) quests"; + reference65 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list66); + ref ChangeEntry reference66 = ref span30[1]; num3 = 1; List list67 = new List(num3); CollectionsMarshal.SetCount(list67, num3); - CollectionsMarshal.AsSpan(list67)[0] = "Added Cinema Mode to not skip cutscenes"; - reference66 = new ChangeEntry(EChangeCategory.Added, "Major Features", list67); - ref ChangeEntry reference67 = ref span28[1]; - num3 = 2; + CollectionsMarshal.AsSpan(list67)[0] = "Added missing quest sequences"; + reference66 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list67); + ref ChangeEntry reference67 = ref span30[2]; + num3 = 1; List list68 = new List(num3); CollectionsMarshal.SetCount(list68, num3); - Span span29 = CollectionsMarshal.AsSpan(list68); - span29[0] = "Added handling for Unsync (Party) to Party Watchdog and configuration to disable if in party"; - span29[1] = "Stop conditions now act as a true stop"; - reference67 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list68); - reference65 = new ChangelogEntry("7.4.6", releaseDate24, list66); + CollectionsMarshal.AsSpan(list68)[0] = "Fixed leveling mode not restarting properly"; + reference67 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list68); + reference64 = new ChangelogEntry("7.4.9", releaseDate24, list65); ref ChangelogEntry reference68 = ref span[24]; - DateOnly releaseDate25 = new DateOnly(2025, 12, 22); + DateOnly releaseDate25 = new DateOnly(2025, 12, 29); num2 = 1; List list69 = new List(num2); CollectionsMarshal.SetCount(list69, num2); @@ -571,63 +594,68 @@ internal static class ChangelogData num3 = 2; List list70 = new List(num3); CollectionsMarshal.SetCount(list70, num3); - Span span30 = CollectionsMarshal.AsSpan(list70); - span30[0] = "Dive adjustments"; - span30[1] = "Logging message adjustments"; - reference69 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list70); - reference68 = new ChangelogEntry("7.4.5", releaseDate25, list69); + Span span31 = CollectionsMarshal.AsSpan(list70); + span31[0] = "Fixed infinite teleport loop when multiple quests compete for priority"; + span31[1] = "Fixed leveling mode enabling for quest duties"; + reference69 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list70); + reference68 = new ChangelogEntry("7.4.8", releaseDate25, list69); ref ChangelogEntry reference70 = ref span[25]; - DateOnly releaseDate26 = new DateOnly(2025, 12, 21); - num2 = 2; + DateOnly releaseDate26 = new DateOnly(2025, 12, 28); + num2 = 4; List list71 = new List(num2); CollectionsMarshal.SetCount(list71, num2); - Span span31 = CollectionsMarshal.AsSpan(list71); - ref ChangeEntry reference71 = ref span31[0]; + Span span32 = CollectionsMarshal.AsSpan(list71); + ref ChangeEntry reference71 = ref span32[0]; num3 = 1; List list72 = new List(num3); CollectionsMarshal.SetCount(list72, num3); - CollectionsMarshal.AsSpan(list72)[0] = "Changelog only shows once per update now"; - reference71 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list72); - ref ChangeEntry reference72 = ref span31[1]; - num3 = 1; + CollectionsMarshal.AsSpan(list72)[0] = "Added leveling mode when underleveled for MSQ"; + reference71 = new ChangeEntry(EChangeCategory.Added, "Major Features", list72); + ref ChangeEntry reference72 = ref span32[1]; + num3 = 3; List list73 = new List(num3); CollectionsMarshal.SetCount(list73, num3); - CollectionsMarshal.AsSpan(list73)[0] = "Fixed gathering paths loading"; - reference72 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list73); - reference70 = new ChangelogEntry("7.4.4", releaseDate26, list71); - ref ChangelogEntry reference73 = ref span[26]; - DateOnly releaseDate27 = new DateOnly(2025, 12, 21); - num2 = 1; - List list74 = new List(num2); - CollectionsMarshal.SetCount(list74, num2); - ref ChangeEntry reference74 = ref CollectionsMarshal.AsSpan(list74)[0]; + Span span33 = CollectionsMarshal.AsSpan(list73); + span33[0] = "Added missing dungeons to Duties"; + span33[1] = "Added Normal Raids and Alliance Raids to duties tab"; + span33[2] = "Added Pause/Stop modes to stop conditions"; + reference72 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list73); + ref ChangeEntry reference73 = ref span32[2]; + num3 = 1; + List list74 = new List(num3); + CollectionsMarshal.SetCount(list74, num3); + CollectionsMarshal.AsSpan(list74)[0] = "Added leveling mode IPC: IsLevelingModeEnabled, SetLevelingModeEnabled, GetMsqLevelLockInfo, StartLevelingMode, StopLevelingMode"; + reference73 = new ChangeEntry(EChangeCategory.Added, "IPC Changes", list74); + ref ChangeEntry reference74 = ref span32[3]; num3 = 1; List list75 = new List(num3); CollectionsMarshal.SetCount(list75, num3); - CollectionsMarshal.AsSpan(list75)[0] = "Fixed changelog version checks"; + CollectionsMarshal.AsSpan(list75)[0] = "Fixed UI appearing in duties when debuff or interrupts occur"; reference74 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list75); - reference73 = new ChangelogEntry("7.4.3", releaseDate27, list74); - ref ChangelogEntry reference75 = ref span[27]; - DateOnly releaseDate28 = new DateOnly(2025, 12, 20); + reference70 = new ChangelogEntry("7.4.7", releaseDate26, list71); + ref ChangelogEntry reference75 = ref span[26]; + DateOnly releaseDate27 = new DateOnly(2025, 12, 23); num2 = 2; List list76 = new List(num2); CollectionsMarshal.SetCount(list76, num2); - Span span32 = CollectionsMarshal.AsSpan(list76); - ref ChangeEntry reference76 = ref span32[0]; + Span span34 = CollectionsMarshal.AsSpan(list76); + ref ChangeEntry reference76 = ref span34[0]; num3 = 1; List list77 = new List(num3); CollectionsMarshal.SetCount(list77, num3); - CollectionsMarshal.AsSpan(list77)[0] = "Add 7.4 Starlight Celebration (2025) quest"; - reference76 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list77); - ref ChangeEntry reference77 = ref span32[1]; - num3 = 1; + CollectionsMarshal.AsSpan(list77)[0] = "Added Cinema Mode to not skip cutscenes"; + reference76 = new ChangeEntry(EChangeCategory.Added, "Major Features", list77); + ref ChangeEntry reference77 = ref span34[1]; + num3 = 2; List list78 = new List(num3); CollectionsMarshal.SetCount(list78, num3); - CollectionsMarshal.AsSpan(list78)[0] = "Fixed 7.4 MSQ"; - reference77 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list78); - reference75 = new ChangelogEntry("7.4.2", releaseDate28, list76); - ref ChangelogEntry reference78 = ref span[28]; - DateOnly releaseDate29 = new DateOnly(2025, 12, 19); + Span span35 = CollectionsMarshal.AsSpan(list78); + span35[0] = "Added handling for Unsync (Party) to Party Watchdog and configuration to disable if in party"; + span35[1] = "Stop conditions now act as a true stop"; + reference77 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list78); + reference75 = new ChangelogEntry("7.4.6", releaseDate27, list76); + ref ChangelogEntry reference78 = ref span[27]; + DateOnly releaseDate28 = new DateOnly(2025, 12, 22); num2 = 1; List list79 = new List(num2); CollectionsMarshal.SetCount(list79, num2); @@ -635,652 +663,716 @@ internal static class ChangelogData num3 = 2; List list80 = new List(num3); CollectionsMarshal.SetCount(list80, num3); - Span span33 = CollectionsMarshal.AsSpan(list80); - span33[0] = "Add 7.4 MSQ"; - span33[1] = "Add 7.4 Arcadion Raid quests"; - reference79 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list80); - reference78 = new ChangelogEntry("7.4.1", releaseDate29, list79); - ref ChangelogEntry reference80 = ref span[29]; - DateOnly releaseDate30 = new DateOnly(2025, 12, 17); - num2 = 1; + Span span36 = CollectionsMarshal.AsSpan(list80); + span36[0] = "Dive adjustments"; + span36[1] = "Logging message adjustments"; + reference79 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list80); + reference78 = new ChangelogEntry("7.4.5", releaseDate28, list79); + ref ChangelogEntry reference80 = ref span[28]; + DateOnly releaseDate29 = new DateOnly(2025, 12, 21); + num2 = 2; List list81 = new List(num2); CollectionsMarshal.SetCount(list81, num2); - CollectionsMarshal.AsSpan(list81)[0] = new ChangeEntry(EChangeCategory.Changed, "Api 14 update"); - reference80 = new ChangelogEntry("7.4.0", releaseDate30, list81); - ref ChangelogEntry reference81 = ref span[30]; - DateOnly releaseDate31 = new DateOnly(2025, 12, 6); - num2 = 2; - List list82 = new List(num2); - CollectionsMarshal.SetCount(list82, num2); - Span span34 = CollectionsMarshal.AsSpan(list82); - ref ChangeEntry reference82 = ref span34[0]; - num3 = 4; + Span span37 = CollectionsMarshal.AsSpan(list81); + ref ChangeEntry reference81 = ref span37[0]; + num3 = 1; + List list82 = new List(num3); + CollectionsMarshal.SetCount(list82, num3); + CollectionsMarshal.AsSpan(list82)[0] = "Changelog only shows once per update now"; + reference81 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list82); + ref ChangeEntry reference82 = ref span37[1]; + num3 = 1; List list83 = new List(num3); CollectionsMarshal.SetCount(list83, num3); - Span span35 = CollectionsMarshal.AsSpan(list83); - span35[0] = "Added reloading and rebuilding to movement system"; - span35[1] = "Improved interrupts and refresh states to allow continuation of questing"; - span35[2] = "Added player input detection to stop automation when manually moving character"; - span35[3] = "Added various missing quest sequences"; - reference82 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list83); - ref ChangeEntry reference83 = ref span34[1]; + CollectionsMarshal.AsSpan(list83)[0] = "Fixed gathering paths loading"; + reference82 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list83); + reference80 = new ChangelogEntry("7.4.4", releaseDate29, list81); + ref ChangelogEntry reference83 = ref span[29]; + DateOnly releaseDate30 = new DateOnly(2025, 12, 21); + num2 = 1; + List list84 = new List(num2); + CollectionsMarshal.SetCount(list84, num2); + ref ChangeEntry reference84 = ref CollectionsMarshal.AsSpan(list84)[0]; num3 = 1; - List list84 = new List(num3); - CollectionsMarshal.SetCount(list84, num3); - CollectionsMarshal.AsSpan(list84)[0] = "Fixed reset task state to prevent stuck interactions after interruption"; - reference83 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list84); - reference81 = new ChangelogEntry("7.38.9", releaseDate31, list82); - ref ChangelogEntry reference84 = ref span[31]; - DateOnly releaseDate32 = new DateOnly(2025, 11, 29); + List list85 = new List(num3); + CollectionsMarshal.SetCount(list85, num3); + CollectionsMarshal.AsSpan(list85)[0] = "Fixed changelog version checks"; + reference84 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list85); + reference83 = new ChangelogEntry("7.4.3", releaseDate30, list84); + ref ChangelogEntry reference85 = ref span[30]; + DateOnly releaseDate31 = new DateOnly(2025, 12, 20); num2 = 2; - List list85 = new List(num2); - CollectionsMarshal.SetCount(list85, num2); - Span span36 = CollectionsMarshal.AsSpan(list85); - ref ChangeEntry reference85 = ref span36[0]; - num3 = 3; - List list86 = new List(num3); - CollectionsMarshal.SetCount(list86, num3); - Span span37 = CollectionsMarshal.AsSpan(list86); - span37[0] = "Movement update with automatic retrying if character can't reach target position"; - span37[1] = "Added Hunt mob data"; - span37[2] = "Refactored commands"; - reference85 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list86); - ref ChangeEntry reference86 = ref span36[1]; - num3 = 3; + List list86 = new List(num2); + CollectionsMarshal.SetCount(list86, num2); + Span span38 = CollectionsMarshal.AsSpan(list86); + ref ChangeEntry reference86 = ref span38[0]; + num3 = 1; List list87 = new List(num3); CollectionsMarshal.SetCount(list87, num3); - Span span38 = CollectionsMarshal.AsSpan(list87); - span38[0] = "Fixed quest (Way of the Archer)"; - span38[1] = "Fixed quest (Spirithold Broken)"; - span38[2] = "Fixed quest (It's Probably Not Pirates)"; - reference86 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list87); - reference84 = new ChangelogEntry("7.38.8", releaseDate32, list85); - ref ChangelogEntry reference87 = ref span[32]; - DateOnly releaseDate33 = new DateOnly(2025, 11, 25); - num2 = 2; - List list88 = new List(num2); - CollectionsMarshal.SetCount(list88, num2); - Span span39 = CollectionsMarshal.AsSpan(list88); - ref ChangeEntry reference88 = ref span39[0]; - num3 = 2; - List list89 = new List(num3); - CollectionsMarshal.SetCount(list89, num3); - Span span40 = CollectionsMarshal.AsSpan(list89); - span40[0] = "Added individual sequence stop condition for each quest"; - span40[1] = "Added Trials to Duties tab in config"; - reference88 = new ChangeEntry(EChangeCategory.Added, "Major features", list89); - ref ChangeEntry reference89 = ref span39[1]; + CollectionsMarshal.AsSpan(list87)[0] = "Add 7.4 Starlight Celebration (2025) quest"; + reference86 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list87); + ref ChangeEntry reference87 = ref span38[1]; num3 = 1; + List list88 = new List(num3); + CollectionsMarshal.SetCount(list88, num3); + CollectionsMarshal.AsSpan(list88)[0] = "Fixed 7.4 MSQ"; + reference87 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list88); + reference85 = new ChangelogEntry("7.4.2", releaseDate31, list86); + ref ChangelogEntry reference88 = ref span[31]; + DateOnly releaseDate32 = new DateOnly(2025, 12, 19); + num2 = 1; + List list89 = new List(num2); + CollectionsMarshal.SetCount(list89, num2); + ref ChangeEntry reference89 = ref CollectionsMarshal.AsSpan(list89)[0]; + num3 = 2; List list90 = new List(num3); CollectionsMarshal.SetCount(list90, num3); - CollectionsMarshal.AsSpan(list90)[0] = "Added IPC for stop conditions: GetQuestSequenceStopCondition, SetQuestSequenceStopCondition, RemoveQuestSequenceStopCondition, GetAllQuestSequenceStopConditions"; - reference89 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list90); - reference87 = new ChangelogEntry("7.38.7", releaseDate33, list88); - ref ChangelogEntry reference90 = ref span[33]; - DateOnly releaseDate34 = new DateOnly(2025, 11, 25); - num2 = 3; + Span span39 = CollectionsMarshal.AsSpan(list90); + span39[0] = "Add 7.4 MSQ"; + span39[1] = "Add 7.4 Arcadion Raid quests"; + reference89 = new ChangeEntry(EChangeCategory.QuestUpdates, "New Quest Paths", list90); + reference88 = new ChangelogEntry("7.4.1", releaseDate32, list89); + ref ChangelogEntry reference90 = ref span[32]; + DateOnly releaseDate33 = new DateOnly(2025, 12, 17); + num2 = 1; List list91 = new List(num2); CollectionsMarshal.SetCount(list91, num2); - Span span41 = CollectionsMarshal.AsSpan(list91); - ref ChangeEntry reference91 = ref span41[0]; - num3 = 2; - List list92 = new List(num3); - CollectionsMarshal.SetCount(list92, num3); - Span span42 = CollectionsMarshal.AsSpan(list92); - span42[0] = "Updated Allied Society journal text"; - span42[1] = "Improved Allied Society rank handling"; - reference91 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list92); - ref ChangeEntry reference92 = ref span41[1]; - num3 = 1; + CollectionsMarshal.AsSpan(list91)[0] = new ChangeEntry(EChangeCategory.Changed, "Api 14 update"); + reference90 = new ChangelogEntry("7.4.0", releaseDate33, list91); + ref ChangelogEntry reference91 = ref span[33]; + DateOnly releaseDate34 = new DateOnly(2025, 12, 6); + num2 = 2; + List list92 = new List(num2); + CollectionsMarshal.SetCount(list92, num2); + Span span40 = CollectionsMarshal.AsSpan(list92); + ref ChangeEntry reference92 = ref span40[0]; + num3 = 4; List list93 = new List(num3); CollectionsMarshal.SetCount(list93, num3); - CollectionsMarshal.AsSpan(list93)[0] = "Added IPC for Allied Society: AddAlliedSocietyOptimalQuests, GetAlliedSocietyOptimalQuests"; - reference92 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list93); - ref ChangeEntry reference93 = ref span41[2]; + Span span41 = CollectionsMarshal.AsSpan(list93); + span41[0] = "Added reloading and rebuilding to movement system"; + span41[1] = "Improved interrupts and refresh states to allow continuation of questing"; + span41[2] = "Added player input detection to stop automation when manually moving character"; + span41[3] = "Added various missing quest sequences"; + reference92 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list93); + ref ChangeEntry reference93 = ref span40[1]; num3 = 1; List list94 = new List(num3); CollectionsMarshal.SetCount(list94, num3); - CollectionsMarshal.AsSpan(list94)[0] = "Fixed quest (We Come in Peace)"; - reference93 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list94); - reference90 = new ChangelogEntry("7.38.6", releaseDate34, list91); + CollectionsMarshal.AsSpan(list94)[0] = "Fixed reset task state to prevent stuck interactions after interruption"; + reference93 = new ChangeEntry(EChangeCategory.Fixed, "Fixes", list94); + reference91 = new ChangelogEntry("7.38.9", releaseDate34, list92); ref ChangelogEntry reference94 = ref span[34]; - DateOnly releaseDate35 = new DateOnly(2025, 11, 24); + DateOnly releaseDate35 = new DateOnly(2025, 11, 29); num2 = 2; List list95 = new List(num2); CollectionsMarshal.SetCount(list95, num2); - Span span43 = CollectionsMarshal.AsSpan(list95); - ref ChangeEntry reference95 = ref span43[0]; - num3 = 1; + Span span42 = CollectionsMarshal.AsSpan(list95); + ref ChangeEntry reference95 = ref span42[0]; + num3 = 3; List list96 = new List(num3); CollectionsMarshal.SetCount(list96, num3); - CollectionsMarshal.AsSpan(list96)[0] = "Added Allied Society daily allowance tracker with bulk quest adding buttons"; - reference95 = new ChangeEntry(EChangeCategory.Added, "Major features", list96); - ref ChangeEntry reference96 = ref span43[1]; - num3 = 1; + Span span43 = CollectionsMarshal.AsSpan(list96); + span43[0] = "Movement update with automatic retrying if character can't reach target position"; + span43[1] = "Added Hunt mob data"; + span43[2] = "Refactored commands"; + reference95 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list96); + ref ChangeEntry reference96 = ref span42[1]; + num3 = 3; List list97 = new List(num3); CollectionsMarshal.SetCount(list97, num3); - CollectionsMarshal.AsSpan(list97)[0] = "Added IPC for Allied Society: GetRemainingAllowances, GetTimeUntilReset, GetAvailableQuestIds, GetAllAvailableQuestCounts, IsMaxRank, GetCurrentRank, GetSocietiesWithAvailableQuests"; - reference96 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list97); - reference94 = new ChangelogEntry("7.38.5", releaseDate35, list95); + Span span44 = CollectionsMarshal.AsSpan(list97); + span44[0] = "Fixed quest (Way of the Archer)"; + span44[1] = "Fixed quest (Spirithold Broken)"; + span44[2] = "Fixed quest (It's Probably Not Pirates)"; + reference96 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list97); + reference94 = new ChangelogEntry("7.38.8", releaseDate35, list95); ref ChangelogEntry reference97 = ref span[35]; - DateOnly releaseDate36 = new DateOnly(2025, 11, 23); + DateOnly releaseDate36 = new DateOnly(2025, 11, 25); num2 = 2; List list98 = new List(num2); CollectionsMarshal.SetCount(list98, num2); - Span span44 = CollectionsMarshal.AsSpan(list98); - ref ChangeEntry reference98 = ref span44[0]; - num3 = 1; + Span span45 = CollectionsMarshal.AsSpan(list98); + ref ChangeEntry reference98 = ref span45[0]; + num3 = 2; List list99 = new List(num3); CollectionsMarshal.SetCount(list99, num3); - CollectionsMarshal.AsSpan(list99)[0] = "Explicitly declare support for BMR singleplayer duty (The Rematch)"; - reference98 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list99); - ref ChangeEntry reference99 = ref span44[1]; - num3 = 8; + Span span46 = CollectionsMarshal.AsSpan(list99); + span46[0] = "Added individual sequence stop condition for each quest"; + span46[1] = "Added Trials to Duties tab in config"; + reference98 = new ChangeEntry(EChangeCategory.Added, "Major features", list99); + ref ChangeEntry reference99 = ref span45[1]; + num3 = 1; List list100 = new List(num3); CollectionsMarshal.SetCount(list100, num3); - Span span45 = CollectionsMarshal.AsSpan(list100); - span45[0] = "Fixed quest (Microbrewing) to not get stuck near ramp"; - span45[1] = "Fixed quest (The Illuminated Land) where pathing would kill the player due to fall damage"; - span45[2] = "Fixed quest (It's Probably Not Pirates) improper pathing and removed unneeded step"; - span45[3] = "Fixed quest (The Black Wolf's Ultimatum) not exiting landing area"; - span45[4] = "Fixed quest (Magiteknical Failure) from not interacting with NPC due to being mounted"; - span45[5] = "Fixed quest (We Come in Peace) shortcut navigation"; - span45[6] = "Fixed quest (Poisoned Hearts) where incorrect pathing caused the player to die"; - span45[7] = "Fixed quests (Savage Snares) and (An Apple a Day) not detecting kills"; - reference99 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list100); - reference97 = new ChangelogEntry("7.38.4", releaseDate36, list98); + CollectionsMarshal.AsSpan(list100)[0] = "Added IPC for stop conditions: GetQuestSequenceStopCondition, SetQuestSequenceStopCondition, RemoveQuestSequenceStopCondition, GetAllQuestSequenceStopConditions"; + reference99 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list100); + reference97 = new ChangelogEntry("7.38.7", releaseDate36, list98); ref ChangelogEntry reference100 = ref span[36]; - DateOnly releaseDate37 = new DateOnly(2025, 11, 23); + DateOnly releaseDate37 = new DateOnly(2025, 11, 25); num2 = 3; List list101 = new List(num2); CollectionsMarshal.SetCount(list101, num2); - Span span46 = CollectionsMarshal.AsSpan(list101); - ref ChangeEntry reference101 = ref span46[0]; + Span span47 = CollectionsMarshal.AsSpan(list101); + ref ChangeEntry reference101 = ref span47[0]; num3 = 2; List list102 = new List(num3); CollectionsMarshal.SetCount(list102, num3); - Span span47 = CollectionsMarshal.AsSpan(list102); - span47[0] = "Added RequireHq to crafting InteractionType"; - span47[1] = "Mark GC quests as Locked if rank not achieved"; + Span span48 = CollectionsMarshal.AsSpan(list102); + span48[0] = "Updated Allied Society journal text"; + span48[1] = "Improved Allied Society rank handling"; reference101 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list102); - ref ChangeEntry reference102 = ref span46[1]; - num3 = 2; + ref ChangeEntry reference102 = ref span47[1]; + num3 = 1; List list103 = new List(num3); CollectionsMarshal.SetCount(list103, num3); - Span span48 = CollectionsMarshal.AsSpan(list103); - span48[0] = "Added IPC for stop conditions: GetStopConditionsEnabled, SetStopConditionsEnabled, GetStopQuestList, AddStopQuest, RemoveStopQuest, ClearStopQuests, GetLevelStopCondition, SetLevelStopCondition, GetSequenceStopCondition, SetSequenceStopCondition"; - span48[1] = "Added IPC for priority quests: GetPriorityQuests, RemovePriorityQuest, ReorderPriorityQuest, GetAvailablePresets, GetPresetQuests, AddPresetToPriority, IsPresetAvailable, IsQuestInPriority, GetQuestPriorityIndex, HasAvailablePriorityQuests"; + CollectionsMarshal.AsSpan(list103)[0] = "Added IPC for Allied Society: AddAlliedSocietyOptimalQuests, GetAlliedSocietyOptimalQuests"; reference102 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list103); - ref ChangeEntry reference103 = ref span46[2]; - num3 = 3; + ref ChangeEntry reference103 = ref span47[2]; + num3 = 1; List list104 = new List(num3); CollectionsMarshal.SetCount(list104, num3); - Span span49 = CollectionsMarshal.AsSpan(list104); - span49[0] = "Fixed line breaks not working in dialog strings"; - span49[1] = "Fixed quest (Labor of Love)"; - span49[2] = "Fixed quest (Sea of Sorrow)"; + CollectionsMarshal.AsSpan(list104)[0] = "Fixed quest (We Come in Peace)"; reference103 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list104); - reference100 = new ChangelogEntry("7.38.3", releaseDate37, list101); + reference100 = new ChangelogEntry("7.38.6", releaseDate37, list101); ref ChangelogEntry reference104 = ref span[37]; - DateOnly releaseDate38 = new DateOnly(2025, 11, 18); - num2 = 3; + DateOnly releaseDate38 = new DateOnly(2025, 11, 24); + num2 = 2; List list105 = new List(num2); CollectionsMarshal.SetCount(list105, num2); - Span span50 = CollectionsMarshal.AsSpan(list105); - ref ChangeEntry reference105 = ref span50[0]; - num3 = 2; + Span span49 = CollectionsMarshal.AsSpan(list105); + ref ChangeEntry reference105 = ref span49[0]; + num3 = 1; List list106 = new List(num3); CollectionsMarshal.SetCount(list106, num3); - Span span51 = CollectionsMarshal.AsSpan(list106); - span51[0] = "Auto Duty unsync options for each duty (Duty Support, Unsync Solo, Unsync Party)"; - span51[1] = "Added Auto Duty unsync options to quest schema and updated quests using old unsync method"; + CollectionsMarshal.AsSpan(list106)[0] = "Added Allied Society daily allowance tracker with bulk quest adding buttons"; reference105 = new ChangeEntry(EChangeCategory.Added, "Major features", list106); - ref ChangeEntry reference106 = ref span50[1]; - num3 = 3; + ref ChangeEntry reference106 = ref span49[1]; + num3 = 1; List list107 = new List(num3); CollectionsMarshal.SetCount(list107, num3); - Span span52 = CollectionsMarshal.AsSpan(list107); - span52[0] = "Added IPC for duty sync handling: GetDefaultDutyMode, SetDefaultDutyMode"; - span52[1] = "Added IPC for duty mode overrides: GetDutyModeOverride, SetDutyModeOverride"; - span52[2] = "Added IPC for clearing overrides: ClearDutyModeOverride, ClearAllDutyModeOverrides"; + CollectionsMarshal.AsSpan(list107)[0] = "Added IPC for Allied Society: GetRemainingAllowances, GetTimeUntilReset, GetAvailableQuestIds, GetAllAvailableQuestCounts, IsMaxRank, GetCurrentRank, GetSocietiesWithAvailableQuests"; reference106 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list107); - span50[2] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest (Constant Cravings)"); - reference104 = new ChangelogEntry("7.38.2", releaseDate38, list105); + reference104 = new ChangelogEntry("7.38.5", releaseDate38, list105); ref ChangelogEntry reference107 = ref span[38]; - DateOnly releaseDate39 = new DateOnly(2025, 11, 18); - num2 = 3; + DateOnly releaseDate39 = new DateOnly(2025, 11, 23); + num2 = 2; List list108 = new List(num2); CollectionsMarshal.SetCount(list108, num2); - Span span53 = CollectionsMarshal.AsSpan(list108); - ref ChangeEntry reference108 = ref span53[0]; + Span span50 = CollectionsMarshal.AsSpan(list108); + ref ChangeEntry reference108 = ref span50[0]; num3 = 1; List list109 = new List(num3); CollectionsMarshal.SetCount(list109, num3); - CollectionsMarshal.AsSpan(list109)[0] = "Added new fields to quest schema"; + CollectionsMarshal.AsSpan(list109)[0] = "Explicitly declare support for BMR singleplayer duty (The Rematch)"; reference108 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list109); - ref ChangeEntry reference109 = ref span53[1]; - num3 = 3; + ref ChangeEntry reference109 = ref span50[1]; + num3 = 8; List list110 = new List(num3); CollectionsMarshal.SetCount(list110, num3); - Span span54 = CollectionsMarshal.AsSpan(list110); - span54[0] = "A Faerie Tale Come True"; - span54[1] = "Constant Cravings"; - span54[2] = "A Bridge Too Full"; - reference109 = new ChangeEntry(EChangeCategory.QuestUpdates, "Added new quest paths", list110); - ref ChangeEntry reference110 = ref span53[2]; - num3 = 3; - List list111 = new List(num3); - CollectionsMarshal.SetCount(list111, num3); - Span span55 = CollectionsMarshal.AsSpan(list111); - span55[0] = "Fixed various quest schemas"; - span55[1] = "Fixed changelog bullet point encoding"; - span55[2] = "Fixed item use to wait until item is used before next action"; - reference110 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list111); - reference107 = new ChangelogEntry("7.38.1", releaseDate39, list108); - ref ChangelogEntry reference111 = ref span[39]; - DateOnly releaseDate40 = new DateOnly(2025, 11, 17); - num2 = 5; - List list112 = new List(num2); - CollectionsMarshal.SetCount(list112, num2); - Span span56 = CollectionsMarshal.AsSpan(list112); - ref ChangeEntry reference112 = ref span56[0]; + Span span51 = CollectionsMarshal.AsSpan(list110); + span51[0] = "Fixed quest (Microbrewing) to not get stuck near ramp"; + span51[1] = "Fixed quest (The Illuminated Land) where pathing would kill the player due to fall damage"; + span51[2] = "Fixed quest (It's Probably Not Pirates) improper pathing and removed unneeded step"; + span51[3] = "Fixed quest (The Black Wolf's Ultimatum) not exiting landing area"; + span51[4] = "Fixed quest (Magiteknical Failure) from not interacting with NPC due to being mounted"; + span51[5] = "Fixed quest (We Come in Peace) shortcut navigation"; + span51[6] = "Fixed quest (Poisoned Hearts) where incorrect pathing caused the player to die"; + span51[7] = "Fixed quests (Savage Snares) and (An Apple a Day) not detecting kills"; + reference109 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list110); + reference107 = new ChangelogEntry("7.38.4", releaseDate39, list108); + ref ChangelogEntry reference110 = ref span[39]; + DateOnly releaseDate40 = new DateOnly(2025, 11, 23); + num2 = 3; + List list111 = new List(num2); + CollectionsMarshal.SetCount(list111, num2); + Span span52 = CollectionsMarshal.AsSpan(list111); + ref ChangeEntry reference111 = ref span52[0]; + num3 = 2; + List list112 = new List(num3); + CollectionsMarshal.SetCount(list112, num3); + Span span53 = CollectionsMarshal.AsSpan(list112); + span53[0] = "Added RequireHq to crafting InteractionType"; + span53[1] = "Mark GC quests as Locked if rank not achieved"; + reference111 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list112); + ref ChangeEntry reference112 = ref span52[1]; num3 = 2; List list113 = new List(num3); CollectionsMarshal.SetCount(list113, num3); - Span span57 = CollectionsMarshal.AsSpan(list113); - span57[0] = "Quest sequence window to show expected sequences in each quest (with quest searching)"; - span57[1] = "Changelog"; - reference112 = new ChangeEntry(EChangeCategory.Added, "Major features", list113); - ref ChangeEntry reference113 = ref span56[1]; - num3 = 2; + Span span54 = CollectionsMarshal.AsSpan(list113); + span54[0] = "Added IPC for stop conditions: GetStopConditionsEnabled, SetStopConditionsEnabled, GetStopQuestList, AddStopQuest, RemoveStopQuest, ClearStopQuests, GetLevelStopCondition, SetLevelStopCondition, GetSequenceStopCondition, SetSequenceStopCondition"; + span54[1] = "Added IPC for priority quests: GetPriorityQuests, RemovePriorityQuest, ReorderPriorityQuest, GetAvailablePresets, GetPresetQuests, AddPresetToPriority, IsPresetAvailable, IsQuestInPriority, GetQuestPriorityIndex, HasAvailablePriorityQuests"; + reference112 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list113); + ref ChangeEntry reference113 = ref span52[2]; + num3 = 3; List list114 = new List(num3); CollectionsMarshal.SetCount(list114, num3); - Span span58 = CollectionsMarshal.AsSpan(list114); - span58[0] = "Updated quest schemas"; - span58[1] = "Added search bar to preferred mounts and capitalization to mirror game mount names"; - reference113 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list114); - ref ChangeEntry reference114 = ref span56[2]; + Span span55 = CollectionsMarshal.AsSpan(list114); + span55[0] = "Fixed line breaks not working in dialog strings"; + span55[1] = "Fixed quest (Labor of Love)"; + span55[2] = "Fixed quest (Sea of Sorrow)"; + reference113 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list114); + reference110 = new ChangelogEntry("7.38.3", releaseDate40, list111); + ref ChangelogEntry reference114 = ref span[40]; + DateOnly releaseDate41 = new DateOnly(2025, 11, 18); + num2 = 3; + List list115 = new List(num2); + CollectionsMarshal.SetCount(list115, num2); + Span span56 = CollectionsMarshal.AsSpan(list115); + ref ChangeEntry reference115 = ref span56[0]; + num3 = 2; + List list116 = new List(num3); + CollectionsMarshal.SetCount(list116, num3); + Span span57 = CollectionsMarshal.AsSpan(list116); + span57[0] = "Auto Duty unsync options for each duty (Duty Support, Unsync Solo, Unsync Party)"; + span57[1] = "Added Auto Duty unsync options to quest schema and updated quests using old unsync method"; + reference115 = new ChangeEntry(EChangeCategory.Added, "Major features", list116); + ref ChangeEntry reference116 = ref span56[1]; num3 = 3; - List list115 = new List(num3); - CollectionsMarshal.SetCount(list115, num3); - Span span59 = CollectionsMarshal.AsSpan(list115); - span59[0] = "Renamed IsQuestCompleted → IsQuestComplete"; - span59[1] = "Renamed IsQuestAvailable → IsReadyToAcceptQuest"; - span59[2] = "Added GetCurrentTask IPC"; - reference114 = new ChangeEntry(EChangeCategory.Changed, "IPC changes", list115); - span56[3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added all Hildibrand quests"); - span56[4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed credits/cutscenes playback"); - reference111 = new ChangelogEntry("7.38.0", releaseDate40, list112); - ref ChangelogEntry reference115 = ref span[40]; - DateOnly releaseDate41 = new DateOnly(2025, 11, 8); - num2 = 1; - List list116 = new List(num2); - CollectionsMarshal.SetCount(list116, num2); - CollectionsMarshal.AsSpan(list116)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Fall Guys quest (Just Crowning Around)"); - reference115 = new ChangelogEntry("6.38", releaseDate41, list116); - ref ChangelogEntry reference116 = ref span[41]; - DateOnly releaseDate42 = new DateOnly(2025, 11, 8); - num2 = 1; - List list117 = new List(num2); - CollectionsMarshal.SetCount(list117, num2); - CollectionsMarshal.AsSpan(list117)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Cosmic Exploration and various unlock quests"); - reference116 = new ChangelogEntry("6.37", releaseDate42, list117); - ref ChangelogEntry reference117 = ref span[42]; - DateOnly releaseDate43 = new DateOnly(2025, 11, 2); - num2 = 1; + List list117 = new List(num3); + CollectionsMarshal.SetCount(list117, num3); + Span span58 = CollectionsMarshal.AsSpan(list117); + span58[0] = "Added IPC for duty sync handling: GetDefaultDutyMode, SetDefaultDutyMode"; + span58[1] = "Added IPC for duty mode overrides: GetDutyModeOverride, SetDutyModeOverride"; + span58[2] = "Added IPC for clearing overrides: ClearDutyModeOverride, ClearAllDutyModeOverrides"; + reference116 = new ChangeEntry(EChangeCategory.Added, "IPC changes", list117); + span56[2] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest (Constant Cravings)"); + reference114 = new ChangelogEntry("7.38.2", releaseDate41, list115); + ref ChangelogEntry reference117 = ref span[41]; + DateOnly releaseDate42 = new DateOnly(2025, 11, 18); + num2 = 3; List list118 = new List(num2); CollectionsMarshal.SetCount(list118, num2); - CollectionsMarshal.AsSpan(list118)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 6 quest (With High Spirits)"); - reference117 = new ChangelogEntry("6.36", releaseDate43, list118); - ref ChangelogEntry reference118 = ref span[43]; - DateOnly releaseDate44 = new DateOnly(2025, 10, 28); - num2 = 1; - List list119 = new List(num2); - CollectionsMarshal.SetCount(list119, num2); - CollectionsMarshal.AsSpan(list119)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed level 3 MSQ handling if character started on non-XP buff world"); - reference118 = new ChangelogEntry("6.35", releaseDate44, list119); - ref ChangelogEntry reference119 = ref span[44]; - DateOnly releaseDate45 = new DateOnly(2025, 10, 23); - num2 = 2; - List list120 = new List(num2); - CollectionsMarshal.SetCount(list120, num2); - Span span60 = CollectionsMarshal.AsSpan(list120); - span60[0] = new ChangeEntry(EChangeCategory.Added, "Added clear priority quests on logout and on completion config settings"); - span60[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed priority quest importing to respect import order"); - reference119 = new ChangelogEntry("6.34", releaseDate45, list120); - ref ChangelogEntry reference120 = ref span[45]; - DateOnly releaseDate46 = new DateOnly(2025, 10, 23); - num2 = 1; - List list121 = new List(num2); - CollectionsMarshal.SetCount(list121, num2); - CollectionsMarshal.AsSpan(list121)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed RSR combat module"); - reference120 = new ChangelogEntry("6.33", releaseDate46, list121); - ref ChangelogEntry reference121 = ref span[46]; - DateOnly releaseDate47 = new DateOnly(2025, 10, 23); - num2 = 1; + Span span59 = CollectionsMarshal.AsSpan(list118); + ref ChangeEntry reference118 = ref span59[0]; + num3 = 1; + List list119 = new List(num3); + CollectionsMarshal.SetCount(list119, num3); + CollectionsMarshal.AsSpan(list119)[0] = "Added new fields to quest schema"; + reference118 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list119); + ref ChangeEntry reference119 = ref span59[1]; + num3 = 3; + List list120 = new List(num3); + CollectionsMarshal.SetCount(list120, num3); + Span span60 = CollectionsMarshal.AsSpan(list120); + span60[0] = "A Faerie Tale Come True"; + span60[1] = "Constant Cravings"; + span60[2] = "A Bridge Too Full"; + reference119 = new ChangeEntry(EChangeCategory.QuestUpdates, "Added new quest paths", list120); + ref ChangeEntry reference120 = ref span59[2]; + num3 = 3; + List list121 = new List(num3); + CollectionsMarshal.SetCount(list121, num3); + Span span61 = CollectionsMarshal.AsSpan(list121); + span61[0] = "Fixed various quest schemas"; + span61[1] = "Fixed changelog bullet point encoding"; + span61[2] = "Fixed item use to wait until item is used before next action"; + reference120 = new ChangeEntry(EChangeCategory.Fixed, "Bug fixes", list121); + reference117 = new ChangelogEntry("7.38.1", releaseDate42, list118); + ref ChangelogEntry reference121 = ref span[42]; + DateOnly releaseDate43 = new DateOnly(2025, 11, 17); + num2 = 5; List list122 = new List(num2); CollectionsMarshal.SetCount(list122, num2); - CollectionsMarshal.AsSpan(list122)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 5 quest (Forged in Corn)"); - reference121 = new ChangelogEntry("6.32", releaseDate47, list122); - ref ChangelogEntry reference122 = ref span[47]; - DateOnly releaseDate48 = new DateOnly(2025, 10, 21); - num2 = 1; - List list123 = new List(num2); - CollectionsMarshal.SetCount(list123, num2); - CollectionsMarshal.AsSpan(list123)[0] = new ChangeEntry(EChangeCategory.Changed, "Added checks for moogle and allied society quests when using add all available quests"); - reference122 = new ChangelogEntry("6.31", releaseDate48, list123); - ref ChangelogEntry reference123 = ref span[48]; - DateOnly releaseDate49 = new DateOnly(2025, 10, 21); - num2 = 1; - List list124 = new List(num2); - CollectionsMarshal.SetCount(list124, num2); - CollectionsMarshal.AsSpan(list124)[0] = new ChangeEntry(EChangeCategory.Added, "Added button to journal that allows adding all available quests to priority"); - reference123 = new ChangelogEntry("6.30", releaseDate49, list124); - ref ChangelogEntry reference124 = ref span[49]; - DateOnly releaseDate50 = new DateOnly(2025, 10, 20); - num2 = 2; - List list125 = new List(num2); - CollectionsMarshal.SetCount(list125, num2); - Span span61 = CollectionsMarshal.AsSpan(list125); - ref ChangeEntry reference125 = ref span61[0]; + Span span62 = CollectionsMarshal.AsSpan(list122); + ref ChangeEntry reference122 = ref span62[0]; num3 = 2; - List list126 = new List(num3); - CollectionsMarshal.SetCount(list126, num3); - Span span62 = CollectionsMarshal.AsSpan(list126); - span62[0] = "Added item count to combat handling rework"; - span62[1] = "Updated Pandora conflicting features"; - reference125 = new ChangeEntry(EChangeCategory.Changed, "Combat handling improvements", list126); - span61[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest to purchase Gysahl Greens if not in inventory"); - reference124 = new ChangelogEntry("6.29", releaseDate50, list125); - ref ChangelogEntry reference126 = ref span[50]; - DateOnly releaseDate51 = new DateOnly(2025, 10, 19); + List list123 = new List(num3); + CollectionsMarshal.SetCount(list123, num3); + Span span63 = CollectionsMarshal.AsSpan(list123); + span63[0] = "Quest sequence window to show expected sequences in each quest (with quest searching)"; + span63[1] = "Changelog"; + reference122 = new ChangeEntry(EChangeCategory.Added, "Major features", list123); + ref ChangeEntry reference123 = ref span62[1]; + num3 = 2; + List list124 = new List(num3); + CollectionsMarshal.SetCount(list124, num3); + Span span64 = CollectionsMarshal.AsSpan(list124); + span64[0] = "Updated quest schemas"; + span64[1] = "Added search bar to preferred mounts and capitalization to mirror game mount names"; + reference123 = new ChangeEntry(EChangeCategory.Changed, "Improvements", list124); + ref ChangeEntry reference124 = ref span62[2]; + num3 = 3; + List list125 = new List(num3); + CollectionsMarshal.SetCount(list125, num3); + Span span65 = CollectionsMarshal.AsSpan(list125); + span65[0] = "Renamed IsQuestCompleted → IsQuestComplete"; + span65[1] = "Renamed IsQuestAvailable → IsReadyToAcceptQuest"; + span65[2] = "Added GetCurrentTask IPC"; + reference124 = new ChangeEntry(EChangeCategory.Changed, "IPC changes", list125); + span62[3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added all Hildibrand quests"); + span62[4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed credits/cutscenes playback"); + reference121 = new ChangelogEntry("7.38.0", releaseDate43, list122); + ref ChangelogEntry reference125 = ref span[43]; + DateOnly releaseDate44 = new DateOnly(2025, 11, 8); + num2 = 1; + List list126 = new List(num2); + CollectionsMarshal.SetCount(list126, num2); + CollectionsMarshal.AsSpan(list126)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Fall Guys quest (Just Crowning Around)"); + reference125 = new ChangelogEntry("6.38", releaseDate44, list126); + ref ChangelogEntry reference126 = ref span[44]; + DateOnly releaseDate45 = new DateOnly(2025, 11, 8); num2 = 1; List list127 = new List(num2); CollectionsMarshal.SetCount(list127, num2); - CollectionsMarshal.AsSpan(list127)[0] = new ChangeEntry(EChangeCategory.Changed, "Reworked kill count combat handling - combat and enemy kills are now processed instantly"); - reference126 = new ChangelogEntry("6.28", releaseDate51, list127); - ref ChangelogEntry reference127 = ref span[51]; - DateOnly releaseDate52 = new DateOnly(2025, 10, 18); - num2 = 2; + CollectionsMarshal.AsSpan(list127)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Cosmic Exploration and various unlock quests"); + reference126 = new ChangelogEntry("6.37", releaseDate45, list127); + ref ChangelogEntry reference127 = ref span[45]; + DateOnly releaseDate46 = new DateOnly(2025, 11, 2); + num2 = 1; List list128 = new List(num2); CollectionsMarshal.SetCount(list128, num2); - Span span63 = CollectionsMarshal.AsSpan(list128); - span63[0] = new ChangeEntry(EChangeCategory.Changed, "Improved Aether Current checking logic"); - span63[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Chocobo Taxi Stand CheckSkip error and Patch 7.3 Fantasia unlock quest date/time"); - reference127 = new ChangelogEntry("6.27", releaseDate52, list128); - ref ChangelogEntry reference128 = ref span[52]; - DateOnly releaseDate53 = new DateOnly(2025, 10, 18); + CollectionsMarshal.AsSpan(list128)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 6 quest (With High Spirits)"); + reference127 = new ChangelogEntry("6.36", releaseDate46, list128); + ref ChangelogEntry reference128 = ref span[46]; + DateOnly releaseDate47 = new DateOnly(2025, 10, 28); num2 = 1; List list129 = new List(num2); CollectionsMarshal.SetCount(list129, num2); - CollectionsMarshal.AsSpan(list129)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests"); - reference128 = new ChangelogEntry("6.26", releaseDate53, list129); - ref ChangelogEntry reference129 = ref span[53]; - DateOnly releaseDate54 = new DateOnly(2025, 10, 17); - num2 = 1; + CollectionsMarshal.AsSpan(list129)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed level 3 MSQ handling if character started on non-XP buff world"); + reference128 = new ChangelogEntry("6.35", releaseDate47, list129); + ref ChangelogEntry reference129 = ref span[47]; + DateOnly releaseDate48 = new DateOnly(2025, 10, 23); + num2 = 2; List list130 = new List(num2); CollectionsMarshal.SetCount(list130, num2); - CollectionsMarshal.AsSpan(list130)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added All Saints' Wake 2025 quests and 7.35 Yok Huy rank 4 quests"); - reference129 = new ChangelogEntry("6.25", releaseDate54, list130); - ref ChangelogEntry reference130 = ref span[54]; - DateOnly releaseDate55 = new DateOnly(2025, 10, 16); + Span span66 = CollectionsMarshal.AsSpan(list130); + span66[0] = new ChangeEntry(EChangeCategory.Added, "Added clear priority quests on logout and on completion config settings"); + span66[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed priority quest importing to respect import order"); + reference129 = new ChangelogEntry("6.34", releaseDate48, list130); + ref ChangelogEntry reference130 = ref span[48]; + DateOnly releaseDate49 = new DateOnly(2025, 10, 23); num2 = 1; List list131 = new List(num2); CollectionsMarshal.SetCount(list131, num2); - CollectionsMarshal.AsSpan(list131)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests and Deep Dungeon quest"); - reference130 = new ChangelogEntry("6.24", releaseDate55, list131); - ref ChangelogEntry reference131 = ref span[55]; - DateOnly releaseDate56 = new DateOnly(2025, 10, 13); + CollectionsMarshal.AsSpan(list131)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed RSR combat module"); + reference130 = new ChangelogEntry("6.33", releaseDate49, list131); + ref ChangelogEntry reference131 = ref span[49]; + DateOnly releaseDate50 = new DateOnly(2025, 10, 23); num2 = 1; List list132 = new List(num2); CollectionsMarshal.SetCount(list132, num2); - CollectionsMarshal.AsSpan(list132)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quest (Larder Logistics)"); - reference131 = new ChangelogEntry("6.23", releaseDate56, list132); - ref ChangelogEntry reference132 = ref span[56]; - DateOnly releaseDate57 = new DateOnly(2025, 10, 12); - num2 = 3; + CollectionsMarshal.AsSpan(list132)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy Rank 5 quest (Forged in Corn)"); + reference131 = new ChangelogEntry("6.32", releaseDate50, list132); + ref ChangelogEntry reference132 = ref span[50]; + DateOnly releaseDate51 = new DateOnly(2025, 10, 21); + num2 = 1; List list133 = new List(num2); CollectionsMarshal.SetCount(list133, num2); - Span span64 = CollectionsMarshal.AsSpan(list133); - span64[0] = new ChangeEntry(EChangeCategory.Changed, "Prevent disabled or Locked quests from being started as 'Start as next quest'"); - span64[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); - span64[2] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Yok Huy quest and journal quest chain priority issues"); - reference132 = new ChangelogEntry("6.22", releaseDate57, list133); - ref ChangelogEntry reference133 = ref span[57]; - DateOnly releaseDate58 = new DateOnly(2025, 10, 12); - num2 = 2; + CollectionsMarshal.AsSpan(list133)[0] = new ChangeEntry(EChangeCategory.Changed, "Added checks for moogle and allied society quests when using add all available quests"); + reference132 = new ChangelogEntry("6.31", releaseDate51, list133); + ref ChangelogEntry reference133 = ref span[51]; + DateOnly releaseDate52 = new DateOnly(2025, 10, 21); + num2 = 1; List list134 = new List(num2); CollectionsMarshal.SetCount(list134, num2); - Span span65 = CollectionsMarshal.AsSpan(list134); - span65[0] = new ChangeEntry(EChangeCategory.Added, "Added expansion abbreviation to journal window"); - span65[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); - reference133 = new ChangelogEntry("6.21", releaseDate58, list134); - ref ChangelogEntry reference134 = ref span[58]; - DateOnly releaseDate59 = new DateOnly(2025, 10, 10); + CollectionsMarshal.AsSpan(list134)[0] = new ChangeEntry(EChangeCategory.Added, "Added button to journal that allows adding all available quests to priority"); + reference133 = new ChangelogEntry("6.30", releaseDate52, list134); + ref ChangelogEntry reference134 = ref span[52]; + DateOnly releaseDate53 = new DateOnly(2025, 10, 20); num2 = 2; List list135 = new List(num2); CollectionsMarshal.SetCount(list135, num2); - Span span66 = CollectionsMarshal.AsSpan(list135); - span66[0] = new ChangeEntry(EChangeCategory.Changed, "Allow completed repeatable quests to be used with 'Add quest and requirements to priority' feature"); - span66[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 quest (A Work of Cart)"); - reference134 = new ChangelogEntry("6.20", releaseDate59, list135); - ref ChangelogEntry reference135 = ref span[59]; - DateOnly releaseDate60 = new DateOnly(2025, 10, 9); - num2 = 3; - List list136 = new List(num2); - CollectionsMarshal.SetCount(list136, num2); - Span span67 = CollectionsMarshal.AsSpan(list136); - span67[0] = new ChangeEntry(EChangeCategory.Added, "Added config to batch Allied Society quest turn-ins"); - span67[1] = new ChangeEntry(EChangeCategory.Changed, "Repeatable quests now show correct availability state in journal"); - span67[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 2 quests"); - reference135 = new ChangelogEntry("6.19", releaseDate60, list136); - ref ChangelogEntry reference136 = ref span[60]; - DateOnly releaseDate61 = new DateOnly(2025, 10, 9); - num2 = 2; + Span span67 = CollectionsMarshal.AsSpan(list135); + ref ChangeEntry reference135 = ref span67[0]; + num3 = 2; + List list136 = new List(num3); + CollectionsMarshal.SetCount(list136, num3); + Span span68 = CollectionsMarshal.AsSpan(list136); + span68[0] = "Added item count to combat handling rework"; + span68[1] = "Updated Pandora conflicting features"; + reference135 = new ChangeEntry(EChangeCategory.Changed, "Combat handling improvements", list136); + span67[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest to purchase Gysahl Greens if not in inventory"); + reference134 = new ChangelogEntry("6.29", releaseDate53, list135); + ref ChangelogEntry reference136 = ref span[53]; + DateOnly releaseDate54 = new DateOnly(2025, 10, 19); + num2 = 1; List list137 = new List(num2); CollectionsMarshal.SetCount(list137, num2); - Span span68 = CollectionsMarshal.AsSpan(list137); - span68[0] = new ChangeEntry(EChangeCategory.Changed, "Show once completed quests with improved state display"); - span68[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy daily quest and improvements to various Yok Huy quests"); - reference136 = new ChangelogEntry("6.18", releaseDate61, list137); - ref ChangelogEntry reference137 = ref span[61]; - DateOnly releaseDate62 = new DateOnly(2025, 10, 8); - num2 = 1; + CollectionsMarshal.AsSpan(list137)[0] = new ChangeEntry(EChangeCategory.Changed, "Reworked kill count combat handling - combat and enemy kills are now processed instantly"); + reference136 = new ChangelogEntry("6.28", releaseDate54, list137); + ref ChangelogEntry reference137 = ref span[54]; + DateOnly releaseDate55 = new DateOnly(2025, 10, 18); + num2 = 2; List list138 = new List(num2); CollectionsMarshal.SetCount(list138, num2); - CollectionsMarshal.AsSpan(list138)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 and rank 2 quests"); - reference137 = new ChangelogEntry("6.17", releaseDate62, list138); - ref ChangelogEntry reference138 = ref span[62]; - DateOnly releaseDate63 = new DateOnly(2025, 10, 8); + Span span69 = CollectionsMarshal.AsSpan(list138); + span69[0] = new ChangeEntry(EChangeCategory.Changed, "Improved Aether Current checking logic"); + span69[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Chocobo Taxi Stand CheckSkip error and Patch 7.3 Fantasia unlock quest date/time"); + reference137 = new ChangelogEntry("6.27", releaseDate55, list138); + ref ChangelogEntry reference138 = ref span[55]; + DateOnly releaseDate56 = new DateOnly(2025, 10, 18); num2 = 1; List list139 = new List(num2); CollectionsMarshal.SetCount(list139, num2); - CollectionsMarshal.AsSpan(list139)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon quest (Faerie Tale)"); - reference138 = new ChangelogEntry("6.16", releaseDate63, list139); - ref ChangelogEntry reference139 = ref span[63]; - DateOnly releaseDate64 = new DateOnly(2025, 10, 8); - num2 = 2; + CollectionsMarshal.AsSpan(list139)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests"); + reference138 = new ChangelogEntry("6.26", releaseDate56, list139); + ref ChangelogEntry reference139 = ref span[56]; + DateOnly releaseDate57 = new DateOnly(2025, 10, 17); + num2 = 1; List list140 = new List(num2); CollectionsMarshal.SetCount(list140, num2); - Span span69 = CollectionsMarshal.AsSpan(list140); - span69[0] = new ChangeEntry(EChangeCategory.Changed, "Dalamud cleanup"); - span69[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest level requirement check log spam"); - reference139 = new ChangelogEntry("6.15", releaseDate64, list140); - ref ChangelogEntry reference140 = ref span[64]; - DateOnly releaseDate65 = new DateOnly(2025, 10, 8); + CollectionsMarshal.AsSpan(list140)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added All Saints' Wake 2025 quests and 7.35 Yok Huy rank 4 quests"); + reference139 = new ChangelogEntry("6.25", releaseDate57, list140); + ref ChangelogEntry reference140 = ref span[57]; + DateOnly releaseDate58 = new DateOnly(2025, 10, 16); num2 = 1; List list141 = new List(num2); CollectionsMarshal.SetCount(list141, num2); - CollectionsMarshal.AsSpan(list141)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check logic if quest were MSQ"); - reference140 = new ChangelogEntry("6.14", releaseDate65, list141); - ref ChangelogEntry reference141 = ref span[65]; - DateOnly releaseDate66 = new DateOnly(2025, 10, 8); - num2 = 2; + CollectionsMarshal.AsSpan(list141)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 4 quests and Deep Dungeon quest"); + reference140 = new ChangelogEntry("6.24", releaseDate58, list141); + ref ChangelogEntry reference141 = ref span[58]; + DateOnly releaseDate59 = new DateOnly(2025, 10, 13); + num2 = 1; List list142 = new List(num2); CollectionsMarshal.SetCount(list142, num2); - Span span70 = CollectionsMarshal.AsSpan(list142); - ref ChangeEntry reference142 = ref span70[0]; - num3 = 3; - List list143 = new List(num3); - CollectionsMarshal.SetCount(list143, num3); - Span span71 = CollectionsMarshal.AsSpan(list143); - span71[0] = "Context menu option to add required quests and their chain to priority list"; - span71[1] = "AetheryteShortcut to multiple quests"; - span71[2] = "Artisan as a recommended plugin/dependency"; - reference142 = new ChangeEntry(EChangeCategory.Added, "Quest improvements", list143); - span70[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check and priority list issues"); - reference141 = new ChangelogEntry("6.13", releaseDate66, list142); - ref ChangelogEntry reference143 = ref span[66]; - DateOnly releaseDate67 = new DateOnly(2025, 10, 7); - num2 = 4; + CollectionsMarshal.AsSpan(list142)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quest (Larder Logistics)"); + reference141 = new ChangelogEntry("6.23", releaseDate59, list142); + ref ChangelogEntry reference142 = ref span[59]; + DateOnly releaseDate60 = new DateOnly(2025, 10, 12); + num2 = 3; + List list143 = new List(num2); + CollectionsMarshal.SetCount(list143, num2); + Span span70 = CollectionsMarshal.AsSpan(list143); + span70[0] = new ChangeEntry(EChangeCategory.Changed, "Prevent disabled or Locked quests from being started as 'Start as next quest'"); + span70[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); + span70[2] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Yok Huy quest and journal quest chain priority issues"); + reference142 = new ChangelogEntry("6.22", releaseDate60, list143); + ref ChangelogEntry reference143 = ref span[60]; + DateOnly releaseDate61 = new DateOnly(2025, 10, 12); + num2 = 2; List list144 = new List(num2); CollectionsMarshal.SetCount(list144, num2); - Span span72 = CollectionsMarshal.AsSpan(list144); - ref ChangeEntry reference144 = ref span72[0]; - num3 = 4; - List list145 = new List(num3); - CollectionsMarshal.SetCount(list145, num3); - Span span73 = CollectionsMarshal.AsSpan(list145); - span73[0] = "FATE combat handling with auto level syncing"; - span73[1] = "Start accepted quests from journal with 'Start as next quest'"; - span73[2] = "Update quest tracking when quests are hidden or prioritised in game"; - span73[3] = "QuestMap as a recommended plugin/dependency"; - reference144 = new ChangeEntry(EChangeCategory.Added, "FATE and quest tracking", list145); - ref ChangeEntry reference145 = ref span72[1]; - num3 = 3; - List list146 = new List(num3); - CollectionsMarshal.SetCount(list146, num3); - Span span74 = CollectionsMarshal.AsSpan(list146); - span74[0] = "Always prioritise next quest during teleportation/zone transitions"; - span74[1] = "Improved accepted quest logic with abandoned quest detection"; - span74[2] = "Show quests without quest paths as Locked"; - reference145 = new ChangeEntry(EChangeCategory.Changed, "Quest prioritisation improvements", list146); - span72[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon, Hildibrand, Yok Huy, Monster Hunter Wilds Collab, and Doman Enclave quests"); - span72[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed accepted/active quest display and Hildibrand quest issues"); - reference143 = new ChangelogEntry("6.12", releaseDate67, list144); - ref ChangelogEntry reference146 = ref span[67]; - DateOnly releaseDate68 = new DateOnly(2025, 10, 3); - num2 = 1; + Span span71 = CollectionsMarshal.AsSpan(list144); + span71[0] = new ChangeEntry(EChangeCategory.Added, "Added expansion abbreviation to journal window"); + span71[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 3 quests"); + reference143 = new ChangelogEntry("6.21", releaseDate61, list144); + ref ChangelogEntry reference144 = ref span[61]; + DateOnly releaseDate62 = new DateOnly(2025, 10, 10); + num2 = 2; + List list145 = new List(num2); + CollectionsMarshal.SetCount(list145, num2); + Span span72 = CollectionsMarshal.AsSpan(list145); + span72[0] = new ChangeEntry(EChangeCategory.Changed, "Allow completed repeatable quests to be used with 'Add quest and requirements to priority' feature"); + span72[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 quest (A Work of Cart)"); + reference144 = new ChangelogEntry("6.20", releaseDate62, list145); + ref ChangelogEntry reference145 = ref span[62]; + DateOnly releaseDate63 = new DateOnly(2025, 10, 9); + num2 = 3; + List list146 = new List(num2); + CollectionsMarshal.SetCount(list146, num2); + Span span73 = CollectionsMarshal.AsSpan(list146); + span73[0] = new ChangeEntry(EChangeCategory.Added, "Added config to batch Allied Society quest turn-ins"); + span73[1] = new ChangeEntry(EChangeCategory.Changed, "Repeatable quests now show correct availability state in journal"); + span73[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 2 quests"); + reference145 = new ChangelogEntry("6.19", releaseDate63, list146); + ref ChangelogEntry reference146 = ref span[63]; + DateOnly releaseDate64 = new DateOnly(2025, 10, 9); + num2 = 2; List list147 = new List(num2); CollectionsMarshal.SetCount(list147, num2); - CollectionsMarshal.AsSpan(list147)[0] = new ChangeEntry(EChangeCategory.Changed, "Added remaining checks for quest priority to prevent infinite teleport looping"); - reference146 = new ChangelogEntry("6.11", releaseDate68, list147); - ref ChangelogEntry reference147 = ref span[68]; - DateOnly releaseDate69 = new DateOnly(2025, 10, 2); + Span span74 = CollectionsMarshal.AsSpan(list147); + span74[0] = new ChangeEntry(EChangeCategory.Changed, "Show once completed quests with improved state display"); + span74[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy daily quest and improvements to various Yok Huy quests"); + reference146 = new ChangelogEntry("6.18", releaseDate64, list147); + ref ChangelogEntry reference147 = ref span[64]; + DateOnly releaseDate65 = new DateOnly(2025, 10, 8); num2 = 1; List list148 = new List(num2); CollectionsMarshal.SetCount(list148, num2); - ref ChangeEntry reference148 = ref CollectionsMarshal.AsSpan(list148)[0]; - num3 = 2; - List list149 = new List(num3); - CollectionsMarshal.SetCount(list149, num3); - Span span75 = CollectionsMarshal.AsSpan(list149); - span75[0] = "Don't show quests as available if player doesn't meet level requirements"; - span75[1] = "Updated 'required for MSQ' text in Crystal Tower quest preset window"; - reference148 = new ChangeEntry(EChangeCategory.Changed, "Quest window improvements", list149); - reference147 = new ChangelogEntry("6.10", releaseDate69, list148); - ref ChangelogEntry reference149 = ref span[69]; - DateOnly releaseDate70 = new DateOnly(2025, 9, 21); - num2 = 5; + CollectionsMarshal.AsSpan(list148)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Yok Huy rank 1 and rank 2 quests"); + reference147 = new ChangelogEntry("6.17", releaseDate65, list148); + ref ChangelogEntry reference148 = ref span[65]; + DateOnly releaseDate66 = new DateOnly(2025, 10, 8); + num2 = 1; + List list149 = new List(num2); + CollectionsMarshal.SetCount(list149, num2); + CollectionsMarshal.AsSpan(list149)[0] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon quest (Faerie Tale)"); + reference148 = new ChangelogEntry("6.16", releaseDate66, list149); + ref ChangelogEntry reference149 = ref span[66]; + DateOnly releaseDate67 = new DateOnly(2025, 10, 8); + num2 = 2; List list150 = new List(num2); CollectionsMarshal.SetCount(list150, num2); - Span span76 = CollectionsMarshal.AsSpan(list150); - ref ChangeEntry reference150 = ref span76[0]; - num3 = 4; - List list151 = new List(num3); - CollectionsMarshal.SetCount(list151, num3); - Span span77 = CollectionsMarshal.AsSpan(list151); - span77[0] = "Reworked event quest handling - automatically displays when events are active"; - span77[1] = "Reworked journal system with improved filtering and display"; - span77[2] = "Reworked Priority Quests tab (Manual Priority and Quest Presets)"; - span77[3] = "Quest path viewer site (https://wigglymuffin.github.io/FFXIV-Tools/)"; - reference150 = new ChangeEntry(EChangeCategory.Added, "Major system reworks", list151); - ref ChangeEntry reference151 = ref span76[1]; - num3 = 4; - List list152 = new List(num3); - CollectionsMarshal.SetCount(list152, num3); - Span span78 = CollectionsMarshal.AsSpan(list152); - span78[0] = "Questionable.IsQuestCompleted"; - span78[1] = "Questionable.IsQuestAvailable"; - span78[2] = "Questionable.IsQuestAccepted"; - span78[3] = "Questionable.IsQuestUnobtainable"; - reference151 = new ChangeEntry(EChangeCategory.Added, "New IPC commands", list152); - ref ChangeEntry reference152 = ref span76[2]; - num3 = 5; + Span span75 = CollectionsMarshal.AsSpan(list150); + span75[0] = new ChangeEntry(EChangeCategory.Changed, "Dalamud cleanup"); + span75[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed quest level requirement check log spam"); + reference149 = new ChangelogEntry("6.15", releaseDate67, list150); + ref ChangelogEntry reference150 = ref span[67]; + DateOnly releaseDate68 = new DateOnly(2025, 10, 8); + num2 = 1; + List list151 = new List(num2); + CollectionsMarshal.SetCount(list151, num2); + CollectionsMarshal.AsSpan(list151)[0] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check logic if quest were MSQ"); + reference150 = new ChangelogEntry("6.14", releaseDate68, list151); + ref ChangelogEntry reference151 = ref span[68]; + DateOnly releaseDate69 = new DateOnly(2025, 10, 8); + num2 = 2; + List list152 = new List(num2); + CollectionsMarshal.SetCount(list152, num2); + Span span76 = CollectionsMarshal.AsSpan(list152); + ref ChangeEntry reference152 = ref span76[0]; + num3 = 3; List list153 = new List(num3); CollectionsMarshal.SetCount(list153, num3); - Span span79 = CollectionsMarshal.AsSpan(list153); - span79[0] = "Improved JSON quest validation with specific error reasons"; - span79[1] = "Added stop at sequence stop condition"; - span79[2] = "Improved Pandora plugin conflict detection"; - span79[3] = "Improved DialogueChoices regex matching"; - span79[4] = "Improved refresh checker for all quest states"; - reference152 = new ChangeEntry(EChangeCategory.Changed, "Various improvements", list153); - span76[3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.31 Occult Crescent quests"); - span76[4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed cutscene crashes, Single Player Duty triggers, and various quest issues"); - reference149 = new ChangelogEntry("6.9", releaseDate70, list150); - ref ChangelogEntry reference153 = ref span[70]; - DateOnly releaseDate71 = new DateOnly(2025, 9, 2); + Span span77 = CollectionsMarshal.AsSpan(list153); + span77[0] = "Context menu option to add required quests and their chain to priority list"; + span77[1] = "AetheryteShortcut to multiple quests"; + span77[2] = "Artisan as a recommended plugin/dependency"; + reference152 = new ChangeEntry(EChangeCategory.Added, "Quest improvements", list153); + span76[1] = new ChangeEntry(EChangeCategory.Fixed, "Fixed abandoned quest check and priority list issues"); + reference151 = new ChangelogEntry("6.13", releaseDate69, list152); + ref ChangelogEntry reference153 = ref span[69]; + DateOnly releaseDate70 = new DateOnly(2025, 10, 7); num2 = 4; List list154 = new List(num2); CollectionsMarshal.SetCount(list154, num2); - Span span80 = CollectionsMarshal.AsSpan(list154); - ref ChangeEntry reference154 = ref span80[0]; - num3 = 3; + Span span78 = CollectionsMarshal.AsSpan(list154); + ref ChangeEntry reference154 = ref span78[0]; + num3 = 4; List list155 = new List(num3); CollectionsMarshal.SetCount(list155, num3); - Span span81 = CollectionsMarshal.AsSpan(list155); - span81[0] = "Help commands and priority quest command"; - span81[1] = "Prevent 'CompleteQuest' step setting"; - span81[2] = "Duty counts and controls in 'Quest Battles' tab"; - reference154 = new ChangeEntry(EChangeCategory.Added, "Command and UI improvements", list155); - span80[1] = new ChangeEntry(EChangeCategory.Changed, "Improved 'Clear All' buttons to require CTRL being held"); - span80[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Zodiac quests and 7.31 Cosmic/Occult Crescent quests"); - span80[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Fishing for Friendship and Cosmic Exploration quests"); - reference153 = new ChangelogEntry("6.8", releaseDate71, list154); - ref ChangelogEntry reference155 = ref span[71]; - DateOnly releaseDate72 = new DateOnly(2025, 8, 27); - num2 = 4; - List list156 = new List(num2); - CollectionsMarshal.SetCount(list156, num2); - Span span82 = CollectionsMarshal.AsSpan(list156); - ref ChangeEntry reference156 = ref span82[0]; - num3 = 2; - List list157 = new List(num3); - CollectionsMarshal.SetCount(list157, num3); - Span span83 = CollectionsMarshal.AsSpan(list157); - span83[0] = "Icon to 'Clear All' button in stop conditions"; - span83[1] = "Duty counts and 'Enable All' button in 'Duties' tab"; - reference156 = new ChangeEntry(EChangeCategory.Added, "UI improvements", list157); - span82[1] = new ChangeEntry(EChangeCategory.Changed, "Renamed 'Clear' button to 'Clear All' in priority window"); - span82[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Rising 2025 Event Quests"); - span82[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed clipboard assigning blacklist to whitelist in 'Duties' tab"); - reference155 = new ChangelogEntry("6.7", releaseDate72, list156); - ref ChangelogEntry reference157 = ref span[72]; - DateOnly releaseDate73 = new DateOnly(2025, 8, 25); - num2 = 2; + Span span79 = CollectionsMarshal.AsSpan(list155); + span79[0] = "FATE combat handling with auto level syncing"; + span79[1] = "Start accepted quests from journal with 'Start as next quest'"; + span79[2] = "Update quest tracking when quests are hidden or prioritised in game"; + span79[3] = "QuestMap as a recommended plugin/dependency"; + reference154 = new ChangeEntry(EChangeCategory.Added, "FATE and quest tracking", list155); + ref ChangeEntry reference155 = ref span78[1]; + num3 = 3; + List list156 = new List(num3); + CollectionsMarshal.SetCount(list156, num3); + Span span80 = CollectionsMarshal.AsSpan(list156); + span80[0] = "Always prioritise next quest during teleportation/zone transitions"; + span80[1] = "Improved accepted quest logic with abandoned quest detection"; + span80[2] = "Show quests without quest paths as Locked"; + reference155 = new ChangeEntry(EChangeCategory.Changed, "Quest prioritisation improvements", list156); + span78[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.35 Deep Dungeon, Hildibrand, Yok Huy, Monster Hunter Wilds Collab, and Doman Enclave quests"); + span78[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed accepted/active quest display and Hildibrand quest issues"); + reference153 = new ChangelogEntry("6.12", releaseDate70, list154); + ref ChangelogEntry reference156 = ref span[70]; + DateOnly releaseDate71 = new DateOnly(2025, 10, 3); + num2 = 1; + List list157 = new List(num2); + CollectionsMarshal.SetCount(list157, num2); + CollectionsMarshal.AsSpan(list157)[0] = new ChangeEntry(EChangeCategory.Changed, "Added remaining checks for quest priority to prevent infinite teleport looping"); + reference156 = new ChangelogEntry("6.11", releaseDate71, list157); + ref ChangelogEntry reference157 = ref span[71]; + DateOnly releaseDate72 = new DateOnly(2025, 10, 2); + num2 = 1; List list158 = new List(num2); CollectionsMarshal.SetCount(list158, num2); - Span span84 = CollectionsMarshal.AsSpan(list158); - ref ChangeEntry reference158 = ref span84[0]; + ref ChangeEntry reference158 = ref CollectionsMarshal.AsSpan(list158)[0]; num3 = 2; List list159 = new List(num3); CollectionsMarshal.SetCount(list159, num3); - Span span85 = CollectionsMarshal.AsSpan(list159); - span85[0] = "Missing emotes to schema and emote handler"; - span85[1] = "Improved stop conditions with 'Clear All' button"; - reference158 = new ChangeEntry(EChangeCategory.Added, "Emote support and stop conditions", list159); - span84[1] = new ChangeEntry(EChangeCategory.Changed, "Stop at level functionality"); - reference157 = new ChangelogEntry("6.6", releaseDate73, list158); - ref ChangelogEntry reference159 = ref span[73]; - DateOnly releaseDate74 = new DateOnly(2025, 8, 25); - num2 = 2; + Span span81 = CollectionsMarshal.AsSpan(list159); + span81[0] = "Don't show quests as available if player doesn't meet level requirements"; + span81[1] = "Updated 'required for MSQ' text in Crystal Tower quest preset window"; + reference158 = new ChangeEntry(EChangeCategory.Changed, "Quest window improvements", list159); + reference157 = new ChangelogEntry("6.10", releaseDate72, list158); + ref ChangelogEntry reference159 = ref span[72]; + DateOnly releaseDate73 = new DateOnly(2025, 9, 21); + num2 = 5; List list160 = new List(num2); CollectionsMarshal.SetCount(list160, num2); - Span span86 = CollectionsMarshal.AsSpan(list160); - span86[0] = new ChangeEntry(EChangeCategory.Fixed, "Potential fix to single/solo duties softlocking"); - span86[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added San d'Oria: The Second Walk and various side quests"); - reference159 = new ChangelogEntry("6.5", releaseDate74, list160); + Span span82 = CollectionsMarshal.AsSpan(list160); + ref ChangeEntry reference160 = ref span82[0]; + num3 = 4; + List list161 = new List(num3); + CollectionsMarshal.SetCount(list161, num3); + Span span83 = CollectionsMarshal.AsSpan(list161); + span83[0] = "Reworked event quest handling - automatically displays when events are active"; + span83[1] = "Reworked journal system with improved filtering and display"; + span83[2] = "Reworked Priority Quests tab (Manual Priority and Quest Presets)"; + span83[3] = "Quest path viewer site (https://wigglymuffin.github.io/FFXIV-Tools/)"; + reference160 = new ChangeEntry(EChangeCategory.Added, "Major system reworks", list161); + ref ChangeEntry reference161 = ref span82[1]; + num3 = 4; + List list162 = new List(num3); + CollectionsMarshal.SetCount(list162, num3); + Span span84 = CollectionsMarshal.AsSpan(list162); + span84[0] = "Questionable.IsQuestCompleted"; + span84[1] = "Questionable.IsQuestAvailable"; + span84[2] = "Questionable.IsQuestAccepted"; + span84[3] = "Questionable.IsQuestUnobtainable"; + reference161 = new ChangeEntry(EChangeCategory.Added, "New IPC commands", list162); + ref ChangeEntry reference162 = ref span82[2]; + num3 = 5; + List list163 = new List(num3); + CollectionsMarshal.SetCount(list163, num3); + Span span85 = CollectionsMarshal.AsSpan(list163); + span85[0] = "Improved JSON quest validation with specific error reasons"; + span85[1] = "Added stop at sequence stop condition"; + span85[2] = "Improved Pandora plugin conflict detection"; + span85[3] = "Improved DialogueChoices regex matching"; + span85[4] = "Improved refresh checker for all quest states"; + reference162 = new ChangeEntry(EChangeCategory.Changed, "Various improvements", list163); + span82[3] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added 7.31 Occult Crescent quests"); + span82[4] = new ChangeEntry(EChangeCategory.Fixed, "Fixed cutscene crashes, Single Player Duty triggers, and various quest issues"); + reference159 = new ChangelogEntry("6.9", releaseDate73, list160); + ref ChangelogEntry reference163 = ref span[73]; + DateOnly releaseDate74 = new DateOnly(2025, 9, 2); + num2 = 4; + List list164 = new List(num2); + CollectionsMarshal.SetCount(list164, num2); + Span span86 = CollectionsMarshal.AsSpan(list164); + ref ChangeEntry reference164 = ref span86[0]; + num3 = 3; + List list165 = new List(num3); + CollectionsMarshal.SetCount(list165, num3); + Span span87 = CollectionsMarshal.AsSpan(list165); + span87[0] = "Help commands and priority quest command"; + span87[1] = "Prevent 'CompleteQuest' step setting"; + span87[2] = "Duty counts and controls in 'Quest Battles' tab"; + reference164 = new ChangeEntry(EChangeCategory.Added, "Command and UI improvements", list165); + span86[1] = new ChangeEntry(EChangeCategory.Changed, "Improved 'Clear All' buttons to require CTRL being held"); + span86[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Zodiac quests and 7.31 Cosmic/Occult Crescent quests"); + span86[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed Fishing for Friendship and Cosmic Exploration quests"); + reference163 = new ChangelogEntry("6.8", releaseDate74, list164); + ref ChangelogEntry reference165 = ref span[74]; + DateOnly releaseDate75 = new DateOnly(2025, 8, 27); + num2 = 4; + List list166 = new List(num2); + CollectionsMarshal.SetCount(list166, num2); + Span span88 = CollectionsMarshal.AsSpan(list166); + ref ChangeEntry reference166 = ref span88[0]; + num3 = 2; + List list167 = new List(num3); + CollectionsMarshal.SetCount(list167, num3); + Span span89 = CollectionsMarshal.AsSpan(list167); + span89[0] = "Icon to 'Clear All' button in stop conditions"; + span89[1] = "Duty counts and 'Enable All' button in 'Duties' tab"; + reference166 = new ChangeEntry(EChangeCategory.Added, "UI improvements", list167); + span88[1] = new ChangeEntry(EChangeCategory.Changed, "Renamed 'Clear' button to 'Clear All' in priority window"); + span88[2] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added Rising 2025 Event Quests"); + span88[3] = new ChangeEntry(EChangeCategory.Fixed, "Fixed clipboard assigning blacklist to whitelist in 'Duties' tab"); + reference165 = new ChangelogEntry("6.7", releaseDate75, list166); + ref ChangelogEntry reference167 = ref span[75]; + DateOnly releaseDate76 = new DateOnly(2025, 8, 25); + num2 = 2; + List list168 = new List(num2); + CollectionsMarshal.SetCount(list168, num2); + Span span90 = CollectionsMarshal.AsSpan(list168); + ref ChangeEntry reference168 = ref span90[0]; + num3 = 2; + List list169 = new List(num3); + CollectionsMarshal.SetCount(list169, num3); + Span span91 = CollectionsMarshal.AsSpan(list169); + span91[0] = "Missing emotes to schema and emote handler"; + span91[1] = "Improved stop conditions with 'Clear All' button"; + reference168 = new ChangeEntry(EChangeCategory.Added, "Emote support and stop conditions", list169); + span90[1] = new ChangeEntry(EChangeCategory.Changed, "Stop at level functionality"); + reference167 = new ChangelogEntry("6.6", releaseDate76, list168); + ref ChangelogEntry reference169 = ref span[76]; + DateOnly releaseDate77 = new DateOnly(2025, 8, 25); + num2 = 2; + List list170 = new List(num2); + CollectionsMarshal.SetCount(list170, num2); + Span span92 = CollectionsMarshal.AsSpan(list170); + span92[0] = new ChangeEntry(EChangeCategory.Fixed, "Potential fix to single/solo duties softlocking"); + span92[1] = new ChangeEntry(EChangeCategory.QuestUpdates, "Added San d'Oria: The Second Walk and various side quests"); + reference169 = new ChangelogEntry("6.5", releaseDate77, list170); Changelogs = list; } } diff --git a/Questionable/Questionable.Data/TerritoryData.cs b/Questionable/Questionable.Data/TerritoryData.cs index ddf1626..b357ee7 100644 --- a/Questionable/Questionable.Data/TerritoryData.cs +++ b/Questionable/Questionable.Data/TerritoryData.cs @@ -198,6 +198,18 @@ internal sealed class TerritoryData : ITerritoryInfo return false; } + public bool IsFieldOperation(uint territoryId) + { + uint value; + bool flag = _dutyTerritories.TryGetValue(territoryId, out value); + if (flag) + { + bool flag2 = ((value == 26 || value == 29 || value == 38) ? true : false); + flag = flag2; + } + return flag; + } + public string? GetInstanceName(uint instanceId) { return _instanceNames.GetValueOrDefault(instanceId); diff --git a/Questionable/Questionable.External/AutoDutyIpc.cs b/Questionable/Questionable.External/AutoDutyIpc.cs index 00d5363..d0ff8f6 100644 --- a/Questionable/Questionable.External/AutoDutyIpc.cs +++ b/Questionable/Questionable.External/AutoDutyIpc.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Dalamud.Plugin; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; @@ -17,6 +19,8 @@ internal sealed class AutoDutyIpc UnsyncRegular } + private readonly IDalamudPluginInterface _pluginInterface; + private readonly Configuration _configuration; private readonly TerritoryData _territoryData; @@ -45,6 +49,7 @@ internal sealed class AutoDutyIpc public AutoDutyIpc(IDalamudPluginInterface pluginInterface, Configuration configuration, TerritoryData territoryData, ILogger logger) { + _pluginInterface = pluginInterface; _configuration = configuration; _territoryData = territoryData; _logger = logger; @@ -210,6 +215,41 @@ internal sealed class AutoDutyIpc } } + public string DescribeInstallation() + { + List list = (from p in _pluginInterface.InstalledPlugins + where p.InternalName.Contains("AutoDuty", StringComparison.OrdinalIgnoreCase) || p.Name.Contains("AutoDuty", StringComparison.OrdinalIgnoreCase) + select $"{p.InternalName} {p.Version} from '{p.Manifest.InstalledFromUrl}' (loaded: {p.IsLoaded}, dev: {p.IsDev})").ToList(); + string value = ((list.Count > 0) ? string.Join("; ", list) : "no AutoDuty plugin installed"); + string value2 = "IPC Run: " + (_run.HasAction ? "registered" : "missing") + ", IsStopped: " + (_isStopped.HasFunction ? "registered" : "missing"); + string value3 = $"GetConfig probe: {ProbeConfig("Questionable.Probe")}, dutyModeEnum: {ProbeConfig("dutyModeEnum")}, LoopTimes: {ProbeConfig("LoopTimes")}, IsStopped: {ProbeIsStopped()}"; + return $"{value}; {value2}; {value3}"; + } + + private string ProbeConfig(string key) + { + try + { + return "'" + _getConfig.InvokeFunc(key) + "'"; + } + catch (IpcError ipcError) + { + return $"<{ipcError.GetType().Name}: {ipcError.Message}>"; + } + } + + private string ProbeIsStopped() + { + try + { + return _isStopped.InvokeFunc().ToString(); + } + catch (IpcError ipcError) + { + return $"<{ipcError.GetType().Name}: {ipcError.Message}>"; + } + } + public bool IsStopped() { try diff --git a/Questionable/Questionable.Navigation/SmartNavReRouteService.cs b/Questionable/Questionable.Navigation/SmartNavReRouteService.cs index 64fbd94..95e2281 100644 --- a/Questionable/Questionable.Navigation/SmartNavReRouteService.cs +++ b/Questionable/Questionable.Navigation/SmartNavReRouteService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using Microsoft.Extensions.Logging; +using Questionable.Controller.CustomDelivery; using Questionable.Controller.Steps; using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Movement; @@ -40,28 +41,32 @@ internal sealed class SmartNavReRouteService(ReRoutePolicy reRoutePolicy, SmartN } } List list = ExtractNonMovementTasks(taskQueue); + if (taskQueue.CurrentTaskExecutor?.CurrentTask is DeliveryNpcApproachTask item) + { + list.Insert(0, item); + } ReRouteDecision reRouteDecision = reRoutePolicy.HandleMovementFailure(failedTargetNodeId, failedApproachNodeId, list.Count > 0); if (!(reRouteDecision is ReRouteDecision.SkipMovement)) { if (reRouteDecision is ReRouteDecision.Replan replan) { taskQueue.Reset(); - foreach (ITask item in taskMapper.MapInstructions(replan.Instructions)) - { - taskQueue.Enqueue(item); - } - foreach (ITask item2 in list) + foreach (ITask item2 in taskMapper.MapInstructions(replan.Instructions)) { taskQueue.Enqueue(item2); } + foreach (ITask item3 in list) + { + taskQueue.Enqueue(item3); + } return true; } return false; } taskQueue.Reset(); - foreach (ITask item3 in list) + foreach (ITask item4 in list) { - taskQueue.Enqueue(item3); + taskQueue.Enqueue(item4); } logger.LogInformation("Re-route: skipping movement with {TaskCount} preserved tasks", list.Count); return true; diff --git a/Questionable/Questionable.Navigation/SmartNavRouteEnqueuer.cs b/Questionable/Questionable.Navigation/SmartNavRouteEnqueuer.cs index a21bd26..7544768 100644 --- a/Questionable/Questionable.Navigation/SmartNavRouteEnqueuer.cs +++ b/Questionable/Questionable.Navigation/SmartNavRouteEnqueuer.cs @@ -63,7 +63,7 @@ internal sealed class SmartNavRouteEnqueuer(NavRouter navRouter, PlayerNavStateB { taskQueue.Enqueue(item); } - reRouteService.ClearDestination(); + reRouteService.SetDestination(territoryId, position); return true; } @@ -156,4 +156,9 @@ internal sealed class SmartNavRouteEnqueuer(NavRouter navRouter, PlayerNavStateB { reRouteService.ClearDestination(); } + + public void SetDestination(uint territoryId, Vector3 position) + { + reRouteService.SetDestination(territoryId, position); + } } diff --git a/Questionable/Questionable.Windows.ConfigComponents/BlacklistConfigComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/BlacklistConfigComponent.cs index e6a1d2a..248621e 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/BlacklistConfigComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/BlacklistConfigComponent.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; @@ -18,6 +19,8 @@ namespace Questionable.Windows.ConfigComponents; internal sealed class BlacklistConfigComponent : ConfigComponent { + private const string ClipboardPrefix = "qst:blacklist:"; + private readonly IDalamudPluginInterface _pluginInterface; private readonly QuestSelector _questSelector; @@ -152,14 +155,62 @@ internal sealed class BlacklistConfigComponent : ConfigComponent } } _questSelector.DrawSelection(); - if (blacklistedQuests.Count > 0 && UiThemeUtils.DestructiveButton(FontAwesomeIcon.Trash, "Clear all")) + DrawClipboardButtons(blacklistedQuests); + if (blacklistedQuests.Count > 0) { - base.Configuration.General.BlacklistedQuests.Clear(); - Save(); + ImGui.SameLine(); + if (UiThemeUtils.DestructiveButton(FontAwesomeIcon.Trash, "Clear all")) + { + base.Configuration.General.BlacklistedQuests.Clear(); + Save(); + } } UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList); } + private void DrawClipboardButtons(HashSet blacklistedQuests) + { + using (ImRaii.Disabled(blacklistedQuests.Count == 0)) + { + if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Copy, "Copy")) + { + string s = string.Join(";", from x in blacklistedQuests + orderby x.ToString() + select x.ToString()); + ImGui.SetClipboardText("qst:blacklist:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s))); + } + } + ImGui.SameLine(); + string text = ThrottledClipboard.GetText(); + using (ImRaii.Disabled(!text.StartsWith("qst:blacklist:", StringComparison.InvariantCulture))) + { + if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Paste, "Paste")) + { + ImportBlacklistedQuests(text); + } + } + } + + private void ImportBlacklistedQuests(string clipboardText) + { + try + { + string s = clipboardText.Substring("qst:blacklist:".Length); + int skippedCount; + List list = ElementId.FromStrings(Encoding.UTF8.GetString(Convert.FromBase64String(s)).Split(";", StringSplitOptions.RemoveEmptyEntries), out skippedCount); + if (list.Count != 0) + { + base.Configuration.General.BlacklistedQuests.Clear(); + base.Configuration.General.BlacklistedQuests.UnionWith(list); + Save(); + } + } + catch (Exception exception) + { + _logger.LogDebug(exception, "Failed to import blacklisted quests from clipboard"); + } + } + private void DrawCurrentlyAcceptedQuests() { List currentlyAcceptedQuests = GetCurrentlyAcceptedQuests(); diff --git a/Questionable/Questionable.Windows.ConfigComponents/DebugConfigComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/DebugConfigComponent.cs index b643ccd..6a7b4fa 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/DebugConfigComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/DebugConfigComponent.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; using System.Numerics; using Dalamud.Bindings.ImGui; @@ -15,12 +14,9 @@ internal sealed class DebugConfigComponent : ConfigComponent { private readonly Dictionary _featurePausingOpenState = new Dictionary(); - private readonly string? _cacheDirectory; - public DebugConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration) : base(pluginInterface, configuration) { - _cacheDirectory = pluginInterface.ConfigDirectory.FullName; } public override void DrawTab() @@ -61,45 +57,8 @@ internal sealed class DebugConfigComponent : ConfigComponent } UiThemeUtils.EndCard(item4, item5, item6); UiThemeUtils.SectionSpacing(); - UiThemeUtils.SectionHeader("Data Cache"); - var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard(); - UiThemeUtils.WrappedText("NPC position and zone boundary caches are rebuilt automatically after game patches. Use this button to force a rebuild on next load."); - bool flag = _cacheDirectory != null && (File.Exists(Path.Combine(_cacheDirectory, "npc-position-cache.bin")) || File.Exists(Path.Combine(_cacheDirectory, "zone-boundary-cache.bin"))); - long num = 0L; - if (_cacheDirectory != null) - { - num += FileSize(Path.Combine(_cacheDirectory, "npc-position-cache.bin")); - num += FileSize(Path.Combine(_cacheDirectory, "zone-boundary-cache.bin")); - } - using (ImRaii.Disabled(!flag)) - { - if (ImGui.Button("Clear LGB Caches") && _cacheDirectory != null) - { - TryDelete(Path.Combine(_cacheDirectory, "npc-position-cache.bin")); - TryDelete(Path.Combine(_cacheDirectory, "zone-boundary-cache.bin")); - } - if (flag && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - { - ImU8String tooltip = new ImU8String(20, 1); - tooltip.AppendLiteral("Current cache size: "); - tooltip.AppendFormatted(FormatBytes(num)); - ImGui.SetTooltip(tooltip); - } - } - if (!flag) - { - ImGui.SameLine(); - ImGui.TextDisabled("(no cache files present)"); - } - else - { - ImGui.SameLine(); - ImGui.TextDisabled("Takes effect on next plugin load."); - } - UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList); - UiThemeUtils.SectionSpacing(); UiThemeUtils.SectionHeader("Danger Zone"); - var (contentStartPos2, availableWidth2, drawList2) = UiThemeUtils.BeginCard(); + var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard(); UiThemeUtils.WrappedTextColored(UiThemeUtils.StatusLocked, "Enabling any option below may cause unexpected behavior. Use at your own risk."); bool value4 = base.Configuration.Advanced.DisablePartyWatchdog; if (UiThemeUtils.WrappedCheckbox("Disable Party Watchdog", ref value4, "The Party Watchdog stops Questionable when entering certain zones with other party members, or when entering unsupported content. Disabling this allows Questionable to continue working while in a party, but may cause unexpected behavior in group content.")) @@ -109,7 +68,7 @@ internal sealed class DebugConfigComponent : ConfigComponent } DrawFeaturePausingSection("Pandora's Box Feature Pausing", PandorasBoxIpc.ConflictingFeatures, "Auto Active Time Maneuver", "Only applies when Auto-Solve QTE is enabled.", base.Configuration.Advanced.PandoraFeatureExclusions); DrawFeaturePausingSection("Bundle of Tweaks Feature Pausing", AutomatonIpc.ConflictingTweaks, "AutoSnipeQuests", "Only applies when AutoSnipe is enabled.", base.Configuration.Advanced.AutomatonTweakExclusions); - UiThemeUtils.EndCard(contentStartPos2, availableWidth2, drawList2); + UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList); } private void DrawFeaturePausingSection(string header, ImmutableHashSet features, string conditionalFeature, string conditionalHelpText, HashSet exclusions) @@ -164,51 +123,4 @@ internal sealed class DebugConfigComponent : ConfigComponent } } } - - private static void TryDelete(string path) - { - try - { - File.Delete(path); - } - catch - { - } - } - - private static long FileSize(string path) - { - try - { - FileInfo fileInfo = new FileInfo(path); - long result; - if (fileInfo != null && fileInfo.Exists) - { - long length = fileInfo.Length; - result = length; - } - else - { - result = 0L; - } - return result; - } - catch - { - return 0L; - } - } - - private static string FormatBytes(long bytes) - { - if (bytes < 1048576) - { - if (bytes >= 1024) - { - return $"{(double)bytes / 1024.0:F1} KB"; - } - return $"{bytes} B"; - } - return $"{(double)bytes / 1048576.0:F1} MB"; - } } diff --git a/Questionable/Questionable.Windows.ConfigComponents/GeneralConfigComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/GeneralConfigComponent.cs index f209ea1..e3aec4c 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/GeneralConfigComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/GeneralConfigComponent.cs @@ -296,7 +296,7 @@ internal sealed class GeneralConfigComponent : ConfigComponent { UiThemeUtils.SectionHeader("Reward redemption"); var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard(); - ImGui.TextWrapped("Quest-reward items in your inventory are used automatically when you accept a quest. Choose which types to redeem."); + ImGui.TextWrapped("Quest-reward items in your inventory are used automatically after quest completion and when you accept a quest. Choose which types to redeem."); ImGui.Spacing(); UiThemeUtils.GridCheckbox[] array = new UiThemeUtils.GridCheckbox[RewardTypeOptions.Length]; for (int i = 0; i < RewardTypeOptions.Length; i++) diff --git a/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs b/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs index f6b115e..d9c3bc3 100644 --- a/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs +++ b/Questionable/Questionable.Windows.ConfigComponents/StopConditionComponent.cs @@ -2,12 +2,15 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface; +using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Questionable.Controller; using Questionable.Controller.Conditions; using Questionable.Data; @@ -21,6 +24,8 @@ namespace Questionable.Windows.ConfigComponents; internal sealed class StopConditionComponent : ConfigComponent { + private const string ClipboardPrefix = "qst:stop:"; + private static readonly string[] ConditionModeNames = new string[2] { "Pause", "Stop" }; private static readonly string[] ConditionTypeNames = new string[7] { "Quest Complete", "Quest Accept", "Level", "Global Sequence", "Inventory Full", "Before Duty", "Gil Threshold" }; @@ -59,6 +64,8 @@ internal sealed class StopConditionComponent : ConfigComponent private long _acceptedQuestsRefreshAtMs; + private StopCondition? _draggedCondition; + public StopConditionComponent(IDalamudPluginInterface pluginInterface, QuestSelector questSelector, QuestFunctions questFunctions, QuestRegistry questRegistry, QuestData questData, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IObjectTable objectTable, QuestController questController, Configuration configuration, ILogger logger) : base(pluginInterface, configuration) { @@ -131,31 +138,120 @@ internal sealed class StopConditionComponent : ConfigComponent private void DrawConditionsList() { List conditions = base.Configuration.Stop.Conditions; + DrawClipboardButtons(conditions); if (conditions.Count == 0) { ImGui.TextDisabled("No conditions configured."); return; } + ImGui.SameLine(); if (UiThemeUtils.DestructiveButton(FontAwesomeIcon.Trash, "Clear all")) { conditions.Clear(); Save(); } int? indexToRemove = null; + StopCondition stopCondition = null; + int index = 0; + float x = ImGui.GetContentRegionAvail().X; + List<(Vector2, Vector2)> list = new List<(Vector2, Vector2)>(); for (int i = 0; i < conditions.Count; i++) { + Vector2 item = ImGui.GetCursorScreenPos() + new Vector2(0f, (0f - ImGui.GetStyle().ItemSpacing.Y) / 2f); using (ImRaii.PushId(i)) { - StopCondition condition = conditions[i]; - DrawConditionRow(condition, i, ref indexToRemove); + StopCondition stopCondition2 = conditions[i]; + if (conditions.Count > 1) + { + ImGuiComponents.IconButton("##Move", FontAwesomeIcon.Bars); + if (_draggedCondition == null && ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) + { + _draggedCondition = stopCondition2; + } + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Drag to reorder"); + } + ImGui.SameLine(); + } + DrawConditionRow(stopCondition2, i, ref indexToRemove); + Vector2 item2 = new Vector2(item.X + x, ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2f); + list.Add((item, item2)); + } + } + if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left)) + { + _draggedCondition = null; + } + else if (_draggedCondition != null) + { + int num = conditions.IndexOf(_draggedCondition); + if (num >= 0) + { + var (pMin, pMax) = list[num]; + ImGui.GetWindowDrawList().AddRect(pMin, pMax, ImGui.ColorConvertFloat4ToU32(UiThemeUtils.CardBorderHighColor), 3f, ImDrawFlags.RoundCornersAll); + int num2 = list.FindIndex(((Vector2 TopLeft, Vector2 BottomRight) tuple2) => ImGui.IsMouseHoveringRect(tuple2.TopLeft, tuple2.BottomRight, clip: true)); + if (num2 >= 0 && num != num2) + { + stopCondition = _draggedCondition; + index = num2; + } } } if (indexToRemove.HasValue) { int valueOrDefault = indexToRemove.GetValueOrDefault(); conditions.RemoveAt(valueOrDefault); + _draggedCondition = null; Save(); } + else if (stopCondition != null) + { + conditions.Remove(stopCondition); + conditions.Insert(index, stopCondition); + Save(); + } + } + + private void DrawClipboardButtons(List conditions) + { + using (ImRaii.Disabled(conditions.Count == 0)) + { + if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Copy, "Copy")) + { + string s = JsonConvert.SerializeObject(conditions); + ImGui.SetClipboardText("qst:stop:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s))); + } + } + ImGui.SameLine(); + string text = ThrottledClipboard.GetText(); + using (ImRaii.Disabled(!text.StartsWith("qst:stop:", StringComparison.InvariantCulture))) + { + if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Paste, "Paste")) + { + ImportConditions(text); + } + } + } + + private void ImportConditions(string clipboardText) + { + try + { + string s = clipboardText.Substring("qst:stop:".Length); + List list = JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(Convert.FromBase64String(s))) ?? new List(); + list.RemoveAll((StopCondition x) => x?.IsSession ?? true); + if (list.Count != 0) + { + base.Configuration.Stop.Conditions.Clear(); + base.Configuration.Stop.Conditions.AddRange(list); + Save(); + } + } + catch (Exception exception) + { + _logger.LogDebug(exception, "Failed to import stop conditions from clipboard"); + } } private void DrawConditionRow(StopCondition condition, int index, ref int? indexToRemove) diff --git a/Questionable/Questionable.Windows.JournalComponents/AttunementJournalComponent.cs b/Questionable/Questionable.Windows.JournalComponents/AttunementJournalComponent.cs index 9cca85b..30dd1a2 100644 --- a/Questionable/Questionable.Windows.JournalComponents/AttunementJournalComponent.cs +++ b/Questionable/Questionable.Windows.JournalComponents/AttunementJournalComponent.cs @@ -125,6 +125,10 @@ internal sealed class AttunementJournalComponent private readonly QuestData _questData; + private readonly QuestRegistry _questRegistry; + + private readonly QuestJournalUtils _questJournalUtils; + private readonly IDataManager _dataManager; private readonly IDalamudPluginInterface _pluginInterface; @@ -219,11 +223,7 @@ internal sealed class AttunementJournalComponent private static readonly int CountPadWidth = 9999.ToString(CultureInfo.CurrentCulture).Length; - public Action? SelectTabAction { get; set; } - - public QuestChainComponent? QuestChainComponent { get; set; } - - public AttunementJournalComponent(AetheryteData aetheryteData, AetherCurrentData aetherCurrentData, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, TerritoryData territoryData, NavRouter navRouter, PlayerNavStateBuilder playerNavStateBuilder, RouteInstructionBuilder instructionBuilder, SmartNavTaskMapper taskMapper, MovementController movementController, AttunementController attunementController, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, QuestData questData, IClientState clientState, IObjectTable objectTable, IDataManager dataManager, IDalamudPluginInterface pluginInterface, ILogger logger) + public AttunementJournalComponent(AetheryteData aetheryteData, AetherCurrentData aetherCurrentData, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, TerritoryData territoryData, NavRouter navRouter, PlayerNavStateBuilder playerNavStateBuilder, RouteInstructionBuilder instructionBuilder, SmartNavTaskMapper taskMapper, MovementController movementController, AttunementController attunementController, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, QuestData questData, QuestRegistry questRegistry, QuestJournalUtils questJournalUtils, IClientState clientState, IObjectTable objectTable, IDataManager dataManager, IDalamudPluginInterface pluginInterface, ILogger logger) { _aetheryteData = aetheryteData; _aetherCurrentData = aetherCurrentData; @@ -243,6 +243,8 @@ internal sealed class AttunementJournalComponent _uiUtils = uiUtils; _questTooltipComponent = questTooltipComponent; _questData = questData; + _questRegistry = questRegistry; + _questJournalUtils = questJournalUtils; _clientState = clientState; _objectTable = objectTable; _dataManager = dataManager; @@ -727,27 +729,14 @@ internal sealed class AttunementJournalComponent label.AppendLiteral("##"); label.AppendFormatted(row.Key); ImGui.Selectable(label); - if (current.QuestId != 0 && ImGui.IsItemHovered() && _questData.TryGetQuestInfo(QuestId.FromRowId(current.QuestId), out IQuestInfo questInfo)) + if (current.QuestId != 0 && _questData.TryGetQuestInfo(QuestId.FromRowId(current.QuestId), out IQuestInfo questInfo)) { - _questTooltipComponent.Draw(questInfo); - } - string text = $"##CurrentQuest_{current.AetherCurrentId}"; - if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - { - ImGui.OpenPopup(text); - } - ImRaii.PopupDisposable popupDisposable = ImRaii.Popup(text); - try - { - if (popupDisposable.Success && ImGui.MenuItem("View Quest") && current.QuestId != 0) + if (ImGui.IsItemHovered()) { - QuestChainComponent?.SelectQuest(QuestId.FromRowId(current.QuestId)); - SelectTabAction?.Invoke("Quest Chain"); + _questTooltipComponent.Draw(questInfo); } - } - finally - { - popupDisposable.Dispose(); + _questRegistry.TryGetQuest(questInfo.QuestId, out Questionable.Model.Quest quest); + _questJournalUtils.ShowContextMenu(questInfo, quest, "AttunementJournalComponent"); } } else @@ -759,13 +748,13 @@ internal sealed class AttunementJournalComponent if (!valueOrDefault) { AetherCurrentPosition overworldPosition = _aetherCurrentData.GetOverworldPosition(current.AetherCurrentId); - string text2 = $"##AttuneCurrent_{current.AetherCurrentId}"; + string text = $"##AttuneCurrent_{current.AetherCurrentId}"; if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) { - ImGui.OpenPopup(text2); + ImGui.OpenPopup(text); } - using ImRaii.PopupDisposable popupDisposable2 = ImRaii.Popup(text2); - if ((bool)popupDisposable2) + using ImRaii.PopupDisposable popupDisposable = ImRaii.Popup(text); + if ((bool)popupDisposable) { bool flag = overworldPosition != null && !IsAnyControllerRunning(); using (ImRaii.Disabled(!flag)) diff --git a/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs b/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs index 57238ed..6675767 100644 --- a/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs +++ b/Questionable/Questionable.Windows.JournalComponents/QuestJournalUtils.cs @@ -43,6 +43,8 @@ internal sealed class QuestJournalUtils private long _nextAvailableCountRefreshMs; + public Action? OpenJournal { get; set; } + public QuestJournalUtils(QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestFunctions questFunctions, QuestData questData, QuestRegistry questRegistry, QuestChainComponent questChainComponent, IChatGui chatGui, ILogger logger) { _questController = questController; @@ -89,12 +91,7 @@ internal sealed class QuestJournalUtils { if (ImGui.MenuItem("Start as next quest")) { - _fateController.Stop("Quest journal start"); - _seasonalDutyController.Stop("Quest journal start"); - _customDeliveryController.Stop("Quest journal start"); - _attunementController.Stop("Quest journal start"); - _questController.SetNextQuest(quest); - _questController.Start(label); + StartQuestAsNext(quest, label); } } if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) @@ -138,12 +135,24 @@ internal sealed class QuestJournalUtils } if (label != "QuestChainComponent" && ImGui.MenuItem("View in Quest Chain")) { + OpenJournal?.Invoke(); _questChainComponent.SelectQuest(questInfo.QuestId); _questChainComponent.SelectTabAction?.Invoke("Quest Chain"); } return true; } + public void StartQuestAsNext(Quest quest, string label) + { + string label2 = label + " start"; + _fateController.Stop(label2); + _seasonalDutyController.Stop(label2); + _customDeliveryController.Stop(label2); + _attunementController.Stop(label2); + _questController.SetNextQuest(quest); + _questController.Start(label); + } + public unsafe List GetIncompletePrerequisiteQuests(IQuestInfo questInfo) { List list = new List(); diff --git a/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs b/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs index 17b9d2a..bbb49db 100644 --- a/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs +++ b/Questionable/Questionable.Windows.QuestComponents/EventInfoComponent.cs @@ -15,6 +15,7 @@ using Questionable.Data; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; +using Questionable.Windows.JournalComponents; namespace Questionable.Windows.QuestComponents; @@ -42,6 +43,8 @@ internal sealed class EventInfoComponent private readonly QuestTooltipComponent _questTooltipComponent; + private readonly QuestJournalUtils _questJournalUtils; + private readonly Configuration _configuration; private readonly IDataManager _dataManager; @@ -71,7 +74,7 @@ internal sealed class EventInfoComponent } } - public EventInfoComponent(QuestData questData, QuestRegistry questRegistry, QuestFunctions questFunctions, UiUtils uiUtils, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestTooltipComponent questTooltipComponent, Configuration configuration, IDataManager dataManager, JournalData journalData, ILogger logger) + public EventInfoComponent(QuestData questData, QuestRegistry questRegistry, QuestFunctions questFunctions, UiUtils uiUtils, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestTooltipComponent questTooltipComponent, QuestJournalUtils questJournalUtils, Configuration configuration, IDataManager dataManager, JournalData journalData, ILogger logger) { _questData = questData; _questRegistry = questRegistry; @@ -83,6 +86,7 @@ internal sealed class EventInfoComponent _customDeliveryController = customDeliveryController; _attunementController = attunementController; _questTooltipComponent = questTooltipComponent; + _questJournalUtils = questJournalUtils; _configuration = configuration; _dataManager = dataManager; _journalData = journalData; @@ -194,12 +198,7 @@ internal sealed class EventInfoComponent { if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { - _fateController.Stop("Seasonal event start"); - _seasonalDutyController.Stop("Seasonal event start"); - _customDeliveryController.Stop("Seasonal event start"); - _attunementController.Stop("Seasonal event start"); - _questController.SetNextQuest(quest); - _questController.Start("SeasonalEventSelection"); + _questJournalUtils.StartQuestAsNext(quest, "SeasonalEventSelection"); } } bool num = ImGui.IsItemHovered(); diff --git a/Questionable/Questionable.Windows.QuestComponents/SavedPresetsComponent.cs b/Questionable/Questionable.Windows.QuestComponents/SavedPresetsComponent.cs index e147651..d34f750 100644 --- a/Questionable/Questionable.Windows.QuestComponents/SavedPresetsComponent.cs +++ b/Questionable/Questionable.Windows.QuestComponents/SavedPresetsComponent.cs @@ -94,7 +94,13 @@ internal sealed class SavedPresetsComponent { DrawSaveSection(); UiThemeUtils.SectionSpacing(); - DrawSavedPresets(); + using (ImRaii.ChildDisposable childDisposable = ImRaii.Child("SavedPresetsList", new Vector2(-1f, 300f), border: true, ImGuiWindowFlags.AlwaysVerticalScrollbar)) + { + if ((bool)childDisposable) + { + DrawSavedPresets(); + } + } UiThemeUtils.SectionSpacing(); DrawBottomButtons(); } diff --git a/Questionable/Questionable.Windows/JournalProgressWindow.cs b/Questionable/Questionable.Windows/JournalProgressWindow.cs index 9cf9de5..7aec422 100644 --- a/Questionable/Questionable.Windows/JournalProgressWindow.cs +++ b/Questionable/Questionable.Windows/JournalProgressWindow.cs @@ -91,13 +91,15 @@ internal sealed class JournalProgressWindow : ThemedWindow, IDisposable _pluginInterface = pluginInterface; _configuration = configuration; _pages = new Action[9] { _questJournalComponent.DrawQuests, _dutyJournalComponent.DrawDuties, _attunementJournalComponent.DrawAttunement, _gatheringJournalComponent.DrawGatheringItems, _questRewardComponent.DrawItemRewards, _questMapComponent.DrawQuestMap, _questChainComponent.DrawQuestChain, _alliedSocietyJournalComponent.DrawAlliedSocietyQuests, _customDeliveryJournalComponent.DrawCustomDeliveries }; + questJournalUtils.OpenJournal = delegate + { + base.IsOpen = true; + }; _questChainComponent.SelectTabAction = SelectTab; _questChainComponent.QuestJournalUtils = questJournalUtils; _questMapComponent.SelectTabAction = SelectTab; _questMapComponent.QuestChainComponent = _questChainComponent; _questMapComponent.QuestJournalUtils = questJournalUtils; - _attunementJournalComponent.SelectTabAction = SelectTab; - _attunementJournalComponent.QuestChainComponent = _questChainComponent; _clientState.Login += _questJournalComponent.RefreshCounts; _clientState.Logout += _dutyJournalComponent.ClearCounts; _clientState.Login += _gatheringJournalComponent.RefreshCounts; diff --git a/Questionable/Questionable.Windows/QuestSelectionWindow.cs b/Questionable/Questionable.Windows/QuestSelectionWindow.cs index b893deb..7eadcbb 100644 --- a/Questionable/Questionable.Windows/QuestSelectionWindow.cs +++ b/Questionable/Questionable.Windows/QuestSelectionWindow.cs @@ -18,6 +18,7 @@ using Questionable.Data; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; +using Questionable.Windows.JournalComponents; using Questionable.Windows.QuestComponents; namespace Questionable.Windows; @@ -56,6 +57,8 @@ internal sealed class QuestSelectionWindow : ThemedWindow private readonly QuestTooltipComponent _questTooltipComponent; + private readonly QuestJournalUtils _questJournalUtils; + private List _quests = new List(); private List _offeredQuests = new List(); @@ -64,7 +67,7 @@ internal sealed class QuestSelectionWindow : ThemedWindow private string _searchText = string.Empty; - public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, QuestFunctions questFunctions, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface, TerritoryData territoryData, IClientState clientState, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent) + public QuestSelectionWindow(QuestData questData, IGameGui gameGui, IChatGui chatGui, QuestFunctions questFunctions, QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestRegistry questRegistry, IDalamudPluginInterface pluginInterface, TerritoryData territoryData, IClientState clientState, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, QuestJournalUtils questJournalUtils) : base("Quest Selection###QuestionableQuestSelection") { _questData = questData; @@ -82,6 +85,7 @@ internal sealed class QuestSelectionWindow : ThemedWindow _clientState = clientState; _uiUtils = uiUtils; _questTooltipComponent = questTooltipComponent; + _questJournalUtils = questJournalUtils; base.Size = new Vector2(500f, 200f); base.SizeCondition = ImGuiCond.Once; base.SizeConstraints = new WindowSizeConstraints @@ -214,6 +218,7 @@ internal sealed class QuestSelectionWindow : ThemedWindow } } UiThemeUtils.RowLabel(item.Name, 0f); + _questJournalUtils.ShowContextMenu(item, quest, "QuestSelectionWindow"); } if (!ImGui.TableNextColumn()) { @@ -266,16 +271,11 @@ internal sealed class QuestSelectionWindow : ThemedWindow } if (flag2) { - _fateController.Stop("Quest selection start"); - _seasonalDutyController.Stop("Quest selection start"); - _customDeliveryController.Stop("Quest selection start"); - _attunementController.Stop("Quest selection start"); - _questController.SetNextQuest(quest); if (!_questController.ManualPriorityQuests.Contains(quest)) { _questController.ManualPriorityQuests.Insert(0, quest); } - _questController.Start("QuestSelectionWindow"); + _questJournalUtils.StartQuestAsNext(quest, "QuestSelectionWindow"); } ImGui.SameLine(); bool num2 = UiThemeUtils.IconButton(FontAwesomeIcon.AngleDoubleRight, ImGui.GetFrameHeight()); diff --git a/Questionable/Questionable.csproj b/Questionable/Questionable.csproj index d7a26d1..d7e961c 100644 --- a/Questionable/Questionable.csproj +++ b/Questionable/Questionable.csproj @@ -61,6 +61,9 @@ C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\InteropGenerator.Runtime.dll + + ..\..\SmartNav.Data.dll + ..\..\Microsoft.Extensions.Logging.dll diff --git a/Questionable/Questionable/QuestionablePlugin.cs b/Questionable/Questionable/QuestionablePlugin.cs index 7c4d256..d07e074 100644 --- a/Questionable/Questionable/QuestionablePlugin.cs +++ b/Questionable/Questionable/QuestionablePlugin.cs @@ -180,6 +180,7 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable _pluginInterface.SavePluginConfig(configuration); } serviceCollection.AddSingleton(configuration); + MigrateLegacyConfigDirectoryFiles(); AddBasicFunctionsAndData(serviceCollection); AddTaskFactories(serviceCollection); AddControllers(serviceCollection); @@ -218,6 +219,59 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable } } + private void MigrateLegacyConfigDirectoryFiles() + { + string[] obj = new string[5] { "npc-position-cache.bin", "zone-boundary-cache.bin", "lgb-worker-manifest.json", "npc-position-cache.bin.tmp", "zone-boundary-cache.bin.tmp" }; + bool flag = false; + string[] array = obj; + foreach (string path in array) + { + try + { + FileInfo fileInfo = new FileInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, path)); + if (fileInfo.Exists) + { + fileInfo.Delete(); + flag = true; + } + } + catch (Exception) + { + } + } + if (flag) + { + _pluginLog.Debug("Deleted legacy LGB worker cache files from the config directory"); + } + string text = Path.Combine(_pluginInterface.ConfigDirectory.FullName, "nav-overrides"); + array = new string[2] { "warp-destinations.json", "teleport-tickets.json" }; + foreach (string path2 in array) + { + try + { + FileInfo fileInfo2 = new FileInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, path2)); + if (fileInfo2.Exists) + { + string text2 = Path.Combine(text, path2); + if (File.Exists(text2)) + { + _pluginLog.Warning($"Not moving legacy override file {fileInfo2.FullName}: {text2} already exists"); + } + else + { + string fullName = fileInfo2.FullName; + Directory.CreateDirectory(text); + fileInfo2.MoveTo(text2); + _pluginLog.Debug("Moved legacy override file " + fullName + " to " + text2); + } + } + } + catch (Exception) + { + } + } + } + private static void AddBasicFunctionsAndData(ServiceCollection serviceCollection) { serviceCollection.AddSingleton(); @@ -264,13 +318,14 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable IDalamudPluginInterface requiredService = sp.GetRequiredService(); DirectoryInfo devSourceDirectory = null; sp.GetRequiredService().GameData.Repositories.TryGetValue("ffxiv", out Repository value); - return new SmartNavDataOptions(requiredService.ConfigDirectory, devSourceDirectory, value?.Version); + return new SmartNavDataOptions(new DirectoryInfo(Path.Combine(requiredService.ConfigDirectory.FullName, "nav-overrides")), devSourceDirectory, value?.Version); }); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - ((IServiceCollection)serviceCollection).AddSingleton((Func)((IServiceProvider sp) => new LgbZoneBoundarySource(sp.GetRequiredService(), sp.GetRequiredService>(), sp.GetRequiredService()))); + serviceCollection.AddSingleton((IServiceProvider sp) => new LgbZoneBoundarySource(sp.GetRequiredService>(), sp.GetRequiredService())); + ((IServiceCollection)serviceCollection).AddSingleton((Func)((IServiceProvider sp) => sp.GetRequiredService())); ((IServiceCollection)serviceCollection).AddSingleton((Func)((IServiceProvider sp) => sp.GetRequiredService())); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -382,9 +437,11 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); + serviceCollection.AddTaskFactoryAndExecutor(); serviceCollection.AddTaskFactory(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); + serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); serviceCollection.AddTaskExecutor(); @@ -413,14 +470,10 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable sp.GetRequiredService().GameData.Repositories.TryGetValue("ffxiv", out Repository value); return new NpcPositionCacheOptions { - CacheDirectory = sp.GetRequiredService().ConfigDirectory.FullName, - GameVersion = value?.Version, - ScanThrottle = TimeSpan.FromMilliseconds(10L) + GameVersion = value?.Version }; }); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -539,7 +592,27 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable { ILogger requiredService = serviceProvider.GetRequiredService>(); Stopwatch stopwatch = Stopwatch.StartNew(); - serviceProvider.GetRequiredService().TryLoadFromDisk(); + try + { + Stopwatch stopwatch2 = Stopwatch.StartNew(); + bool flag; + using (Stream stream = AssemblyLgbCacheLoader.OpenNpcPositionCache()) + { + flag = serviceProvider.GetRequiredService().TryLoadFromStream(stream, acceptStaleVersion: true); + } + if (flag) + { + requiredService.LogInformation("NPC position cache hydrated in {Duration}ms", (long)stopwatch2.Elapsed.TotalMilliseconds); + } + else + { + requiredService.LogError("Embedded NPC position cache was rejected (bad magic, version or truncated) - derived warp sources, mender and vendor resolution are unavailable this session"); + } + } + catch (Exception exception) + { + requiredService.LogError(exception, "Unable to hydrate the embedded NPC position cache"); + } InlineArray12 buffer = default(InlineArray12); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, Task>(ref buffer, 0) = Task.Run(() => serviceProvider.GetRequiredService()); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, Task>(ref buffer, 1) = Task.Run(() => serviceProvider.GetRequiredService()); @@ -578,7 +651,6 @@ public sealed class QuestionablePlugin : IAsyncDalamudPlugin, IAsyncDisposable serviceProvider.GetRequiredService(); serviceProvider.GetRequiredService(); serviceProvider.GetRequiredService(); - serviceProvider.GetRequiredService(); ChangelogWindow requiredService2 = serviceProvider.GetRequiredService(); Configuration requiredService3 = serviceProvider.GetRequiredService(); if (requiredService3.IsPluginSetupComplete() && requiredService3.General.ShowChangelogOnUpdate) diff --git a/SeasonalDutyPaths/Questionable.SeasonalDutyPaths.SeasonalDutyBundle b/SeasonalDutyPaths/Questionable.SeasonalDutyPaths.SeasonalDutyBundle index 0a93c5d..6f54328 100644 Binary files a/SeasonalDutyPaths/Questionable.SeasonalDutyPaths.SeasonalDutyBundle and b/SeasonalDutyPaths/Questionable.SeasonalDutyPaths.SeasonalDutyBundle differ diff --git a/SeasonalDutyPaths/Questionable.SeasonalDutyPaths/PathBundleSecret.cs b/SeasonalDutyPaths/Questionable.SeasonalDutyPaths/PathBundleSecret.cs index fac6799..26096e8 100644 --- a/SeasonalDutyPaths/Questionable.SeasonalDutyPaths/PathBundleSecret.cs +++ b/SeasonalDutyPaths/Questionable.SeasonalDutyPaths/PathBundleSecret.cs @@ -4,18 +4,18 @@ internal static class PathBundleSecret { private static readonly byte[] A = new byte[32] { - 251, 250, 177, 24, 254, 78, 188, 97, 60, 65, - 105, 80, 183, 91, 229, 181, 77, 98, 171, 136, - 116, 248, 217, 243, 101, 83, 97, 216, 103, 115, - 176, 184 + 192, 237, 68, 154, 123, 161, 164, 81, 179, 101, + 208, 145, 185, 118, 184, 66, 42, 173, 202, 110, + 201, 113, 73, 242, 250, 58, 135, 189, 143, 92, + 151, 77 }; private static readonly byte[] B = new byte[32] { - 10, 142, 23, 61, 218, 98, 70, 165, 207, 244, - 222, 75, 120, 153, 93, 56, 21, 57, 47, 72, - 65, 245, 253, 11, 142, 198, 115, 115, 202, 36, - 6, 70 + 239, 73, 204, 3, 150, 201, 87, 43, 137, 223, + 14, 253, 125, 35, 109, 186, 32, 12, 130, 132, + 201, 118, 255, 168, 253, 104, 107, 98, 213, 28, + 83, 69 }; internal static byte[] Key() diff --git a/SmartNav.Data/SmartNav.Data.chocobo-taxi-stands.bin b/SmartNav.Data/SmartNav.Data.chocobo-taxi-stands.bin index 7f11366..668da49 100644 Binary files a/SmartNav.Data/SmartNav.Data.chocobo-taxi-stands.bin and b/SmartNav.Data/SmartNav.Data.chocobo-taxi-stands.bin differ diff --git a/SmartNav.Data/SmartNav.Data.csproj b/SmartNav.Data/SmartNav.Data.csproj index 33a608b..3eaf940 100644 --- a/SmartNav.Data/SmartNav.Data.csproj +++ b/SmartNav.Data/SmartNav.Data.csproj @@ -18,11 +18,15 @@ + + + + diff --git a/SmartNav.Data/SmartNav.Data.derived-arrivals.bin b/SmartNav.Data/SmartNav.Data.derived-arrivals.bin index c802c27..fc16578 100644 Binary files a/SmartNav.Data/SmartNav.Data.derived-arrivals.bin and b/SmartNav.Data/SmartNav.Data.derived-arrivals.bin differ diff --git a/SmartNav.Data/SmartNav.Data.npc-position-cache.bin b/SmartNav.Data/SmartNav.Data.npc-position-cache.bin new file mode 100644 index 0000000..e0d05ff Binary files /dev/null and b/SmartNav.Data/SmartNav.Data.npc-position-cache.bin differ diff --git a/SmartNav.Data/SmartNav.Data.teleport-tickets.bin b/SmartNav.Data/SmartNav.Data.teleport-tickets.bin index 287cbd2..b11869b 100644 Binary files a/SmartNav.Data/SmartNav.Data.teleport-tickets.bin and b/SmartNav.Data/SmartNav.Data.teleport-tickets.bin differ diff --git a/SmartNav.Data/SmartNav.Data.warp-destinations.bin b/SmartNav.Data/SmartNav.Data.warp-destinations.bin index 71ffb4b..9315cb2 100644 Binary files a/SmartNav.Data/SmartNav.Data.warp-destinations.bin and b/SmartNav.Data/SmartNav.Data.warp-destinations.bin differ diff --git a/SmartNav.Data/SmartNav.Data.zone-boundary-cache.bin b/SmartNav.Data/SmartNav.Data.zone-boundary-cache.bin new file mode 100644 index 0000000..2b3bd58 Binary files /dev/null and b/SmartNav.Data/SmartNav.Data.zone-boundary-cache.bin differ diff --git a/SmartNav.Data/SmartNav.Data.zone-sub-regions.bin b/SmartNav.Data/SmartNav.Data.zone-sub-regions.bin index 22610a4..e82fa48 100644 Binary files a/SmartNav.Data/SmartNav.Data.zone-sub-regions.bin and b/SmartNav.Data/SmartNav.Data.zone-sub-regions.bin differ diff --git a/SmartNav.Data/SmartNav.Data/AssemblyLgbCacheLoader.cs b/SmartNav.Data/SmartNav.Data/AssemblyLgbCacheLoader.cs new file mode 100644 index 0000000..c73c41f --- /dev/null +++ b/SmartNav.Data/SmartNav.Data/AssemblyLgbCacheLoader.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace SmartNav.Data; + +public static class AssemblyLgbCacheLoader +{ + public static Stream OpenNpcPositionCache() + { + return SmartNavResources.OpenSealed("npc-position-cache.bin"); + } + + public static Stream OpenZoneBoundaryCache() + { + return SmartNavResources.OpenSealed("zone-boundary-cache.bin"); + } +} diff --git a/SmartNav.Data/SmartNav.Data/DataBundleSecret.cs b/SmartNav.Data/SmartNav.Data/DataBundleSecret.cs index 407b0bc..bc2854f 100644 --- a/SmartNav.Data/SmartNav.Data/DataBundleSecret.cs +++ b/SmartNav.Data/SmartNav.Data/DataBundleSecret.cs @@ -4,18 +4,18 @@ internal static class DataBundleSecret { private static readonly byte[] A = new byte[32] { - 249, 130, 120, 19, 233, 226, 108, 14, 107, 82, - 130, 220, 135, 231, 132, 145, 238, 79, 114, 87, - 123, 65, 202, 110, 156, 178, 236, 146, 151, 20, - 187, 222 + 13, 38, 21, 255, 71, 40, 19, 213, 79, 201, + 20, 24, 146, 227, 159, 156, 142, 23, 138, 144, + 119, 132, 179, 149, 190, 134, 23, 209, 204, 192, + 234, 63 }; private static readonly byte[] B = new byte[32] { - 4, 22, 39, 195, 114, 48, 170, 103, 209, 88, - 134, 221, 112, 108, 144, 135, 150, 118, 49, 201, - 74, 13, 92, 179, 155, 25, 114, 34, 102, 151, - 79, 34 + 252, 191, 81, 239, 79, 239, 196, 247, 66, 179, + 73, 171, 175, 76, 172, 241, 89, 23, 110, 4, + 105, 193, 2, 137, 162, 77, 122, 136, 209, 181, + 229, 197 }; internal static byte[] Key() diff --git a/SmartNav.Data/SmartNav.Data/SmartNavResources.cs b/SmartNav.Data/SmartNav.Data/SmartNavResources.cs index c231172..6c76efe 100644 --- a/SmartNav.Data/SmartNav.Data/SmartNavResources.cs +++ b/SmartNav.Data/SmartNav.Data/SmartNavResources.cs @@ -32,13 +32,32 @@ internal static class SmartNavResources private static T? DeserializeResource(string resourceName) { - string name = Path.ChangeExtension(resourceName, ".bin"); - using Stream stream = Assembly.GetManifestResourceStream(name); - if (stream == null) + byte[] array = Unseal(Path.ChangeExtension(resourceName, ".bin")); + if (array == null) { return default(T); } - using MemoryStream memoryStream = new MemoryStream(); + ReadOnlySpan readOnlySpan = array; + if (readOnlySpan.StartsWith("\ufeff"u8)) + { + readOnlySpan = readOnlySpan.Slice(3); + } + return JsonSerializer.Deserialize(readOnlySpan); + } + + public static Stream OpenSealed(string fileName) + { + return new MemoryStream(Unseal("SmartNav.Data." + fileName) ?? throw new InvalidOperationException("embedded resource '" + fileName + "' is missing"), writable: false); + } + + private static byte[]? Unseal(string sealedName) + { + using Stream stream = Assembly.GetManifestResourceStream(sealedName); + if (stream == null) + { + return null; + } + using MemoryStream memoryStream = new MemoryStream((int)stream.Length); stream.CopyTo(memoryStream); byte[] array = memoryStream.ToArray(); byte[] array2 = new byte[array.Length - 28]; @@ -52,7 +71,9 @@ internal static class SmartNavResources { CryptographicOperations.ZeroMemory(array3); } - using DeflateStream utf8Json = new DeflateStream(new MemoryStream(array2), CompressionMode.Decompress); - return JsonSerializer.Deserialize(utf8Json); + using DeflateStream deflateStream = new DeflateStream(new MemoryStream(array2), CompressionMode.Decompress); + using MemoryStream memoryStream2 = new MemoryStream(array2.Length * 4); + deflateStream.CopyTo(memoryStream2); + return memoryStream2.ToArray(); } } diff --git a/SmartNav.Model/SmartNav.Model/EAetheryteLocation.cs b/SmartNav.Model/SmartNav.Model/EAetheryteLocation.cs index b0b9b26..476981b 100644 --- a/SmartNav.Model/SmartNav.Model/EAetheryteLocation.cs +++ b/SmartNav.Model/SmartNav.Model/EAetheryteLocation.cs @@ -217,6 +217,7 @@ public enum EAetheryteLocation TuliyollalIhuykatumu = 227, TuliyollalDirigibleLandingYakTel = 228, TuliyollalXakTuralSkygate = 229, + TuliyollalPhantomVillage = 239, SolutionNine = 217, SolutionNineInformationCenter = 230, SolutionNineTrueVue = 231, diff --git a/SmartNav/SmartNav.Data/AetheryteData.cs b/SmartNav/SmartNav.Data/AetheryteData.cs index d165245..1a18a2d 100644 --- a/SmartNav/SmartNav.Data/AetheryteData.cs +++ b/SmartNav/SmartNav.Data/AetheryteData.cs @@ -308,6 +308,10 @@ public sealed class AetheryteData EAetheryteLocation.IshgardGatesOfJudgement, new Vector3(-160.8786f, 304.1538f, -322.6239f) }, + { + EAetheryteLocation.IshgardFirmament, + new Vector3(9.92315f, -15.2f, 173.5059f) + }, { EAetheryteLocation.Idyllshire, new Vector3(71.94617f, 211.26111f, -18.905945f) @@ -824,6 +828,10 @@ public sealed class AetheryteData EAetheryteLocation.TuliyollalXakTuralSkygate, new Vector3(284.959f, 15.999984f, 771.9063f) }, + { + EAetheryteLocation.TuliyollalPhantomVillage, + new Vector3(43.3027f, 0.0199971f, -1.07516f) + }, { EAetheryteLocation.SolutionNine, new Vector3(-0.015319824f, 8.987488f, -0.015319824f) diff --git a/SmartNav/SmartNav.Data/LgbZoneBoundarySource.cs b/SmartNav/SmartNav.Data/LgbZoneBoundarySource.cs index 6861f7a..6c402b4 100644 --- a/SmartNav/SmartNav.Data/LgbZoneBoundarySource.cs +++ b/SmartNav/SmartNav.Data/LgbZoneBoundarySource.cs @@ -2,13 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Numerics; -using System.Threading; -using Dalamud.Plugin.Services; -using LLib.GameData; -using Lumina.Data.Files; -using Lumina.Data.Parsing.Layer; -using Lumina.Excel; -using Lumina.Excel.Sheets; +using System.Text; using Microsoft.Extensions.Logging; using SmartNav.Model.Navigation; @@ -16,51 +10,61 @@ namespace SmartNav.Data; public sealed class LgbZoneBoundarySource : IZoneBoundarySource { + private sealed record LgbData(List ExitRanges, Dictionary<(ushort TerritoryId, uint InstanceId), PopRangeEntry> PopRangeIndex); + private const uint BoundaryFileMagic = 1112425562u; private const int BoundaryFileFormatVersion = 1; private const string CacheFileName = "zone-boundary-cache.bin"; - private readonly IDataManager _dataManager; + private const int MaxPreallocatedEntries = 131072; private readonly ILogger _logger; - private readonly Lazy> _boundaries; - - private readonly string? _cacheDirectory; + private readonly string? _overrideDirectory; private readonly string? _gameVersion; - public LgbZoneBoundarySource(IDataManager dataManager, ILogger logger, SmartNavDataOptions? dataOptions = null) + private readonly object _buildLock = new object(); + + private IReadOnlyList? _boundaries; + + public LgbZoneBoundarySource(ILogger logger, SmartNavDataOptions? dataOptions = null) { - _dataManager = dataManager; _logger = logger; - _cacheDirectory = dataOptions?.UserOverrideDirectory?.FullName; + _overrideDirectory = dataOptions?.UserOverrideDirectory?.FullName; _gameVersion = dataOptions?.GameVersion; - _boundaries = new Lazy>(Build, LazyThreadSafetyMode.ExecutionAndPublication); } public IReadOnlyList GetBoundaries() { - return _boundaries.Value; + lock (_buildLock) + { + return _boundaries ?? (_boundaries = Build()); + } + } + + public void Invalidate() + { + lock (_buildLock) + { + _boundaries = null; + } } private IReadOnlyList Build() { try { - (List, Dictionary<(ushort, uint), PopRangeEntry>)? tuple = TryLoadFromDisk(); - if (!tuple.HasValue) + var (lgbData, text) = LoadRawData(); + if (lgbData == null) { - _logger.LogInformation("No boundary cache found, falling back to in-process LGB scan"); + return Array.Empty(); } - (List, Dictionary<(ushort, uint), PopRangeEntry>) obj = tuple ?? CollectLgbData(); - List item = obj.Item1; - Dictionary<(ushort, uint), PopRangeEntry> item2 = obj.Item2; - List list = ZoneBoundaryDerivation.Derive(item, item2, _logger); + List list = ZoneBoundaryDerivation.Derive(lgbData.ExitRanges, lgbData.PopRangeIndex, _logger); ZoneBoundaryDerivation.ApplyOverrides(list, ZoneBoundaryOverrides.FlyingPairs, ZoneBoundaryOverrides.PositionOverrides, _logger); - _logger.LogDebug("Derived {Count} zone boundaries from {ExitCount} ExitRanges{Source}", list.Count, item.Count, tuple.HasValue ? " (from cache)" : ""); + _logger.LogDebug("Derived {Count} zone boundaries from {ExitCount} ExitRanges ({Source})", list.Count, lgbData.ExitRanges.Count, text); return list; } catch (Exception exception) @@ -70,171 +74,95 @@ public sealed class LgbZoneBoundarySource : IZoneBoundarySource } } - private (List ExitRanges, Dictionary<(ushort TerritoryId, uint InstanceId), PopRangeEntry> PopRangeIndex)? TryLoadFromDisk() + private (LgbData? Data, string Source) LoadRawData() { - if (string.IsNullOrEmpty(_cacheDirectory) || string.IsNullOrEmpty(_gameVersion)) + LgbData lgbData = TryLoadOverrideFile("override file"); + if (lgbData != null) + { + return (Data: lgbData, Source: "override file"); + } + return (Data: TryLoadEmbedded("embedded resource"), Source: "embedded resource"); + } + + private LgbData? TryLoadOverrideFile(string source) + { + if (string.IsNullOrEmpty(_overrideDirectory)) { return null; } - string path = Path.Combine(_cacheDirectory, "zone-boundary-cache.bin"); - if (!File.Exists(path)) + string text = Path.Combine(_overrideDirectory, "zone-boundary-cache.bin"); + if (!File.Exists(text)) { return null; } try { - using BinaryReader binaryReader = new BinaryReader(File.OpenRead(path)); - if (binaryReader.ReadUInt32() != 1112425562 || binaryReader.ReadInt32() != 1) + using Stream stream = File.OpenRead(text); + LgbData lgbData = ReadCache(stream, source); + if (lgbData.ExitRanges.Count == 0 && lgbData.PopRangeIndex.Count == 0) { - _logger.LogDebug("Zone boundary cache has unknown header, discarding"); + _logger.LogWarning("Zone boundary {Source} {Path} holds no exits and no pops, ignoring it and using the embedded data", source, text); return null; } - if (binaryReader.ReadString() != _gameVersion) - { - _logger.LogDebug("Zone boundary cache is for a different game version, discarding"); - return null; - } - int num = binaryReader.ReadInt32(); - List list = new List(num); - for (int i = 0; i < num; i++) - { - list.Add(new ExitRangeEntry(binaryReader.ReadUInt16(), binaryReader.ReadUInt32(), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()), binaryReader.ReadUInt16(), binaryReader.ReadUInt32(), binaryReader.ReadUInt32(), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()))); - } - int num2 = binaryReader.ReadInt32(); - Dictionary<(ushort, uint), PopRangeEntry> dictionary = new Dictionary<(ushort, uint), PopRangeEntry>(num2); - for (int j = 0; j < num2; j++) - { - ushort num3 = binaryReader.ReadUInt16(); - uint num4 = binaryReader.ReadUInt32(); - Vector3 position = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); - dictionary[(num3, num4)] = new PopRangeEntry(num3, num4, position); - } - _logger.LogDebug("Zone boundary cache loaded from disk ({ExitCount} exits, {PopCount} pops)", list.Count, dictionary.Count); - return (list, dictionary); + return lgbData; } catch (Exception exception) { - _logger.LogWarning(exception, "Failed to load zone boundary cache, falling back to LGB scan"); + _logger.LogWarning(exception, "Failed to read zone boundary {Source}, falling back to the embedded data", source); + return null; } + } + + private LgbData? TryLoadEmbedded(string source) + { try { - File.Delete(path); + using Stream stream = AssemblyLgbCacheLoader.OpenZoneBoundaryCache(); + return ReadCache(stream, source); } - catch + catch (Exception exception) { - } - return null; - } - - private (List ExitRanges, Dictionary<(ushort TerritoryId, uint InstanceId), PopRangeEntry> PopRangeIndex) CollectLgbData() - { - List list = new List(); - Dictionary<(ushort, uint), PopRangeEntry> dictionary = new Dictionary<(ushort, uint), PopRangeEntry>(); - HashSet hashSet = new HashSet(); - foreach (Aetheryte item in _dataManager.GetExcelSheet()) - { - hashSet.Add(item.Territory.RowId); - } - ExcelSheet excelSheet = _dataManager.GetExcelSheet(); - HashSet visitedLgbPaths = new HashSet(); - bool cacheFileResources = _dataManager.GameData.Options.CacheFileResources; - _dataManager.GameData.Options.CacheFileResources = false; - try - { - foreach (TerritoryType item2 in excelSheet) - { - if (hashSet.Contains(item2.RowId)) - { - ScanTerritory(list, dictionary, visitedLgbPaths, item2); - } - } - foreach (TerritoryType item3 in excelSheet) - { - ScanTerritory(list, dictionary, visitedLgbPaths, item3); - } - } - finally - { - _dataManager.GameData.Options.CacheFileResources = cacheFileResources; - } - return (ExitRanges: list, PopRangeIndex: dictionary); - } - - private static bool IsNavigableOverworld(ETerritoryIntendedUse use) - { - switch (use) - { - case ETerritoryIntendedUse.Town: - case ETerritoryIntendedUse.Overworld: - case ETerritoryIntendedUse.OpeningArea: - case ETerritoryIntendedUse.HousingOutdoor: - case ETerritoryIntendedUse.Firmament: - case ETerritoryIntendedUse.SanctumOfTheTwelve: - case ETerritoryIntendedUse.GoldSaucer: - case ETerritoryIntendedUse.Eureka: - case ETerritoryIntendedUse.Bozja: - case ETerritoryIntendedUse.IslandSanctuary: - case ETerritoryIntendedUse.CosmicExploration: - case ETerritoryIntendedUse.OccultCrescent: - return true; - default: - return false; + _logger.LogError(exception, "Failed to read zone boundary {Source} - building graph without boundary edges", source); + return null; } } - private void ScanTerritory(List exitRanges, Dictionary<(ushort TerritoryId, uint InstanceId), PopRangeEntry> popRangeIndex, HashSet visitedLgbPaths, TerritoryType territory) + private LgbData ReadCache(Stream stream, string source) { - if (territory.RowId == 0 || !IsNavigableOverworld((ETerritoryIntendedUse)territory.TerritoryIntendedUse.RowId)) + using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); + if (binaryReader.ReadUInt32() != 1112425562 || binaryReader.ReadInt32() != 1) { - return; + throw new InvalidDataException("zone boundary " + source + " has an unknown header"); } - string text = territory.Bg.ExtractText(); - if (string.IsNullOrEmpty(text)) + string text = binaryReader.ReadString(); + if (!string.IsNullOrEmpty(_gameVersion) && text != _gameVersion) { - return; + _logger.LogInformation("Zone boundary {Source} was built for game version {FileVersion}, running {GameVersion} - using it anyway", source, text, _gameVersion); } - int num = text.IndexOf("/level/", StringComparison.Ordinal); + int num = binaryReader.ReadInt32(); if (num < 0) { - return; + throw new InvalidDataException($"zone boundary {source} has a negative exit count ({num})"); } - string text2 = "bg/" + text.Substring(0, num + 1) + "level/planmap.lgb"; - if (!visitedLgbPaths.Add(text2)) + List list = new List(Math.Min(num, 131072)); + for (int i = 0; i < num; i++) { - return; + list.Add(new ExitRangeEntry(binaryReader.ReadUInt16(), binaryReader.ReadUInt32(), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()), binaryReader.ReadUInt16(), binaryReader.ReadUInt32(), binaryReader.ReadUInt32(), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()), new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()))); } - ushort num2 = (ushort)territory.RowId; - LgbFile file; - try + int num2 = binaryReader.ReadInt32(); + if (num2 < 0) { - file = _dataManager.GetFile(text2); + throw new InvalidDataException($"zone boundary {source} has a negative pop count ({num2})"); } - catch (Exception exception) + Dictionary<(ushort, uint), PopRangeEntry> dictionary = new Dictionary<(ushort, uint), PopRangeEntry>(Math.Min(num2, 131072)); + for (int j = 0; j < num2; j++) { - _logger.LogTrace(exception, "Failed to load {Path}", text2); - return; - } - if (file == null) - { - return; - } - LayerCommon.Layer[] layers = file.Layers; - for (int i = 0; i < layers.Length; i++) - { - LayerCommon.InstanceObject[] instanceObjects = layers[i].InstanceObjects; - for (int j = 0; j < instanceObjects.Length; j++) - { - LayerCommon.InstanceObject instanceObject = instanceObjects[j]; - if (instanceObject.AssetType == LayerEntryType.ExitRange) - { - LayerCommon.ExitRangeInstanceObject exitRangeInstanceObject = (LayerCommon.ExitRangeInstanceObject)(object)instanceObject.Object; - exitRanges.Add(new ExitRangeEntry(num2, instanceObject.InstanceId, new Vector3(instanceObject.Transform.Translation.X, instanceObject.Transform.Translation.Y, instanceObject.Transform.Translation.Z), exitRangeInstanceObject.TerritoryType, exitRangeInstanceObject.DestInstanceId, exitRangeInstanceObject.ReturnInstanceId, new Vector3(instanceObject.Transform.Rotation.X, instanceObject.Transform.Rotation.Y, instanceObject.Transform.Rotation.Z), new Vector3(instanceObject.Transform.Scale.X, instanceObject.Transform.Scale.Y, instanceObject.Transform.Scale.Z))); - } - else if (instanceObject.AssetType == LayerEntryType.PopRange) - { - popRangeIndex[(num2, instanceObject.InstanceId)] = new PopRangeEntry(num2, instanceObject.InstanceId, new Vector3(instanceObject.Transform.Translation.X, instanceObject.Transform.Translation.Y, instanceObject.Transform.Translation.Z)); - } - } + ushort num3 = binaryReader.ReadUInt16(); + uint num4 = binaryReader.ReadUInt32(); + Vector3 position = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle()); + dictionary[(num3, num4)] = new PopRangeEntry(num3, num4, position); } + _logger.LogDebug("Zone boundary data loaded from {Source} ({ExitCount} exits, {PopCount} pops)", source, list.Count, dictionary.Count); + return new LgbData(list, dictionary); } } diff --git a/SmartNav/SmartNav/NavGraphBuilder.cs b/SmartNav/SmartNav/NavGraphBuilder.cs index 002c1ad..cebc437 100644 --- a/SmartNav/SmartNav/NavGraphBuilder.cs +++ b/SmartNav/SmartNav/NavGraphBuilder.cs @@ -133,6 +133,32 @@ public sealed class NavGraphBuilder } } } + NavNode navNode = null; + foreach (NavNode value5 in graph.Nodes.Values) + { + if (GetAethernetParticipant(value5) == EAetheryteLocation.IshgardFirmament) + { + navNode = value5; + break; + } + } + if (navNode != null) + { + foreach (NavNode value6 in graph.Nodes.Values) + { + EAetheryteLocation? aethernetParticipant2 = GetAethernetParticipant(value6); + if (!(value6.Id == navNode.Id) && aethernetParticipant2.HasValue) + { + EAetheryteLocation valueOrDefault3 = aethernetParticipant2.GetValueOrDefault(); + if (valueOrDefault3.IsFirmamentAetheryte()) + { + graph.AddEdge(new NavEdge(navNode.Id, value6.Id, NavEdgeType.AethernetHop, 0f, 0)); + graph.AddEdge(new NavEdge(value6.Id, navNode.Id, NavEdgeType.AethernetHop, 0f, 0)); + num += 2; + } + } + } + } _logger.LogDebug("Added {Count} aethernet edges across {GroupCount} groups", num, dictionary.Count); } diff --git a/SmartNav/SmartNav/RouteInstructionBuilder.cs b/SmartNav/SmartNav/RouteInstructionBuilder.cs index 1ed20a0..8d1c977 100644 --- a/SmartNav/SmartNav/RouteInstructionBuilder.cs +++ b/SmartNav/SmartNav/RouteInstructionBuilder.cs @@ -103,7 +103,7 @@ public sealed class RouteInstructionBuilder(AetheryteData aetheryteData, ITerrit if ((object)trigger != null && TryComputeApproach(trigger, seg.From.Position, previous?.From.Position, out var approach)) { instructions.Add(new NavInstruction.Move((ushort)territoryId3, approach, Fly: true, LandAtTarget: true, 3f, null, null, DisableNavmesh: false, AllowZoneTransition: true, seg.From.Id, null, IsFinal: false)); - instructions.Add(new NavInstruction.Move((ushort)territoryId3, seg.From.Position, Fly: false, LandAtTarget: false, 0f, null, null, DisableNavmesh: false, AllowZoneTransition: true, seg.From.Id, null, IsFinal: false)); + instructions.Add(new NavInstruction.Move((ushort)territoryId3, seg.From.Position, Fly: false, LandAtTarget: false, 0f, null, null, DisableNavmesh: true, AllowZoneTransition: true, seg.From.Id, null, IsFinal: false)); goto IL_04b4; } }