Indicate navmesh build % in UI

This commit is contained in:
Liza 2025-02-23 18:31:51 +01:00
parent 59793d19dc
commit c7f4865201
Signed by: liza
GPG Key ID: 2C41B84815CF6445
16 changed files with 100 additions and 22 deletions

View File

@ -302,6 +302,7 @@
"WakingSandsSolar",
"RisingStonesSolar",
"RoguesGuild",
"NotRoguesGuild",
"DockStorehouse"
]
}

View File

@ -11,6 +11,7 @@ public sealed class SkipConditionConverter() : EnumConverter<EExtraSkipCondition
{ EExtraSkipCondition.WakingSandsSolar, "WakingSandsSolar" },
{ EExtraSkipCondition.RisingStonesSolar, "RisingStonesSolar"},
{ EExtraSkipCondition.RoguesGuild, "RoguesGuild"},
{ EExtraSkipCondition.NotRoguesGuild, "NotRoguesGuild"},
{ EExtraSkipCondition.DockStorehouse, "DockStorehouse"},
};
}

View File

@ -15,6 +15,7 @@ public enum EExtraSkipCondition
/// Location for ROG quests in Limsa Lominsa; located far underneath the actual lower decks.
/// </summary>
RoguesGuild,
NotRoguesGuild,
/// <summary>
/// Location for NIN quests in Eastern La Noscea; located far underneath the actual zone.

View File

@ -37,6 +37,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=tertium/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tural/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=urqopacha/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vnavmesh/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wachumeqimeqi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wachunpelo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wolekdorf/@EntryIndexedValue">True</s:Boolean>

View File

@ -89,6 +89,7 @@ internal sealed class MovementController : IDisposable
public bool IsPathfinding => _pathfindTask is { IsCompleted: false };
public DestinationData? Destination { get; set; }
public DateTime MovementStartedAt { get; private set; } = DateTime.Now;
public int BuiltNavmeshPercent => _navmeshIpc.GetBuildProgress();
public void Update()
{

View File

@ -687,6 +687,17 @@ internal sealed class QuestController : MiniTaskController<QuestController>
public bool IsRunning => !_taskQueue.AllTasksComplete;
public TaskQueue TaskQueue => _taskQueue;
public string? CurrentTaskState
{
get
{
if (_taskQueue.CurrentTaskExecutor is IDebugStateProvider debugStateProvider)
return debugStateProvider.GetDebugState();
else
return null;
}
}
public sealed class QuestProgress
{
public Quest Quest { get; }

View File

@ -0,0 +1,27 @@
namespace Questionable.Controller.Steps.Common;
internal sealed class WaitNavmesh
{
internal sealed record Task : ITask
{
public override string ToString() => "Wait(navmesh)";
}
internal sealed class Executor(MovementController movementController) : TaskExecutor<Task>, IDebugStateProvider
{
protected override bool Start() => true;
public override ETaskResult Update() =>
movementController.IsNavmeshReady ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
public override bool ShouldInterruptOnDamage() => false;
public string? GetDebugState()
{
if (!movementController.IsNavmeshReady)
return $"Navmesh: {movementController.BuiltNavmeshPercent}%";
else
return null;
}
}
}

View File

@ -96,7 +96,9 @@ internal static class SinglePlayerDuty
}
internal sealed class WaitSinglePlayerDutyExecutor(
BossModIpc bossModIpc) : TaskExecutor<WaitSinglePlayerDuty>, IStoppableTaskExecutor
BossModIpc bossModIpc,
MovementController movementController)
: TaskExecutor<WaitSinglePlayerDuty>, IStoppableTaskExecutor, IDebugStateProvider
{
protected override bool Start() => true;
@ -110,6 +112,14 @@ internal static class SinglePlayerDuty
public void StopNow() => bossModIpc.DisableAi();
public override bool ShouldInterruptOnDamage() => false;
public string? GetDebugState()
{
if (!movementController.IsNavmeshReady)
return $"Navmesh: {movementController.BuiltNavmeshPercent}%";
else
return null;
}
}
internal sealed record DisableAi : ITask

View File

@ -20,7 +20,6 @@ namespace Questionable.Controller.Steps.Shared;
internal static class AethernetShortcut
{
internal sealed class Factory(
MovementController movementController,
AetheryteData aetheryteData,
TerritoryData territoryData,
IClientState clientState)
@ -31,8 +30,7 @@ internal static class AethernetShortcut
if (step.AethernetShortcut == null)
yield break;
yield return new WaitCondition.Task(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
yield return new WaitNavmesh.Task();
yield return new Task(step.AethernetShortcut.From, step.AethernetShortcut.To,
step.SkipConditions?.AethernetShortcutIf ?? new());

View File

@ -38,7 +38,6 @@ internal static class Gather
}
internal sealed class DelayedGatheringExecutor(
MovementController movementController,
GatheringData gatheringData,
GatheringPointRegistry gatheringPointRegistry,
TerritoryData territoryData,
@ -85,8 +84,7 @@ internal static class Gather
yield return new WaitCondition.Task(() => clientState.TerritoryType == territoryId,
$"Wait(territory: {territoryData.GetNameAndId(territoryId)})");
yield return new WaitCondition.Task(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
yield return new WaitNavmesh.Task();
yield return new GatheringTask(gatheringPointId, Task.GatheredItem);
yield return new WaitAtEnd.WaitDelay();

View File

@ -25,7 +25,6 @@ namespace Questionable.Controller.Steps.Shared;
internal static class MoveTo
{
internal sealed class Factory(
MovementController movementController,
IClientState clientState,
AetheryteData aetheryteData,
TerritoryData territoryData,
@ -67,10 +66,7 @@ internal static class MoveTo
$"Wait(territory: {territoryData.GetNameAndId(step.TerritoryId)})");
if (!step.DisableNavmesh)
{
yield return new WaitCondition.Task(() => movementController.IsNavmeshReady,
"Wait(navmesh ready)");
}
yield return new WaitNavmesh.Task();
yield return new MoveTask(step, destination);

View File

@ -310,6 +310,7 @@ internal static class SkipCondition
EExtraSkipCondition.WakingSandsSolar => territoryType == 212 && position.X >= 24,
EExtraSkipCondition.RisingStonesSolar => territoryType == 351 && position.Z <= -28,
EExtraSkipCondition.RoguesGuild => territoryType == 129 && position.Y <= -115,
EExtraSkipCondition.NotRoguesGuild => territoryType == 129 && position.Y > -115,
EExtraSkipCondition.DockStorehouse => territoryType == 137 && position.Y <= -20,
_ => throw new ArgumentOutOfRangeException(nameof(condition), condition, null)
};

View File

@ -30,6 +30,11 @@ internal interface IStoppableTaskExecutor : ITaskExecutor
void StopNow();
}
internal interface IDebugStateProvider : ITaskExecutor
{
string? GetDebugState();
}
internal abstract class TaskExecutor<T> : ITaskExecutor
where T : class, ITask
{

View File

@ -20,6 +20,7 @@ internal sealed class NavmeshIpc
private readonly ICallGateSubscriber<List<Vector3>> _pathListWaypoints;
private readonly ICallGateSubscriber<float, object> _pathSetTolerance;
private readonly ICallGateSubscriber<Vector3, bool, float, Vector3?> _queryPointOnFloor;
private readonly ICallGateSubscriber<float> _buildProgress;
public NavmeshIpc(IDalamudPluginInterface pluginInterface, ILogger<NavmeshIpc> logger)
{
@ -35,6 +36,7 @@ internal sealed class NavmeshIpc
_pathSetTolerance = pluginInterface.GetIpcSubscriber<float, object>("vnavmesh.Path.SetTolerance");
_queryPointOnFloor =
pluginInterface.GetIpcSubscriber<Vector3, bool, float, Vector3?>("vnavmesh.Query.Mesh.PointOnFloor");
_buildProgress = pluginInterface.GetIpcSubscriber<float>("vnavmesh.Nav.BuildProgress");
}
public bool IsReady
@ -136,4 +138,19 @@ internal sealed class NavmeshIpc
else
return [];
}
public int GetBuildProgress()
{
try
{
float progress = _buildProgress.InvokeFunc();
if (progress < 0)
return 100;
return (int)(progress * 100);
}
catch (IpcError)
{
return 0;
}
}
}

View File

@ -235,6 +235,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddTaskExecutor<SinglePlayerDuty.SetTarget, SinglePlayerDuty.SetTargetExecutor>();
serviceCollection.AddTaskExecutor<WaitCondition.Task, WaitCondition.WaitConditionExecutor>();
serviceCollection.AddTaskExecutor<WaitNavmesh.Task, WaitNavmesh.Executor>();
serviceCollection.AddTaskFactory<WaitAtEnd.Factory>();
serviceCollection.AddTaskExecutor<WaitAtEnd.WaitDelay, WaitAtEnd.WaitDelayExecutor>();
serviceCollection.AddTaskExecutor<WaitAtEnd.WaitNextStepOrSequence, WaitAtEnd.WaitNextStepOrSequenceExecutor>();

View File

@ -73,25 +73,34 @@ internal sealed partial class ActiveQuestComponent
if (_combatController.IsRunning)
ImGui.TextColored(ImGuiColors.DalamudOrange, "In Combat");
else if (_questController.CurrentTaskState is { } currentTaskState)
{
using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange);
ImGui.TextUnformatted(currentTaskState);
}
else
{
ImGui.BeginDisabled();
using var _ = ImRaii.Disabled();
ImGui.TextUnformatted(_questController.DebugState ?? string.Empty);
ImGui.EndDisabled();
}
QuestSequence? currentSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
QuestStep? currentStep = currentSequence?.FindStep(currentQuest.Step);
if (!isMinimized)
{
bool colored = currentStep is
{ InteractionType: EInteractionType.Instruction or EInteractionType.WaitForManualProgress or EInteractionType.Snipe };
if (colored)
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudOrange);
ImGui.TextUnformatted(currentStep?.Comment ??
currentSequence?.Comment ?? currentQuest.Quest.Root.Comment ?? string.Empty);
if (colored)
ImGui.PopStyleColor();
using (var color = new ImRaii.Color())
{
bool colored = currentStep is
{
InteractionType: EInteractionType.Instruction or EInteractionType.WaitForManualProgress
or EInteractionType.Snipe
};
if (colored)
color.Push(ImGuiCol.Text, ImGuiColors.DalamudOrange);
ImGui.TextUnformatted(currentStep?.Comment ??
currentSequence?.Comment ?? currentQuest.Quest.Root.Comment ?? string.Empty);
}
//var nextStep = _questController.GetNextStep();
//ImGui.BeginDisabled(nextStep.Step == null);