using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Windowing; using Dalamud.Plugin.Services; using Questionable.Controller; using Questionable.Data; using Questionable.Model; using Questionable.Model.Common; using Questionable.Model.Questing; namespace Questionable.Windows; internal sealed class DebugOverlay : Window { private readonly QuestController _questController; private readonly QuestRegistry _questRegistry; private readonly IGameGui _gameGui; private readonly IClientState _clientState; private readonly ICondition _condition; private readonly AetheryteData _aetheryteData; private readonly IObjectTable _objectTable; private readonly CombatController _combatController; private readonly Configuration _configuration; public ElementId? HighlightedQuest { get; set; } public DebugOverlay(QuestController questController, QuestRegistry questRegistry, IGameGui gameGui, IClientState clientState, ICondition condition, AetheryteData aetheryteData, IObjectTable objectTable, CombatController combatController, Configuration configuration) : base("Questionable Debug Overlay###QuestionableDebugOverlay", ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoSavedSettings, forceMainWindow: true) { _questController = questController; _questRegistry = questRegistry; _gameGui = gameGui; _clientState = clientState; _condition = condition; _aetheryteData = aetheryteData; _objectTable = objectTable; _combatController = combatController; _configuration = configuration; base.Position = Vector2.Zero; base.PositionCondition = ImGuiCond.Always; base.Size = ImGui.GetIO().DisplaySize; base.SizeCondition = ImGuiCond.Always; base.IsOpen = true; base.ShowCloseButton = false; base.RespectCloseHotkey = false; } public override bool DrawConditions() { return _configuration.Advanced.DebugOverlay; } public override void PreDraw() { base.Size = ImGui.GetIO().DisplaySize; } public override void Draw() { if (_condition[ConditionFlag.OccupiedInCutSceneEvent]) { return; } IClientState clientState = _clientState; if (clientState != null && clientState.IsLoggedIn && clientState.LocalPlayer != null && !clientState.IsPvPExcludingDen && _questController.IsQuestWindowOpen) { DrawCurrentQuest(); DrawHighlightedQuest(); if (_configuration.Advanced.CombatDataOverlay) { DrawCombatTargets(); } } } private void DrawCurrentQuest() { QuestController.QuestProgress currentQuest = _questController.CurrentQuest; if (currentQuest == null) { return; } QuestSequence questSequence = currentQuest.Quest.FindSequence(currentQuest.Sequence); if (questSequence == null) { return; } for (int i = currentQuest.Step; i <= questSequence.Steps.Count; i++) { QuestStep questStep = questSequence.FindStep(i); if (questStep != null && TryGetPosition(questStep, out var position)) { DrawStep(i.ToString(CultureInfo.InvariantCulture), questStep, position.Value, (Vector3.Distance(_clientState.LocalPlayer.Position, position.Value) < questStep.CalculateActualStopDistance()) ? 4278255360u : 4278190335u); } } } private void DrawHighlightedQuest() { if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest, out Quest quest)) { return; } foreach (QuestSequence item in quest.Root.QuestSequence) { for (int i = 0; i < item.Steps.Count; i++) { QuestStep questStep = item.FindStep(i); if (questStep != null && TryGetPosition(questStep, out var position)) { DrawStep($"{quest.Id} / {item.Sequence} / {i}", questStep, position.Value, uint.MaxValue); } } } } private void DrawStep(string counter, QuestStep step, Vector3 position, uint color) { if (!step.Disabled && step.TerritoryId == _clientState.TerritoryType && _gameGui.WorldToScreen(position, out var screenPos)) { ImGui.GetWindowDrawList().AddCircleFilled(screenPos, 3f, color); ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList(); Vector2 pos = screenPos + new Vector2(10f, -8f); ImU8String text = new ImU8String(7, 5); text.AppendFormatted(counter); text.AppendLiteral(": "); text.AppendFormatted(step.InteractionType); text.AppendLiteral("\n"); text.AppendFormatted(position.ToString("G", CultureInfo.InvariantCulture)); text.AppendLiteral(" ["); text.AppendFormatted((position - _clientState.LocalPlayer.Position).Length(), "N2"); text.AppendLiteral("]\n"); text.AppendFormatted(step.Comment); windowDrawList.AddText(pos, color, text); } } private void DrawCombatTargets() { if (!_combatController.IsRunning) { return; } foreach (IGameObject item3 in _objectTable.Skip(1)) { if (item3 is IBattleNpc && _gameGui.WorldToScreen(item3.Position, out var screenPos)) { (int Priority, string Reason) killPriority = _combatController.GetKillPriority(item3); int item = killPriority.Priority; string item2 = killPriority.Reason; ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList(); Vector2 pos = screenPos + new Vector2(10f, -8f); int col = ((item > 0) ? (-16711936) : (-1)); ImU8String text = new ImU8String(12, 7); text.AppendFormatted(item3.Name); text.AppendLiteral("/"); text.AppendFormatted(item3.GameObjectId, "X"); text.AppendLiteral(", "); text.AppendFormatted(item3.BaseId); text.AppendLiteral(", "); text.AppendFormatted(item); text.AppendLiteral(" - "); text.AppendFormatted(item2); text.AppendLiteral(", "); text.AppendFormatted(Vector3.Distance(item3.Position, _clientState.LocalPlayer.Position), "N2"); text.AppendLiteral(", "); text.AppendFormatted(item3.IsTargetable); windowDrawList.AddText(pos, (uint)col, text); } } } private bool TryGetPosition(QuestStep step, [NotNullWhen(true)] out Vector3? position) { if (step.Position.HasValue) { position = step.Position; return true; } EAetheryteLocation valueOrDefault = default(EAetheryteLocation); bool flag; if (step != null) { EInteractionType interactionType = step.InteractionType; if ((uint)(interactionType - 4) <= 1u) { EAetheryteLocation? aetheryte = step.Aetheryte; if (aetheryte.HasValue) { valueOrDefault = aetheryte.GetValueOrDefault(); flag = true; goto IL_004e; } } } flag = false; goto IL_004e; IL_004e: if (flag) { position = _aetheryteData.Locations[valueOrDefault]; return true; } if (step != null && step.InteractionType == EInteractionType.AttuneAethernetShard) { EAetheryteLocation? aetheryte = step.AethernetShard; if (aetheryte.HasValue) { EAetheryteLocation valueOrDefault2 = aetheryte.GetValueOrDefault(); position = _aetheryteData.Locations[valueOrDefault2]; return true; } } position = null; return false; } }