feat(ui): port retail radar and compass

This commit is contained in:
Erik 2026-07-10 16:14:37 +02:00
parent c4af181b92
commit 3cbe4b00a1
43 changed files with 2882 additions and 262 deletions

View file

@ -51,7 +51,9 @@ public sealed record GameplaySettings(
ShowTooltips: true,
VividTargetingIndicator: false,
SideBySideVitals: false,
CoordinatesOnRadar: false,
// Retail default character-options mask 0x50C4A54A includes
// CoordinatesOnRadar (0x00400000).
CoordinatesOnRadar: true,
SpellDuration: true,
AllowGive: true,
ShowHelm: true,

View file

@ -7,6 +7,9 @@ using AcDream.UI.Abstractions.Settings;
namespace AcDream.UI.Abstractions.Panels.Settings;
/// <summary>Per-character retained-window position in screen pixels.</summary>
public readonly record struct UiWindowPosition(float X, float Y);
/// <summary>
/// JSON-backed persistence for non-keybind settings (Display today; future
/// tabs Audio / Gameplay / Chat / Character will be added to the same
@ -290,6 +293,70 @@ public sealed class SettingsStore
File.WriteAllText(_path, root.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));
}
/// <summary>Load a named retained-window position for one character.</summary>
public UiWindowPosition? LoadWindowPosition(string toonKey, string windowName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(toonKey);
ArgumentException.ThrowIfNullOrWhiteSpace(windowName);
if (!File.Exists(_path)) return null;
try
{
var root = JsonNode.Parse(File.ReadAllText(_path)) as JsonObject;
var node = root?["windowPositions"]?[toonKey]?[windowName] as JsonObject;
if (node is null) return null;
float? x = node["x"]?.GetValue<float>();
float? y = node["y"]?.GetValue<float>();
return x is not null && y is not null ? new UiWindowPosition(x.Value, y.Value) : null;
}
catch (Exception ex)
{
Console.WriteLine($"settings: failed to load window position from {_path}: {ex.Message}");
return null;
}
}
/// <summary>
/// Save a named retained-window position under
/// <c>windowPositions[toonKey][windowName]</c>, preserving all settings sections.
/// </summary>
public void SaveWindowPosition(string toonKey, string windowName, UiWindowPosition position)
{
ArgumentException.ThrowIfNullOrWhiteSpace(toonKey);
ArgumentException.ThrowIfNullOrWhiteSpace(windowName);
var dir = Path.GetDirectoryName(_path);
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
JsonObject root;
if (File.Exists(_path))
{
try { root = JsonNode.Parse(File.ReadAllText(_path)) as JsonObject ?? new JsonObject(); }
catch { root = new JsonObject(); }
}
else
{
root = new JsonObject();
}
if (root["windowPositions"] is not JsonObject positions)
{
positions = new JsonObject();
root["windowPositions"] = positions;
}
if (positions[toonKey] is not JsonObject toon)
{
toon = new JsonObject();
positions[toonKey] = toon;
}
toon[windowName] = new JsonObject
{
["x"] = position.X,
["y"] = position.Y,
};
root["version"] = CurrentSchemaVersion;
File.WriteAllText(_path, root.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));
}
private static SortedDictionary<string, object> BuildChatObject(ChatSettings c)
=> new(StringComparer.Ordinal)
{