From 801ff5fd44b08418cba214dc246c4204043a2992 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 6 Aug 2026 22:45:45 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20#338=20answered=20=E2=80=94=20retail=20?= =?UTF-8?q?DOES=20read=20the=20authored=20step=20height,=20so=20this=20is?= =?UTF-8?q?=20real?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gating question is closed against the decomp rather than reasoned from our source. CTransition::step_up @0x0050b610 defaults step_up_height to 0.0399999991f and substitutes object_info.step_up_height when (state & 2); step_down has the same shape at 0x0050b852 and reads the authored value unconditionally at 0x0050c232. Two things fall out. Retail's fallback is 0.04, not 0.4 — our value matches neither the fallback nor the authored 0.600/1.500. And state bit 0x2 is OnWalkable, so retail applies the authored height only while standing on walkable ground. We already port that gate faithfully in Transition.DoStepUp, including the stepDownHeight = oi.StepUpHeight assignment that reads oddly but is exactly what retail passes. The gate is not the defect; only the value fed into it is. The local player is the only affected population: its controller fields initialise to 0.4f, while remotes and live entities get Setup-derived values. The property's doc comment names PlayerModeController. ApplyStepHeights as the authoritative writer — that method does not exist anywhere in the tree; the identifier appears once, in the comment. Deliberately NOT fixed. A real writer does exist further out, and RuntimeSetPositionMoverPreparation does compute the Setup-derived value, so the plumbing is there. Whether it runs for the local player or runs and is overwritten is unproven — the probe reading 0.400 says the controller held its default, not why. Setting the field without knowing which path won would be a coin flip. Co-Authored-By: Claude Opus 5 --- docs/ISSUES.md | 68 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 2a7cfe43..16f94ce7 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -42,18 +42,68 @@ The human Setup `0x02000001` authors `StepUpHeight = 0.600` and `StepDownHeight = 1.500`. The live `[support]` probe lines show the player resolving with `stepUp=0.400 stepDown=0.400`. -### What is NOT yet established +### ANSWERED 2026-08-06 — retail DOES read the authored field, so this is real + +The gating question is closed. `CTransition::step_up` @0x0050b610 +(`acclient_2013_pseudo_c.txt:273109-273117`): + +``` +0050b655 float step_up_height = 0.0399999991f; // fallback +0050b661 if ((this->object_info.state & 2) != 0) { // <- the gate +0050b665 OBJECTINFO::get_walkable_z(this); +0050b671 step_up_height = this->object_info.step_up_height; // authored + } +0050b6ba CTransition::step_down(this, step_up_height, arg2) +``` + +`step_down` has the same shape at `0x0050b852` (default `0.04`, conditional +substitution) and reads the authored value unconditionally at `0x0050c232`, +where it is then halved against the sphere radius if it exceeds a diameter. + +**Two facts fall out of this, and the second is the more useful one.** + +**(1) The fallback is `0.04`, not `0.4`.** Our value matches neither retail's +fallback nor the authored `0.600`/`1.500`. It is an order of magnitude above +retail's fallback and well below the authored value. + +**(2) `state & 2` is `OnWalkable`** in our own `ObjectInfoState`. So retail +applies the authored step height ONLY while standing on walkable ground, and +drops to `0.04` otherwise. **We already port that gate correctly** — +`Transition.DoStepUp` (`TransitionTypes.cs` ~5836) is a faithful copy, +including `stepDownHeight = oi.StepUpHeight`, which reads oddly but is exactly +what retail passes. **The gate is not the defect. Only the VALUE fed into it +is.** + +### Where the 0.4 comes from — mapped, with one hop unproven + +- `PlayerMovementController._stepUpHeight` / `_stepDownHeight` are + **initialised to `0.4f`** (`PlayerMovementController.cs:159-160`). +- The `StepUpHeight` property's own doc comment says the authoritative source + is the player's `Setup.StepUpHeight`, set by + **`PlayerModeController.ApplyStepHeights`**. **That method does not exist + anywhere in the tree** — the identifier appears exactly once, inside that + comment. Either the wiring was removed and the comment survived, or it never + landed. +- **Remotes and live entities are NOT affected.** They get Setup-derived + values: `LiveEntityMotionRuntimeController.cs:321` + (`setup.StepUpHeight * scale`, falling back to `0.4f`) and + `RuntimeSetPositionMoverPreparation.cs:180`. The local player is the odd one + out — which is the population that matters, since it is what the user feels. + +**NOT ESTABLISHED, and must be before any fix.** There IS one real writer: +`RuntimeLocalPlayerPhysicsPublicationState.cs:215-216` assigns from +`command.Physics.StepUpHeight`, and that command is built by +`RuntimeSetPositionMoverPreparation` — which *does* compute the Setup-derived +value. So the plumbing exists. Whether it runs for the local player, or runs +and is then overwritten, is unproven; the live probe reading `0.400` says the +controller held its default at that moment, not why. **Print the controller's +two values at world entry and at the first resolve before changing anything.** +A fix that sets the field without knowing which path won will be a coin flip. + +### Remaining open question -- Whether the authored Setup values are what retail feeds `CPhysicsObj`, or - whether retail also substitutes constants at this seam. **Grep - `named-retail` for `get_stepup_height` / `get_stepdown_height` and their - callers before touching anything** — the authored DAT field being ignored is - only a defect if retail reads it. -- Where the 0.400 comes from: a hardcoded default, a stance/posture-dependent - value, or a value that never got wired from the Setup. - Whether it has any observable consequence. Do not open this by reasoning from the source; the #337 lineage already burned two diagnoses that way. - ### Related Sits next to #32 (local-player cliff edge-slide), which has its own research