forked from liza/Questionable
Use ServiceHost/ILogger
This commit is contained in:
parent
fe49d5354f
commit
78357dc288
@ -290,7 +290,7 @@ dotnet_diagnostic.CA1806.severity = warning
|
||||
dotnet_diagnostic.CA1810.severity = warning
|
||||
|
||||
# CA1812: Avoid uninstantiated internal classes
|
||||
dotnet_diagnostic.CA1812.severity = warning
|
||||
dotnet_diagnostic.CA1812.severity = suggestion
|
||||
|
||||
# CA1813: Avoid unsealed attributes
|
||||
dotnet_diagnostic.CA1813.severity = warning
|
||||
@ -392,7 +392,7 @@ dotnet_diagnostic.CA1846.severity = warning
|
||||
dotnet_diagnostic.CA1847.severity = warning
|
||||
|
||||
# CA1848: Use the LoggerMessage delegates
|
||||
dotnet_diagnostic.CA1848.severity = warning
|
||||
dotnet_diagnostic.CA1848.severity = suggestion
|
||||
|
||||
# CA1849: Call async methods when in an async method
|
||||
dotnet_diagnostic.CA1849.severity = warning
|
||||
|
@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using LLib.GameUI;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model.V1;
|
||||
using Quest = Questionable.Model.Quest;
|
||||
|
||||
@ -21,17 +22,17 @@ internal sealed class GameUiController : IDisposable
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly QuestController _questController;
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly ILogger<GameUiController> _logger;
|
||||
|
||||
public GameUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, GameFunctions gameFunctions,
|
||||
QuestController questController, IGameGui gameGui, IPluginLog pluginLog)
|
||||
QuestController questController, IGameGui gameGui, ILogger<GameUiController> logger)
|
||||
{
|
||||
_addonLifecycle = addonLifecycle;
|
||||
_dataManager = dataManager;
|
||||
_gameFunctions = gameFunctions;
|
||||
_questController = questController;
|
||||
_gameGui = gameGui;
|
||||
_pluginLog = pluginLog;
|
||||
_logger = logger;
|
||||
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CutSceneSelectString", CutsceneSelectStringPostSetup);
|
||||
@ -45,26 +46,26 @@ internal sealed class GameUiController : IDisposable
|
||||
{
|
||||
if (_gameGui.TryGetAddonByName("SelectString", out AddonSelectString* addonSelectString))
|
||||
{
|
||||
_pluginLog.Information("SelectString window is open");
|
||||
_logger.LogInformation("SelectString window is open");
|
||||
SelectStringPostSetup(addonSelectString, true);
|
||||
}
|
||||
|
||||
if (_gameGui.TryGetAddonByName("CutSceneSelectString",
|
||||
out AddonCutSceneSelectString* addonCutSceneSelectString))
|
||||
{
|
||||
_pluginLog.Information("CutSceneSelectString window is open");
|
||||
_logger.LogInformation("CutSceneSelectString window is open");
|
||||
CutsceneSelectStringPostSetup(addonCutSceneSelectString, true);
|
||||
}
|
||||
|
||||
if (_gameGui.TryGetAddonByName("SelectIconString", out AddonSelectIconString* addonSelectIconString))
|
||||
{
|
||||
_pluginLog.Information("SelectIconString window is open");
|
||||
_logger.LogInformation("SelectIconString window is open");
|
||||
SelectIconStringPostSetup(addonSelectIconString, true);
|
||||
}
|
||||
|
||||
if (_gameGui.TryGetAddonByName("SelectYesno", out AddonSelectYesno* addonSelectYesno))
|
||||
{
|
||||
_pluginLog.Information("SelectYesno window is open");
|
||||
_logger.LogInformation("SelectYesno window is open");
|
||||
SelectYesnoPostSetup(addonSelectYesno, true);
|
||||
}
|
||||
}
|
||||
@ -151,7 +152,7 @@ internal sealed class GameUiController : IDisposable
|
||||
var currentQuest = _questController.CurrentQuest;
|
||||
if (currentQuest == null)
|
||||
{
|
||||
_pluginLog.Information("Ignoring list choice, no active quest");
|
||||
_logger.LogInformation("Ignoring list choice, no active quest");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -167,7 +168,7 @@ internal sealed class GameUiController : IDisposable
|
||||
var step = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step);
|
||||
if (step == null)
|
||||
{
|
||||
_pluginLog.Information("Ignoring list choice, no active step");
|
||||
_logger.LogInformation("Ignoring list choice, no active step");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -178,7 +179,7 @@ internal sealed class GameUiController : IDisposable
|
||||
{
|
||||
if (dialogueChoice.Answer == null)
|
||||
{
|
||||
_pluginLog.Information("Ignoring entry in DialogueChoices, no answer");
|
||||
_logger.LogInformation("Ignoring entry in DialogueChoices, no answer");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -212,28 +213,31 @@ internal sealed class GameUiController : IDisposable
|
||||
|
||||
if (actualPrompt == null && !string.IsNullOrEmpty(excelPrompt))
|
||||
{
|
||||
_pluginLog.Information($"Unexpected excelPrompt: {excelPrompt}");
|
||||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}", excelPrompt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actualPrompt != null && (excelPrompt == null || !GameStringEquals(actualPrompt, excelPrompt)))
|
||||
{
|
||||
_pluginLog.Information($"Unexpected excelPrompt: {excelPrompt}, actualPrompt: {actualPrompt}");
|
||||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}, actualPrompt: {ActualPrompt}",
|
||||
excelPrompt, actualPrompt);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < answers.Count; ++i)
|
||||
{
|
||||
_pluginLog.Verbose($"Checking if {answers[i]} == {excelAnswer}");
|
||||
_logger.LogTrace("Checking if {ActualAnswer} == {ExpectedAnswer}",
|
||||
answers[i], excelAnswer);
|
||||
if (GameStringEquals(answers[i], excelAnswer))
|
||||
{
|
||||
_pluginLog.Information($"Returning {i}: '{answers[i]}' for '{actualPrompt}'");
|
||||
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}'",
|
||||
i, answers[i], actualPrompt);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pluginLog.Information($"No matching answer found for {actualPrompt}.");
|
||||
_logger.LogInformation("No matching answer found for {Prompt}.", actualPrompt);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -249,7 +253,7 @@ internal sealed class GameUiController : IDisposable
|
||||
if (actualPrompt == null)
|
||||
return;
|
||||
|
||||
_pluginLog.Verbose($"Prompt: '{actualPrompt}'");
|
||||
_logger.LogTrace("Prompt: '{Prompt}'", actualPrompt);
|
||||
|
||||
var currentQuest = _questController.CurrentQuest;
|
||||
if (currentQuest == null)
|
||||
@ -277,7 +281,7 @@ internal sealed class GameUiController : IDisposable
|
||||
private unsafe bool HandleDefaultYesNo(AddonSelectYesno* addonSelectYesno, Quest quest,
|
||||
IList<DialogueChoice> dialogueChoices, string actualPrompt, bool checkAllSteps)
|
||||
{
|
||||
_pluginLog.Verbose($"DefaultYesNo: Choice count: {dialogueChoices.Count}");
|
||||
_logger.LogTrace("DefaultYesNo: Choice count: {Count}", dialogueChoices.Count);
|
||||
foreach (var dialogueChoice in dialogueChoices)
|
||||
{
|
||||
string? excelPrompt;
|
||||
@ -325,21 +329,22 @@ internal sealed class GameUiController : IDisposable
|
||||
bool increaseStepCount = true;
|
||||
QuestStep? step = sequence.FindStep(currentQuest.Step);
|
||||
if (step != null)
|
||||
_pluginLog.Verbose($"Current step: {step.TerritoryId}, {step.TargetTerritoryId}");
|
||||
_logger.LogTrace("Current step: {CurrentTerritory}, {TargetTerritory}", step.TerritoryId,
|
||||
step.TargetTerritoryId);
|
||||
|
||||
if (step == null || step.TargetTerritoryId == null)
|
||||
{
|
||||
_pluginLog.Verbose("TravelYesNo: Checking previous step...");
|
||||
_logger.LogTrace("TravelYesNo: Checking previous step...");
|
||||
step = sequence.FindStep(currentQuest.Step == 255 ? (sequence.Steps.Count - 1) : (currentQuest.Step - 1));
|
||||
increaseStepCount = false;
|
||||
|
||||
if (step != null)
|
||||
_pluginLog.Verbose($"Previous step: {step.TerritoryId}, {step.TargetTerritoryId}");
|
||||
_logger.LogTrace("Previous step: {CurrentTerritory}, {TargetTerritory}", step.TerritoryId, step.TargetTerritoryId);
|
||||
}
|
||||
|
||||
if (step == null || step.TargetTerritoryId == null)
|
||||
{
|
||||
_pluginLog.Verbose("TravelYesNo: Not found");
|
||||
_logger.LogTrace("TravelYesNo: Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -351,11 +356,11 @@ internal sealed class GameUiController : IDisposable
|
||||
string? excelPrompt = entry.Question?.ToString();
|
||||
if (excelPrompt == null || !GameStringEquals(excelPrompt, actualPrompt))
|
||||
{
|
||||
_pluginLog.Information($"Ignoring prompt '{excelPrompt}'");
|
||||
_logger.LogDebug("Ignoring prompt '{Prompt}'", excelPrompt);
|
||||
continue;
|
||||
}
|
||||
|
||||
_pluginLog.Information($"Using warp {entry.RowId}, {excelPrompt}");
|
||||
_logger.LogInformation("Using warp {Id}, {Prompt}", entry.RowId, excelPrompt);
|
||||
addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
|
||||
if (increaseStepCount)
|
||||
_questController.IncreaseStepCount();
|
||||
@ -365,7 +370,7 @@ internal sealed class GameUiController : IDisposable
|
||||
|
||||
private unsafe void CreditPostSetup(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
_pluginLog.Information("Closing Credits sequence");
|
||||
_logger.LogInformation("Closing Credits sequence");
|
||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||
addon->FireCallbackInt(-2);
|
||||
}
|
||||
@ -374,7 +379,7 @@ internal sealed class GameUiController : IDisposable
|
||||
{
|
||||
if (_questController.CurrentQuest?.Quest.QuestId == 4526)
|
||||
{
|
||||
_pluginLog.Information("Closing Unending Codex");
|
||||
_logger.LogInformation("Closing Unending Codex");
|
||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||
addon->FireCallbackInt(-2);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.External;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.V1;
|
||||
@ -26,18 +27,18 @@ internal sealed class MovementController : IDisposable
|
||||
private readonly IClientState _clientState;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly ILogger<MovementController> _logger;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private Task<List<Vector3>>? _pathfindTask;
|
||||
|
||||
public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, GameFunctions gameFunctions,
|
||||
ICondition condition, IPluginLog pluginLog)
|
||||
ICondition condition, ILogger<MovementController> logger)
|
||||
{
|
||||
_navmeshIpc = navmeshIpc;
|
||||
_clientState = clientState;
|
||||
_gameFunctions = gameFunctions;
|
||||
_condition = condition;
|
||||
_pluginLog = pluginLog;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsNavmeshReady => _navmeshIpc.IsReady;
|
||||
@ -51,9 +52,8 @@ internal sealed class MovementController : IDisposable
|
||||
{
|
||||
if (_pathfindTask.IsCompletedSuccessfully)
|
||||
{
|
||||
_pluginLog.Information(
|
||||
string.Create(CultureInfo.InvariantCulture,
|
||||
$"Pathfinding complete, route: [{string.Join(" → ", _pathfindTask.Result.Select(x => x.ToString()))}]"));
|
||||
_logger.LogInformation("Pathfinding complete, route: [{Route}]",
|
||||
string.Join(" → ", _pathfindTask.Result.Select(x => x.ToString("G", CultureInfo.InvariantCulture))));
|
||||
|
||||
var navPoints = _pathfindTask.Result.Skip(1).ToList();
|
||||
Vector3 start = _clientState.LocalPlayer?.Position ?? navPoints[0];
|
||||
@ -83,7 +83,7 @@ internal sealed class MovementController : IDisposable
|
||||
if (actualDistance > 100f &&
|
||||
ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 4) == 0)
|
||||
{
|
||||
_pluginLog.Information("Triggering Sprint");
|
||||
_logger.LogInformation("Triggering Sprint");
|
||||
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 4);
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ internal sealed class MovementController : IDisposable
|
||||
}
|
||||
else if (_pathfindTask.IsCompleted)
|
||||
{
|
||||
_pluginLog.Information("Unable to complete pathfinding task");
|
||||
_logger.LogWarning("Unable to complete pathfinding task");
|
||||
ResetPathfinding();
|
||||
}
|
||||
}
|
||||
@ -170,7 +170,7 @@ internal sealed class MovementController : IDisposable
|
||||
{
|
||||
fly |= _condition[ConditionFlag.Diving];
|
||||
PrepareNavigation(type, dataId, to, fly, sprint, stopDistance);
|
||||
_pluginLog.Information($"Pathfinding to {Destination}");
|
||||
_logger.LogInformation("Pathfinding to {Destination}", Destination);
|
||||
|
||||
_cancellationTokenSource = new();
|
||||
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
|
||||
@ -183,7 +183,7 @@ internal sealed class MovementController : IDisposable
|
||||
fly |= _condition[ConditionFlag.Diving];
|
||||
PrepareNavigation(type, dataId, to.Last(), fly, sprint, stopDistance);
|
||||
|
||||
_pluginLog.Information($"Moving to {Destination}");
|
||||
_logger.LogInformation("Moving to {Destination}", Destination);
|
||||
_navmeshIpc.MoveTo(to, fly);
|
||||
}
|
||||
|
||||
|
38
Questionable/Controller/NavigationShortcutController.cs
Normal file
38
Questionable/Controller/NavigationShortcutController.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using Questionable.Model;
|
||||
|
||||
namespace Questionable.Controller;
|
||||
|
||||
internal sealed class NavigationShortcutController
|
||||
{
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
|
||||
public NavigationShortcutController(IGameGui gameGui, MovementController movementController,
|
||||
GameFunctions gameFunctions)
|
||||
{
|
||||
_gameGui = gameGui;
|
||||
_movementController = movementController;
|
||||
_gameFunctions = gameFunctions;
|
||||
}
|
||||
|
||||
public unsafe void HandleNavigationShortcut()
|
||||
{
|
||||
var inputData = UIInputData.Instance();
|
||||
if (inputData == null)
|
||||
return;
|
||||
|
||||
if (inputData->IsGameWindowFocused &&
|
||||
inputData->UIFilteredMouseButtonReleasedFlags.HasFlag(MouseButtonFlags.LBUTTON) &&
|
||||
inputData->GetKeyState(SeVirtualKey.MENU).HasFlag(KeyStateFlags.Down) &&
|
||||
_gameGui.ScreenToWorld(new Vector2(inputData->CursorXPosition, inputData->CursorYPosition),
|
||||
out Vector3 worldPos))
|
||||
{
|
||||
_movementController.NavigateTo(EMovementType.Shortcut, null, worldPos,
|
||||
_gameFunctions.IsFlyingUnlockedInCurrentZone(), true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Model;
|
||||
@ -20,39 +16,34 @@ namespace Questionable.Controller;
|
||||
|
||||
internal sealed class QuestController
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IDataManager _dataManager;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly ILogger<QuestController> _logger;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly IFramework _framework;
|
||||
private readonly AetheryteData _aetheryteData;
|
||||
private readonly LifestreamIpc _lifestreamIpc;
|
||||
private readonly TerritoryData _territoryData;
|
||||
private readonly Dictionary<ushort, Quest> _quests = new();
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
|
||||
public QuestController(DalamudPluginInterface pluginInterface, IDataManager dataManager, IClientState clientState,
|
||||
GameFunctions gameFunctions, MovementController movementController, IPluginLog pluginLog, ICondition condition,
|
||||
IChatGui chatGui, IFramework framework, AetheryteData aetheryteData, LifestreamIpc lifestreamIpc)
|
||||
public QuestController(IClientState clientState, GameFunctions gameFunctions, MovementController movementController,
|
||||
ILogger<QuestController> logger, ICondition condition, IChatGui chatGui, IFramework framework,
|
||||
AetheryteData aetheryteData, LifestreamIpc lifestreamIpc, TerritoryData territoryData,
|
||||
QuestRegistry questRegistry)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_dataManager = dataManager;
|
||||
_clientState = clientState;
|
||||
_gameFunctions = gameFunctions;
|
||||
_movementController = movementController;
|
||||
_pluginLog = pluginLog;
|
||||
_logger = logger;
|
||||
_condition = condition;
|
||||
_chatGui = chatGui;
|
||||
_framework = framework;
|
||||
_aetheryteData = aetheryteData;
|
||||
_lifestreamIpc = lifestreamIpc;
|
||||
_territoryData = new TerritoryData(dataManager);
|
||||
|
||||
Reload();
|
||||
_gameFunctions.QuestController = this;
|
||||
_territoryData = territoryData;
|
||||
_questRegistry = questRegistry;
|
||||
}
|
||||
|
||||
|
||||
@ -62,88 +53,10 @@ internal sealed class QuestController
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
_quests.Clear();
|
||||
|
||||
CurrentQuest = null;
|
||||
DebugState = null;
|
||||
|
||||
#if RELEASE
|
||||
_pluginLog.Information("Loading quests from assembly");
|
||||
QuestPaths.AssemblyQuestLoader.LoadQuestsFromEmbeddedResources(LoadQuestFromStream);
|
||||
#else
|
||||
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation?.Directory?.Parent?.Parent;
|
||||
if (solutionDirectory != null)
|
||||
{
|
||||
DirectoryInfo pathProjectDirectory =
|
||||
new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "QuestPaths"));
|
||||
if (pathProjectDirectory.Exists)
|
||||
{
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "Shadowbringers")));
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "Endwalker")));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests")));
|
||||
|
||||
foreach (var (questId, quest) in _quests)
|
||||
{
|
||||
var questData =
|
||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets.Quest>()!.GetRow((uint)questId + 0x10000);
|
||||
if (questData == null)
|
||||
continue;
|
||||
|
||||
quest.Name = questData.Name.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadQuestFromStream(string fileName, Stream stream)
|
||||
{
|
||||
_pluginLog.Verbose($"Loading quest from '{fileName}'");
|
||||
var (questId, name) = ExtractQuestDataFromName(fileName);
|
||||
Quest quest = new Quest
|
||||
{
|
||||
QuestId = questId,
|
||||
Name = name,
|
||||
Data = JsonSerializer.Deserialize<QuestData>(stream)!,
|
||||
};
|
||||
_quests[questId] = quest;
|
||||
}
|
||||
|
||||
public bool IsKnownQuest(ushort questId) => _quests.ContainsKey(questId);
|
||||
|
||||
private void LoadFromDirectory(DirectoryInfo directory)
|
||||
{
|
||||
if (!directory.Exists)
|
||||
{
|
||||
_pluginLog.Information($"Not loading quests from {directory} (doesn't exist)");
|
||||
return;
|
||||
}
|
||||
|
||||
_pluginLog.Information($"Loading quests from {directory}");
|
||||
foreach (FileInfo fileInfo in directory.GetFiles("*.json"))
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
||||
LoadQuestFromStream(fileInfo.Name, stream);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidDataException($"Unable to load file {fileInfo.FullName}", e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo childDirectory in directory.GetDirectories())
|
||||
LoadFromDirectory(childDirectory);
|
||||
}
|
||||
|
||||
private static (ushort QuestId, string Name) ExtractQuestDataFromName(string resourceName)
|
||||
{
|
||||
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
||||
name = name.Substring(name.LastIndexOf('.') + 1);
|
||||
|
||||
string[] parts = name.Split('_', 2);
|
||||
return (ushort.Parse(parts[0], CultureInfo.InvariantCulture), parts[1]);
|
||||
_questRegistry.Reload();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
@ -158,7 +71,7 @@ internal sealed class QuestController
|
||||
}
|
||||
else if (CurrentQuest == null || CurrentQuest.Quest.QuestId != currentQuestId)
|
||||
{
|
||||
if (_quests.TryGetValue(currentQuestId, out var quest))
|
||||
if (_questRegistry.TryGetQuest(currentQuestId, out var quest))
|
||||
CurrentQuest = new QuestProgress(quest, currentSequence, 0);
|
||||
else if (CurrentQuest != null)
|
||||
CurrentQuest = null;
|
||||
@ -244,7 +157,7 @@ internal sealed class QuestController
|
||||
(QuestSequence? seq, QuestStep? step) = GetNextStep();
|
||||
if (CurrentQuest == null || seq == null || step == null)
|
||||
{
|
||||
_pluginLog.Warning("Unable to retrieve next quest step, not increasing step count");
|
||||
_logger.LogWarning("Unable to retrieve next quest step, not increasing step count");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -271,7 +184,7 @@ internal sealed class QuestController
|
||||
(QuestSequence? seq, QuestStep? step) = GetNextStep();
|
||||
if (CurrentQuest == null || seq == null || step == null)
|
||||
{
|
||||
_pluginLog.Warning("Unable to retrieve next quest step, not increasing dialogue choice count");
|
||||
_logger.LogWarning("Unable to retrieve next quest step, not increasing dialogue choice count");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -292,13 +205,13 @@ internal sealed class QuestController
|
||||
(QuestSequence? seq, QuestStep? step) = GetNextStep();
|
||||
if (CurrentQuest == null || seq == null || step == null)
|
||||
{
|
||||
_pluginLog.Warning("Could not retrieve next quest step, not doing anything");
|
||||
_logger.LogWarning("Could not retrieve next quest step, not doing anything");
|
||||
return;
|
||||
}
|
||||
|
||||
if (step.Disabled)
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as it is disabled");
|
||||
_logger.LogInformation("Skipping step, as it is disabled");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -315,14 +228,14 @@ internal sealed class QuestController
|
||||
(_aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.From) < 20 ||
|
||||
_aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.To) < 20)))
|
||||
{
|
||||
_pluginLog.Information("Skipping aetheryte teleport");
|
||||
_logger.LogInformation("Skipping aetheryte teleport");
|
||||
skipTeleport = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (skipTeleport)
|
||||
{
|
||||
_pluginLog.Information("Marking aetheryte shortcut as used");
|
||||
_logger.LogInformation("Marking aetheryte shortcut as used");
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
StepProgress = CurrentQuest.StepProgress with { AetheryteShortcutUsed = true }
|
||||
@ -332,12 +245,12 @@ internal sealed class QuestController
|
||||
{
|
||||
if (!_gameFunctions.IsAetheryteUnlocked(step.AetheryteShortcut.Value))
|
||||
{
|
||||
_pluginLog.Error($"Aetheryte {step.AetheryteShortcut.Value} is not unlocked.");
|
||||
_logger.LogError("Aetheryte {Aetheryte} is not unlocked.", step.AetheryteShortcut.Value);
|
||||
_chatGui.Print($"[Questionable] Aetheryte {step.AetheryteShortcut.Value} is not unlocked.");
|
||||
}
|
||||
else if (_gameFunctions.TeleportAetheryte(step.AetheryteShortcut.Value))
|
||||
{
|
||||
_pluginLog.Information("Travelling via aetheryte...");
|
||||
_logger.LogInformation("Travelling via aetheryte...");
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
StepProgress = CurrentQuest.StepProgress with { AetheryteShortcutUsed = true }
|
||||
@ -345,7 +258,7 @@ internal sealed class QuestController
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginLog.Warning("Unable to teleport to aetheryte");
|
||||
_logger.LogWarning("Unable to teleport to aetheryte");
|
||||
_chatGui.Print("[Questionable] Unable to teleport to aetheryte.");
|
||||
}
|
||||
|
||||
@ -355,12 +268,12 @@ internal sealed class QuestController
|
||||
|
||||
if (!step.SkipIf.Contains(ESkipCondition.Never))
|
||||
{
|
||||
_pluginLog.Information($"Checking skip conditions; {string.Join(",", step.SkipIf)}");
|
||||
_logger.LogInformation("Checking skip conditions; {ConfiguredConditions}", string.Join(",", step.SkipIf));
|
||||
|
||||
if (step.SkipIf.Contains(ESkipCondition.FlyingUnlocked) &&
|
||||
_gameFunctions.IsFlyingUnlocked(step.TerritoryId))
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as flying is unlocked");
|
||||
_logger.LogInformation("Skipping step, as flying is unlocked");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -368,7 +281,7 @@ internal sealed class QuestController
|
||||
if (step.SkipIf.Contains(ESkipCondition.FlyingLocked) &&
|
||||
!_gameFunctions.IsFlyingUnlocked(step.TerritoryId))
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as flying is locked");
|
||||
_logger.LogInformation("Skipping step, as flying is locked");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -380,7 +293,7 @@ internal sealed class QuestController
|
||||
} &&
|
||||
_gameFunctions.IsAetheryteUnlocked((EAetheryteLocation)step.DataId.Value))
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as aetheryte/aethernet shard is unlocked");
|
||||
_logger.LogInformation("Skipping step, as aetheryte/aethernet shard is unlocked");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -388,7 +301,7 @@ internal sealed class QuestController
|
||||
if (step is { DataId: not null, InteractionType: EInteractionType.AttuneAetherCurrent } &&
|
||||
_gameFunctions.IsAetherCurrentUnlocked(step.DataId.Value))
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as current is unlocked");
|
||||
_logger.LogInformation("Skipping step, as current is unlocked");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -396,7 +309,7 @@ internal sealed class QuestController
|
||||
QuestWork? questWork = _gameFunctions.GetQuestEx(CurrentQuest.Quest.QuestId);
|
||||
if (questWork != null && step.MatchesQuestVariables(questWork.Value))
|
||||
{
|
||||
_pluginLog.Information("Skipping step, as quest variables match");
|
||||
_logger.LogInformation("Skipping step, as quest variables match");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -418,7 +331,7 @@ internal sealed class QuestController
|
||||
{
|
||||
if (_aetheryteData.CalculateDistance(playerPosition, territoryType, from) < 11)
|
||||
{
|
||||
_pluginLog.Information($"Using lifestream to teleport to {to}");
|
||||
_logger.LogInformation("Using lifestream to teleport to {Destination}", to);
|
||||
_lifestreamIpc.Teleport(to);
|
||||
CurrentQuest = CurrentQuest with
|
||||
{
|
||||
@ -427,7 +340,7 @@ internal sealed class QuestController
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginLog.Information("Moving to aethernet shortcut");
|
||||
_logger.LogInformation("Moving to aethernet shortcut");
|
||||
_movementController.NavigateTo(EMovementType.Quest, (uint)from, _aetheryteData.Locations[from],
|
||||
false, true,
|
||||
AetheryteConverter.IsLargeAetheryte(from) ? 10.9f : 6.9f);
|
||||
@ -437,14 +350,16 @@ internal sealed class QuestController
|
||||
}
|
||||
}
|
||||
else
|
||||
_pluginLog.Warning(
|
||||
$"Aethernet shortcut not unlocked (from: {step.AethernetShortcut.From}, to: {step.AethernetShortcut.To}), walking manually");
|
||||
_logger.LogWarning(
|
||||
"Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), walking manually",
|
||||
step.AethernetShortcut.From, step.AethernetShortcut.To);
|
||||
}
|
||||
|
||||
if (step.TargetTerritoryId.HasValue && step.TerritoryId != step.TargetTerritoryId && step.TargetTerritoryId == _clientState.TerritoryType)
|
||||
if (step.TargetTerritoryId.HasValue && step.TerritoryId != step.TargetTerritoryId &&
|
||||
step.TargetTerritoryId == _clientState.TerritoryType)
|
||||
{
|
||||
// we assume whatever e.g. interaction, walkto etc. we have will trigger the zone transition
|
||||
_pluginLog.Information("Zone transition, skipping rest of step");
|
||||
_logger.LogInformation("Zone transition, skipping rest of step");
|
||||
IncreaseStepCount();
|
||||
return;
|
||||
}
|
||||
@ -453,7 +368,7 @@ internal sealed class QuestController
|
||||
(_clientState.LocalPlayer!.Position - step.JumpDestination.Position).Length() <=
|
||||
(step.JumpDestination.StopDistance ?? 1f))
|
||||
{
|
||||
_pluginLog.Information("We're at the jump destination, skipping movement");
|
||||
_logger.LogInformation("We're at the jump destination, skipping movement");
|
||||
}
|
||||
else if (step.Position != null)
|
||||
{
|
||||
@ -468,7 +383,7 @@ internal sealed class QuestController
|
||||
|
||||
if (step.Mount == true && !_gameFunctions.HasStatusPreventingSprintOrMount())
|
||||
{
|
||||
_pluginLog.Information("Step explicitly wants a mount, trying to mount...");
|
||||
_logger.LogInformation("Step explicitly wants a mount, trying to mount...");
|
||||
if (!_condition[ConditionFlag.Mounted] && !_condition[ConditionFlag.InCombat] &&
|
||||
_territoryData.CanUseMount(_clientState.TerritoryType))
|
||||
{
|
||||
@ -478,7 +393,7 @@ internal sealed class QuestController
|
||||
}
|
||||
else if (step.Mount == false)
|
||||
{
|
||||
_pluginLog.Information("Step explicitly wants no mount, trying to unmount...");
|
||||
_logger.LogInformation("Step explicitly wants no mount, trying to unmount...");
|
||||
if (_condition[ConditionFlag.Mounted])
|
||||
{
|
||||
_gameFunctions.Unmount();
|
||||
@ -499,7 +414,7 @@ internal sealed class QuestController
|
||||
if (actualDistance > distance)
|
||||
{
|
||||
_movementController.NavigateTo(EMovementType.Quest, step.DataId, step.Position.Value,
|
||||
fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
|
||||
fly: step.Fly == true && _gameFunctions.IsFlyingUnlockedInCurrentZone(),
|
||||
sprint: step.Sprint != false,
|
||||
stopDistance: distance);
|
||||
return;
|
||||
@ -511,7 +426,7 @@ internal sealed class QuestController
|
||||
if (actualDistance > distance)
|
||||
{
|
||||
_movementController.NavigateTo(EMovementType.Quest, step.DataId, [step.Position.Value],
|
||||
fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
|
||||
fly: step.Fly == true && _gameFunctions.IsFlyingUnlockedInCurrentZone(),
|
||||
sprint: step.Sprint != false,
|
||||
stopDistance: distance);
|
||||
return;
|
||||
@ -524,12 +439,12 @@ internal sealed class QuestController
|
||||
if (gameObject == null ||
|
||||
(gameObject.Position - _clientState.LocalPlayer!.Position).Length() > step.StopDistance)
|
||||
{
|
||||
_pluginLog.Warning("Object not found or too far away, no position so we can't move");
|
||||
_logger.LogWarning("Object not found or too far away, no position so we can't move");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_pluginLog.Information($"Running logic for {step.InteractionType}");
|
||||
_logger.LogInformation("Running logic for {InteractionType}", step.InteractionType);
|
||||
switch (step.InteractionType)
|
||||
{
|
||||
case EInteractionType.Interact:
|
||||
@ -538,7 +453,7 @@ internal sealed class QuestController
|
||||
GameObject? gameObject = _gameFunctions.FindObjectByDataId(step.DataId.Value);
|
||||
if (gameObject == null)
|
||||
{
|
||||
_pluginLog.Warning($"No game object with dataId {step.DataId}");
|
||||
_logger.LogWarning("No game object with dataId {DataId}", step.DataId);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -555,7 +470,7 @@ internal sealed class QuestController
|
||||
IncreaseStepCount();
|
||||
}
|
||||
else
|
||||
_pluginLog.Warning("Not interacting on current step, DataId is null");
|
||||
_logger.LogWarning("Not interacting on current step, DataId is null");
|
||||
|
||||
break;
|
||||
|
||||
@ -584,8 +499,10 @@ internal sealed class QuestController
|
||||
case EInteractionType.AttuneAetherCurrent:
|
||||
if (step.DataId != null)
|
||||
{
|
||||
_pluginLog.Information(
|
||||
$"{step.AetherCurrentId} → {_gameFunctions.IsAetherCurrentUnlocked(step.AetherCurrentId.GetValueOrDefault())}");
|
||||
_logger.LogInformation(
|
||||
"{AetherCurrentId} is unlocked = {Unlocked}",
|
||||
step.AetherCurrentId,
|
||||
_gameFunctions.IsAetherCurrentUnlocked(step.AetherCurrentId.GetValueOrDefault()));
|
||||
if (step.AetherCurrentId == null ||
|
||||
!_gameFunctions.IsAetherCurrentUnlocked(step.AetherCurrentId.Value))
|
||||
_gameFunctions.InteractWith(step.DataId.Value);
|
||||
@ -662,7 +579,8 @@ internal sealed class QuestController
|
||||
|
||||
if (step.ChatMessage != null)
|
||||
{
|
||||
string? excelString = _gameFunctions.GetDialogueText(CurrentQuest.Quest, step.ChatMessage.ExcelSheet,
|
||||
string? excelString = _gameFunctions.GetDialogueText(CurrentQuest.Quest,
|
||||
step.ChatMessage.ExcelSheet,
|
||||
step.ChatMessage.Key);
|
||||
if (excelString == null)
|
||||
return;
|
||||
@ -722,7 +640,7 @@ internal sealed class QuestController
|
||||
break;
|
||||
|
||||
default:
|
||||
_pluginLog.Warning($"Action '{step.InteractionType}' is not implemented");
|
||||
_logger.LogWarning("Action '{InteractionType}' is not implemented", step.InteractionType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
116
Questionable/Controller/QuestRegistry.cs
Normal file
116
Questionable/Controller/QuestRegistry.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.V1;
|
||||
|
||||
namespace Questionable.Controller;
|
||||
|
||||
internal sealed class QuestRegistry
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IDataManager _dataManager;
|
||||
private readonly ILogger<QuestRegistry> _logger;
|
||||
|
||||
private readonly Dictionary<ushort, Quest> _quests = new();
|
||||
|
||||
public QuestRegistry(DalamudPluginInterface pluginInterface, IDataManager dataManager, ILogger<QuestRegistry> logger)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_dataManager = dataManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
_quests.Clear();
|
||||
|
||||
#if RELEASE
|
||||
_logger.LogInformation("Loading quests from assembly");
|
||||
QuestPaths.AssemblyQuestLoader.LoadQuestsFromEmbeddedResources(LoadQuestFromStream);
|
||||
#else
|
||||
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation?.Directory?.Parent?.Parent;
|
||||
if (solutionDirectory != null)
|
||||
{
|
||||
DirectoryInfo pathProjectDirectory =
|
||||
new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "QuestPaths"));
|
||||
if (pathProjectDirectory.Exists)
|
||||
{
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "Shadowbringers")));
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "Endwalker")));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "Quests")));
|
||||
|
||||
foreach (var (questId, quest) in _quests)
|
||||
{
|
||||
var questData =
|
||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets.Quest>()!.GetRow((uint)questId + 0x10000);
|
||||
if (questData == null)
|
||||
continue;
|
||||
|
||||
quest.Name = questData.Name.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadQuestFromStream(string fileName, Stream stream)
|
||||
{
|
||||
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
|
||||
var (questId, name) = ExtractQuestDataFromName(fileName);
|
||||
Quest quest = new Quest
|
||||
{
|
||||
QuestId = questId,
|
||||
Name = name,
|
||||
Data = JsonSerializer.Deserialize<QuestData>(stream)!,
|
||||
};
|
||||
_quests[questId] = quest;
|
||||
}
|
||||
|
||||
private void LoadFromDirectory(DirectoryInfo directory)
|
||||
{
|
||||
if (!directory.Exists)
|
||||
{
|
||||
_logger.LogInformation("Not loading quests from {DirectoryName} (doesn't exist)", directory);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loading quests from {DirectoryName}", directory);
|
||||
foreach (FileInfo fileInfo in directory.GetFiles("*.json"))
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
||||
LoadQuestFromStream(fileInfo.Name, stream);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidDataException($"Unable to load file {fileInfo.FullName}", e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo childDirectory in directory.GetDirectories())
|
||||
LoadFromDirectory(childDirectory);
|
||||
}
|
||||
|
||||
private static (ushort QuestId, string Name) ExtractQuestDataFromName(string resourceName)
|
||||
{
|
||||
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
||||
name = name.Substring(name.LastIndexOf('.') + 1);
|
||||
|
||||
string[] parts = name.Split('_', 2);
|
||||
return (ushort.Parse(parts[0], CultureInfo.InvariantCulture), parts[1]);
|
||||
}
|
||||
|
||||
public bool IsKnownQuest(ushort questId) => _quests.ContainsKey(questId);
|
||||
|
||||
public bool TryGetQuest(ushort questId, [NotNullWhen(true)] out Quest? quest)
|
||||
=> _quests.TryGetValue(questId, out quest);
|
||||
}
|
65
Questionable/DalamudInitializer.cs
Normal file
65
Questionable/DalamudInitializer.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Windows;
|
||||
|
||||
namespace Questionable;
|
||||
|
||||
internal sealed class DalamudInitializer : IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IFramework _framework;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly QuestController _questController;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly NavigationShortcutController _navigationShortcutController;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly DebugWindow _debugWindow;
|
||||
|
||||
public DalamudInitializer(DalamudPluginInterface pluginInterface, IFramework framework,
|
||||
ICommandManager commandManager, QuestController questController, MovementController movementController,
|
||||
GameUiController gameUiController, NavigationShortcutController navigationShortcutController, WindowSystem windowSystem, DebugWindow debugWindow)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_framework = framework;
|
||||
_commandManager = commandManager;
|
||||
_questController = questController;
|
||||
_movementController = movementController;
|
||||
_navigationShortcutController = navigationShortcutController;
|
||||
_windowSystem = windowSystem;
|
||||
_debugWindow = debugWindow;
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||
_pluginInterface.UiBuilder.OpenMainUi += _debugWindow.Toggle;
|
||||
_framework.Update += FrameworkUpdate;
|
||||
_commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand)
|
||||
{
|
||||
HelpMessage = "Opens the Questing window"
|
||||
});
|
||||
|
||||
_framework.RunOnTick(gameUiController.HandleCurrentDialogueChoices, TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
|
||||
private void FrameworkUpdate(IFramework framework)
|
||||
{
|
||||
_questController.Update();
|
||||
_navigationShortcutController.HandleNavigationShortcut();
|
||||
_movementController.Update();
|
||||
}
|
||||
|
||||
private void ProcessCommand(string command, string arguments)
|
||||
{
|
||||
_debugWindow.Toggle();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_commandManager.RemoveHandler("/qst");
|
||||
_framework.Update -= FrameworkUpdate;
|
||||
_pluginInterface.UiBuilder.OpenMainUi -= _debugWindow.Toggle;
|
||||
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using Lumina.Excel.CustomSheets;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Model.V1;
|
||||
using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
|
||||
@ -51,17 +52,20 @@ internal sealed unsafe class GameFunctions
|
||||
private readonly ITargetManager _targetManager;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly QuestRegistry _questRegistry;
|
||||
private readonly ILogger<GameFunctions> _logger;
|
||||
|
||||
public GameFunctions(IDataManager dataManager, IObjectTable objectTable, ISigScanner sigScanner,
|
||||
ITargetManager targetManager, ICondition condition, IClientState clientState, IPluginLog pluginLog)
|
||||
ITargetManager targetManager, ICondition condition, IClientState clientState, QuestRegistry questRegistry,
|
||||
ILogger<GameFunctions> logger)
|
||||
{
|
||||
_dataManager = dataManager;
|
||||
_objectTable = objectTable;
|
||||
_targetManager = targetManager;
|
||||
_condition = condition;
|
||||
_clientState = clientState;
|
||||
_pluginLog = pluginLog;
|
||||
_questRegistry = questRegistry;
|
||||
_logger = logger;
|
||||
_processChatBox =
|
||||
Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(sigScanner.ScanText(Signatures.SendChat));
|
||||
_sanitiseString =
|
||||
@ -85,9 +89,6 @@ internal sealed unsafe class GameFunctions
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
// FIXME
|
||||
public QuestController QuestController { private get; set; } = null!;
|
||||
|
||||
public (ushort CurrentQuest, byte Sequence) GetCurrentQuest()
|
||||
{
|
||||
ushort currentQuest;
|
||||
@ -108,7 +109,7 @@ internal sealed unsafe class GameFunctions
|
||||
break;
|
||||
}
|
||||
|
||||
if (QuestController.IsKnownQuest(currentQuest))
|
||||
if (_questRegistry.IsKnownQuest(currentQuest))
|
||||
return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
|
||||
}
|
||||
}
|
||||
@ -190,6 +191,8 @@ internal sealed unsafe class GameFunctions
|
||||
playerState->IsAetherCurrentZoneComplete(aetherCurrentCompFlgSet);
|
||||
}
|
||||
|
||||
public bool IsFlyingUnlockedInCurrentZone() => IsFlyingUnlocked(_clientState.TerritoryType);
|
||||
|
||||
public bool IsAetherCurrentUnlocked(uint aetherCurrentId)
|
||||
{
|
||||
var playerState = PlayerState.Instance();
|
||||
@ -335,7 +338,7 @@ internal sealed unsafe class GameFunctions
|
||||
}
|
||||
}
|
||||
|
||||
_pluginLog.Warning($"Could not find GameObject with dataId {dataId}");
|
||||
_logger.LogWarning("Could not find GameObject with dataId {DataId}", dataId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -344,7 +347,7 @@ internal sealed unsafe class GameFunctions
|
||||
GameObject? gameObject = FindObjectByDataId(dataId);
|
||||
if (gameObject != null)
|
||||
{
|
||||
_pluginLog.Information($"Setting target with {dataId} to {gameObject.ObjectId}");
|
||||
_logger.LogInformation("Setting target with {DataId} to {ObjectId}", dataId, gameObject.ObjectId);
|
||||
_targetManager.Target = gameObject;
|
||||
|
||||
TargetSystem.Instance()->InteractWithObject(
|
||||
@ -400,7 +403,7 @@ internal sealed unsafe class GameFunctions
|
||||
|
||||
public bool HasStatusPreventingSprintOrMount()
|
||||
{
|
||||
if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlocked(_clientState.TerritoryType))
|
||||
if (_condition[ConditionFlag.Swimming] && !IsFlyingUnlockedInCurrentZone())
|
||||
return true;
|
||||
|
||||
// company chocobo is locked
|
||||
@ -428,7 +431,7 @@ internal sealed unsafe class GameFunctions
|
||||
{
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, 71) == 0)
|
||||
{
|
||||
_pluginLog.Information("Using SDS Fenrir as mount");
|
||||
_logger.LogInformation("Using SDS Fenrir as mount");
|
||||
ActionManager.Instance()->UseAction(ActionType.Mount, 71);
|
||||
}
|
||||
}
|
||||
@ -436,7 +439,7 @@ internal sealed unsafe class GameFunctions
|
||||
{
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 9) == 0)
|
||||
{
|
||||
_pluginLog.Information("Using mount roulette");
|
||||
_logger.LogInformation("Using mount roulette");
|
||||
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 9);
|
||||
}
|
||||
}
|
||||
@ -449,11 +452,11 @@ internal sealed unsafe class GameFunctions
|
||||
{
|
||||
if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23) == 0)
|
||||
{
|
||||
_pluginLog.Information("Unmounting...");
|
||||
_logger.LogInformation("Unmounting...");
|
||||
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23);
|
||||
}
|
||||
else
|
||||
_pluginLog.Warning("Can't unmount right now?");
|
||||
_logger.LogWarning("Can't unmount right now?");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -468,11 +471,12 @@ internal sealed unsafe class GameFunctions
|
||||
if (UIState.IsInstanceContentUnlocked(contentId))
|
||||
AgentContentsFinder.Instance()->OpenRegularDuty(contentFinderConditionId);
|
||||
else
|
||||
_pluginLog.Error(
|
||||
$"Trying to access a locked duty (cf: {contentFinderConditionId}, content: {contentId})");
|
||||
_logger.LogError(
|
||||
"Trying to access a locked duty (cf: {ContentFinderId}, content: {ContentId})",
|
||||
contentFinderConditionId, contentId);
|
||||
}
|
||||
else
|
||||
_pluginLog.Error($"Could not find content for content finder condition (cf: {contentFinderConditionId})");
|
||||
_logger.LogError("Could not find content for content finder condition (cf: {ContentFinderId})", contentFinderConditionId);
|
||||
}
|
||||
|
||||
public string? GetDialogueText(Quest currentQuest, string? excelSheetName, string key)
|
||||
@ -482,7 +486,7 @@ internal sealed unsafe class GameFunctions
|
||||
var questRow = _dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId + 0x10000);
|
||||
if (questRow == null)
|
||||
{
|
||||
_pluginLog.Error($"Could not find quest row for {currentQuest.QuestId}");
|
||||
_logger.LogError("Could not find quest row for {QuestId}", currentQuest.QuestId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -492,7 +496,7 @@ internal sealed unsafe class GameFunctions
|
||||
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
|
||||
if (excelSheet == null)
|
||||
{
|
||||
_pluginLog.Error($"Unknown excel sheet '{excelSheetName}'");
|
||||
_logger.LogError("Unknown excel sheet '{SheetName}'", excelSheetName);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
10
Questionable/GlobalSuppressions.cs
Normal file
10
Questionable/GlobalSuppressions.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global",
|
||||
Justification = "Properties are used for serialization",
|
||||
Scope = "namespaceanddescendants",
|
||||
Target = "Questionable.Model.V1")]
|
||||
[assembly: SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global",
|
||||
Justification = "Properties are used for serialization",
|
||||
Scope = "namespaceanddescendants",
|
||||
Target = "Questionable.Model.V1")]
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(AethernetShortcutConverter))]
|
||||
public sealed class AethernetShortcut
|
||||
internal sealed class AethernetShortcut
|
||||
{
|
||||
public EAetheryteLocation From { get; set; }
|
||||
public EAetheryteLocation To { get; set; }
|
||||
|
@ -1,6 +1,9 @@
|
||||
namespace Questionable.Model.V1;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
public sealed class ChatMessage
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class ChatMessage
|
||||
{
|
||||
public string? ExcelSheet { get; set; }
|
||||
public string Key { get; set; } = null!;
|
||||
|
@ -6,7 +6,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class AethernetShortcutConverter : JsonConverter<AethernetShortcut>
|
||||
internal sealed class AethernetShortcutConverter : JsonConverter<AethernetShortcut>
|
||||
{
|
||||
private static readonly Dictionary<EAetheryteLocation, string> EnumToString = new()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class AetheryteConverter() : EnumConverter<EAetheryteLocation>(Values)
|
||||
internal sealed class AetheryteConverter() : EnumConverter<EAetheryteLocation>(Values)
|
||||
{
|
||||
private static readonly Dictionary<EAetheryteLocation, string> Values = new()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class DialogueChoiceTypeConverter() : EnumConverter<EDialogChoiceType>(Values)
|
||||
internal sealed class DialogueChoiceTypeConverter() : EnumConverter<EDialogChoiceType>(Values)
|
||||
{
|
||||
private static readonly Dictionary<EDialogChoiceType, string> Values = new()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class EmoteConverter() : EnumConverter<EEmote>(Values)
|
||||
internal sealed class EmoteConverter() : EnumConverter<EEmote>(Values)
|
||||
{
|
||||
private static readonly Dictionary<EEmote, string> Values = new()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class EnemySpawnTypeConverter() : EnumConverter<EEnemySpawnType>(Values)
|
||||
internal sealed class EnemySpawnTypeConverter() : EnumConverter<EEnemySpawnType>(Values)
|
||||
{
|
||||
private static readonly Dictionary<EEnemySpawnType, string> Values = new()
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public abstract class EnumConverter<T> : JsonConverter<T>
|
||||
internal abstract class EnumConverter<T> : JsonConverter<T>
|
||||
where T : Enum
|
||||
{
|
||||
private readonly ReadOnlyDictionary<T, string> _enumToString;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>(Values)
|
||||
internal sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>(Values)
|
||||
{
|
||||
private static readonly Dictionary<EInteractionType, string> Values = new()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public sealed class SkipConditionConverter() : EnumConverter<ESkipCondition>(Values)
|
||||
internal sealed class SkipConditionConverter() : EnumConverter<ESkipCondition>(Values)
|
||||
{
|
||||
private static readonly Dictionary<ESkipCondition, string> Values = new()
|
||||
{
|
||||
|
@ -5,7 +5,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.V1.Converter;
|
||||
|
||||
public class VectorConverter : JsonConverter<Vector3>
|
||||
internal sealed class VectorConverter : JsonConverter<Vector3>
|
||||
{
|
||||
public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
|
@ -1,14 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using Questionable.Model.V1.Converter;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public class DialogueChoice
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class DialogueChoice
|
||||
{
|
||||
[JsonConverter(typeof(DialogueChoiceTypeConverter))]
|
||||
public EDialogChoiceType Type { get; set; }
|
||||
public string? ExcelSheet { get; set; }
|
||||
public string? Prompt { get; set; } = null!;
|
||||
public string? Prompt { get; set; }
|
||||
public bool Yes { get; set; } = true;
|
||||
public string? Answer { get; set; }
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(AetheryteConverter))]
|
||||
public enum EAetheryteLocation
|
||||
internal enum EAetheryteLocation
|
||||
{
|
||||
None = 0,
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public enum EDialogChoiceType
|
||||
internal enum EDialogChoiceType
|
||||
{
|
||||
None,
|
||||
YesNo,
|
||||
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(EmoteConverter))]
|
||||
public enum EEmote
|
||||
internal enum EEmote
|
||||
{
|
||||
None = 0,
|
||||
|
||||
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(EnemySpawnTypeConverter))]
|
||||
public enum EEnemySpawnType
|
||||
internal enum EEnemySpawnType
|
||||
{
|
||||
None = 0,
|
||||
AfterInteraction,
|
||||
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(InteractionTypeConverter))]
|
||||
public enum EInteractionType
|
||||
internal enum EInteractionType
|
||||
{
|
||||
Interact,
|
||||
WalkTo,
|
||||
|
@ -4,7 +4,7 @@ using Questionable.Model.V1.Converter;
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
[JsonConverter(typeof(SkipConditionConverter))]
|
||||
public enum ESkipCondition
|
||||
internal enum ESkipCondition
|
||||
{
|
||||
None,
|
||||
Never,
|
||||
|
@ -1,10 +1,12 @@
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using Questionable.Model.V1.Converter;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public sealed class JumpDestination
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class JumpDestination
|
||||
{
|
||||
[JsonConverter(typeof(VectorConverter))]
|
||||
public Vector3 Position { get; set; }
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public class QuestData
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class QuestData
|
||||
{
|
||||
public required string Author { get; set; }
|
||||
public List<string> Contributors { get; set; } = new();
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public class QuestSequence
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class QuestSequence
|
||||
{
|
||||
public required int Sequence { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
|
@ -1,12 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
||||
using JetBrains.Annotations;
|
||||
using Questionable.Model.V1.Converter;
|
||||
|
||||
namespace Questionable.Model.V1;
|
||||
|
||||
public class QuestStep
|
||||
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]
|
||||
internal sealed class QuestStep
|
||||
{
|
||||
public EInteractionType InteractionType { get; set; }
|
||||
|
||||
|
@ -23,7 +23,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="3.0.0" />
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.3"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,113 +1,84 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dalamud.Extensions.MicrosoftLogging;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Controller;
|
||||
using Questionable.Data;
|
||||
using Questionable.External;
|
||||
using Questionable.Model;
|
||||
using Questionable.Windows;
|
||||
|
||||
namespace Questionable;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global")]
|
||||
public sealed class QuestionablePlugin : IDalamudPlugin
|
||||
{
|
||||
private readonly WindowSystem _windowSystem = new(nameof(Questionable));
|
||||
private readonly ServiceProvider? _serviceProvider;
|
||||
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly IFramework _framework;
|
||||
private readonly IGameGui _gameGui;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
private readonly QuestController _questController;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly GameUiController _gameUiController;
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
public QuestionablePlugin(DalamudPluginInterface pluginInterface, IClientState clientState,
|
||||
ITargetManager targetManager, IFramework framework, IGameGui gameGui, IDataManager dataManager,
|
||||
ISigScanner sigScanner, IObjectTable objectTable, IPluginLog pluginLog, ICondition condition, IChatGui chatGui,
|
||||
ICommandManager commandManager, IAddonLifecycle addonLifecycle)
|
||||
public QuestionablePlugin(DalamudPluginInterface pluginInterface,
|
||||
IClientState clientState,
|
||||
ITargetManager targetManager,
|
||||
IFramework framework,
|
||||
IGameGui gameGui,
|
||||
IDataManager dataManager,
|
||||
ISigScanner sigScanner,
|
||||
IObjectTable objectTable,
|
||||
IPluginLog pluginLog,
|
||||
ICondition condition,
|
||||
IChatGui chatGui,
|
||||
ICommandManager commandManager,
|
||||
IAddonLifecycle addonLifecycle)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(pluginInterface);
|
||||
ArgumentNullException.ThrowIfNull(sigScanner);
|
||||
ArgumentNullException.ThrowIfNull(dataManager);
|
||||
ArgumentNullException.ThrowIfNull(objectTable);
|
||||
|
||||
_pluginInterface = pluginInterface;
|
||||
_clientState = clientState;
|
||||
_framework = framework;
|
||||
_gameGui = gameGui;
|
||||
_commandManager = commandManager;
|
||||
_gameFunctions = new GameFunctions(dataManager, objectTable, sigScanner, targetManager, condition, clientState,
|
||||
pluginLog);
|
||||
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
|
||||
ServiceCollection serviceCollection = new();
|
||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace)
|
||||
.ClearProviders()
|
||||
.AddDalamudLogger(pluginLog));
|
||||
serviceCollection.AddSingleton<IDalamudPlugin>(this);
|
||||
serviceCollection.AddSingleton(pluginInterface);
|
||||
serviceCollection.AddSingleton(clientState);
|
||||
serviceCollection.AddSingleton(targetManager);
|
||||
serviceCollection.AddSingleton(framework);
|
||||
serviceCollection.AddSingleton(gameGui);
|
||||
serviceCollection.AddSingleton(dataManager);
|
||||
serviceCollection.AddSingleton(sigScanner);
|
||||
serviceCollection.AddSingleton(objectTable);
|
||||
serviceCollection.AddSingleton(condition);
|
||||
serviceCollection.AddSingleton(chatGui);
|
||||
serviceCollection.AddSingleton(commandManager);
|
||||
serviceCollection.AddSingleton(addonLifecycle);
|
||||
serviceCollection.AddSingleton(new WindowSystem(nameof(Questionable)));
|
||||
serviceCollection.AddSingleton((Configuration?)pluginInterface.GetPluginConfig() ?? new Configuration());
|
||||
|
||||
AetheryteData aetheryteData = new AetheryteData(dataManager);
|
||||
NavmeshIpc navmeshIpc = new NavmeshIpc(pluginInterface);
|
||||
LifestreamIpc lifestreamIpc = new LifestreamIpc(pluginInterface, aetheryteData);
|
||||
_movementController =
|
||||
new MovementController(navmeshIpc, clientState, _gameFunctions, condition, pluginLog);
|
||||
_questController = new QuestController(pluginInterface, dataManager, _clientState, _gameFunctions,
|
||||
_movementController, pluginLog, condition, chatGui, framework, aetheryteData, lifestreamIpc);
|
||||
_gameUiController =
|
||||
new GameUiController(addonLifecycle, dataManager, _gameFunctions, _questController, gameGui, pluginLog);
|
||||
serviceCollection.AddSingleton<GameFunctions>();
|
||||
serviceCollection.AddSingleton<AetheryteData>();
|
||||
serviceCollection.AddSingleton<TerritoryData>();
|
||||
serviceCollection.AddSingleton<NavmeshIpc>();
|
||||
serviceCollection.AddSingleton<LifestreamIpc>();
|
||||
|
||||
_windowSystem.AddWindow(new DebugWindow(pluginInterface, _movementController, _questController, _gameFunctions,
|
||||
clientState, framework, targetManager, _gameUiController, _configuration));
|
||||
serviceCollection.AddSingleton<MovementController>();
|
||||
serviceCollection.AddSingleton<QuestRegistry>();
|
||||
serviceCollection.AddSingleton<QuestController>();
|
||||
serviceCollection.AddSingleton<GameUiController>();
|
||||
serviceCollection.AddSingleton<NavigationShortcutController>();
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||
_framework.Update += FrameworkUpdate;
|
||||
_commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand));
|
||||
serviceCollection.AddSingleton<DebugWindow>();
|
||||
serviceCollection.AddSingleton<DalamudInitializer>();
|
||||
|
||||
_framework.RunOnTick(() => _gameUiController.HandleCurrentDialogueChoices(), TimeSpan.FromMilliseconds(200));
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
_serviceProvider.GetRequiredService<QuestRegistry>().Reload();
|
||||
_serviceProvider.GetRequiredService<DebugWindow>();
|
||||
_serviceProvider.GetRequiredService<DalamudInitializer>();
|
||||
}
|
||||
|
||||
private void FrameworkUpdate(IFramework framework)
|
||||
{
|
||||
_questController.Update();
|
||||
|
||||
HandleNavigationShortcut();
|
||||
_movementController.Update();
|
||||
}
|
||||
|
||||
private void ProcessCommand(string command, string arguments)
|
||||
{
|
||||
_windowSystem.Windows.Single(x => x is DebugWindow).Toggle();
|
||||
}
|
||||
|
||||
private unsafe void HandleNavigationShortcut()
|
||||
{
|
||||
var inputData = UIInputData.Instance();
|
||||
if (inputData == null)
|
||||
return;
|
||||
|
||||
if (inputData->IsGameWindowFocused &&
|
||||
inputData->UIFilteredMouseButtonReleasedFlags.HasFlag(MouseButtonFlags.LBUTTON) &&
|
||||
inputData->GetKeyState(SeVirtualKey.MENU).HasFlag(KeyStateFlags.Down) &&
|
||||
_gameGui.ScreenToWorld(new Vector2(inputData->CursorXPosition, inputData->CursorYPosition),
|
||||
out Vector3 worldPos))
|
||||
{
|
||||
_movementController.NavigateTo(EMovementType.Shortcut, null, worldPos,
|
||||
_gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_commandManager.RemoveHandler("/qst");
|
||||
_framework.Update -= FrameworkUpdate;
|
||||
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||
|
||||
_gameUiController.Dispose();
|
||||
_movementController.Dispose();
|
||||
_serviceProvider?.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,10 @@ using Questionable.Model.V1;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
internal sealed class DebugWindow : LWindow, IPersistableWindowConfig, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly MovementController _movementController;
|
||||
private readonly QuestController _questController;
|
||||
private readonly GameFunctions _gameFunctions;
|
||||
@ -30,12 +31,14 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
private readonly GameUiController _gameUiController;
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
public DebugWindow(DalamudPluginInterface pluginInterface, MovementController movementController,
|
||||
QuestController questController, GameFunctions gameFunctions, IClientState clientState, IFramework framework,
|
||||
ITargetManager targetManager, GameUiController gameUiController, Configuration configuration)
|
||||
public DebugWindow(DalamudPluginInterface pluginInterface, WindowSystem windowSystem,
|
||||
MovementController movementController, QuestController questController, GameFunctions gameFunctions,
|
||||
IClientState clientState, IFramework framework, ITargetManager targetManager, GameUiController gameUiController,
|
||||
Configuration configuration)
|
||||
: base("Questionable", ImGuiWindowFlags.AlwaysAutoResize)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_windowSystem = windowSystem;
|
||||
_movementController = movementController;
|
||||
_questController = questController;
|
||||
_gameFunctions = gameFunctions;
|
||||
@ -51,6 +54,8 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
MinimumSize = new Vector2(200, 30),
|
||||
MaximumSize = default
|
||||
};
|
||||
|
||||
_windowSystem.AddWindow(this);
|
||||
}
|
||||
|
||||
public WindowConfig WindowConfig => _configuration.DebugWindowConfig;
|
||||
@ -128,7 +133,7 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.Text(
|
||||
$"Current TerritoryId: {_clientState.TerritoryType}, Flying: {(_gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType) ? "Yes" : "No")}");
|
||||
$"Current TerritoryId: {_clientState.TerritoryType}, Flying: {(_gameFunctions.IsFlyingUnlockedInCurrentZone() ? "Yes" : "No")}");
|
||||
|
||||
var q = _gameFunctions.GetCurrentQuest();
|
||||
ImGui.Text($"Current Quest: {q.CurrentQuest} → {q.Sequence}");
|
||||
@ -169,7 +174,7 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
if (ImGui.Button("Move to Target"))
|
||||
{
|
||||
_movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
|
||||
_targetManager.Target.Position, _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
|
||||
_targetManager.Target.Position, _gameFunctions.IsFlyingUnlockedInCurrentZone(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
@ -234,7 +239,7 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
map->FlagMapMarker.TerritoryId != _clientState.TerritoryType);
|
||||
if (ImGui.Button("Move to Flag"))
|
||||
_gameFunctions.ExecuteCommand(
|
||||
$"/vnav {(_gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType) ? "flyflag" : "moveflag")}");
|
||||
$"/vnav {(_gameFunctions.IsFlyingUnlockedInCurrentZone() ? "flyflag" : "moveflag")}");
|
||||
ImGui.EndDisabled();
|
||||
|
||||
ImGui.SameLine();
|
||||
@ -251,4 +256,9 @@ internal sealed class DebugWindow : LWindow, IPersistableWindowConfig
|
||||
TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_windowSystem.RemoveWindow(this);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,36 @@
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0-windows7.0": {
|
||||
"Dalamud.Extensions.MicrosoftLogging": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "jWK3r/cZUXN8H9vHf78gEzeRmMk4YAbCUYzLcTqUAcega8unUiFGwYy+iOjVYJ9urnr9r+hk+vBi1y9wyv+e7Q==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "8.0.0"
|
||||
}
|
||||
},
|
||||
"DalamudPackager": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.1.12, )",
|
||||
"resolved": "2.1.12",
|
||||
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||
},
|
||||
"JetBrains.Annotations": {
|
||||
"type": "Direct",
|
||||
"requested": "[2023.3.0, )",
|
||||
"resolved": "2023.3.0",
|
||||
"contentHash": "PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.3, )",
|
||||
@ -17,6 +41,43 @@
|
||||
"System.Text.Encodings.Web": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "8.0.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Options": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
|
Loading…
Reference in New Issue
Block a user