fix(combat): preserve authored meter child geometry
Carry the consumed dark-range child's own retail edge policy into the procedural scrollbar, widening it to the authored post-reflow interval without covering the rendered labels. Restore the bright layer to live attack-charge feedback across the full bar from the left. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
f8c174d707
commit
e671b8a5c4
10 changed files with 66 additions and 105 deletions
|
|
@ -93,8 +93,7 @@ public sealed class CombatUiController : IRetainedPanelController
|
|||
|
||||
_powerControl.SetScalarPosition(_attacks.DesiredPower);
|
||||
_powerControl.ScalarChanged = _attacks.SetDesiredPower;
|
||||
_powerControl.ScalarFill = () => null;
|
||||
_powerControl.ScalarFillFollowsThumb = true;
|
||||
_powerControl.ScalarFill = () => _attacks.PowerBarLevel;
|
||||
|
||||
BindAttackButton(_high, AttackHeight.High);
|
||||
BindAttackButton(_medium, AttackHeight.Medium);
|
||||
|
|
@ -110,7 +109,6 @@ public sealed class CombatUiController : IRetainedPanelController
|
|||
UiText? powerLabel = layout.FindElement(PowerLabelId) as UiText;
|
||||
SetStaticText(speedLabel, labels.Speed, rightAligned: false);
|
||||
SetStaticText(powerLabel, labels.Power, rightAligned: true);
|
||||
ConfigurePowerRange(speedLabel, powerLabel);
|
||||
|
||||
_repeatAttacks.OnClick = () =>
|
||||
_setGameplay(_gameplay() with { AutoRepeatAttack = _repeatAttacks.Selected });
|
||||
|
|
@ -194,24 +192,6 @@ public sealed class CombatUiController : IRetainedPanelController
|
|||
_keepInView.Selected = gameplay.ViewCombatTarget;
|
||||
}
|
||||
|
||||
private void ConfigurePowerRange(UiText? speedLabel, UiText? powerLabel)
|
||||
{
|
||||
if (speedLabel is null || powerLabel is null) return;
|
||||
|
||||
// All three elements are siblings in gmCombatUI's basic page. Keeping
|
||||
// the meter between the authored text rectangles leaves the base gray
|
||||
// track behind Speed/Power while the dark/live red textures occupy the
|
||||
// semantic power range between them.
|
||||
_powerControl.ScalarRangeProvider = () =>
|
||||
{
|
||||
float left = speedLabel.Left + speedLabel.Width - _powerControl.Left;
|
||||
float right = powerLabel.Left - _powerControl.Left;
|
||||
float clampedLeft = Math.Clamp(left, 0f, _powerControl.Width);
|
||||
return (clampedLeft, Math.Max(
|
||||
0f, Math.Clamp(right, 0f, _powerControl.Width) - clampedLeft));
|
||||
};
|
||||
}
|
||||
|
||||
private static void SetStaticText(UiText? text, string value, bool rightAligned)
|
||||
{
|
||||
if (text is null) return;
|
||||
|
|
@ -236,8 +216,6 @@ public sealed class CombatUiController : IRetainedPanelController
|
|||
_attacks.StateChanged -= OnAttackStateChanged;
|
||||
_powerControl.ScalarChanged = null;
|
||||
_powerControl.ScalarFill = () => null;
|
||||
_powerControl.ScalarFillFollowsThumb = false;
|
||||
_powerControl.ScalarRangeProvider = null;
|
||||
_high.OnPressed = null;
|
||||
_high.OnReleased = null;
|
||||
_medium.OnPressed = null;
|
||||
|
|
|
|||
|
|
@ -110,23 +110,7 @@ public static class DatWidgetFactory
|
|||
// design parent and intentionally remain on the compatibility path until a
|
||||
// window mount assigns its own outer-frame policy.
|
||||
if (info.HasOriginalParentSize)
|
||||
{
|
||||
e.LayoutPolicy = new UiLayoutPolicy(
|
||||
info.Left,
|
||||
info.Top,
|
||||
info.Right,
|
||||
info.Bottom,
|
||||
UiPixelRect.FromPositionAndSize(
|
||||
(int)info.X,
|
||||
(int)info.Y,
|
||||
(int)info.Width,
|
||||
(int)info.Height),
|
||||
UiPixelRect.FromPositionAndSize(
|
||||
0,
|
||||
0,
|
||||
(int)info.OriginalParentWidth,
|
||||
(int)info.OriginalParentHeight));
|
||||
}
|
||||
e.LayoutPolicy = CreateLayoutPolicy(info);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
|
@ -186,6 +170,9 @@ public static class DatWidgetFactory
|
|||
bar.ScalarRangeSprite = scalarRange is null
|
||||
? 0u
|
||||
: DefaultImage(scalarRange);
|
||||
bar.ScalarRangeLayoutPolicy = scalarRange is null
|
||||
? null
|
||||
: CreateLayoutPolicy(scalarRange);
|
||||
// UIElement_Meter::UIElement_Meter @ 0x0046F4C0 defaults direction
|
||||
// 1; DrawChildren @ 0x0046FBD0 clips that direction left-to-right.
|
||||
// Direction 3 is the horizontal reverse. Read authored attribute
|
||||
|
|
@ -221,6 +208,26 @@ public static class DatWidgetFactory
|
|||
return bar;
|
||||
}
|
||||
|
||||
private static UiLayoutPolicy? CreateLayoutPolicy(ElementInfo info)
|
||||
{
|
||||
if (!info.HasOriginalParentSize) return null;
|
||||
return new UiLayoutPolicy(
|
||||
info.Left,
|
||||
info.Top,
|
||||
info.Right,
|
||||
info.Bottom,
|
||||
UiPixelRect.FromPositionAndSize(
|
||||
(int)info.X,
|
||||
(int)info.Y,
|
||||
(int)info.Width,
|
||||
(int)info.Height),
|
||||
UiPixelRect.FromPositionAndSize(
|
||||
0,
|
||||
0,
|
||||
(int)info.OriginalParentWidth,
|
||||
(int)info.OriginalParentHeight));
|
||||
}
|
||||
|
||||
private static uint ReferencedElementId(ElementInfo info, uint propertyId)
|
||||
{
|
||||
if (!info.TryGetEffectiveProperty(propertyId, out var property))
|
||||
|
|
|
|||
|
|
@ -49,13 +49,10 @@ public sealed class UiScrollbar : UiElement
|
|||
public float ScalarRangeLeft { get; set; }
|
||||
public float ScalarRangeWidth { get; set; } = float.PositiveInfinity;
|
||||
/// <summary>
|
||||
/// Optional live range geometry. Imported sibling controls reflow immediately
|
||||
/// before drawing, so combat derives its centered range from their current
|
||||
/// rectangles instead of freezing pre-reflow prototype coordinates.
|
||||
/// Authored layout policy for a consumed range child. This preserves the
|
||||
/// child's own DAT edge modes even though the scrollbar draws it procedurally.
|
||||
/// </summary>
|
||||
public Func<(float Left, float Width)>? ScalarRangeProvider { get; set; }
|
||||
/// <summary>Draw the scalar fill from the absolute left edge to the thumb center.</summary>
|
||||
public bool ScalarFillFollowsThumb { get; set; }
|
||||
public UiLayoutPolicy? ScalarRangeLayoutPolicy { get; set; }
|
||||
/// <summary>
|
||||
/// Draw the scalar meter from the power end (right) toward the speed end
|
||||
/// (left). This is the authored gmCombatUI power meter direction.
|
||||
|
|
@ -150,20 +147,15 @@ public sealed class UiScrollbar : UiElement
|
|||
DrawTiled(ctx, resolve, ScalarRangeSprite,
|
||||
rangeLeft, 0f, rangeWidth, Height);
|
||||
float thumbWidth = ScalarThumbWidth(resolve);
|
||||
if (ScalarFillSprite != 0 && ScalarFillFollowsThumb)
|
||||
if (ScalarFill() is float fill && ScalarFillSprite != 0)
|
||||
{
|
||||
var (fillX, visibleWidth) = ScalarThumbFillRect(
|
||||
Width, thumbWidth, ScalarPosition);
|
||||
// The authored charge child spans the entire scrollbar; it does
|
||||
// not share the dark range child's inset geometry.
|
||||
var (fillX, visibleWidth) = ScalarFillRect(
|
||||
Width, fill, ScalarFillFromRight);
|
||||
DrawTiledClipped(ctx, resolve, ScalarFillSprite,
|
||||
0f, fillX, visibleWidth, Height);
|
||||
}
|
||||
else if (ScalarFill() is float fill && ScalarFillSprite != 0)
|
||||
{
|
||||
var (fillX, visibleWidth) = ScalarFillRect(
|
||||
rangeLeft, rangeWidth, fill, ScalarFillFromRight);
|
||||
DrawTiledClipped(ctx, resolve, ScalarFillSprite,
|
||||
rangeLeft, fillX, visibleWidth, Height);
|
||||
}
|
||||
float travel = MathF.Max(0f, Width - thumbWidth);
|
||||
float x = travel * ScalarPosition;
|
||||
DrawSprite(ctx, resolve, ThumbSprite, x, 0f, thumbWidth, Height);
|
||||
|
|
@ -246,10 +238,18 @@ public sealed class UiScrollbar : UiElement
|
|||
ctx.DrawSprite(tex, x, 0f, w, h, u0, 0f, u1, h / th, Vector4.One);
|
||||
}
|
||||
|
||||
private (float left, float width) ScalarRangeRect()
|
||||
internal (float left, float width) ScalarRangeRect()
|
||||
{
|
||||
(float configuredLeft, float configuredWidth) = ScalarRangeProvider?.Invoke()
|
||||
?? (ScalarRangeLeft, ScalarRangeWidth);
|
||||
float configuredLeft = ScalarRangeLeft;
|
||||
float configuredWidth = ScalarRangeWidth;
|
||||
if (ScalarRangeLayoutPolicy is { } policy)
|
||||
{
|
||||
UiPixelRect currentParent = UiPixelRect.FromPositionAndSize(
|
||||
0, 0, (int)Width, (int)Height);
|
||||
UiPixelRect range = policy.Apply(policy.OriginalChild, currentParent);
|
||||
configuredLeft = range.X0;
|
||||
configuredWidth = range.Width;
|
||||
}
|
||||
float left = Math.Clamp(configuredLeft, 0f, Width);
|
||||
float requestedWidth = float.IsPositiveInfinity(configuredWidth)
|
||||
? Width - left
|
||||
|
|
@ -258,17 +258,6 @@ public sealed class UiScrollbar : UiElement
|
|||
return (left, width);
|
||||
}
|
||||
|
||||
/// <summary>Returns the absolute-left fill ending at the scalar thumb center.</summary>
|
||||
public static (float x, float width) ScalarThumbFillRect(
|
||||
float totalWidth, float thumbWidth, float position)
|
||||
{
|
||||
float safeWidth = MathF.Max(0f, totalWidth);
|
||||
float safeThumb = Math.Clamp(thumbWidth, 0f, safeWidth);
|
||||
float travel = safeWidth - safeThumb;
|
||||
float center = travel * Math.Clamp(position, 0f, 1f) + safeThumb * 0.5f;
|
||||
return (0f, center);
|
||||
}
|
||||
|
||||
public override bool OnEvent(in UiEvent e)
|
||||
{
|
||||
if (Horizontal && ScalarChanged is not null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue