the ghost world gets its own sky, and a corpse cannot steer
- a dead player's sky is DeathClouds now — `lightSkyboxID` was named in the schema and read by nobody and `LightSkybox.dbc` was never loaded at all, so a ghost kept the ordinary gradient dome; the shipped data has exactly 5 of 426 LightParams rows carrying a skybox id, every one of them DeathClouds.mdx, reached from the death profile (slot 4) of all 374 Light rows and gated on the ghost bit, with a hardcoded weight of 1.0 that skips the WMO skybox outright; it resolves off the same (map, camera position) seed the atmosphere uses, so the ghost sky and the death-profile fog it hangs over switch on the same frame — and the lane sheds its `wmo_sky` name for `skybox`, no longer being WMO-only - a corpse cannot steer: the reference's two movement-input predicates share a precondition benilla never modelled — `UNIT_FIELD_HEALTH > 0` — so at zero health the keyboard turn and the right-drag close together, exactly as they do inside an Ice Block, while the ghost (health 1) keeps moving; the wow-re §5 round then corrected three byte trails (the mouse turn has its own eight-conjunct predicate, the supposed on-taxi test is the Knockdown animation lockout — set on exactly one of 208 AnimationData rows — and the "charm bit" is the far-sight latch) and added three verified health gates we lacked: the jump IS health-gated, sit and stand are refused while the body reads dead (a feigner too), and a follow tears down only when BOTH predicates drop — we had that wrong in both directions - the walk-gait cross-check folds back confirming `min(walk, run)` and the speed-only animation channel (by a 2,304-function closure census), and finds the trap we lacked: every incoming spline re-authors the gait, and the spline's RUNMODE flag is an INVERSE — a spline marked as a run clears walk mode, one not so marked SETS it — so a charge, taxi, knockback or flee hand-off leaves the body in whatever gait its path was authored with, applied once on the spline's start edge; the post-worldport zero-walk fallback is retracted on a re-run (the ctor's zero is a transient thirty instructions wide, never observable), leaving the plain minimum the bytes compute — the better failure mode too, since a fallback makes a walker silently RUN if a walk speed ever goes missing, where the transcription makes them visibly stop Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -490,6 +490,7 @@ impl World {
|
||||
stop,
|
||||
duration_ms,
|
||||
flying,
|
||||
..
|
||||
} => {
|
||||
let mine = *guid == self.self_guid;
|
||||
if mine {
|
||||
|
||||
@@ -343,6 +343,9 @@ pub enum SessionEvent {
|
||||
/// `true` ⇒ a 3-D flight path (keep the spline's Z); `false` ⇒ a ground walk whose Z the app
|
||||
/// re-derives from the terrain under the unit (see the renderer's creature ground-clamp).
|
||||
flying: bool,
|
||||
/// `SPLINEFLAG_RUNMODE` — the path is travelled at run speed. Its **absence** forces
|
||||
/// `MOVEFLAG_WALK_MODE` on for the unit the spline moves (decision 1758).
|
||||
run_mode: bool,
|
||||
},
|
||||
/// Same-map teleport (`MSG_MOVE_TELEPORT_ACK`): the app snaps our player + echoes the ack.
|
||||
Teleport {
|
||||
|
||||
@@ -636,6 +636,7 @@ pub fn decode(packet: ServerPacket) -> Vec<SessionEvent> {
|
||||
stop,
|
||||
duration_ms,
|
||||
flying,
|
||||
run_mode,
|
||||
} => vec![SessionEvent::MonsterMove {
|
||||
guid,
|
||||
start: v3(start),
|
||||
@@ -645,6 +646,7 @@ pub fn decode(packet: ServerPacket) -> Vec<SessionEvent> {
|
||||
stop,
|
||||
duration_ms,
|
||||
flying,
|
||||
run_mode,
|
||||
}],
|
||||
ServerPacket::PlayerMove {
|
||||
guid,
|
||||
|
||||
@@ -25,6 +25,13 @@ const MONSTER_MOVE_STOP: u8 = 0x1;
|
||||
/// its Z. The client gates on this same bit (`0x6018f0: test ah,0x2`). It is also `Mask_CatmullRom` —
|
||||
/// flying paths interpolate (and wire-encode) as a curve, ground paths as straight segments.
|
||||
const SPLINE_FLAG_FLYING: u32 = 0x200;
|
||||
/// `SPLINEFLAG_RUNMODE` — the path is travelled at run speed. Its **absence** is what matters:
|
||||
/// the real client feeds this bit straight into `CMovement::SetRunMode 0x7c71c0`
|
||||
/// (`0x7c6ac2 and edi,0x100` → `0x7c6acb call 0x7c71c0`, inside the `SMSG_MONSTER_MOVE` commit
|
||||
/// `0x7c6a50`), and that setter's argument is *run* — so a spline **without** this bit **sets**
|
||||
/// `MOVEFLAG_WALK_MODE` on the unit it moves. The two `0x100`s are inverses of each other
|
||||
/// (wow-re `collision/scratch/walk-mode-law.md` §5.2; benilla decision 1758).
|
||||
const SPLINE_FLAG_RUNMODE: u32 = 0x100;
|
||||
|
||||
/// Parse an `SMSG_MONSTER_MOVE` body into [`ServerPacket::MonsterMove`]. The head (mover, `start`, spline
|
||||
/// id, `moveType` + its final facing) is always present; a **stop** (`moveType 1`) ends there (the client
|
||||
@@ -62,11 +69,15 @@ pub(super) fn read_monster_move(r: &mut &[u8]) -> io::Result<ServerPacket> {
|
||||
stop: true,
|
||||
duration_ms: 0,
|
||||
flying: false,
|
||||
// The stop form carries no spline-flags dword at all, so there is no run/walk verdict
|
||||
// in it — and it builds no path, so nothing downstream reads this.
|
||||
run_mode: true,
|
||||
}
|
||||
} else {
|
||||
let spline_flags = read_u32_le(r)?;
|
||||
let duration_ms = read_u32_le(r)?;
|
||||
let flying = spline_flags & SPLINE_FLAG_FLYING != 0;
|
||||
let run_mode = spline_flags & SPLINE_FLAG_RUNMODE != 0;
|
||||
// The decoded waypoints *after* the start (see [`read_monster_move_spline`]): a ground path's
|
||||
// first packed point re-encodes `start` (quantized), so drop it and anchor the path with the
|
||||
// exact wire `start`; a flying path sends only post-start points, kept whole. The result is the
|
||||
@@ -89,6 +100,7 @@ pub(super) fn read_monster_move(r: &mut &[u8]) -> io::Result<ServerPacket> {
|
||||
stop: false,
|
||||
duration_ms,
|
||||
flying,
|
||||
run_mode,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,6 +146,10 @@ pub enum ServerPacket {
|
||||
/// The path is a 3-D **flight** (`SPLINE_FLAG_FLYING` = `Mask_CatmullRom`): keep the spline's own Z.
|
||||
/// Clear ⇒ a ground walk whose Z the client re-derives from the terrain (the app terrain-clamps it).
|
||||
flying: bool,
|
||||
/// The path's `SPLINEFLAG_RUNMODE` bit — and its **absence** is the load-bearing half: a
|
||||
/// spline without it forces `MOVEFLAG_WALK_MODE` **on** for the unit it moves (see
|
||||
/// [`super::monster_move`]'s `SPLINE_FLAG_RUNMODE`).
|
||||
run_mode: bool,
|
||||
},
|
||||
/// A relayed *player* movement packet (`MSG_MOVE_*`, see [`super::parse::is_movement_relay`]): the mover's
|
||||
/// authoritative pose plus its live CMovement `moveFlags`. The app snaps the entity to `position`
|
||||
|
||||
@@ -49,6 +49,9 @@ const SPLINE_FLAG_FINAL_ANGLE: u32 = 0x4_0000;
|
||||
/// (`MoveSplineFlag.h:48,77`), the same split [`crate::ServerPacket::MonsterMove`] reads: set ⇒ a 3-D
|
||||
/// flight path that keeps its own Z, clear ⇒ a ground walk whose Z the client re-derives from terrain.
|
||||
const SPLINE_FLAG_FLYING: u32 = 0x200;
|
||||
/// `SPLINEFLAG_RUNMODE` — see [`super::super::monster_move`]'s copy for why its *absence* is what
|
||||
/// matters: a spline without it forces walk mode on for the unit it moves.
|
||||
const SPLINE_FLAG_RUNMODE: u32 = 0x100;
|
||||
/// `MoveSplineFlag::Cyclic` (`MoveSplineFlag.h:59`) — the path loops back on itself forever.
|
||||
const SPLINE_FLAG_CYCLIC: u32 = 0x10_0000;
|
||||
|
||||
@@ -86,6 +89,8 @@ pub struct CreateSpline {
|
||||
/// `MoveSplineFlag::Cyclic` — the server loops this path forever. The app rides it once; a looping
|
||||
/// follow is unbuilt (the same gap `MonsterMove`'s cyclic paths have).
|
||||
pub cyclic: bool,
|
||||
/// `MoveSplineFlag::Runmode` — and its absence forces walk mode on (decision 1758).
|
||||
pub run_mode: bool,
|
||||
}
|
||||
|
||||
/// The decoded fields a movement block carries: the pose (from its `LIVING`/`HAS_POSITION` flag),
|
||||
@@ -199,6 +204,7 @@ impl MovementBlock {
|
||||
duration_ms,
|
||||
flying: spline_flags & SPLINE_FLAG_FLYING != 0,
|
||||
cyclic: spline_flags & SPLINE_FLAG_CYCLIC != 0,
|
||||
run_mode: spline_flags & SPLINE_FLAG_RUNMODE != 0,
|
||||
});
|
||||
}
|
||||
} else if update_flag & UPDATE_FLAG_HAS_POSITION != 0 {
|
||||
|
||||
Reference in New Issue
Block a user