From 0bd15c03f5502ca8145ec172f55a2a7d6d1098eb Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Wed, 10 Jul 2024 17:58:11 +0200 Subject: [PATCH] Add experimental navmesh path replay (mostly for finding stuck places) --- .../MSQ/C-Yak T'el/4907_Mamook Speaks.json | 10 ++ Questionable/Controller/QuestController.cs | 22 ++++- Questionable/DalamudInitializer.cs | 52 +++++++++- Questionable/Windows/QuestWindow.cs | 96 +++++++++++++++++++ 4 files changed, 174 insertions(+), 6 deletions(-) diff --git a/QuestPaths/Dawntrail/MSQ/C-Yak T'el/4907_Mamook Speaks.json b/QuestPaths/Dawntrail/MSQ/C-Yak T'el/4907_Mamook Speaks.json index dd7de4f96..9565e80e5 100644 --- a/QuestPaths/Dawntrail/MSQ/C-Yak T'el/4907_Mamook Speaks.json +++ b/QuestPaths/Dawntrail/MSQ/C-Yak T'el/4907_Mamook Speaks.json @@ -263,6 +263,16 @@ { "Sequence": 5, "Steps": [ + { + "Position": { + "X": -136.90475, + "Y": -215.01514, + "Z": 330.17505 + }, + "TerritoryId": 1189, + "InteractionType": "WalkTo", + "Mount": true + }, { "DataId": 1047669, "Position": { diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index 48463285f..07f21d728 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -53,6 +53,7 @@ internal sealed class QuestController public QuestProgress? CurrentQuest { get; set; } + public SimulationProgress? SimulatedQuest { get; set; } public string? DebugState { get; private set; } public string? Comment { get; private set; } @@ -110,7 +111,16 @@ internal sealed class QuestController { DebugState = null; - (ushort currentQuestId, byte currentSequence) = _gameFunctions.GetCurrentQuest(); + ushort currentQuestId; + byte currentSequence; + if (SimulatedQuest != null) + { + currentQuestId = SimulatedQuest.Quest.QuestId; + currentSequence = SimulatedQuest.Sequence; + } + else + (currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest(); + if (currentQuestId == 0) { if (CurrentQuest != null) @@ -302,6 +312,14 @@ internal sealed class QuestController } } + public void SimulateQuest(Quest? quest) + { + if (quest != null) + SimulatedQuest = new SimulationProgress(quest, 0); + else + SimulatedQuest = null; + } + private void UpdateCurrentTask() { if (_gameFunctions.IsOccupied()) @@ -464,6 +482,8 @@ internal sealed class QuestController } } + public sealed record SimulationProgress(Quest Quest, byte Sequence); + public void Skip(ushort questQuestId, byte currentQuestSequence) { lock (_lock) diff --git a/Questionable/DalamudInitializer.cs b/Questionable/DalamudInitializer.cs index c45df4173..caa490f17 100644 --- a/Questionable/DalamudInitializer.cs +++ b/Questionable/DalamudInitializer.cs @@ -6,6 +6,7 @@ using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Data; +using Questionable.Model; using Questionable.Windows; namespace Questionable; @@ -18,15 +19,18 @@ internal sealed class DalamudInitializer : IDisposable private readonly QuestController _questController; private readonly MovementController _movementController; private readonly NavigationShortcutController _navigationShortcutController; + private readonly IChatGui _chatGui; private readonly WindowSystem _windowSystem; private readonly QuestWindow _questWindow; - private readonly ConfigWindow _configWindow; private readonly DebugOverlay _debugOverlay; + private readonly ConfigWindow _configWindow; + private readonly QuestRegistry _questRegistry; public DalamudInitializer(IDalamudPluginInterface pluginInterface, IFramework framework, ICommandManager commandManager, QuestController questController, MovementController movementController, - GameUiController gameUiController, NavigationShortcutController navigationShortcutController, - WindowSystem windowSystem, QuestWindow questWindow, DebugOverlay debugOverlay, ConfigWindow configWindow) + GameUiController gameUiController, NavigationShortcutController navigationShortcutController, IChatGui chatGui, + WindowSystem windowSystem, QuestWindow questWindow, DebugOverlay debugOverlay, ConfigWindow configWindow, + QuestRegistry questRegistry) { _pluginInterface = pluginInterface; _framework = framework; @@ -34,10 +38,12 @@ internal sealed class DalamudInitializer : IDisposable _questController = questController; _movementController = movementController; _navigationShortcutController = navigationShortcutController; + _chatGui = chatGui; _windowSystem = windowSystem; _questWindow = questWindow; - _configWindow = configWindow; _debugOverlay = debugOverlay; + _configWindow = configWindow; + _questRegistry = questRegistry; _windowSystem.AddWindow(questWindow); _windowSystem.AddWindow(configWindow); @@ -83,10 +89,46 @@ internal sealed class DalamudInitializer : IDisposable } else if (arguments.StartsWith("do", StringComparison.Ordinal)) { + if (!_debugOverlay.DrawConditions()) + { + _chatGui.PrintError("[Questionable] You don't have the debug overlay enabled."); + return; + } + if (arguments.Length >= 4 && ushort.TryParse(arguments.AsSpan(3), out ushort questId)) - _debugOverlay.HighlightedQuest = questId; + { + if (_questRegistry.IsKnownQuest(questId)) + { + _debugOverlay.HighlightedQuest = questId; + _chatGui.Print($"[Questionable] Set highlighted quest to {questId}."); + } + else + _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); + } else + { _debugOverlay.HighlightedQuest = null; + _chatGui.Print("[Questionable] Cleared highlighted quest."); + } + } + else if (arguments.StartsWith("sim", StringComparison.InvariantCulture)) + { + string[] parts = arguments.Split(' '); + if (parts.Length == 2 && ushort.TryParse(parts[1], out ushort questId)) + { + if (_questRegistry.TryGetQuest(questId, out Quest? quest)) + { + _questController.SimulateQuest(quest); + _chatGui.Print($"[Questionable] Simulating quest {questId}."); + } + else + _chatGui.PrintError($"[Questionable] Unknown quest {questId}."); + } + else + { + _questController.SimulateQuest(null); + _chatGui.Print("[Questionable] Cleared simulated quest."); + } } else if (string.IsNullOrEmpty(arguments)) _questWindow.Toggle(); diff --git a/Questionable/Windows/QuestWindow.cs b/Questionable/Windows/QuestWindow.cs index e7871118c..aef55d0b2 100644 --- a/Questionable/Windows/QuestWindow.cs +++ b/Questionable/Windows/QuestWindow.cs @@ -8,6 +8,7 @@ using Dalamud.Game.Text; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; @@ -202,6 +203,101 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig _configuration.General.AutoAcceptNextQuest = autoAcceptNextQuest; _pluginInterface.SavePluginConfig(_configuration); } + + + if (_questController.SimulatedQuest != null) + { + ImGui.Separator(); + ImGui.TextColored(ImGuiColors.DalamudRed, "Quest sim active (experimental)"); + ImGui.Text($"Sequence: {_questController.SimulatedQuest.Sequence}"); + + ImGui.BeginDisabled(_questController.SimulatedQuest.Sequence == 0); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) + { + _movementController.Stop(); + _questController.Stop("Sim-"); + + byte oldSequence = _questController.SimulatedQuest.Sequence; + byte newSequence = _questController.SimulatedQuest.Quest.Data.QuestSequence + .Select(x => (byte)x.Sequence) + .LastOrDefault(x => x < oldSequence, byte.MinValue); + + _questController.SimulatedQuest = _questController.SimulatedQuest with + { + Sequence = newSequence, + }; + } + + ImGui.EndDisabled(); + + ImGui.SameLine(); + ImGui.BeginDisabled(_questController.SimulatedQuest.Sequence >= 255); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) + { + _movementController.Stop(); + _questController.Stop("Sim+"); + + byte oldSequence = _questController.SimulatedQuest.Sequence; + byte newSequence = _questController.SimulatedQuest.Quest.Data.QuestSequence + .Select(x => (byte)x.Sequence) + .FirstOrDefault(x => x > oldSequence, byte.MaxValue); + + _questController.SimulatedQuest = _questController.SimulatedQuest with + { + Sequence = newSequence, + }; + } + + ImGui.EndDisabled(); + + var simulatedSequence = + _questController.SimulatedQuest.Quest.FindSequence(_questController.SimulatedQuest.Sequence); + if (simulatedSequence != null) + { + using var _ = ImRaii.PushId("SimulatedStep"); + + ImGui.Text($"Step: {currentQuest.Step} / {simulatedSequence.Steps.Count - 1}"); + + ImGui.BeginDisabled(currentQuest.Step == 0); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) + { + _movementController.Stop(); + _questController.Stop("SimStep-"); + + _questController.CurrentQuest = currentQuest with + { + Step = Math.Min(currentQuest.Step - 1, simulatedSequence.Steps.Count - 1), + }; + } + + ImGui.EndDisabled(); + + ImGui.SameLine(); + ImGui.BeginDisabled(currentQuest.Step >= simulatedSequence.Steps.Count); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) + { + _movementController.Stop(); + _questController.Stop("SimStep+"); + + _questController.CurrentQuest = currentQuest with + { + Step = currentQuest.Step == simulatedSequence.Steps.Count - 1 + ? 255 + : (currentQuest.Step + 1), + }; + } + + ImGui.EndDisabled(); + + if (ImGui.Button("Clear sim")) + { + _questController.SimulateQuest(null); + + _movementController.Stop(); + _questController.Stop("ClearSim"); + } + } + } } else ImGui.Text("No active quest");