From 6d7403edc3f678de1f6226a564822f0630906530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Thu, 21 Aug 2025 20:57:19 -0300 Subject: [PATCH] Debug Triggers --- .../Graphics/DebugFieldRenderer.cs | 43 +++++++ .../Graphics/DebugGraphicsContext.cs | 3 +- .../Ui/Windows/VisualizationControlsWindow.cs | 6 + .../Tools/ListExtensionTests.cs | 116 ++++++++++++++++++ Maple2.Tools/Extensions/ListExtension.cs | 5 +- 5 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 Maple2.Server.Tests/Tools/ListExtensionTests.cs diff --git a/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs b/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs index 04b1edccc..83264743e 100644 --- a/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs +++ b/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs @@ -52,6 +52,8 @@ public bool IsActive { public bool ShowMobs = true; public bool ShowSellableTiles; // toggle for rendering sellable plot tiles public bool ShowPlotLabels = true; // toggle for showing floating plot labels (status/owner/etc.) + public bool ShowTriggers = true; + public bool ShowTriggerInformation = true; public bool PlayerMoveMode; public bool ForceMove; @@ -240,6 +242,14 @@ private void RenderFieldEntities3D(DebugFieldWindow window) { RenderVibrateObjects(window); } + // Render trigger boxes + if (ShowTriggers) { + RenderTriggerBoxes(window); + if (ShowTriggerInformation) { + RenderTriggerTextLabels(); + } + } + // Render portals if (ShowPortals) { RenderPortals(window); @@ -1169,4 +1179,37 @@ private void UpdateWireframeInstance(DebugFieldWindow window) { window.SceneState.BindConstantBuffer(window.InstanceConstantBuffer, 1, Enum.ShaderStageFlags.Vertex); window.SceneState.UpdateBindings(); } + + private void RenderTriggerBoxes(DebugFieldWindow window) { + if (Field.TriggerObjects.Boxes.Count == 0) return; + instanceBuffer.Color = new Vector4(1.0f, 0.3f, 0.8f, 0.9f); // magenta-ish + foreach (TriggerBox box in Field.TriggerObjects.Boxes.Values) { + Vector3 size = box.Metadata.Dimensions; + Vector3 center = box.Metadata.Position; + instanceBuffer.Transformation = Matrix4x4.Transpose(Matrix4x4.CreateScale(size) * Matrix4x4.CreateTranslation(center)); + UpdateWireframeInstance(window); + Context.CoreModels!.WireCube.Draw(); + } + } + + private void RenderTriggerTextLabels() { + if (Field.TriggerObjects.Boxes.Count == 0) return; + ImDrawListPtr drawList = ImGui.GetBackgroundDrawList(); + foreach (TriggerBox box in Field.TriggerObjects.Boxes.Values) { + Vector3 textPos = box.Metadata.Position + new Vector3(0, 0, 150); + if (!TryWorldToScreen(textPos, out Vector2 screenPos)) continue; + string idText = $"TriggerBox Id: {box.Id}"; + Vector2 textSize = ImGui.CalcTextSize(idText); + float left = screenPos.X - (textSize.X * 0.5f); + Vector2 topLeft = new(left, screenPos.Y); + Vector4 textColor = new(1.0f, 0.3f, 0.8f, 1.0f); + Vector4 bgColor = new(0, 0, 0, 0.75f); + Vector4 border = new(0.3f, 0.3f, 0.3f, 0.9f); + Vector2 bgMin = topLeft - new Vector2(4, 2); + Vector2 bgMax = topLeft + textSize + new Vector2(4, 2); + drawList.AddRectFilled(bgMin, bgMax, ImGui.ColorConvertFloat4ToU32(bgColor)); + drawList.AddRect(bgMin, bgMax, ImGui.ColorConvertFloat4ToU32(border)); + drawList.AddText(topLeft, ImGui.ColorConvertFloat4ToU32(textColor), idText); + } + } } diff --git a/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs b/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs index ddfa3c50a..7efeded4c 100644 --- a/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs +++ b/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs @@ -482,8 +482,6 @@ public void FieldRemoved(FieldManager field) { return; } - Logger.Information("Field removed {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); - renderer.CleanUp(); fieldRendererMutex.WaitOne(); @@ -491,6 +489,7 @@ public void FieldRemoved(FieldManager field) { fieldRendererMutex.ReleaseMutex(); fields.Remove(field); + Logger.Information("Field removed {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); } public DebugFieldWindow FieldWindowOpened() { diff --git a/Maple2.Server.DebugGame/Graphics/Ui/Windows/VisualizationControlsWindow.cs b/Maple2.Server.DebugGame/Graphics/Ui/Windows/VisualizationControlsWindow.cs index c12f92c21..da9efa6cb 100644 --- a/Maple2.Server.DebugGame/Graphics/Ui/Windows/VisualizationControlsWindow.cs +++ b/Maple2.Server.DebugGame/Graphics/Ui/Windows/VisualizationControlsWindow.cs @@ -35,6 +35,12 @@ public void Render() { ImGui.Checkbox("Show Portal Connections", ref renderer.ShowPortalConnections); ImGui.Unindent(); } + ImGui.Checkbox("Show Triggers", ref renderer.ShowTriggers); + if (renderer.ShowTriggers) { + ImGui.Indent(); + ImGui.Checkbox("Show Trigger Information", ref renderer.ShowTriggerInformation); + ImGui.Unindent(); + } ImGui.Checkbox("Show Sellable Tiles (Plots)", ref renderer.ShowSellableTiles); if (renderer.ShowSellableTiles) { ImGui.Indent(); diff --git a/Maple2.Server.Tests/Tools/ListExtensionTests.cs b/Maple2.Server.Tests/Tools/ListExtensionTests.cs new file mode 100644 index 000000000..2f38bda18 --- /dev/null +++ b/Maple2.Server.Tests/Tools/ListExtensionTests.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Maple2.Tools.Extensions; + +namespace Maple2.Server.Tests.Tools; + +public class ListExtensionTests { + [Test] + public void AddSorted_MaintainsAscendingOrder_WithRandomInputs() { + var list = new List(); + int[] inputs = [5, 1, 9, 3, 7, 2, 8, 4, 6, 0]; + + foreach (int x in inputs) { + list.AddSorted(x); + } + + Assert.That(list, Is.EqualTo(inputs.OrderBy(x => x))); + } + + [Test] + public void AddSorted_InsertsAtBeginningAndEnd() { + var list = new List(); + list.AddSorted(10); + list.AddSorted(20); + list.AddSorted(0); // should go to beginning + list.AddSorted(30); // should go to end + + Assert.Multiple(() => { + Assert.That(list.First(), Is.EqualTo(0)); + Assert.That(list.Last(), Is.EqualTo(30)); + Assert.That(list, Is.Ordered); + }); + } + + [Test] + public void RemoveSorted_RemovesExisting_FirstAndLast() { + var list = new List(); + foreach (int x in Enumerable.Range(1, 10)) { + list.AddSorted(x); + } + + list.RemoveSorted(1); // first + list.RemoveSorted(10); // last + + Assert.Multiple(() => { + Assert.That(list, Does.Not.Contain(1)); + Assert.That(list, Does.Not.Contain(10)); + Assert.That(list, Is.Ordered); + }); + } + + [Test] + public void RemoveSorted_RemovesSingleInstance_WhenDuplicatesPresent() { + var list = new List(); + list.AddSorted(3); + list.AddSorted(3); + list.AddSorted(3); + list.AddSorted(2); + list.AddSorted(4); + + list.RemoveSorted(3); + + Assert.Multiple(() => { + Assert.That(list.Count(x => x == 3), Is.EqualTo(2)); + Assert.That(list, Is.Ordered); + }); + } + + [Test] + public void RemoveSorted_DoesNothing_WhenItemNotPresent() { + var list = new List(); + int[] itemsToAdd = [1, 3, 5, 7]; + foreach (int x in itemsToAdd) { + list.AddSorted(x); + } + + int[] snapshot = list.ToArray(); + list.RemoveSorted(2); + + Assert.That(list, Is.EqualTo(snapshot)); + Assert.That(list, Is.Ordered); + } + + [Test] + public void AddRemoveSorted_WithCustomDescendingComparer() { + var list = new List(); + Comparer desc = Comparer.Create((a, b) => b.CompareTo(a)); + + int[] itemsToAdd = [5, 1, 9, 3, 7]; + foreach (int x in itemsToAdd) { + list.AddSorted(x, desc); + } + + // verify strictly descending + int[] expected = [9, 7, 5, 3, 1]; + Assert.That(list, Is.EqualTo(expected)); + + // remove boundaries under the same comparer + list.RemoveSorted(9, desc); // first (largest) + list.RemoveSorted(1, desc); // last (smallest) + + Assert.That(list, Is.EqualTo(new[] { + 7, + 5, + 3, + })); + + // remove middle + list.RemoveSorted(5, desc); + Assert.That(list, Is.EqualTo(new[] { + 7, + 3, + })); + } +} diff --git a/Maple2.Tools/Extensions/ListExtension.cs b/Maple2.Tools/Extensions/ListExtension.cs index 3f3b74ddd..a50d0b6a6 100644 --- a/Maple2.Tools/Extensions/ListExtension.cs +++ b/Maple2.Tools/Extensions/ListExtension.cs @@ -48,11 +48,11 @@ public static void RemoveSorted(this List @this, T item, Comparer compa return; } - if (comparer.Compare(@this.Last(), item) > 0) { + if (comparer.Compare(@this.Last(), item) < 0) { // item greater than last -> cannot be present return; } - if (comparer.Compare(@this.First(), item) < 0) { + if (comparer.Compare(@this.First(), item) > 0) { // item smaller than first -> cannot be present return; } @@ -63,4 +63,3 @@ public static void RemoveSorted(this List @this, T item, Comparer compa } } } -