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

@ -0,0 +1,159 @@
using System.Numerics;
using AcDream.App.UI;
using AcDream.App.UI.Layout;
using AcDream.Core.Ui;
namespace AcDream.App.Tests.UI.Layout;
public sealed class RadarControllerTests
{
[Fact]
public void Bind_RealFixture_ReusesImportedCoordinateTextAndDatBackground()
{
var layout = FixtureLoader.LoadRadar();
_ = RadarController.Bind(
layout,
() => new UiRadarSnapshot(0f, Array.Empty<UiRadarBlip>(), "42.1N,33.6E"));
var coordinates = Assert.IsType<UiText>(
layout.FindElement(RadarController.CoordinateContainerId));
Assert.Equal(0x06004CC0u, coordinates.BackgroundSprite);
Assert.Empty(coordinates.Children);
Assert.True(coordinates.ClickThrough);
Assert.False(coordinates.AcceptsFocus);
Assert.Equal("42.1N,33.6E", Assert.Single(coordinates.LinesProvider()).Text);
}
[Fact]
public void Bind_UsesImportedChildrenForCompassCoordinatesAndLockChrome()
{
var layout = BuildRadarLayout();
var state = new UiRadarSnapshot(
0f,
Array.Empty<UiRadarBlip>(),
"42.1N,33.6E",
UiLocked: false);
bool? requestedLock = null;
_ = RadarController.Bind(
layout,
() => state,
setUiLocked: value => requestedLock = value);
var radar = Assert.IsType<UiRadar>(layout.Root);
Assert.Equal(new Vector2(60f, 60f), radar.Center);
Assert.True(radar.Draggable);
var coordinateContainer = Assert.IsType<UiDatElement>(
layout.FindElement(RadarController.CoordinateContainerId));
Assert.True(coordinateContainer.Visible);
var coordinateText = Assert.IsType<UiText>(Assert.Single(coordinateContainer.Children));
Assert.Equal("42.1N,33.6E", Assert.Single(coordinateText.LinesProvider()).Text);
var north = layout.FindElement(RadarController.NorthTokenId)!;
Assert.Equal(55f, north.Left);
Assert.Equal(1f, north.Top);
var lockButton = Assert.IsType<UiButton>(layout.FindElement(RadarController.LockButtonId));
Assert.Equal("UnlockedUI", lockButton.ActiveState);
var click = new UiEvent(lockButton.EventId, lockButton, UiEventType.Click);
Assert.True(lockButton.OnEvent(in click));
Assert.True(requestedLock);
state = state with
{
PlayerHeadingDegrees = 90f,
CoordinatesText = null,
UiLocked = true,
};
radar.Refresh();
Assert.False(radar.Draggable);
Assert.False(coordinateContainer.Visible);
Assert.False(coordinateText.Visible);
Assert.Equal("LockedUI", lockButton.ActiveState);
var drag = layout.FindElement(RadarController.DragButtonId)!;
Assert.False(drag.Visible);
Assert.False(drag.ClickThrough);
float northMagnitude = Vector2.Distance(new Vector2(60f, 5.5f), radar.Center);
var expectedNorth = RetailRadar.GetCompassTokenTopLeft(
90f,
RadarCompassPoint.North,
radar.Center,
northMagnitude,
new Vector2(north.Width, north.Height));
Assert.Equal(expectedNorth.X, north.Left);
Assert.Equal(expectedNorth.Y, north.Top);
}
[Fact]
public void Bind_LeavesStaticDatChildrenInRetainedTree()
{
var layout = BuildRadarLayout();
_ = RadarController.Bind(layout, () => UiRadarSnapshot.Empty);
Assert.Equal(8, layout.Root.Children.Count);
Assert.IsType<UiDatElement>(layout.FindElement(RadarController.RadarDiscId));
Assert.IsType<UiButton>(layout.FindElement(RadarController.LockButtonId));
Assert.IsType<UiDatElement>(layout.FindElement(RadarController.DragButtonId));
Assert.False(layout.FindElement(RadarController.CoordinateContainerId)!.Visible);
}
private static ImportedLayout BuildRadarLayout()
{
var root = new ElementInfo
{
Id = RadarController.RootId,
Type = UiRadar.RetailClassId,
Width = 120,
Height = 140,
};
root.Children.Add(Sprite(RadarController.CoordinateContainerId, 0, 120, 120, 18, 0x06004CC0u));
root.Children.Add(Sprite(RadarController.RadarDiscId, 0, 0, 120, 120, 0x06004CC1u));
root.Children.Add(Sprite(RadarController.NorthTokenId, 55, 1, 10, 9, 0x060011FBu));
root.Children.Add(Sprite(RadarController.EastTokenId, 110, 55, 10, 9, 0x06001938u));
root.Children.Add(Sprite(RadarController.SouthTokenId, 55, 110, 10, 9, 0x0600193Au));
root.Children.Add(Sprite(RadarController.WestTokenId, 0, 55, 10, 9, 0x0600193Cu));
var lockButton = new ElementInfo
{
Id = RadarController.LockButtonId,
Type = 1,
X = 6,
Y = 6,
Width = 27,
Height = 27,
};
lockButton.StateMedia["LockedUI"] = (0x060074B7u, 3);
lockButton.StateMedia["UnlockedUI"] = (0x060074B8u, 3);
root.Children.Add(lockButton);
root.Children.Add(Sprite(RadarController.DragButtonId, 87, 6, 27, 27, 0x060074C9u, type: 2));
return LayoutImporter.Build(root, _ => (0u, 0, 0), null);
}
private static ElementInfo Sprite(
uint id,
float x,
float y,
float width,
float height,
uint sprite,
uint type = 3)
{
var info = new ElementInfo
{
Id = id,
Type = type,
X = x,
Y = y,
Width = width,
Height = height,
};
info.StateMedia[""] = (sprite, 1);
return info;
}
}