fix(combat): charge power bar from speed

Read the authored meter direction instead of inferring it from the combat element id, so the bright charge texture grows left-to-right from Speed. Keep the user-approved dark-red middle baseline visible independently and record its exact retail Recklessness visibility edge.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-12 21:25:24 +02:00
parent 256c1930bd
commit 707add8539
13 changed files with 50 additions and 49 deletions

View file

@ -66,7 +66,6 @@ public sealed class CombatUiController : IRetainedPanelController
CombatAttackController attacks,
Func<GameplaySettings> gameplay,
Action<GameplaySettings> setGameplay,
Func<bool> recklessnessTrained,
CombatUiLabels labels,
Action<bool> setWindowVisible)
{
@ -95,7 +94,6 @@ public sealed class CombatUiController : IRetainedPanelController
_powerControl.SetScalarPosition(_attacks.DesiredPower);
_powerControl.ScalarChanged = _attacks.SetDesiredPower;
_powerControl.ScalarFill = () => _attacks.PowerBarLevel;
_powerControl.ScalarRangeVisible = recklessnessTrained;
BindAttackButton(_high, AttackHeight.High);
BindAttackButton(_medium, AttackHeight.Medium);
@ -131,7 +129,6 @@ public sealed class CombatUiController : IRetainedPanelController
CombatAttackController attacks,
Func<GameplaySettings> gameplay,
Action<GameplaySettings> setGameplay,
Func<bool> recklessnessTrained,
CombatUiLabels labels,
Action<bool> setWindowVisible)
{
@ -140,7 +137,6 @@ public sealed class CombatUiController : IRetainedPanelController
ArgumentNullException.ThrowIfNull(attacks);
ArgumentNullException.ThrowIfNull(gameplay);
ArgumentNullException.ThrowIfNull(setGameplay);
ArgumentNullException.ThrowIfNull(recklessnessTrained);
ArgumentNullException.ThrowIfNull(labels);
ArgumentNullException.ThrowIfNull(setWindowVisible);
@ -158,8 +154,7 @@ public sealed class CombatUiController : IRetainedPanelController
return new CombatUiController(
layout, basic, advanced, power, high, medium, low,
repeatAttacks, autoTarget, keepInView,
combat, attacks, gameplay, setGameplay, recklessnessTrained,
labels, setWindowVisible);
combat, attacks, gameplay, setGameplay, labels, setWindowVisible);
}
public void SyncVisibility() => OnCombatModeChanged(_combat.CurrentMode);
@ -238,7 +233,6 @@ public sealed class CombatUiController : IRetainedPanelController
_attacks.StateChanged -= OnAttackStateChanged;
_powerControl.ScalarChanged = null;
_powerControl.ScalarFill = () => null;
_powerControl.ScalarRangeVisible = () => false;
_high.OnPressed = null;
_high.OnReleased = null;
_medium.OnPressed = null;

View file

@ -178,19 +178,22 @@ public static class DatWidgetFactory
.FirstOrDefault();
bar.ScalarFillSprite = fill is null ? 0u : DefaultImage(fill);
// gmCombatUI preserves a second authored meter child for the
// trained-Recklessness range. The controller owns its visibility,
// exactly like gmCombatUI::ListenToElementMessage @ 0x004CC430;
// the widget only retains the media because it consumes DAT children.
// gmCombatUI preserves a second authored meter child for its dark
// red interior range. The widget retains the media because horizontal
// scrollbars consume their DAT children.
ElementInfo? scalarRange = meter?.Children.FirstOrDefault(
child => child.Id == 0x100005EFu);
bar.ScalarRangeSprite = scalarRange is null
? 0u
: DefaultImage(scalarRange);
// gmCombatUI meter 0x10000050 grows from the Power end at the
// right toward Speed at the left (retail screenshot oracle +
// gmCombatUI::RecvNotice_SetPowerbarLevel 0x004CC0E0).
bar.ScalarFillFromRight = meter?.Id == 0x10000050u;
// 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
// 0x6F instead of inferring direction from the combat element id.
bar.ScalarFillFromRight = meter is not null
&& meter.TryGetEffectiveProperty(0x6Fu, out UiPropertyValue direction)
&& direction.Kind == UiPropertyKind.Enum
&& direction.UnsignedValue == 3u;
return bar;
}