Binds LayoutDesc 0x2100005C through OP2's template-list mechanism and OP3's per-page OptionPage model: the General Options header + two DualHash-linked opacity sliders (Option_DefaultOpacity_Property 0x10000080 / Option_ActiveOpacity_Property 0x10000081, live-apply on drag through RetailWindowOpacityController, defaults read from the installed DAT's DBProperties collection at DID 0x78000001 via ChatOptionsDatDefaults), and the five per-window text-filter blocks (main window 12 rows minus Gameplay, four floaties 13 rows each — the byte-verified authored order cross-checked against the raw gmChatOptionsUI::InitOptions/AddCheckboxBitfield64Option pseudo-C, not just the research doc's own table) writing AcDream.Core.Chat. ChatWindowState directly, the same state CH6's chat windows already read. AP-195 retired: ported both halves left open at the OP2 re-review — the ALL-set LED media swap (new UiButton.FaceFileOverride, driven by the block-level P0x10000082/P0x10000083 sprites now threaded through ElementInfo/DatWidgetFactory) and the CreateChildren self-sizing tail (UiCheckboxBitfield64.Height grows with its stacked row content; the enclosing ListBox reflows around the block's FINAL height via the new UiTemplateListBox.AddPrebuiltRow, reusing the ListBox's own stacking rather than a third stacking path). AP-187 broadened to cover the main window's own filter (previously only the four floaties) and the new live-editing write path. The main chat window's filter (retail window id 8, ChatWindowState id 0) gains its own settings.json persistence (ChatSettings. ChatWindowMainFilter) alongside the pre-existing floaty 1-4 fields; opacity persistence is now wired on every live slider change, not only through the old dev-scaffold Settings panel. Fixture regeneration (ACDREAM_REGENERATE_UI_FIXTURES=1) picked up the new ElementInfo.LedCheckedSprite/LedUncheckedSprite fields across all 19 committed layout fixtures — purely additive, confirmed against the live installed DAT (0x10000520's own 0x82/0x83 properties resolve to 0x06004D17/0x06004D19 exactly as AP-195 documented). Conformance: FilterRows/FilterBlocks pinned against the byte-verified authored order and ChatWindowState's own default constants; the AP-195 LED swap and self-sizing behavior; the DAT opacity-default extraction against the live installed DAT; live filter/opacity writes reaching ChatWindowState/RetailWindowOpacityController; OnShown re-seed and Reset/Defaults ghosting per the OP4 binding-pattern discipline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
92 lines
4.4 KiB
C#
92 lines
4.4 KiB
C#
using AcDream.Content;
|
|
using DatReaderWriter.DBObjs;
|
|
using DatReaderWriter.Types;
|
|
|
|
namespace AcDream.App.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Campaign OP slice OP5: reads the Chat tab's two opacity-slider defaults from the
|
|
/// installed DAT's <c>DBProperties</c> collection — retail
|
|
/// <c>UIOption::InqDefaultGameplayOptionProperty @0x004ef8d0</c>'s
|
|
/// <c>m_propName</c>-bound path, the ONE genuine consumer of that mechanism (the
|
|
/// Character tab's 49/50 checkboxes are <c>PlayerOption</c>-bound, not
|
|
/// <c>m_propName</c>-bound — see <see cref="CharacterOptionsPageController"/>'s own
|
|
/// U1 trace for why THAT tab does not use this seam).
|
|
///
|
|
/// <para>
|
|
/// Byte-verified recipe (<c>DBCache::GetDIDFromEnum @0x00413940</c>): the outer call
|
|
/// site is <c>GetDIDFromEnumStatic(&ret, 0x16, 2)</c> — outer arg2 (<c>0x16</c>)
|
|
/// feeds the inner function's arg3 (the SUB-MAP lookup key), outer arg3 (<c>2</c>)
|
|
/// feeds arg4 (the MASTER-MAP lookup key FIRST). So:
|
|
/// <c>master.ClientEnumToID[2] → subMapDid; subMap.ClientEnumToID[0x16] → DID</c> —
|
|
/// i.e. <see cref="RetailDataIdResolver.Resolve"/>'s own <c>(enumValue, enumCategory)</c>
|
|
/// parameter order is <c>(0x16, 2)</c>, matching
|
|
/// <c>RetailUiRuntime.cs</c>'s existing <c>Resolve(dats, 2u, 5u)</c> convention
|
|
/// (enumCategory selects the master-map submap FIRST, enumValue indexes it SECOND).
|
|
/// Empirically confirmed against the installed DATs: resolves to DID
|
|
/// <c>0x78000001</c>, a 3-entry <c>DBProperties</c> —
|
|
/// <c>0x1000007F</c> (<c>Bitfield64</c>, the Chat filter blocks' shared
|
|
/// <c>m_propName</c> — NOT a per-window default; the five blocks' actual defaults are
|
|
/// the literal constants <c>gmChatOptionsUI::InitOptions</c> authors per window,
|
|
/// ported verbatim in <c>ChatWindowState</c>/<see cref="ChatOptionsPageController"/>),
|
|
/// <c>0x10000080</c> (<c>Float</c>, <c>0.5</c> — <c>Option_DefaultOpacity_Property</c>),
|
|
/// <c>0x10000081</c> (<c>Float</c>, <c>1.0</c> — <c>Option_ActiveOpacity_Property</c>).
|
|
/// </para>
|
|
/// </summary>
|
|
public static class ChatOptionsDatDefaults
|
|
{
|
|
/// <summary>DAT enum-category key for the FIRST (master-map) lookup — see the
|
|
/// class doc's byte-verified recipe.</summary>
|
|
private const uint EnumCategory = 2u;
|
|
|
|
/// <summary>DAT enum-value key for the SECOND (sub-map) lookup.</summary>
|
|
private const uint EnumValue = 0x16u;
|
|
|
|
/// <summary><c>Option_DefaultOpacity_Property</c> — the unfocused-window opacity
|
|
/// slider's <c>m_propName</c>.</summary>
|
|
public const uint DefaultOpacityPropertyId = 0x10000080u;
|
|
|
|
/// <summary><c>Option_ActiveOpacity_Property</c> — the focused-window opacity
|
|
/// slider's <c>m_propName</c>.</summary>
|
|
public const uint ActiveOpacityPropertyId = 0x10000081u;
|
|
|
|
/// <summary>Retail base-constructor fallback (<c>ChatInterface::ChatInterface
|
|
/// @0x004F4550</c>) — used only when the DAT read fails (missing DAT entry,
|
|
/// corrupt install); the live installed DAT resolves to these exact values today.</summary>
|
|
public const float FallbackDefaultOpacity = 0.5f;
|
|
|
|
public const float FallbackActiveOpacity = 1.0f;
|
|
|
|
/// <summary>
|
|
/// Resolves the two slider defaults from the live DAT. Returns
|
|
/// <see langword="false"/> (with both out params set to the retail-constructor
|
|
/// fallback) if the DID does not resolve, the <c>DBProperties</c> object is
|
|
/// missing, or either property is absent/not a <c>Float</c> — matching this
|
|
/// codebase's "degrade to a documented fallback, never invent a value" discipline.
|
|
/// </summary>
|
|
public static bool TryRead(
|
|
IDatReaderWriter dats, out float defaultOpacity, out float activeOpacity)
|
|
{
|
|
defaultOpacity = FallbackDefaultOpacity;
|
|
activeOpacity = FallbackActiveOpacity;
|
|
|
|
uint did = RetailDataIdResolver.Resolve(dats, EnumValue, EnumCategory);
|
|
if (did == 0u || !dats.Portal.TryGet<DBProperties>(did, out DBProperties? props) || props is null)
|
|
return false;
|
|
|
|
bool ok = true;
|
|
if (props.Properties.TryGetValue(DefaultOpacityPropertyId, out BaseProperty? d)
|
|
&& d is FloatBaseProperty defaultProp)
|
|
defaultOpacity = defaultProp.Value;
|
|
else
|
|
ok = false;
|
|
|
|
if (props.Properties.TryGetValue(ActiveOpacityPropertyId, out BaseProperty? a)
|
|
&& a is FloatBaseProperty activeProp)
|
|
activeOpacity = activeProp.Value;
|
|
else
|
|
ok = false;
|
|
|
|
return ok;
|
|
}
|
|
}
|