369 lines
14 KiB
C#
Raw Normal View History

2024-06-06 18:49:49 +02:00
using System;
2024-06-09 16:37:26 +02:00
using System.Diagnostics;
2024-06-06 18:49:49 +02:00
using System.Globalization;
using System.Linq;
2024-05-25 23:51:37 +02:00
using System.Numerics;
using Dalamud.Game.ClientState.Objects;
2024-05-26 21:45:26 +02:00
using Dalamud.Interface;
2024-06-10 19:56:13 +02:00
using Dalamud.Interface.Colors;
2024-05-26 21:45:26 +02:00
using Dalamud.Interface.Components;
2024-06-08 11:30:26 +02:00
using Dalamud.Plugin;
2024-05-25 23:51:37 +02:00
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
2024-05-25 23:51:37 +02:00
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
2024-05-25 23:51:37 +02:00
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET;
2024-06-08 11:30:26 +02:00
using LLib.ImGui;
using Microsoft.Extensions.Logging;
2024-05-25 23:51:37 +02:00
using Questionable.Controller;
2024-06-10 19:56:13 +02:00
using Questionable.Controller.Steps.BaseFactory;
using Questionable.External;
2024-06-01 22:01:50 +02:00
using Questionable.Model;
2024-05-26 21:45:26 +02:00
using Questionable.Model.V1;
2024-05-25 23:51:37 +02:00
namespace Questionable.Windows;
2024-06-18 17:51:23 +02:00
internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
2024-05-25 23:51:37 +02:00
{
2024-06-08 11:30:26 +02:00
private readonly DalamudPluginInterface _pluginInterface;
2024-05-25 23:51:37 +02:00
private readonly MovementController _movementController;
2024-05-26 21:45:26 +02:00
private readonly QuestController _questController;
2024-05-25 23:51:37 +02:00
private readonly GameFunctions _gameFunctions;
private readonly ChatFunctions _chatFunctions;
2024-05-25 23:51:37 +02:00
private readonly IClientState _clientState;
2024-06-06 18:49:49 +02:00
private readonly IFramework _framework;
2024-05-25 23:51:37 +02:00
private readonly ITargetManager _targetManager;
2024-06-06 18:49:49 +02:00
private readonly GameUiController _gameUiController;
2024-06-08 11:30:26 +02:00
private readonly Configuration _configuration;
private readonly NavmeshIpc _navmeshIpc;
private readonly QuestRegistry _questRegistry;
2024-06-18 17:51:23 +02:00
private readonly ILogger<QuestWindow> _logger;
2024-05-25 23:51:37 +02:00
2024-06-18 17:51:23 +02:00
public QuestWindow(DalamudPluginInterface pluginInterface,
2024-06-12 18:03:48 +02:00
MovementController movementController,
QuestController questController,
GameFunctions gameFunctions,
ChatFunctions chatFunctions,
2024-06-12 18:03:48 +02:00
IClientState clientState,
IFramework framework,
ITargetManager targetManager,
GameUiController gameUiController,
Configuration configuration,
NavmeshIpc navmeshIpc,
QuestRegistry questRegistry,
2024-06-18 17:51:23 +02:00
ILogger<QuestWindow> logger)
: base("Questionable###Questionable", ImGuiWindowFlags.AlwaysAutoResize)
2024-05-25 23:51:37 +02:00
{
2024-06-08 11:30:26 +02:00
_pluginInterface = pluginInterface;
2024-05-25 23:51:37 +02:00
_movementController = movementController;
2024-05-26 21:45:26 +02:00
_questController = questController;
2024-05-25 23:51:37 +02:00
_gameFunctions = gameFunctions;
_chatFunctions = chatFunctions;
2024-05-25 23:51:37 +02:00
_clientState = clientState;
2024-06-06 18:49:49 +02:00
_framework = framework;
2024-05-25 23:51:37 +02:00
_targetManager = targetManager;
2024-06-06 18:49:49 +02:00
_gameUiController = gameUiController;
2024-06-08 11:30:26 +02:00
_configuration = configuration;
_navmeshIpc = navmeshIpc;
_questRegistry = questRegistry;
_logger = logger;
2024-05-25 23:51:37 +02:00
2024-06-17 00:32:24 +02:00
#if DEBUG
2024-05-25 23:51:37 +02:00
IsOpen = true;
2024-06-17 00:32:24 +02:00
#endif
2024-05-25 23:51:37 +02:00
SizeConstraints = new WindowSizeConstraints
{
2024-05-27 21:54:34 +02:00
MinimumSize = new Vector2(200, 30),
2024-05-25 23:51:37 +02:00
MaximumSize = default
};
RespectCloseHotkey = false;
2024-05-25 23:51:37 +02:00
}
2024-06-08 11:30:26 +02:00
public WindowConfig WindowConfig => _configuration.DebugWindowConfig;
public void SaveWindowConfig() => _pluginInterface.SavePluginConfig(_configuration);
public override bool DrawConditions()
{
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null)
return false;
var currentQuest = _questController.CurrentQuest;
return currentQuest == null || !currentQuest.Quest.Data.TerritoryBlacklist.Contains(_clientState.TerritoryType);
}
2024-06-09 16:37:26 +02:00
public override void Draw()
2024-05-25 23:51:37 +02:00
{
2024-06-09 16:37:26 +02:00
DrawQuest();
ImGui.Separator();
DrawCreationUtils();
ImGui.Separator();
DrawQuickAccessButtons();
2024-06-10 19:56:13 +02:00
DrawRemainingTasks();
2024-06-09 16:37:26 +02:00
}
2024-05-25 23:51:37 +02:00
2024-06-09 16:37:26 +02:00
private unsafe void DrawQuest()
{
2024-05-26 21:45:26 +02:00
var currentQuest = _questController.CurrentQuest;
if (currentQuest != null)
{
ImGui.TextUnformatted($"Quest: {currentQuest.Quest.Name} / {currentQuest.Sequence} / {currentQuest.Step}");
ImGui.BeginDisabled();
var questWork = _gameFunctions.GetQuestEx(currentQuest.Quest.QuestId);
if (questWork != null)
{
var qw = questWork.Value;
string vars = "";
for (int i = 0; i < 6; ++i)
{
vars += qw.Variables[i] + " ";
if (i % 2 == 1)
vars += " ";
}
// For combat quests, a sequence to kill 3 enemies works a bit like this:
// Trigger enemies → 0
// Kill first enemy → 1
// Kill second enemy → 2
// Last enemy → increase sequence, reset variable to 0
// The order in which enemies are killed doesn't seem to matter.
// If multiple waves spawn, this continues to count up (e.g. 1 enemy from wave 1, 2 enemies from wave 2, 1 from wave 3) would count to 3 then 0
ImGui.Text($"QW: {vars.Trim()}");
}
else
ImGui.TextUnformatted("(Not accepted)");
2024-05-26 21:45:26 +02:00
ImGui.TextUnformatted(_questController.DebugState ?? "--");
ImGui.EndDisabled();
2024-05-27 21:54:34 +02:00
ImGui.TextUnformatted(_questController.Comment ?? "--");
2024-05-26 21:45:26 +02:00
//var nextStep = _questController.GetNextStep();
//ImGui.BeginDisabled(nextStep.Step == null);
ImGui.Text(_questController.ToStatString());
//ImGui.EndDisabled();
2024-06-12 18:03:48 +02:00
ImGui.BeginDisabled(_questController.IsRunning);
2024-05-26 21:45:26 +02:00
if (ImGuiComponents.IconButton(FontAwesomeIcon.Play))
{
_questController.ExecuteNextStep(true);
2024-05-26 21:45:26 +02:00
}
ImGui.SameLine();
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step"))
2024-05-26 21:45:26 +02:00
{
_questController.ExecuteNextStep(false);
2024-05-26 21:45:26 +02:00
}
2024-06-12 18:03:48 +02:00
ImGui.EndDisabled();
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop))
{
_movementController.Stop();
2024-06-10 19:56:13 +02:00
_questController.Stop("Manual");
}
2024-06-10 19:56:13 +02:00
QuestStep? currentStep = currentQuest.Quest
.FindSequence(currentQuest.Sequence)
?.FindStep(currentQuest.Step);
bool lastStep = currentStep ==
currentQuest.Quest.FindSequence(currentQuest.Sequence)?.Steps.LastOrDefault();
bool colored = currentStep != null
&& !lastStep
&& currentStep.InteractionType == EInteractionType.Instruction
&& _questController.HasCurrentTaskMatching<WaitAtEnd.WaitNextStepOrSequence>();
ImGui.BeginDisabled(lastStep);
2024-06-10 19:56:13 +02:00
if (colored)
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip"))
{
2024-06-10 19:56:13 +02:00
_movementController.Stop();
_questController.Skip(currentQuest.Quest.QuestId, currentQuest.Sequence);
}
2024-06-12 18:03:48 +02:00
2024-06-10 19:56:13 +02:00
if (colored)
ImGui.PopStyleColor();
ImGui.EndDisabled();
2024-06-12 18:03:48 +02:00
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
if (ImGui.Checkbox("Automatically accept next quest", ref autoAcceptNextQuest))
{
_configuration.General.AutoAcceptNextQuest = autoAcceptNextQuest;
_pluginInterface.SavePluginConfig(_configuration);
}
2024-05-26 21:45:26 +02:00
}
else
ImGui.Text("No active quest");
2024-06-09 16:37:26 +02:00
}
2024-05-26 21:45:26 +02:00
2024-06-09 16:37:26 +02:00
private unsafe void DrawCreationUtils()
{
Debug.Assert(_clientState.LocalPlayer != null, "_clientState.LocalPlayer != null");
2024-05-26 21:45:26 +02:00
2024-05-26 15:43:33 +02:00
ImGui.Text(
2024-06-08 21:16:57 +02:00
$"Current TerritoryId: {_clientState.TerritoryType}, Flying: {(_gameFunctions.IsFlyingUnlockedInCurrentZone() ? "Yes" : "No")}");
2024-05-25 23:51:37 +02:00
var q = _gameFunctions.GetCurrentQuest();
ImGui.Text($"Current Quest: {q.CurrentQuest} → {q.Sequence}");
#if false
2024-05-29 00:17:19 +02:00
var questManager = QuestManager.Instance();
if (questManager != null)
{
for (int i = questManager->TrackedQuestsSpan.Length - 1; i >= 0; --i)
2024-05-29 00:17:19 +02:00
{
var trackedQuest = questManager->TrackedQuestsSpan[i];
switch (trackedQuest.QuestType)
{
default:
ImGui.Text($"Tracked quest {i}: {trackedQuest.QuestType}, {trackedQuest.Index}");
break;
case 1:
_questRegistry.TryGetQuest(questManager->NormalQuestsSpan[trackedQuest.Index].QuestId,
out var quest);
2024-05-29 00:17:19 +02:00
ImGui.Text(
$"Tracked quest: {questManager->NormalQuestsSpan[trackedQuest.Index].QuestId}, {trackedQuest.Index}: {quest?.Name}");
2024-05-29 00:17:19 +02:00
break;
}
}
}
#endif
2024-05-29 00:17:19 +02:00
2024-05-25 23:51:37 +02:00
if (_targetManager.Target != null)
{
ImGui.Separator();
2024-05-26 21:45:26 +02:00
ImGui.Text(string.Create(CultureInfo.InvariantCulture,
2024-06-01 01:26:46 +02:00
$"Target: {_targetManager.Target.Name} ({_targetManager.Target.ObjectKind}; {_targetManager.Target.DataId})"));
GameObject* gameObject = (GameObject*)_targetManager.Target.Address;
2024-06-01 01:26:46 +02:00
ImGui.Text(string.Create(CultureInfo.InvariantCulture,
$"Distance: {(_targetManager.Target.Position - _clientState.LocalPlayer.Position).Length():F2}, Y: {_targetManager.Target.Position.Y - _clientState.LocalPlayer.Position.Y:F2} | QM: {gameObject->NamePlateIconId}"));
2024-05-25 23:51:37 +02:00
ImGui.BeginDisabled(!_movementController.IsNavmeshReady);
if (!_movementController.IsPathfinding)
{
if (ImGui.Button("Move to Target"))
{
_movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
2024-06-08 21:16:57 +02:00
_targetManager.Target.Position, _gameFunctions.IsFlyingUnlockedInCurrentZone(),
2024-06-01 18:46:57 +02:00
true);
2024-05-25 23:51:37 +02:00
}
}
else
{
if (ImGui.Button("Cancel pathfinding"))
_movementController.ResetPathfinding();
}
ImGui.EndDisabled();
ImGui.SameLine();
if (ImGui.Button("Interact"))
{
ulong result = TargetSystem.Instance()->InteractWithObject(
2024-06-09 16:37:26 +02:00
(GameObject*)_targetManager.Target.Address, false);
_logger.LogInformation("XXXXX Interaction Result: {Result}", result);
2024-05-25 23:51:37 +02:00
}
ImGui.SameLine();
2024-05-26 21:45:26 +02:00
ImGui.Button("Copy");
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
2024-05-25 23:51:37 +02:00
{
ImGui.SetClipboardText($$"""
"DataId": {{_targetManager.Target.DataId}},
"Position": {
"X": {{_targetManager.Target.Position.X.ToString(CultureInfo.InvariantCulture)}},
"Y": {{_targetManager.Target.Position.Y.ToString(CultureInfo.InvariantCulture)}},
"Z": {{_targetManager.Target.Position.Z.ToString(CultureInfo.InvariantCulture)}}
},
"TerritoryId": {{_clientState.TerritoryType}},
"InteractionType": "Interact"
""");
}
2024-05-26 21:45:26 +02:00
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
EAetheryteLocation location = (EAetheryteLocation)_targetManager.Target.DataId;
ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
$"{{EAetheryteLocation.{location}, new({_targetManager.Target.Position.X}f, {_targetManager.Target.Position.Y}f, {_targetManager.Target.Position.Z}f)}},"));
}
2024-05-25 23:51:37 +02:00
}
else
{
2024-06-12 18:03:48 +02:00
ImGui.Button($"Copy");
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
2024-05-25 23:51:37 +02:00
{
ImGui.SetClipboardText($$"""
"Position": {
"X": {{_clientState.LocalPlayer.Position.X.ToString(CultureInfo.InvariantCulture)}},
"Y": {{_clientState.LocalPlayer.Position.Y.ToString(CultureInfo.InvariantCulture)}},
"Z": {{_clientState.LocalPlayer.Position.Z.ToString(CultureInfo.InvariantCulture)}}
},
"TerritoryId": {{_clientState.TerritoryType}},
"InteractionType": ""
""");
}
2024-06-12 18:03:48 +02:00
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
Vector3 position = _clientState.LocalPlayer!.Position;
ImGui.SetClipboardText(string.Create(CultureInfo.InvariantCulture,
$"new({position.X}f, {position.Y}f, {position.Z}f)"));
}
2024-05-25 23:51:37 +02:00
}
2024-06-09 16:37:26 +02:00
}
2024-05-25 23:51:37 +02:00
2024-06-09 16:37:26 +02:00
private unsafe void DrawQuickAccessButtons()
{
2024-05-25 23:51:37 +02:00
var map = AgentMap.Instance();
2024-05-26 15:43:33 +02:00
ImGui.BeginDisabled(map == null || map->IsFlagMarkerSet == 0 ||
map->FlagMapMarker.TerritoryId != _clientState.TerritoryType ||
!_navmeshIpc.IsReady);
2024-05-25 23:51:37 +02:00
if (ImGui.Button("Move to Flag"))
{
_movementController.Destination = null;
_chatFunctions.ExecuteCommand(
2024-06-08 21:16:57 +02:00
$"/vnav {(_gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}");
}
2024-05-25 23:51:37 +02:00
ImGui.EndDisabled();
ImGui.SameLine();
ImGui.BeginDisabled(!_movementController.IsPathRunning);
if (ImGui.Button("Stop Nav"))
{
2024-05-25 23:51:37 +02:00
_movementController.Stop();
2024-06-10 19:56:13 +02:00
_questController.Stop("Manual");
}
2024-05-25 23:51:37 +02:00
ImGui.EndDisabled();
2024-05-27 21:54:34 +02:00
if (ImGui.Button("Reload Data"))
2024-06-06 18:49:49 +02:00
{
2024-05-27 21:54:34 +02:00
_questController.Reload();
2024-06-06 18:49:49 +02:00
_framework.RunOnTick(() => _gameUiController.HandleCurrentDialogueChoices(),
TimeSpan.FromMilliseconds(200));
}
2024-06-10 19:56:13 +02:00
}
2024-06-10 19:56:13 +02:00
private void DrawRemainingTasks()
{
var remainingTasks = _questController.GetRemainingTaskNames();
if (remainingTasks.Count > 0)
{
ImGui.Separator();
ImGui.BeginDisabled();
foreach (var task in remainingTasks)
ImGui.TextUnformatted(task);
ImGui.EndDisabled();
}
2024-05-25 23:51:37 +02:00
}
}