fix(chat): Campaign CH user-gate round 1 — jump-in-air edge, portal cue cadence, wrap/prefix/color fixes
The user tested Campaign CH's CODE-COMPLETE build live and reported ten defects (docs/plans/2026-08-09-chat-parity-campaign.md, "User gate — round 1"). Items A-G are fixed here; the remaining three (extra chat windows on 1/2/3/4, resize working in only one corner, transparency/ artifacts) are out of scope for a fix and filed as slice CH6. A. Jump-in-air refusal never fired live: the jump block only ever evaluated input.Jump inside the grounded-charge or already-charging branches. PlayerMovementController now detects the press RISING EDGE while airborne and reports WeenieError.NotGrounded once per press, leaving the grounded charge/fire path untouched. B. ChatVM's invented "[System] " prefix is dropped — retail prints system text bare. [Popup] is unchanged (AP-175). C. SpewBoxController's color is now the user-pinned exact value (1, 1, 0.247, 1), the same bright yellow as an incoming Tell. Register row AP-178 updated: color CLOSES, size/position/font stay open per the user's live report that they still differ. D. Closes #329: PortalTunnelPresentation now emits the portal wait cue unconditionally on every rotation-segment boundary, matching gmSmartBoxUI::UseTime's decompiled else-arm exactly instead of gating on a 5-second hold local transits never reached. PortalWaitNotice Controller now renders it in the same pinned yellow as item C. Register row AP-150 retired. E. Closes #362: new ClientCommandResponses.cs parses and renders the four previously-unhandled inbound GameEvents (ChannelIndex, ChannelList, AvailableHouses, AllegianceInfoResponse), each ported line-for-line from the named-retail decomp's inbound handlers. Register row TS-70 retired. F. ChatWindowController.WrapText now splits on embedded '\n'/'\r\n' first, then word-wraps each segment independently — server text like /help's reply no longer collapses onto one line. G. The chat input field's right edge no longer holds a fixed absolute pixel position across a window resize; Bind now upgrades it to retail edge-mode 1 (UiLayoutPolicy) or the AnchorEdges.Right stretch fallback so it tracks the window's client width instead of overflowing past a narrower resize. Full Release suite: 12,247 passed / 4 skipped / 0 failed (baseline 12,221/4/0 + 26 new tests across items A, E, F, G). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d1c1368a5e
commit
47e40900f3
17 changed files with 1428 additions and 81 deletions
|
|
@ -260,7 +260,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
_rotationEndAngle = 0f;
|
||||
_rotationCurrentAngle = 0f;
|
||||
_camera.DirectionDegrees = 0f;
|
||||
SetWaitCue(false);
|
||||
ClearWaitCueNotice();
|
||||
_visible = true;
|
||||
RebuildPose();
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
if (_disposed)
|
||||
return;
|
||||
_visible = false;
|
||||
SetWaitCue(false);
|
||||
ClearWaitCueNotice();
|
||||
_animationHooks.Clear();
|
||||
_sequence.ClearAnimations();
|
||||
}
|
||||
|
|
@ -287,6 +287,18 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
TickRotation(dt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The hold-delay-gated arm/disarm <c>LocalPlayerTeleportController</c>
|
||||
/// still drives every frame from <c>RuntimeWorldTransitState.ObserveWait</c>
|
||||
/// (own telemetry: <c>RuntimePortalSnapshot.WaitCueShown</c>). This is
|
||||
/// deliberately NOT the retail cue-emission path any more — see
|
||||
/// <see cref="TickRotation"/>'s unconditional per-segment write (item D,
|
||||
/// #329). Kept only so the controller's own hold bookkeeping still has
|
||||
/// somewhere to land; because <c>visible</c> is false for the entire
|
||||
/// common case (a transit that never crosses the invented 5-second
|
||||
/// hold), this is a same-value no-op there and never contends with the
|
||||
/// per-segment write above.
|
||||
/// </summary>
|
||||
public void SetWaitCue(bool visible)
|
||||
{
|
||||
if (_waitCueVisible == visible)
|
||||
|
|
@ -297,6 +309,21 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
visible ? "In Portal Space - Please Wait..." : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unconditionally hides any wait-cue notice text and resets the
|
||||
/// hold-delay dedup state, independent of <see cref="_waitCueVisible"/>'s
|
||||
/// current value. <see cref="TickRotation"/> now writes the notice text
|
||||
/// directly (bypassing <see cref="SetWaitCue"/>'s dedup), so the old
|
||||
/// `SetWaitCue(false)` calls at Enter/Exit/Dispose could no-op and leave
|
||||
/// a stale "In Portal Space..." line on screen after the presentation
|
||||
/// went invisible — this always clears it.
|
||||
/// </summary>
|
||||
private void ClearWaitCueNotice()
|
||||
{
|
||||
_waitCueVisible = false;
|
||||
_displayNotice?.Invoke(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw retail portal space into the active viewport. The caller suppresses
|
||||
/// the normal world viewport while this scene is visible, then draws the
|
||||
|
|
@ -377,8 +404,29 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
_rotationDuration = NextDouble(RotationDurationMin, RotationDurationMax);
|
||||
_rotationStartAngle = _rotationCurrentAngle;
|
||||
_rotationEndAngle = (float)NextDouble(0.0, 360.0);
|
||||
if (_waitCueVisible)
|
||||
_displayNotice?.Invoke("In Portal Space - Please Wait...");
|
||||
// Campaign CH user-gate round 1 (item D, #329): retail's
|
||||
// gmSmartBoxUI::UseTime @0x004D6E30 emits
|
||||
// ECM_UI::SendNotice_DisplayStringInfo(0x1a, "In Portal Space -
|
||||
// Please Wait...") in the else arm of the rotation-segment-
|
||||
// expiry test at 0x004D6FCD UNCONDITIONALLY -- every time a
|
||||
// segment expires, with no hold/threshold check anywhere in
|
||||
// that decompiled function. acdream's own RotationDurationMin/
|
||||
// Max already match retail's RandDouble(0.6, 1.8) segment
|
||||
// window decoded at 0x004D6FE6; the only bug was gating this
|
||||
// call on `_waitCueVisible`, which only ever became true after
|
||||
// RuntimeWorldTransitState.RetailWaitCueDelay's invented 5-
|
||||
// second hold -- a threshold most local transits never reach,
|
||||
// so the cue silently never fired. This write is deliberately
|
||||
// independent of `_waitCueVisible`/SetWaitCue (see that
|
||||
// method's own doc comment): LocalPlayerTeleportController
|
||||
// still drives SetWaitCue every frame from its own hold-delay
|
||||
// bookkeeping, but because that call is a same-value no-op for
|
||||
// the entire common case (a transit that never crosses the 5s
|
||||
// hold), it never fights this unconditional per-segment write.
|
||||
// Enter/Exit/Dispose clear the notice directly (not through
|
||||
// SetWaitCue's dedup) so a stale line can never survive past
|
||||
// this presentation going invisible.
|
||||
_displayNotice?.Invoke("In Portal Space - Please Wait...");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -469,7 +517,7 @@ public sealed class PortalTunnelPresentation : IDisposable
|
|||
try
|
||||
{
|
||||
_visible = false;
|
||||
SetWaitCue(false);
|
||||
ClearWaitCueNotice();
|
||||
_animationHooks.Clear();
|
||||
_sequence.ClearAnimations();
|
||||
_meshReferences.Dispose();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue