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:
Erik 2026-07-13 10:22:03 +02:00
parent 27619b2328
commit db03b4bda8
18 changed files with 3045 additions and 10 deletions

View file

@ -311,7 +311,9 @@ public static class DatWidgetFactory
.OrderBy(c => c.ReadOrder)
.ToList();
if (containers.Count >= 2)
if (containers.Count >= 2
&& HasThreeSliceShape(containers[0])
&& HasThreeSliceShape(containers[1]))
{
// Vitals 3-slice shape: two Type-3 containers each holding 3 grandchild images
// (left-cap / center-tile / right-cap). Back is the lower ReadOrder; front is higher.
@ -325,7 +327,7 @@ public static class DatWidgetFactory
m.FrontTile = ft;
m.FrontRight = fr;
}
else if (containers.Count == 1)
else if (containers.Count == 1 && containers[0].StateMedia.ContainsKey(""))
{
// Single-image shape used by the toolbar selected-object meters
// (health 0x100001A1, mana 0x100001A2).
@ -354,15 +356,50 @@ public static class DatWidgetFactory
m.FrontTile = containers[0].StateMedia.TryGetValue("", out var fm) ? fm.File : 0u;
m.FrontRight = 0;
}
else if (containers.Any(HasStatefulFill))
{
// Stateful single-image shape used by gmPowerbarUI (LayoutDesc
// 0x21000072). The meter's DirectState is the empty track. One
// Type-3 child supplies named Jump/Melee/Missile/DDD state media;
// another carries the optional recklessness range as DirectState.
// gmPowerbarUI::PostInit @ 0x004DA4E0 hides that range, so it is not
// promoted into a meter slice here.
m.BackLeft = 0;
m.BackTile = info.StateMedia.TryGetValue("", out var track) ? track.File : 0u;
m.BackRight = 0;
m.FrontLeft = 0;
m.FrontTile = 0;
m.FrontRight = 0;
foreach (ElementInfo container in containers)
{
foreach (var (stateId, state) in container.States)
{
if (stateId == UiStateInfo.DirectStateId
|| !container.StateMedia.TryGetValue(state.Name, out var media))
continue;
m.ConfigureStateFill(stateId, media.File);
}
}
}
else
{
// Count == 0: no Type-3 containers at all — genuinely malformed meter dat.
Console.WriteLine($"[D.2b] meter 0x{info.Id:X8}: {containers.Count} Type-3 slice containers (expected 1 or 2) — bars may render as solid-color fallback.");
Console.WriteLine($"[D.2b] meter 0x{info.Id:X8}: {containers.Count} Type-3 containers but no recognized 3-slice, direct-fill, or stateful-fill shape — bar may render as solid-color fallback.");
}
return m;
}
private static bool HasThreeSliceShape(ElementInfo container)
=> container.Children.Count(c =>
c.StateMedia.TryGetValue("", out var media) && media.File != 0) >= 3;
private static bool HasStatefulFill(ElementInfo container)
=> container.States.Any(pair =>
pair.Key != UiStateInfo.DirectStateId
&& container.StateMedia.TryGetValue(pair.Value.Name, out var media)
&& media.File != 0);
/// <summary>
/// Returns the (left, tile, right) sprite ids for a 3-slice container,
/// extracting them from the container's image children that carry a DirectState