From 9fda9f4f4c95357d1905c18b9402eb98cdeaa72c Mon Sep 17 00:00:00 2001 From: Andreas Dobloug Date: Wed, 19 Aug 2026 10:33:02 +0200 Subject: [PATCH 1/2] fix: correctness bugs in migration, editor, import and teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five defects, none of which required new behaviour to surface: - v7 job migration aliased its own array. `bool[] JobTemp = doodle.JobsBool` is a reference, so the ascending shift read slots it had already written and smeared old index 14 (BRD) across MCH, DNC, BLM, SMN and RDM. Now shifts descending in place. Configs already stamped Version 7 are past the point of repair; this only protects users still on a pre-7 config. - The migration also blind-indexed [20] and [22], throwing on any stored array shorter than 21. Guarded on the expected length, and the v4 block is bounds- and null-checked the same way. - Dispose removed OpenConfigUi while the constructor subscribed OpenMainUi, so the live handler leaked across reloads. The stale commented-out OpenConfigUi line goes with it; the gear icon stays deliberately inert. - The first-run welcome window called ImGui.Begin with no matching End. - Clipboard import trusted foreign JSON: a null document threw inside AddRange, and a pre-7.0 export carries 21 job flags where everything downstream assumes 23, overrunning _doodleJobs and _doodleJobsUint on a later frame — outside the try, so the catch never saw it. Imports are now null-filtered and normalised, as is the config load path. The editor drew every doodle at the running sum of all preceding offsets, because dotPosX/dotPosY were declared outside the loop and only ever accumulated. The anchor is now immutable and each doodle offsets a local copy, which also un-skews the line-endpoint hit tests. Not compiled: no .NET SDK or Dalamud assemblies were available. Co-Authored-By: Claude Opus 5 --- PixelPerfect/Config.cs | 26 ++++++++---- PixelPerfect/Editor.cs | 89 ++++++++++++++++++++++-------------------- PixelPerfect/Main.cs | 58 +++++++++++++++++---------- 3 files changed, 103 insertions(+), 70 deletions(-) diff --git a/PixelPerfect/Config.cs b/PixelPerfect/Config.cs index 0282e92..84d86d0 100644 --- a/PixelPerfect/Config.cs +++ b/PixelPerfect/Config.cs @@ -25,6 +25,7 @@ private void DrawConfig() { _config = true; } + ImGui.End(); } var deleteNum = -1; @@ -171,9 +172,22 @@ private void DrawConfig() var jsonBytes = Convert.FromBase64String(base64); var json = Encoding.UTF8.GetString(jsonBytes); var bag = JsonConvert.DeserializeObject>(json); - _doodleBag.AddRange(bag); - SaveConfig(); - this.AddNotification("Imported successfully", NotificationType.Success); + if (bag == null) + { + this.AddNotification("Could not import", NotificationType.Error); + } + else + { + bag.RemoveAll(d => d == null); + foreach (var doodle in bag) + { + NormalizeJobs(doodle); + } + + _doodleBag.AddRange(bag); + SaveConfig(); + this.AddNotification("Imported successfully", NotificationType.Success); + } } catch { @@ -223,9 +237,9 @@ private void DrawConfig() ImGui.ColorEdit4($"Colour ##{number}", ref colour, ImGuiColorEditFlags.NoInputs); if (ImGui.TreeNode($"Jobs##{number}")) { - var loop = 0; ImGui.Columns(6); - foreach (var jobb in doodle.JobsBool) + var count = Math.Min(jobsBool.Length, _doodleJobs.Length); + for (var loop = 0; loop < count; loop++) { ImGui.Checkbox($"{_doodleJobs[loop]}", ref jobsBool[loop]); @@ -233,8 +247,6 @@ private void DrawConfig() { ImGui.NextColumn(); } - - loop++; } ImGui.Columns(1); diff --git a/PixelPerfect/Editor.cs b/PixelPerfect/Editor.cs index 0c1223a..a94fc43 100644 --- a/PixelPerfect/Editor.cs +++ b/PixelPerfect/Editor.cs @@ -43,9 +43,9 @@ private void DrawEditor() { ImGui.GetWindowDrawList().AddLine(new Vector2(windowPos.X + (10 * i * _editorScale), windowPos.Y + 100), new Vector2(windowPos.X + (10 * i * _editorScale), windowPos.Y + windowMax.Y + 100), ImGui.GetColorU32(new Vector4(0.8f, 0.8f, 0.8f, 0.5f))); } - var dotPosX = windowPos.X + (windowMax.X / 2); - var dotPosY = windowPos.Y + 50 + (windowMax.Y / 2); - ImGui.GetWindowDrawList().AddCircleFilled(new Vector2(dotPosX, dotPosY), 10f, ImGui.GetColorU32(new Vector4(0.8f, 0.8f, 0.8f, 0.5f))); + var anchorX = windowPos.X + (windowMax.X / 2); + var anchorY = windowPos.Y + 50 + (windowMax.Y / 2); + ImGui.GetWindowDrawList().AddCircleFilled(new Vector2(anchorX, anchorY), 10f, ImGui.GetColorU32(new Vector4(0.8f, 0.8f, 0.8f, 0.5f))); var loop = 0; var skip = false; @@ -62,14 +62,17 @@ private void DrawEditor() continue; } + var drawX = anchorX; + var drawY = anchorY; + int alpha; if (loop == _selected) { alpha = 4; - if (mX > dotPosX + (doodle.Vector.W * 10 * _editorScale) - 20 - && mX < dotPosX + (doodle.Vector.W * 10 * _editorScale) + 20 - && mY > dotPosY + (doodle.Vector.X * 10 * _editorScale) - 20 - && mY < dotPosY + (doodle.Vector.X * 10 * _editorScale) + 20) + if (mX > drawX + (doodle.Vector.W * 10 * _editorScale) - 20 + && mX < drawX + (doodle.Vector.W * 10 * _editorScale) + 20 + && mY > drawY + (doodle.Vector.X * 10 * _editorScale) - 20 + && mY < drawY + (doodle.Vector.X * 10 * _editorScale) + 20) { if (_grabbed == -1 && ImGui.IsMouseClicked(ImGuiMouseButton.Left) && !skip) { @@ -84,13 +87,13 @@ private void DrawEditor() } if (_grabbed == 1) { - doodle.Vector = doodle.Vector with { X = (mY - dotPosY) / (10 * _editorScale), W = (mX - dotPosX) / (10 * _editorScale) }; + doodle.Vector = doodle.Vector with { X = (mY - drawY) / (10 * _editorScale), W = (mX - drawX) / (10 * _editorScale) }; } - if (mX > dotPosX + (doodle.Vector.Y * 10 * _editorScale) - 20 - && mX < dotPosX + (doodle.Vector.Y * 10 * _editorScale) + 20 - && mY > dotPosY + (doodle.Vector.Z * 10 * _editorScale) - 20 - && mY < dotPosY + (doodle.Vector.Z * 10 * _editorScale) + 20) + if (mX > drawX + (doodle.Vector.Y * 10 * _editorScale) - 20 + && mX < drawX + (doodle.Vector.Y * 10 * _editorScale) + 20 + && mY > drawY + (doodle.Vector.Z * 10 * _editorScale) - 20 + && mY < drawY + (doodle.Vector.Z * 10 * _editorScale) + 20) { if (_grabbed == -1 && ImGui.IsMouseClicked(ImGuiMouseButton.Left) && !skip) { @@ -105,7 +108,7 @@ private void DrawEditor() } if (_grabbed == 2) { - doodle.Vector = doodle.Vector with { Y = (mX - dotPosX) / (10 * _editorScale), Z = (mY - dotPosY) / (10 * _editorScale) }; + doodle.Vector = doodle.Vector with { Y = (mX - drawX) / (10 * _editorScale), Z = (mY - drawY) / (10 * _editorScale) }; } } else @@ -116,8 +119,8 @@ private void DrawEditor() { if (doodle.Offset && !doodle.RotateOffset) { - dotPosX += (doodle.Vector.X * 10 * _editorScale); - dotPosY += (doodle.Vector.Y * 10 * _editorScale); + drawX += (doodle.Vector.X * 10 * _editorScale); + drawY += (doodle.Vector.Y * 10 * _editorScale); } if (doodle.RotateOffset) @@ -125,10 +128,10 @@ private void DrawEditor() var angle = -_ot.LocalPlayer.Rotation; var cosTheta = MathF.Cos(angle); var sinTheta = MathF.Sin(angle); - dotPosX += (cosTheta * (doodle.Vector.X * 10 * _editorScale) - sinTheta * (doodle.Vector.Y * 10 * _editorScale)); - dotPosY += (sinTheta * (doodle.Vector.X * 10 * _editorScale) + cosTheta * (doodle.Vector.Y * 10 * _editorScale)); + drawX += (cosTheta * (doodle.Vector.X * 10 * _editorScale) - sinTheta * (doodle.Vector.Y * 10 * _editorScale)); + drawY += (sinTheta * (doodle.Vector.X * 10 * _editorScale) + cosTheta * (doodle.Vector.Y * 10 * _editorScale)); } - DrawRingEditor(dotPosX, dotPosY, + DrawRingEditor(drawX, drawY, doodle.Radius * 10 * _editorScale, doodle.Segments, doodle.Thickness, @@ -136,11 +139,11 @@ private void DrawEditor() } if (doodle.Type == 1)//Line { - var x1 = dotPosX + (doodle.Vector.W * 10 * _editorScale); - var y1 = dotPosY + (doodle.Vector.X * 10 * _editorScale); + var x1 = drawX + (doodle.Vector.W * 10 * _editorScale); + var y1 = drawY + (doodle.Vector.X * 10 * _editorScale); - var x2 = dotPosX + (doodle.Vector.Y * 10 * _editorScale); - var y2 = dotPosY + (doodle.Vector.Z * 10 * _editorScale); + var x2 = drawX + (doodle.Vector.Y * 10 * _editorScale); + var y2 = drawY + (doodle.Vector.Z * 10 * _editorScale); if (doodle.North) { @@ -154,11 +157,11 @@ private void DrawEditor() { var sin = Math.Sin(-_ot.LocalPlayer.Rotation + Math.PI); var cos = Math.Cos(-_ot.LocalPlayer.Rotation + Math.PI); - var xr1 = cos * (x1 - dotPosX) - sin * (y1 - dotPosY) + dotPosX; - var yr1 = sin * (x1 - dotPosX) + cos * (y1 - dotPosY) + dotPosY; + var xr1 = cos * (x1 - drawX) - sin * (y1 - drawY) + drawX; + var yr1 = sin * (x1 - drawX) + cos * (y1 - drawY) + drawY; - var xr2 = cos * (x2 - dotPosX) - sin * (y2 - dotPosY) + dotPosX; - var yr2 = sin * (x2 - dotPosX) + cos * (y2 - dotPosY) + dotPosY; + var xr2 = cos * (x2 - drawX) - sin * (y2 - drawY) + drawX; + var yr2 = sin * (x2 - drawX) + cos * (y2 - drawY) + drawY; ImGui.GetWindowDrawList().AddLine( new Vector2((float)xr1, (float)yr1), @@ -170,8 +173,8 @@ private void DrawEditor() { if (doodle.Offset) { - dotPosX += (doodle.Vector.X * 10 * _editorScale); - dotPosY += (doodle.Vector.Y * 10 * _editorScale); + drawX += (doodle.Vector.X * 10 * _editorScale); + drawY += (doodle.Vector.Y * 10 * _editorScale); } if (doodle.North) @@ -179,7 +182,7 @@ private void DrawEditor() if (doodle.Outline) { ImGui.GetWindowDrawList().AddCircle( - new Vector2(dotPosX, dotPosY), + new Vector2(drawX, drawY), doodle.Radius + doodle.Thickness * 0.6f, ImGui.GetColorU32(doodle.OutlineColour with { W = doodle.OutlineColour.W * (0.25f * alpha) }), doodle.Segments, doodle.Thickness); @@ -187,7 +190,7 @@ private void DrawEditor() if (doodle.Filled) { ImGui.GetWindowDrawList().AddCircleFilled( - new Vector2(dotPosX, dotPosY), + new Vector2(drawX, drawY), doodle.Radius, ImGui.GetColorU32(doodle.Colour with { W = doodle.Colour.W * (0.25f * alpha) }), doodle.Segments); @@ -195,7 +198,7 @@ private void DrawEditor() else { ImGui.GetWindowDrawList().AddCircle( - new Vector2(dotPosX, dotPosY), + new Vector2(drawX, drawY), doodle.Radius, ImGui.GetColorU32(doodle.Colour with { W = doodle.Colour.W * (0.25f * alpha) }), doodle.Segments, doodle.Thickness); @@ -203,13 +206,13 @@ private void DrawEditor() } else { - var x1 = dotPosX + (doodle.Vector.W * 10 * _editorScale); - var y1 = dotPosY + (doodle.Vector.X * 10 * _editorScale); + var x1 = drawX + (doodle.Vector.W * 10 * _editorScale); + var y1 = drawY + (doodle.Vector.X * 10 * _editorScale); var sin = Math.Sin(-_ot.LocalPlayer.Rotation + Math.PI); var cos = Math.Cos(-_ot.LocalPlayer.Rotation + Math.PI); - var xr1 = cos * (x1 - dotPosX) - sin * (y1 - dotPosY) + dotPosX; - var yr1 = sin * (x1 - dotPosX) + cos * (y1 - dotPosY) + dotPosY; + var xr1 = cos * (x1 - drawX) - sin * (y1 - drawY) + drawX; + var yr1 = sin * (x1 - drawX) + cos * (y1 - drawY) + drawY; if (doodle.Outline) { @@ -241,27 +244,27 @@ private void DrawEditor() { if (doodle.Offset && !doodle.RotateOffset) { - dotPosX += (doodle.Vector.X * 10 * _editorScale); - dotPosY += (doodle.Vector.Y * 10 * _editorScale); + drawX += (doodle.Vector.X * 10 * _editorScale); + drawY += (doodle.Vector.Y * 10 * _editorScale); } if (doodle.RotateOffset) { var angle = -_ot.LocalPlayer.Rotation; var cosTheta = MathF.Cos(angle); var sinTheta = MathF.Sin(angle); - dotPosX += (cosTheta * (doodle.Vector.X * 10 * _editorScale) - sinTheta * (doodle.Vector.Y * 10 * _editorScale)); - dotPosY += (sinTheta * (doodle.Vector.X * 10 * _editorScale) + cosTheta * (doodle.Vector.Y * 10 * _editorScale)); + drawX += (cosTheta * (doodle.Vector.X * 10 * _editorScale) - sinTheta * (doodle.Vector.Y * 10 * _editorScale)); + drawY += (sinTheta * (doodle.Vector.X * 10 * _editorScale) + cosTheta * (doodle.Vector.Y * 10 * _editorScale)); } float segAng = MathF.Tau / doodle.Segments; uint col = ImGui.GetColorU32(doodle.Colour with { W = doodle.Colour.W * (0.25f * alpha) }); for (int i = 0; i < doodle.Segments; i++) { Vector2 pos1 = new Vector2( - dotPosX + doodle.Radius * 10 * _editorScale * MathF.Sin(segAng * i), - dotPosY + doodle.Radius * 10 * _editorScale * MathF.Cos(segAng * i)); + drawX + doodle.Radius * 10 * _editorScale * MathF.Sin(segAng * i), + drawY + doodle.Radius * 10 * _editorScale * MathF.Cos(segAng * i)); Vector2 pos2 = new Vector2( - dotPosX + doodle.Radius * 10 * _editorScale * MathF.Sin(segAng * (i + 0.4f)), - dotPosY + doodle.Radius * 10 * _editorScale * MathF.Cos(segAng * (i + 0.4f))); + drawX + doodle.Radius * 10 * _editorScale * MathF.Sin(segAng * (i + 0.4f)), + drawY + doodle.Radius * 10 * _editorScale * MathF.Cos(segAng * (i + 0.4f))); ImGui.GetWindowDrawList().AddLine(pos1, pos2, col, doodle.Thickness); } } diff --git a/PixelPerfect/Main.cs b/PixelPerfect/Main.cs index c455679..1891d0e 100644 --- a/PixelPerfect/Main.cs +++ b/PixelPerfect/Main.cs @@ -80,7 +80,7 @@ IObjectTable objectTable _configuration.Version = 4; foreach(var doodle in _doodleBag) { - if (doodle.Job > 0) + if (doodle.Job > 0 && doodle.JobsBool != null && doodle.Job < doodle.JobsBool.Length) { doodle.JobsBool[0] = false; doodle.JobsBool[doodle.Job] = true; @@ -96,19 +96,19 @@ IObjectTable objectTable _configuration.Version = 7; foreach (var doodle in _doodleBag) { + if (doodle.JobsBool is not { Length: 21 }) continue; + doodle.JobsBool = AddElementToArray(doodle.JobsBool, false); doodle.JobsBool = AddElementToArray(doodle.JobsBool, false); - bool[] JobTemp = doodle.JobsBool; - JobTemp[22] = doodle.JobsBool[20];//Add in PCT - for(int i = 14; i < 20;i++)//Add in VPR + doodle.JobsBool[22] = doodle.JobsBool[20];//Add in PCT + for(int i = 19; i >= 14; i--)//Add in VPR { - JobTemp[i + 1] = doodle.JobsBool[i]; + doodle.JobsBool[i + 1] = doodle.JobsBool[i]; } - JobTemp[14] = false; - JobTemp[21] = false; - doodle.JobsBool = JobTemp; + doodle.JobsBool[14] = false; + doodle.JobsBool[21] = false; } - + SaveConfig(); } @@ -137,7 +137,11 @@ IObjectTable objectTable 23, 31, 38, 25, 27, 35,42, 36 }; - + + foreach (var doodle in _doodleBag) + { + NormalizeJobs(doodle); + } _editorScale = 4f; _selected = -1; @@ -146,7 +150,6 @@ IObjectTable objectTable pluginInterface.UiBuilder.Draw += DrawDoodles; pluginInterface.UiBuilder.Draw += DrawEditor; pluginInterface.UiBuilder.Draw += DrawConfig; - //luginInterface.UiBuilder.OpenConfigUi += ConfigWindow; commandManager.AddHandler("/pp", new CommandInfo(Command) { HelpMessage = "Pixel Perfect config." @@ -164,7 +167,7 @@ public void Dispose() _pi.UiBuilder.Draw -= DrawConfig; _pi.UiBuilder.Draw -= DrawDoodles; _pi.UiBuilder.Draw -= DrawEditor; - _pi.UiBuilder.OpenConfigUi -= ConfigWindow; + _pi.UiBuilder.OpenMainUi -= ConfigWindow; _cm.RemoveHandler("/pp"); } @@ -176,22 +179,37 @@ private void Command(string command, string arguments) private bool CheckJob(uint jobUint, bool[] jobList) { + var count = Math.Min(jobList.Length, _doodleJobsUint.Length); + if (count == 0) return false; + var check = jobList[0]; - var loop = 0; - foreach(var job in jobList ) + for (var i = 0; i < count; i++) { - if ( job ) + if (jobList[i] && _doodleJobsUint[i] == jobUint) { - if (_doodleJobsUint[loop] == jobUint) - { - check = true; - } + check = true; } - loop++; } return check; } + + private void NormalizeJobs(Drawing doodle) + { + var length = _doodleJobs.Length; + if (doodle.JobsBool == null) + { + doodle.JobsBool = new bool[length]; + doodle.JobsBool[0] = true; + return; + } + + if (doodle.JobsBool.Length == length) return; + + var resized = new bool[length]; + Array.Copy(doodle.JobsBool, resized, Math.Min(doodle.JobsBool.Length, length)); + doodle.JobsBool = resized; + } private void SaveConfig() { From 92ad7f27e137cf025a63abd77d7c2c196f617edc Mon Sep 17 00:00:00 2001 From: Andreas Dobloug Date: Wed, 19 Aug 2026 10:38:30 +0200 Subject: [PATCH 2/2] chore: drop dead code and save config on change instead of on a timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup: - Remove usings that nothing references: the stray JSType import, the unused Num and Condition aliases, Lumina.Excel.Sheets, System.Linq, Dalamud.Interface, Dalamud.Interface.Windowing, and Dalamud.Game.Gui (IGameGui is declared in Dalamud.Plugin.Services). Dalamud.Game and Dalamud.Game.ClientState are left alone — grep says dead, but that cannot be confirmed without compiling. - Remove IdToJob and the JobIds enum. Neither has a caller; the live job mapping is _doodleJobsUint. Drawing.Job stays, since it is serialised config the v4 migration still reads. - Remove the target coordinate and atan readouts left in the Cone tab. - Remove the `vector` and `job` locals, both overwritten a line later. - Drop AssemblyVersion from the manifest. DalamudPackager fills it from the assembly and overwrites whatever is there, which is how it drifted to 3.3.3.0 against the csproj's 3.3.4.0 in the first place. - Hoist the Occupied38 cutscene check out of the per-doodle loop. Saving: The config was written every 100 frames while the window was open, regardless of whether anything changed, and edits in the last <100 frames were lost if the game exited. Add Doodle, Delete, reorder and the editor's endpoint drag never saved at all. Each widget now reports its own change through its return value, and a single flush runs once per frame gated on IsAnyItemActive, so a drag coalesces into one write on release. The editor marks dirty when an endpoint is dropped rather than on every frame it is held, since a canvas drag leaves no ImGui item active and would otherwise write every frame. The reorder branch's early return is now an if/else so the flush stays reachable on that path. Not compiled: no .NET SDK or Dalamud assemblies were available. Co-Authored-By: Claude Opus 5 --- PixelPerfect/Config.cs | 162 +++++++++++++++------------------ PixelPerfect/Doodles.cs | 4 +- PixelPerfect/Editor.cs | 2 + PixelPerfect/Main.cs | 39 +------- PixelPerfect/PixelPerfect.json | 1 - 5 files changed, 78 insertions(+), 130 deletions(-) diff --git a/PixelPerfect/Config.cs b/PixelPerfect/Config.cs index 84d86d0..6b25fc2 100644 --- a/PixelPerfect/Config.cs +++ b/PixelPerfect/Config.cs @@ -43,7 +43,7 @@ private void DrawConfig() if (ImGui.BeginTabItem("Config##Doodles")) { var number2 = 0; - ImGui.Checkbox("Hide Updates", ref _bitch); + if (ImGui.Checkbox("Hide Updates", ref _bitch)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Never show any messages."); @@ -58,28 +58,28 @@ private void DrawConfig() var unsheathed = doodle.Unsheathed; var name = doodle.Name; - ImGui.Checkbox($"Enable ##{number2}", ref enabled); + if (ImGui.Checkbox($"Enable ##{number2}", ref enabled)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Turn the doodle on/off entirely"); } ImGui.SameLine(); - ImGui.Checkbox($"Combat ##{number2}", ref combat); + if (ImGui.Checkbox($"Combat ##{number2}", ref combat)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Only show when engaged in combat"); } ImGui.SameLine(); - ImGui.Checkbox($"Instance ##{number2}", ref instance); + if (ImGui.Checkbox($"Instance ##{number2}", ref instance)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Only show when in an instance (a dungeon/raid etc)"); } ImGui.SameLine(); - ImGui.Checkbox($"Unsheathed ##{number2}", ref unsheathed); + if (ImGui.Checkbox($"Unsheathed ##{number2}", ref unsheathed)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Only show when your weapon is unsheathed"); @@ -87,7 +87,7 @@ private void DrawConfig() ImGui.SameLine(); ImGui.PushItemWidth(150); - ImGui.InputText($"Name##{number2}", ref name, 20); + if (ImGui.InputText($"Name##{number2}", ref name, 20)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Name the doodle!"); @@ -138,6 +138,7 @@ private void DrawConfig() if (ImGui.Button("Add Doodle")) { _doodleBag.Add(new Drawing()); + _dirty = true; } if (ImGui.Button("Show Editor")) @@ -216,7 +217,6 @@ private void DrawConfig() var north = doodle.North; var thickness = doodle.Thickness; var segments = doodle.Segments; - var vector = doodle.Vector; var filled = doodle.Filled; var x1 = doodle.Vector.X; var z1 = doodle.Vector.Y; @@ -225,7 +225,6 @@ private void DrawConfig() var zed = doodle.Zed; var zedding = doodle.Zedding; var radius = doodle.Radius; - var job = doodle.Job; var jobsBool = doodle.JobsBool; var offset = doodle.Offset; var rotateOffset = doodle.RotateOffset; @@ -233,15 +232,15 @@ private void DrawConfig() var outlineColour = doodle.OutlineColour; ImGui.PushItemWidth(300); - ImGui.Combo($"Type ##{number}", ref type, _doodleOptions, _doodleOptions.Length); - ImGui.ColorEdit4($"Colour ##{number}", ref colour, ImGuiColorEditFlags.NoInputs); + if (ImGui.Combo($"Type ##{number}", ref type, _doodleOptions, _doodleOptions.Length)) _dirty = true; + if (ImGui.ColorEdit4($"Colour ##{number}", ref colour, ImGuiColorEditFlags.NoInputs)) _dirty = true; if (ImGui.TreeNode($"Jobs##{number}")) { ImGui.Columns(6); var count = Math.Min(jobsBool.Length, _doodleJobs.Length); for (var loop = 0; loop < count; loop++) { - ImGui.Checkbox($"{_doodleJobs[loop]}", ref jobsBool[loop]); + if (ImGui.Checkbox($"{_doodleJobs[loop]}", ref jobsBool[loop])) _dirty = true; if (loop == 0 | loop == 4 | loop == 8 | loop == 14 | loop == 17) { @@ -253,72 +252,72 @@ private void DrawConfig() ImGui.TreePop(); } - ImGui.InputFloat($"Thickness ##{number}", ref thickness, 0.1f, 1f); + if (ImGui.InputFloat($"Thickness ##{number}", ref thickness, 0.1f, 1f)) _dirty = true; if (type == 0) //ring { - ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f); - ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10); - ImGui.Checkbox($"Offset##{number}", ref offset); - ImGui.Checkbox($"Fill##{number}", ref filled); - ImGui.Checkbox($"Z##{number}", ref zedding); + if (ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f)) _dirty = true; + if (ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10)) _dirty = true; + if (ImGui.Checkbox($"Offset##{number}", ref offset)) _dirty = true; + if (ImGui.Checkbox($"Fill##{number}", ref filled)) _dirty = true; + if (ImGui.Checkbox($"Z##{number}", ref zedding)) _dirty = true; if (zedding) { - ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f); + if (ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f)) _dirty = true; } if (offset) { ImGui.SameLine(); - ImGui.Checkbox($"Rotate##{number}", ref rotateOffset); - ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f); - ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f); + if (ImGui.Checkbox($"Rotate##{number}", ref rotateOffset)) _dirty = true; + if (ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f)) _dirty = true; } } if (type == 1) //line { - ImGui.Checkbox($"Locked North ##{number}", ref north); + if (ImGui.Checkbox($"Locked North ##{number}", ref north)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Otherwise, player relative"); } - ImGui.Checkbox($"Z##{number}", ref zedding); + if (ImGui.Checkbox($"Z##{number}", ref zedding)) _dirty = true; if (zedding) { - ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f); + if (ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f)) _dirty = true; } ImGui.PushItemWidth(100); - ImGui.InputFloat($"X 1##{number}", ref x1, 0.1f, 1f); + if (ImGui.InputFloat($"X 1##{number}", ref x1, 0.1f, 1f)) _dirty = true; ImGui.SameLine(); - ImGui.InputFloat($"Y 1##{number}", ref z1, 0.1f, 1f); - ImGui.InputFloat($"X 2##{number}", ref x2, 0.1f, 1f); + if (ImGui.InputFloat($"Y 1##{number}", ref z1, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"X 2##{number}", ref x2, 0.1f, 1f)) _dirty = true; ImGui.SameLine(); - ImGui.InputFloat($"Y 2##{number}", ref z2, 0.1f, 1f); + if (ImGui.InputFloat($"Y 2##{number}", ref z2, 0.1f, 1f)) _dirty = true; ImGui.PopItemWidth(); } if (type == 2) //dot { - ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f); - ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10); - ImGui.Checkbox($"Filled##{number}", ref filled); + if (ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f)) _dirty = true; + if (ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10)) _dirty = true; + if (ImGui.Checkbox($"Filled##{number}", ref filled)) _dirty = true; ImGui.SameLine(); - ImGui.Checkbox($"Offset##{number}", ref offset); + if (ImGui.Checkbox($"Offset##{number}", ref offset)) _dirty = true; ImGui.SameLine(); - ImGui.Checkbox($"Outline##{number}", ref outline); + if (ImGui.Checkbox($"Outline##{number}", ref outline)) _dirty = true; if (outline) { - ImGui.ColorEdit4($"Outline Colour ##{number}", ref outlineColour, - ImGuiColorEditFlags.NoInputs); + if (ImGui.ColorEdit4($"Outline Colour ##{number}", ref outlineColour, + ImGuiColorEditFlags.NoInputs)) _dirty = true; } - ImGui.Checkbox($"Z##{number}", ref zedding); + if (ImGui.Checkbox($"Z##{number}", ref zedding)) _dirty = true; if (zedding) { - ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f); + if (ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f)) _dirty = true; } - ImGui.Checkbox($"Locked North ##{number}", ref north); + if (ImGui.Checkbox($"Locked North ##{number}", ref north)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Otherwise, player relative"); @@ -326,69 +325,60 @@ private void DrawConfig() if (offset) { - ImGui.Checkbox($"Rotate offset relative to player##{number}", ref rotateOffset); - ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f); - ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f); + if (ImGui.Checkbox($"Rotate offset relative to player##{number}", ref rotateOffset)) _dirty = true; + if (ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f)) _dirty = true; } if (!north) { - ImGui.InputFloat($"Offset X2##{number}", ref x2, 0.1f, 1f); - ImGui.InputFloat($"Offset Y2##{number}", ref z2, 0.1f, 1f); + if (ImGui.InputFloat($"Offset X2##{number}", ref x2, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"Offset Y2##{number}", ref z2, 0.1f, 1f)) _dirty = true; } } if (type == 3) //dashed ring { - ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f); - ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10); - ImGui.Checkbox($"Z##{number}", ref zedding); + if (ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f)) _dirty = true; + if (ImGui.InputInt($"Segments ##{number}", ref segments, 1, 10)) _dirty = true; + if (ImGui.Checkbox($"Z##{number}", ref zedding)) _dirty = true; if (zedding) { - ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f); + if (ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f)) _dirty = true; } - ImGui.Checkbox($"Offset##{number}", ref offset); + if (ImGui.Checkbox($"Offset##{number}", ref offset)) _dirty = true; if (offset) { ImGui.SameLine(); - ImGui.Checkbox($"Rotate##{number}", ref rotateOffset); - ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f); - ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f); + if (ImGui.Checkbox($"Rotate##{number}", ref rotateOffset)) _dirty = true; + if (ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f)) _dirty = true; } } if (type == 4) //Cone { - if (_ot.LocalPlayer?.TargetObject != null) { - ImGui.Text($"{_ot.LocalPlayer.TargetObject.Position.X}"); - ImGui.Text($"{_ot.LocalPlayer.TargetObject.Position.Z}"); - var atan = Math.Atan2(_ot.LocalPlayer.TargetObject.Position.X - _ot.LocalPlayer.Position.X, _ot.LocalPlayer.TargetObject.Position.Z - _ot.LocalPlayer.Position.Z); - var degr = atan * (180 / Math.PI); - ImGui.Text($"{atan}"); - ImGui.Text($"{degr}"); - } - - ImGui.Checkbox($"Locked North ##{number}", ref north); + if (ImGui.Checkbox($"Locked North ##{number}", ref north)) _dirty = true; if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Otherwise, player relative"); } - ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f); - ImGui.InputInt($"Degrees ##{number}", ref segments, 1, 10); - ImGui.Checkbox($"Offset##{number}", ref offset); - ImGui.Checkbox($"Fill##{number}", ref filled); - ImGui.Checkbox($"Target##{number}", ref outline); - ImGui.Checkbox($"Z##{number}", ref zedding); + if (ImGui.InputFloat($"Radius##{number}", ref radius, 0.1f, 1f)) _dirty = true; + if (ImGui.InputInt($"Degrees ##{number}", ref segments, 1, 10)) _dirty = true; + if (ImGui.Checkbox($"Offset##{number}", ref offset)) _dirty = true; + if (ImGui.Checkbox($"Fill##{number}", ref filled)) _dirty = true; + if (ImGui.Checkbox($"Target##{number}", ref outline)) _dirty = true; + if (ImGui.Checkbox($"Z##{number}", ref zedding)) _dirty = true; if (zedding) { - ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f); + if (ImGui.InputFloat($"Z-value##{number}", ref zed, 0.01f, 0.1f)) _dirty = true; } if (offset) { ImGui.SameLine(); - ImGui.Checkbox($"Rotate##{number}", ref rotateOffset); - ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f); - ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f); + if (ImGui.Checkbox($"Rotate##{number}", ref rotateOffset)) _dirty = true; + if (ImGui.InputFloat($"Offset X##{number}", ref x1, 0.1f, 1f)) _dirty = true; + if (ImGui.InputFloat($"Offset Y##{number}", ref z1, 0.1f, 1f)) _dirty = true; } } ImGui.PopItemWidth(); @@ -412,13 +402,11 @@ private void DrawConfig() } doodle.Segments = segments; - doodle.Vector = vector; doodle.Filled = filled; doodle.Radius = radius; doodle.Zed = zed; doodle.Zedding = zedding; doodle.Vector = new Vector4(x1, z1, x2, z2); - doodle.Job = job; doodle.JobsBool = jobsBool; doodle.Offset = offset; doodle.RotateOffset = rotateOffset; @@ -464,30 +452,26 @@ private void DrawConfig() ImGui.PopStyleVar(); ImGui.End(); - if (_dirtyHack > 100) - { - SaveConfig(); - _dirtyHack = 0; - } - - _dirtyHack++; if (deleteNum != -1) { _doodleBag.RemoveAt(deleteNum); + _dirty = true; } - if (moveNum == -1) return; - var doodleA = _doodleBag[moveNum]; - _doodleBag.RemoveAt(moveNum); - if (moveUp) + if (moveNum != -1) { - _doodleBag.Insert(moveNum - 1, doodleA); - } - else - { - _doodleBag.Insert(moveNum + 1, doodleA); + var doodleA = _doodleBag[moveNum]; + _doodleBag.RemoveAt(moveNum); + _doodleBag.Insert(moveUp ? moveNum - 1 : moveNum + 1, doodleA); + _dirty = true; } } + + if (_dirty && !ImGui.IsAnyItemActive()) + { + SaveConfig(); + _dirty = false; + } } public void AddNotification( diff --git a/PixelPerfect/Doodles.cs b/PixelPerfect/Doodles.cs index ee17617..658a770 100644 --- a/PixelPerfect/Doodles.cs +++ b/PixelPerfect/Doodles.cs @@ -12,6 +12,8 @@ private void DrawDoodles() { if (_ot.LocalPlayer == null) return; + if (_condition[ConditionFlag.Occupied38]) return; // is in-combat cutscene + var actor = _ot.LocalPlayer; ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); @@ -25,8 +27,6 @@ private void DrawDoodles() foreach (var doodle in _doodleBag) { if (!doodle.Enabled) continue; - - if (_condition[ConditionFlag.Occupied38]) continue; // is in-combat cutscene if (!CheckJob(_ot.LocalPlayer.ClassJob.RowId, doodle.JobsBool)) continue; diff --git a/PixelPerfect/Editor.cs b/PixelPerfect/Editor.cs index a94fc43..88fd32f 100644 --- a/PixelPerfect/Editor.cs +++ b/PixelPerfect/Editor.cs @@ -83,6 +83,7 @@ private void DrawEditor() { skip = true; _grabbed = -1; + _dirty = true; } } if (_grabbed == 1) @@ -104,6 +105,7 @@ private void DrawEditor() { skip = true; _grabbed = -1; + _dirty = true; } } if (_grabbed == 2) diff --git a/PixelPerfect/Main.cs b/PixelPerfect/Main.cs index 1891d0e..8f950a4 100644 --- a/PixelPerfect/Main.cs +++ b/PixelPerfect/Main.cs @@ -3,20 +3,12 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.Command; -using Dalamud.Game.Gui; using Dalamud.Plugin; using Dalamud.Bindings.ImGui; -using Num = System.Numerics; using System.Collections.Generic; using System.Numerics; -using Condition = Dalamud.Game.ClientState.Conditions.ConditionFlag; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; -using Dalamud.Interface; -using Dalamud.Interface.Windowing; -using Lumina.Excel.Sheets; -using System.Linq; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace PixelPerfect @@ -36,7 +28,7 @@ public partial class PixelPerfect : IDalamudPlugin private bool _editor; private bool _firstTime; private bool _editorHelp; - private int _dirtyHack; + private bool _dirty; private readonly string[] _doodleOptions; private readonly string[] _doodleJobs; private readonly uint[] _doodleJobsUint; @@ -329,8 +321,6 @@ private static void DrawRingEditor(float dX, float dY, float radius, int numSegm } ImGui.GetWindowDrawList().PathStroke(colour, ImDrawFlags.Closed, thicc); } - - public static JobIds IdToJob(uint job) => job < 19 ? JobIds.OTHER : (JobIds)job; } @@ -368,31 +358,4 @@ public class Config : IPluginConfiguration public bool Bitch { get; set; } public List DoodleBag { get; set; } = new(); } - - public enum JobIds : uint - { - OTHER = 0, - GNB = 37, - AST = 33, - PLD = 19, - WAR = 21, - DRK = 32, - SCH = 28, - WHM = 24, - BRD = 23, - DRG = 22, - SMN = 27, - SAM = 34, - BLM = 25, - RDM = 35, - MCH = 31, - DNC = 38, - NIN = 30, - MNK = 20, - BLU = 36, - RPR = 39, - SGE = 40, - VPR=41, - PCT=42 - } } diff --git a/PixelPerfect/PixelPerfect.json b/PixelPerfect/PixelPerfect.json index befca4a..e64846b 100644 --- a/PixelPerfect/PixelPerfect.json +++ b/PixelPerfect/PixelPerfect.json @@ -2,7 +2,6 @@ "Author": "Haplo064", "Name": "Pixel Perfect", "InternalName": "PixelPerfect", - "AssemblyVersion": "3.3.3.0", "Description": "Showing exactly where to stand since 2020. Endorsed by raiders!", "ApplicableVersion": "any", "RepoUrl": "https://github.com/Haplo064/PixelPerfect",