feat(ui): port retail jump power bar
Import gmFloatyPowerBarUI LayoutDesc 0x21000072, teach the meter factory its stateful single-image shape, and project the movement-owned retail jump charge through a focused retained controller. Preserve authored resize constraints and state-managed visibility, with named-decomp pseudocode plus controller, movement, and production-DAT conformance coverage. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
27619b2328
commit
db03b4bda8
18 changed files with 3045 additions and 10 deletions
|
|
@ -313,6 +313,37 @@ public class DatWidgetFactoryTests
|
|||
Assert.Equal(0u, m.FrontRight);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildMeter_StatefulSingleImageShape_MapsNumericFillStatesAndHidesDirectRange()
|
||||
{
|
||||
const uint TrackFile = 0x06004D0Bu;
|
||||
const uint JumpState = 0x10000042u;
|
||||
const uint JumpFile = 0x06001354u;
|
||||
const uint RangeFile = 0x0600715Eu;
|
||||
|
||||
var meter = new ElementInfo { Type = 7, Id = 0x10000034u, Width = 600, Height = 15 };
|
||||
meter.StateMedia[""] = (TrackFile, 1);
|
||||
|
||||
var fills = new ElementInfo { Type = 3, Id = 2u, ReadOrder = 2 };
|
||||
fills.States[JumpState] = new UiStateInfo { Id = JumpState, Name = "JumpMode" };
|
||||
fills.StateMedia["JumpMode"] = (JumpFile, 1);
|
||||
meter.Children.Add(fills);
|
||||
|
||||
var range = new ElementInfo { Type = 3, Id = 0x100005EEu, ReadOrder = 1 };
|
||||
range.StateMedia[""] = (RangeFile, 1);
|
||||
meter.Children.Add(range);
|
||||
|
||||
var result = Assert.IsType<UiMeter>(DatWidgetFactory.Create(meter, NoTex, null));
|
||||
|
||||
Assert.Equal(TrackFile, result.BackTile);
|
||||
Assert.Equal(0u, result.FrontTile);
|
||||
Assert.True(result.TrySetRetailState(JumpState));
|
||||
Assert.Equal(JumpState, result.ActiveRetailStateId);
|
||||
Assert.Equal(JumpFile, result.FrontTile);
|
||||
Assert.NotEqual(RangeFile, result.BackTile);
|
||||
Assert.NotEqual(RangeFile, result.FrontTile);
|
||||
}
|
||||
|
||||
// ── Test 6: Meter slice extraction (the important one) ───────────────────
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -95,6 +95,12 @@ public static class FixtureLoader
|
|||
public static ElementInfo LoadCombatInfos()
|
||||
=> LoadInfos("combat_21000073.json");
|
||||
|
||||
public static ImportedLayout LoadPowerbar()
|
||||
=> LayoutImporter.Build(LoadPowerbarInfos(), _ => (0u, 0, 0), null);
|
||||
|
||||
public static ElementInfo LoadPowerbarInfos()
|
||||
=> LoadInfos("powerbar_21000072.json");
|
||||
|
||||
// ── Shared loader ────────────────────────────────────────────────────────
|
||||
|
||||
private static AcDream.App.UI.Layout.ElementInfo LoadInfos(string fileName)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
using AcDream.App.Input;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class JumpPowerbarControllerTests
|
||||
{
|
||||
private const uint JumpFill = 0x06001354u;
|
||||
|
||||
[Fact]
|
||||
public void MovementCharge_ShowsUpdatesAndHidesAuthoredJumpMeter()
|
||||
{
|
||||
JumpChargeSnapshot state = default;
|
||||
ImportedLayout layout = BuildLayout();
|
||||
var visibility = new List<bool>();
|
||||
using var controller = JumpPowerbarController.Bind(
|
||||
layout, () => state, visibility.Add)!;
|
||||
var meter = Assert.IsType<UiMeter>(layout.FindElement(JumpPowerbarController.MeterId));
|
||||
|
||||
Assert.Equal([false], visibility);
|
||||
Assert.Equal(0f, meter.Fill());
|
||||
Assert.Equal(JumpPowerbarController.JumpModeState, meter.ActiveRetailStateId);
|
||||
Assert.Equal(JumpFill, meter.FrontTile);
|
||||
|
||||
state = new JumpChargeSnapshot(true, 0.35f);
|
||||
controller.Tick();
|
||||
Assert.Equal([false, true], visibility);
|
||||
Assert.Equal(0.35f, meter.Fill());
|
||||
|
||||
state = new JumpChargeSnapshot(true, 1.4f);
|
||||
controller.Tick();
|
||||
Assert.Equal([false, true], visibility);
|
||||
Assert.Equal(1f, meter.Fill());
|
||||
|
||||
state = default;
|
||||
controller.Tick();
|
||||
Assert.Equal([false, true, false], visibility);
|
||||
Assert.Equal(0f, meter.Fill());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bind_RejectsLayoutWithoutJumpModeMeter()
|
||||
{
|
||||
var root = new UiPanel();
|
||||
var meter = new UiMeter { ElementId = JumpPowerbarController.MeterId };
|
||||
var layout = new ImportedLayout(root, new Dictionary<uint, UiElement>
|
||||
{
|
||||
[JumpPowerbarController.MeterId] = meter,
|
||||
});
|
||||
|
||||
Assert.Null(JumpPowerbarController.Bind(layout, () => default, _ => { }));
|
||||
}
|
||||
|
||||
private static ImportedLayout BuildLayout()
|
||||
{
|
||||
var root = new UiPanel { Width = 610, Height = 25 };
|
||||
var meter = new UiMeter
|
||||
{
|
||||
ElementId = JumpPowerbarController.MeterId,
|
||||
Left = 5,
|
||||
Top = 5,
|
||||
Width = 600,
|
||||
Height = 15,
|
||||
};
|
||||
meter.ConfigureStateFill(JumpPowerbarController.JumpModeState, JumpFill);
|
||||
root.AddChild(meter);
|
||||
return new ImportedLayout(root, new Dictionary<uint, UiElement>
|
||||
{
|
||||
[JumpPowerbarController.MeterId] = meter,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class JumpPowerbarLayoutConformanceTests
|
||||
{
|
||||
[Fact]
|
||||
public void RetailFixture_UsesExactFloatyGeometryTrackAndJumpFill()
|
||||
{
|
||||
ElementInfo info = FixtureLoader.LoadPowerbarInfos();
|
||||
ImportedLayout layout = LayoutImporter.Build(info, _ => (0u, 0, 0), null);
|
||||
var meter = Assert.IsType<UiMeter>(
|
||||
layout.FindElement(JumpPowerbarController.MeterId));
|
||||
|
||||
Assert.Equal((610f, 25f), (layout.Root.Width, layout.Root.Height));
|
||||
Assert.Equal((5f, 5f, 600f, 15f),
|
||||
(meter.Left, meter.Top, meter.Width, meter.Height));
|
||||
Assert.Equal(0x06004D0Bu, meter.BackTile);
|
||||
Assert.True(meter.TrySetRetailState(JumpPowerbarController.JumpModeState));
|
||||
Assert.Equal(0x06001354u, meter.FrontTile);
|
||||
|
||||
Assert.Equal((0f, 0f, 300f, 15f),
|
||||
UiMeter.ComputeFillRect(0.5f, meter.Width, meter.Height));
|
||||
|
||||
Assert.True(info.TryGetEffectiveInteger(0x3Fu, out int minWidth));
|
||||
Assert.True(info.TryGetEffectiveInteger(0x3Du, out int maxWidth));
|
||||
Assert.True(info.TryGetEffectiveInteger(0x3Eu, out int minHeight));
|
||||
Assert.True(info.TryGetEffectiveInteger(0x3Cu, out int maxHeight));
|
||||
Assert.Equal((50, 810, 25, 25),
|
||||
(minWidth, maxWidth, minHeight, maxHeight));
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ public sealed class RetailLayoutFixtureGenerator
|
|||
(0x21000024u, "paperdoll_21000024.json"),
|
||||
(0x2100002Eu, "character_2100002E.json"),
|
||||
(0x2100006Cu, "vitals_2100006C.json"),
|
||||
(0x21000072u, "powerbar_21000072.json"),
|
||||
(0x21000073u, "combat_21000073.json"),
|
||||
(0x21000074u, "radar_21000074.json"),
|
||||
};
|
||||
|
|
|
|||
2467
tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json
Normal file
2467
tests/AcDream.App.Tests/UI/Layout/fixtures/powerbar_21000072.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -293,6 +293,24 @@ public class PlayerMovementControllerTests
|
|||
Assert.True(controller.VerticalVelocity > 0f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JumpChargeSnapshot_TracksHeldChargeAndResetsOnRelease()
|
||||
{
|
||||
var engine = MakeFlatEngine();
|
||||
var controller = new PlayerMovementController(engine);
|
||||
controller.SetPosition(new Vector3(96f, 96f, 50f), 0x0001);
|
||||
|
||||
Assert.Equal(default, controller.JumpCharge);
|
||||
|
||||
controller.Update(0.25f, new MovementInput(Jump: true));
|
||||
Assert.True(controller.JumpCharge.IsCharging);
|
||||
Assert.Equal(0.25f, controller.JumpCharge.Power, precision: 3);
|
||||
|
||||
controller.Update(0.016f, new MovementInput(Jump: false));
|
||||
Assert.False(controller.JumpCharge.IsCharging);
|
||||
Assert.Equal(0f, controller.JumpCharge.Power);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_AirborneFrames_ZRiseThenFalls()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue