feat(ui): Campaign OP slice OP4 — the Character tab
Binds LayoutDesc 0x21000028 (gmCharacterSettingsUI) through OP2's template-list mechanism and OP3's OptionPage model: 6 authored group headers + 50 toggle rows (49 from the 2013 build + D3's "Listen to PK death messages", AP-193) in research doc §2's authored order, each row resolved by PlayerOption id through CharacterOptionTable, seeded from live RuntimeCharacterOptionsState, defaulted from CharacterOptionTable. ClientDefault (byte-verified against UIOption_Checkbox::SetPlayerOption @0x00486e80's own GetDefaultOptionValue call — AP-194 updated to confirm the directive was followed), labels/tooltips resolved by name from string table 0x23000003 (never hard-coded English), and registered with OptionsPanelController.CharacterPage. Apply/Reset/Defaults (0x100001FC/FD/FE) are now wired per-page via a scoped subtree search (UiElement.FindDescendant, promoted from UiTabPanel) since Character/ Chat/Config each author their own physical instance under the SAME element ids. Consumers: Group A (29 ids) wire+store only via the existing SetSingleCharacterOptionRuntimeCmd/TrySetOption seam. Group B: Display Timestamps prefixes new transcript lines (RuntimeCommunicationState. DisplayTimestampsSource); Disable Distance Fog forces FogMode.Off (WeatherSystem.DisableDistanceFogSource, retiring half of TS-73); Run as Default Movement inverts the walk-mode modifier's default (RuntimeLocalPlayerMovementState.RunAsDefaultMovementSource). Group C re-points AutoTarget/AutoRepeatAttack/ViewCombatTarget (CharacterOptionCombatSettingsSource), VividTargetingIndicator/ CoordinatesOnRadar/LockUI/AcceptLootPermits from the client-local GameplaySettings record to the canonical server bit — closing two previously-unfiled divergences where AutoRepeatAttack and AcceptCorpseLootingPermissions never reached the wire despite being retail auto-save ids. TS-73 narrowed to its two still-open cases; TS-75..TS-80 file the genuine gaps (no day/night force, no weather- particle/profanity-filter/salvage/housing/pickup-preference subsystem, fellowship-create's unaudited client-sourced field) rather than inventing stand-ins. Conformance: CharacterOptionsPageControllerTests pins all 50 rows against CharacterOptionTable in both directions (an invented or dropped row fails the build), the authored group/order row-by-row, and the build/seed/Apply/Reset/Defaults/wire-publish behavior end-to-end against the committed fixture. 52 new tests; full solution suite 13,008 passed / 4 skipped / 0 failed (was 12,956/4/0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7e9f372ce8
commit
22b86b9ff4
25 changed files with 1674 additions and 41 deletions
|
|
@ -283,7 +283,10 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
InteractionRetainedUiDependencies d,
|
||||
DeferredSelectionUiAuthority selection) =>
|
||||
d.CombatTargetOperations.BindOwned(new LiveCombatTargetOperations(
|
||||
autoTarget: () => d.Settings.Gameplay.AutoTarget,
|
||||
// D7 Group-C re-point (Campaign OP OP4, 2026-08-11): server
|
||||
// bit — see CharacterOptionCombatSettingsSource's doc comment.
|
||||
autoTarget: () => d.Character.Options.GetOptionBit(
|
||||
CharacterOptionId.AutoTarget),
|
||||
selectClosestTarget: () =>
|
||||
selection.SelectClosestCombatTarget(showToast: false)));
|
||||
|
||||
|
|
@ -474,7 +477,15 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
checkpoint(InteractionRetainedUiCompositionPoint.UiHostAcquired);
|
||||
inputCapture = d.RetainedInputCapture.Bind(host.Root);
|
||||
checkpoint(InteractionRetainedUiCompositionPoint.InputCaptureBound);
|
||||
host.Root.UiLocked = d.Settings.Gameplay.LockUI;
|
||||
// D7 Group-C re-point (Campaign OP OP4, 2026-08-11): server
|
||||
// bit at mount time — see CharacterOptionCombatSettingsSource's
|
||||
// doc comment. Live toggling afterward still flows through
|
||||
// RuntimeSettingsController.SetUiLocked's existing
|
||||
// _runtimeTargets?.ApplyUiLock push (ToggleUiLock's Bindings
|
||||
// site, LiveSessionRuntimeFactory.cs, now also sends the wire
|
||||
// bit — see its own comment).
|
||||
host.Root.UiLocked = d.Character.Options.GetOptionBit(
|
||||
CharacterOptionId.LockUI);
|
||||
|
||||
var cursorFeedback = new CursorFeedbackController(
|
||||
itemInteraction,
|
||||
|
|
@ -673,7 +684,14 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
VividTarget: new VividTargetRuntimeBindings(
|
||||
d.Actions.Selection,
|
||||
() => d.PlayerIdentity.ServerGuid,
|
||||
() => d.Settings.Gameplay.VividTargetingIndicator,
|
||||
// D7 Group-C re-point (Campaign OP OP4, 2026-08-11):
|
||||
// server bit, not the client-local GameplaySettings
|
||||
// record — CH3 precedent (server-authoritative,
|
||||
// local-write-then-send; the Character-tab panel row
|
||||
// writes through RuntimeCharacterOptionsState.
|
||||
// TrySetOption before this read can observe it).
|
||||
() => d.Character.Options.GetOptionBit(
|
||||
CharacterOptionId.VividTargetingIndicator),
|
||||
late.Selection.ResolveVividTargetInfo,
|
||||
late.SelectionCamera.UiSnapshot),
|
||||
Indicators: new IndicatorRuntimeBindings(
|
||||
|
|
@ -838,7 +856,10 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
|||
DisplayMouseTurningMacroLine: text =>
|
||||
d.Communication.AddText(text, RetailLogTextType.Magic),
|
||||
LoadCameraTurning: d.Settings.LoadCameraTurning,
|
||||
SaveCameraTurning: d.Settings.SaveCameraTurning),
|
||||
SaveCameraTurning: d.Settings.SaveCameraTurning,
|
||||
// Campaign OP slice OP4 (2026-08-11): the Character-tab
|
||||
// panel's row-seed source.
|
||||
CurrentCharacterOption: id => d.Character.Options.GetOptionBit(id)),
|
||||
StackSplitQuantity: d.StackSplitQuantity,
|
||||
Plugins: d.UiRegistry,
|
||||
Persistence: persistence,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue