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
|
|
@ -125,6 +125,19 @@ public sealed class WeatherSystem
|
|||
// SetKindFromDayGroupName, the internal RollKind hash is disabled.
|
||||
private bool _externallyDriven;
|
||||
|
||||
/// <summary>
|
||||
/// Campaign OP slice OP4 (2026-08-11): retail <c>PlayerOption
|
||||
/// DisableDistanceFog</c> — <c>CPlayerModule::OnChanged @0x0059A8E0</c>
|
||||
/// case <c>0x30</c> writes <c>LScape::m_fFogEnabled = !value</c>. Polled
|
||||
/// (not pushed) so the App composition layer only needs to bind this
|
||||
/// ONCE per session to a live <c>RuntimeCharacterOptionsState.GetOptionBit</c>
|
||||
/// reader — every later toggle (the panel row, a bot, a slash command)
|
||||
/// is reflected with no separate notify/resync wiring. Null (the
|
||||
/// default) means the option was never bound — fog stays enabled,
|
||||
/// matching retail's <c>m_fFogEnabled</c> ctor default.
|
||||
/// </summary>
|
||||
public Func<bool>? DisableDistanceFogSource { get; set; }
|
||||
|
||||
public WeatherSystem(Random? rng = null)
|
||||
{
|
||||
// The random-seed ctor argument remains for test API compat,
|
||||
|
|
@ -295,6 +308,7 @@ public sealed class WeatherSystem
|
|||
Vector3 fogColor = kf.FogColor;
|
||||
float fogStart = kf.FogStart;
|
||||
float fogEnd = kf.FogEnd;
|
||||
FogMode fogMode = kf.FogMode;
|
||||
|
||||
// AdminEnvirons server override: replace fog COLOR only.
|
||||
// Keyframe distances unchanged until we find evidence retail
|
||||
|
|
@ -304,13 +318,21 @@ public sealed class WeatherSystem
|
|||
if (_override != EnvironOverride.None)
|
||||
fogColor = EnvironOverrideColor(_override);
|
||||
|
||||
// OP4 (2026-08-11): PlayerOption DisableDistanceFog ->
|
||||
// LScape::m_fFogEnabled = !value (see DisableDistanceFogSource's
|
||||
// doc comment). Forces FogMode.Off regardless of the keyframe's
|
||||
// own authored mode; distances are left alone since the shader
|
||||
// never reads them once fog is off.
|
||||
if (DisableDistanceFogSource?.Invoke() == true)
|
||||
fogMode = FogMode.Off;
|
||||
|
||||
return new AtmosphereSnapshot(
|
||||
Kind: _kind, // informational
|
||||
Intensity: 1f, // no per-Kind easing in retail
|
||||
FogColor: fogColor,
|
||||
FogStart: fogStart,
|
||||
FogEnd: fogEnd,
|
||||
FogMode: kf.FogMode,
|
||||
FogMode: fogMode,
|
||||
LightningFlash: _flashLevel, // 0 in production; TriggerFlash hook for tests
|
||||
Override: _override);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue