fix #416 #415: the retail button state/media machine — roster hover highlight clears; probe wait verbs bind without an artifact dir
#416 (char-select roster highlight never cleared on hover-leave): three decomp-grounded mechanisms replace the media-keyed _availableStates approximation. - UIElement_Button::UpdateState_ @0x00471CF0: the button machine commits ONLY states authored on the button's OWN ElementDesc (AccessStateDesc gate); unauthored requests no-op, preserving custom semantic states. - UIElement::SetState @0x00464E70: an unauthored state id is coerced to state 0 (the unnamed base state) and committed — ported into UiDatElement.TrySetRetailState with the base-descriptor PassToChildren cascade arm. - The SetState media rule @0x004651c0: a committed state replaces the playing media ONLY when its media array is non-empty. UiButton now keeps per-face-segment media states under that rule (segments model retail's PassToChildren children), and LayoutImporter records the raw MediaCount including the File=0 draw-nothing images the drawable filter drops — the roster bar children's base state is exactly such an image, and it is what clears the bar. The row template truth (probe, installed DAT): the row authors EMPTY Normal/rollover/Highlight descriptors with PassToChildren; the three bar children author rollover/Highlight media, NO Normal state, and a File=0 base image. An empty-media Normal_pressed still never blanks a Normal-art button (the media rule keeps the previous art — the exact behavior the old gate approximated), and the Appearance spins' property-only Highlight now genuinely commits: label recolors, arrow art lingers — the retail split AP-222 approximated with a requested-keyed label hack, now retired. Live-verified at char select: hover +alex shows the grey bar, moving off clears it, the selected row keeps its amber bar. #415 (probe wait world-* verbs dead): the filed snapshot-reset diagnosis was wrong — the automation bridge simply never bound without ACDREAM_AUTOMATION_ARTIFACT_DIR. A facts-only WorldRevealFactsAutomationRuntime now binds whenever the retained UI exists; checkpoint/screenshot verbs still require the artifact directory and now report that instead of a generic timeout. App tests 5568/3 skips, Runtime 1756/0, UI.Abstractions 926/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7aa08045d8
commit
91c1962b0d
11 changed files with 469 additions and 86 deletions
|
|
@ -207,13 +207,98 @@ public class UiDatElementTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void NumericStateBridge_MissingStatePreservesCurrentState()
|
||||
public void NumericStateBridge_MissingStateCommitsBaseState()
|
||||
{
|
||||
// Retail UIElement::SetState @0x00464E70 coerces an UNAUTHORED state
|
||||
// id to state 0 (the unnamed base state) and commits it — it never
|
||||
// preserves the current state (#416; this test previously codified
|
||||
// the pre-decomp misreading).
|
||||
var info = new ElementInfo { DefaultStateName = "Normal" };
|
||||
info.StateMedia["Normal"] = (0x06000001u, 1);
|
||||
var element = new UiDatElement(info, _ => (0u, 0, 0));
|
||||
|
||||
Assert.False(element.TrySetRetailState(RetailUiStateIds.ShowDetail));
|
||||
Assert.Equal("Normal", element.ActiveState);
|
||||
Assert.True(element.TrySetRetailState(RetailUiStateIds.ShowDetail));
|
||||
Assert.Equal("", element.ActiveState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #416 — the character-select roster row shape: the highlight-bar
|
||||
/// children (0x10000481/82/83 in 0x21000004) author Normal_rollover
|
||||
/// media but NO 'Normal' state, so the row's PassToChildren hover-leave
|
||||
/// cascade commits an unauthored Normal on them. Retail's state-0 arm
|
||||
/// clears the bar; the old refusal latched it highlighted forever.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TrySetRetailState_UnauthoredNormal_ClearsRolloverMedia()
|
||||
{
|
||||
var info = new ElementInfo();
|
||||
info.States[UiStateInfo.DirectStateId] =
|
||||
new UiStateInfo { Id = UiStateInfo.DirectStateId };
|
||||
info.States[2u] = new UiStateInfo { Id = 2u, Name = "Normal_rollover" };
|
||||
info.StateMedia["Normal_rollover"] = (0x06005EB6u, 1);
|
||||
var element = new UiDatElement(info, _ => (0u, 0, 0));
|
||||
|
||||
Assert.True(element.TrySetRetailState(2u));
|
||||
Assert.Equal("Normal_rollover", element.ActiveState);
|
||||
|
||||
Assert.True(element.TrySetRetailState(UiButtonStateMachine.Normal));
|
||||
Assert.Equal("", element.ActiveState);
|
||||
Assert.Equal((0u, 0), element.ActiveMedia());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state-0 fallback must NOT honor a base-DirectState Invisible
|
||||
/// (dat 0x3B) — that is the #408 construction-time class, gated
|
||||
/// separately; retail's per-state Invisible honor applies to NAMED
|
||||
/// authored states only.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TrySetRetailState_UnauthoredStateFallback_DoesNotHonorBaseInvisible()
|
||||
{
|
||||
var info = new ElementInfo();
|
||||
var baseState = new UiStateInfo { Id = UiStateInfo.DirectStateId };
|
||||
baseState.Properties.Values[0x3Bu] = new UiPropertyValue
|
||||
{
|
||||
Kind = UiPropertyKind.Bool,
|
||||
BoolValue = true,
|
||||
};
|
||||
info.States[UiStateInfo.DirectStateId] = baseState;
|
||||
info.StateMedia["Normal_rollover"] = (0x06005EB6u, 1);
|
||||
var element = new UiDatElement(info, _ => (0u, 0, 0));
|
||||
Assert.True(element.Visible);
|
||||
|
||||
Assert.True(element.TrySetRetailState(UiButtonStateMachine.Normal));
|
||||
|
||||
Assert.Equal("", element.ActiveState);
|
||||
Assert.True(element.Visible);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's state-0 arm cascades state 0 to children when the BASE
|
||||
/// descriptor authors PassToChildren (m_desc.m_bPassToChildren,
|
||||
/// @0x00464eca).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TrySetRetailState_UnauthoredStateFallback_CascadesBasePerBaseDescriptor()
|
||||
{
|
||||
var childInfo = new ElementInfo();
|
||||
childInfo.StateMedia[""] = (0x06000002u, 1);
|
||||
childInfo.StateMedia["Normal_rollover"] = (0x06005EB6u, 1);
|
||||
childInfo.States[2u] = new UiStateInfo { Id = 2u, Name = "Normal_rollover" };
|
||||
var child = new UiDatElement(childInfo, _ => (0u, 0, 0));
|
||||
Assert.True(child.TrySetRetailState(2u));
|
||||
|
||||
var parentInfo = new ElementInfo();
|
||||
parentInfo.States[UiStateInfo.DirectStateId] = new UiStateInfo
|
||||
{
|
||||
Id = UiStateInfo.DirectStateId,
|
||||
PassToChildren = true,
|
||||
};
|
||||
var parent = new UiDatElement(parentInfo, _ => (0u, 0, 0));
|
||||
parent.AddChild(child);
|
||||
|
||||
Assert.True(parent.TrySetRetailState(UiButtonStateMachine.Normal));
|
||||
|
||||
Assert.Equal("", child.ActiveState);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue