Field locomotion
What happens, every frame, between holding a d-pad direction and Vahn taking a step in a town, a dungeon or on the overworld. The controller reads the pad relative to the camera, inches the player forward two units at a time against a per-scene wall grid, settles the height afterwards, and fires doors and story beats on the tile it lands on.
At a glance
- Controller
FUN_801D01B0in the field overlay (PROT0897), shared by towns, dungeons and the overworld walk. Not the tile-board puzzle mode that shares the overlay.- Collision grid
- Per scene, one byte per 128-unit tile: high nibble = four sub-cell wall bits, low nibble = floor tier. Streamed from the scene's
.MAPfile at load; scripts paint story-conditional changes on top. - Speed
- 480 units (3.75 tiles) per second walking, 720 running - counted in vsyncs, so the same on a busy frame as an empty one.
- Height
- A second controller after the walk: floor sampled from the grid's low nibble, or from per-tile ramp records; ledges trigger a scripted hop.
- In the port
World::step_field_locomotion(engine-core); collision is retail by default, precise free-angle movement is an opt-in enhancement (below).- Confidence
- Confirmed - runtime write-watchpoints on the player position, wall-press captures, disassembly (how we know).
How walking works
One pass of the controller, in order. Everything below the second step is about one question: may the player move two more units this way?
- GateSkip everything while the player is held: an encounter queued, a cutscene or talk owns him (flag
0x80000). This also blocks the pause menu. - Camera remapRotate the held d-pad by the camera's eighth-turn so "up" means "away from the camera"; snap the heading to one of eight angles.
- Wall slideIf the wanted direction is blocked, probe along the wall and add a perpendicular direction, so the player skids instead of sticking.
- SpeedBase step (8 walk, 12 run) × the actor's multiplier × the frame scalar; halved on slow terrain, trimmed by a quarter on diagonals.
- Step loopSpend the speed 2 units at a time, Z then X; each axis moves only if its collision probe is clear.
- SettleRe-sample the floor height under the new position; hop a ledge if the floor ahead rises a step.
- TriggersEntering a new tile checks the trigger block: teleports, door records, story beats, encounters.
- Direction. The remapped mask names axes:
0x1000Z+,0x4000Z−,0x2000X+,0x8000X−. A diagonal is two axes resolved independently, so a diagonal into a wall still slides along the open axis. - Wall slide. For a blocked cardinal, three probes 62 units ahead (centre, ±33 lateral) sweep an offset table across the travel axis and add the slide direction the sweep's sign picks. Pure diagonals and symmetric dead ends add nothing.
- Animation follows motion. A committed step selects the walk or run clip; no step selects idle. The pad is never consulted for the clip, and walking and wall contact are silent.
- Run is an exclusive or. The Field Move option sets the default and the held button inverts it, so with Run selected the button walks. The button is
Cross | R1(mask0x48), seeded once at new game and never rewritten: retail cannot rebind it.
A walkability tile
The world is cut into 128-unit tiles, and each tile is split again into four 64-unit sub-cells. One byte per tile carries both a wall bit for each sub-cell and the tile's floor height.
1 << ((row & 1) × 2 + (col & 1))). Right: what a step tests. The footprint, not the centre, is checked, and one probe in a wall sub-cell is enough to refuse that axis. Probe distances are drawn to scale.- Two nibbles, two mappings. The collision probe maps world to sub-cell with a bias (Z floored then +2, X rounded up then −1), which pushes the half-tile-centred player onto the forward tile; the floor sampler reads the same bytes with plain indexing. A sweep that reads the whole byte as a wall mask calls every raised tile a wall.
- The grid wraps. Both the row and the column are masked to 7 bits, so a Z past the last row reads a real row rather than "no wall".
- Actors block too. Before the wall probes, three wider probes test other actors: a static prop blocks within ±80 units of its footprint, a moving NPC within ±40 of its live position. A hit links the pair, which is how a door's touch script fires on contact.
How fast is a frame?
Retail runs two clocks: the vsync (60/s) and the game tick, one pass of the master frame driver. A scratchpad byte, the frame scalar, says how many vsyncs the last tick spanned (1..4, floored at 2 in field scenes and 3 on the overworld). Every duration and velocity inside the tick is multiplied by it, so a cadence change moves the sample rate, never the speed.
| Base step | Selected when | Units / second | Tiles / second |
|---|---|---|---|
5 | a kingdom world map (the run test is skipped) | 300 at multiplier 1.0 | 2.34 |
8 | plain walk | 480 | 3.75 |
0xC | run | 720 | 5.63 |
0x18 | debug turbo (dev flags only) | 1440 | 11.25 |
The overworld step comes from a per-scene MAN flag set on exactly the three kingdom maps, and each kingdom's entry script also sets the player's multiplier to 0xC00, so the overworld walk is (5 × 0xC00) >> 12 = 3 units per vsync. Reading a per-frame number in the wrong clock is where "the player moves 1.67× too fast" comes from; the port ticks once per vsync with the scalar at 1.
The field buffer: where walls come from
Nothing in a town's script says "this is a wall". The scene loader streams the scene's .MAP file into a per-scene buffer, and the collision grid is one region of it.
| Region | Content | Filled by |
|---|---|---|
+0x0000 | Object records: the buildings, fences and props (anchor tile, mesh index, rotation) | the .MAP file |
+0x4000 | Collision + floor grid, 0x80-byte rows | .MAP; story deltas painted by op 4C 7s |
+0x8000 | Per-tile word: low 9 bits object index, 0x400 object footprint, 0x800 elevation override | .MAP; footprint bits stamped at scene init |
+0x10000 | Trigger block: teleports, record triggers, ramp heights, region boxes | .MAP |
+0x12000 | Field pack and efect.dat; also the trigger lookup's fallback window | separate files |
The .MAP is the first 0x12000 bytes, streamed by CD-DMA at scene load; a write-watchpoint on the live grid catches only the DMA writer. Scripts then paint story-conditional rectangles on top with 4C 7s - Rim Elm's south gate stays painted shut until the story opens it.
Pressing the action button runs a separate probe: a radius-64 compass point in the facing sector against a ±72 NPC box. A hit engages the NPC's script and turns the player toward it. Props divide into two classes on one authored bit: doors open on body contact, cupboards (which run 31 1E in their spawn prologue) only on the button.
Height: two floor models
The walk controller only writes X and Z. After it commits, a second controller re-samples the floor under the new position and glides Y toward it; a third probes ahead along the last step and starts a scripted hop when the floor rises a ledge.
Tile word bit 0x800 | Model | Used for |
|---|---|---|
| clear | Bilinear nibble surface: the four corner tiles' tiers through a 16-entry height LUT, weighted by sub-tile position | flat ground, gentle terrain |
| set | Elevation override: flat mean of the four corner tiers plus the tile's kind-2 record (−32 per whole-tile step, −16 per sub-cell step). No interpolation | ramps and staircases |
The kind-2 record replaces the bilinear branch; it is not a fast path on top of it. Rim Elm's shore ramps sit on sea-level nibbles and hold their whole elevation in kind-2 records; interpolating their nibbles walks the player under the drawn stairs.
Triggers: doors, beats and teleports
Crossing onto a new tile (tile = (world − 0x40) >> 7, compared against last frame's) looks the tile up in the trigger block. Scene entry marks the compare stale, so the arrival tile fires on the first tick.
| Kind | Record | What it does |
|---|---|---|
0 | [x][z][dest_half_x][dest_half_z] | Intra-scene teleport - most house exits |
1, gate 1 | [x][z][p2_record][1] | Spawn a MAN partition-2 record if its story-flag gates pass: town exits (the record runs the named scene change), walk-on story beats |
1, gate 0 | [x][z][p0_record][0] | At scene init, bind a partition-0 record to the object on that tile as its touch script and idle clip - house doors, cupboards |
2 | [x][z][coarse:i8][quads:u8] | Ramp / staircase height (above) |
3 | [x0][z0][x1][z1][type], 8 bytes | Region boxes: camera zones, encounter regions |
A crossing made while a script holds the player - a cutscene, a conversation, a helper record - is spent, not deferred: the tile is stored and nothing is looked up. Deferring it instead fired the landing pad of rugi's warp pair on the first free frame and bounced the player between the two pads forever.
A beat that should play once self-latches: its script sets the very flag its gate lists, so it runs on the crossing that finds the flag clear and blocks itself afterwards. The gate bitmap is the field VM's own story-flag bank, so a set is visible to the next record immediately.
Walking into a house
A town house is not a scene change: the interior is a sub-area of the same 128×128 grid parked in an unused corner, and the door repositions the player. Two door mechanisms exist, and one house can use one of each.
| Class | Mechanism | Lives in |
|---|---|---|
| Script door | A gate-0 bind whose record moves the player channel 0xF8 on contact - instant teleport (A3 F8), teleport + move clip (CC F8 51) or an animated walk to a tile (C7 F8) | MAN partition 0 |
| Map door | A kind-0 teleport tile: no object, no script, no name | .MAP trigger block |
Map doors are the larger class, and a MAN-only census cannot see them. Vahn's house is the mixed case: script door in, map door out.
A door teleport re-centres the placed-object window. The sub-area sweep keeps a list of the window-owned placements around the camera’s latched region box, and only a re-centre rebuilds it. A script door’s player arm (0x23 or 4C 51 aimed at the player) calls the re-centre on the landing tile, as do the kind-0 warp landing and the leader swap. Without it, leaving a house keeps the interior’s box and the street’s window-owned scenery stays hidden until something else re-centres. The port re-centres from the same arms, including the walk-touch teleport it decodes for a door with no prop.
Prop-door rides: lifts and swinging doors
A lift or door whose object has an animated prop rides on its own record: contact hands the touch to the prop runner, which executes the whole record - both doors, both walks and the jump between them. Sol Tower’s floor lifts show the shape; every pair is the same record with the partner id, the walk tiles and the landing changed.
Two engine shortcuts used to hide both doors: applying the record’s decoded teleport on the first contact frame put the player on the far floor before the near door moved, and stepping the partner ops on the record’s own context swung the near door twice and the partner never. A prop door’s record is now left to the runner; an interact-gated object, such as a lever, waits for the confirm press instead.
A door with no prop still takes the decoded teleport at contact, and the engine keeps the effect of the record’s own arrival bracket for it: the touch dispatch and the collision probe skip the partner until the player has stepped clear of it, and a landing the walls seal on every side carries the player past the platform. The same cross-object ops drive lifts, gondolas and gates in several other dungeons.
Talking through a stand-in
Pressing the action button probes a point ahead of the player against every actor's box, and the hit is a conversation when the actor carries the talk flag. An undrawn placement can stand in for a speaker the probe cannot reach: its record only marks another actor as touched, and the context runner then plays that actor's conversation. Conkram's gate guards (concnow) are spoken to this way. The port reads such stand-ins at scene entry and answers a hit on one with its target.
The ledge hop is its own little machine
When the floor ahead rises a ledge, the walk does not climb it: a setup routine spawns two helper actors, raises the movement lock and hands the player to an arc controller with its own cursor, separate from the clip system.
- The probe looks along the last step. The settle controller keeps the direction of the last committed step in two globals; the hop probe samples the floor a step ahead in that direction and posts the hop only when the rise is a ledge.
- The lock is the same bit a cutscene holds, which is why the pad opens no menu in mid-air. The take-off also raises the flag that hides the player's ground shadow (renderer).
- Retail never tests the landing. The probe checks the wall bits along the way, but nothing checks the cell the arc sets the player down in. A hop into an open sub-cell that is walled on every side leaves the player where all four directions are refused, and with no step committed the probe never runs again - one diagonal step-up in
jouindlands exactly so. The port's landing guard refuses such a hop; it is an enhancement, on by default and off in the retail-faithful mode.
Touching and talking
Collision asks two questions at once: is there a wall, and is there somebody. Before the wall probes, each step tests three points against every other actor - a wider sweep than the wall edge, 64 units ahead and 32 to either side, because actors block with a body box. The same step then decides whether the contact means anything.
| Contact | Needs a press? | What happens |
|---|---|---|
| Walking into a static prop of the touch class | no | The prop's event is posted on the spot, on every contact step |
| Anything else - an NPC, a cupboard, a sign | yes, a fresh press of the interact button | One probe at a single point 64 units ahead in the facing's 45-degree sector, box-tested against the whole actor list |
- Talking is not an opcode. A talk is this button probe posting an event to the actor it found; the actor's own script takes it from there (script VM).
- The event goes to the last actor hit, not the first. The list walk has no early exit and overwrites the partner pointer on every box hit. Scene init appends story actors before placed props, so a point inside both an NPC's box and a prop's box is delivered to the prop. It is an oversight a port has to keep to match which thing answers.
- All of it is off while the player is engaged - the same flag that gates movement - or while a dialog owns field control.
Where you stand when a scene starts
The locomotion controller never places the player. The scene initializer does, and it picks between two behaviours from a one-shot word the departing mode leaves behind and the initializer clears on its way out.
| Entry word | Meaning | Reached by |
|---|---|---|
| 0 | Cold: a fresh player actor, seated on the destination's entry coordinates | every door, New Game, movie exits that name a scene |
| 2 | Warp: the existing actor reused, standing where the party left | the return from a battle, a minigame or a resumed movie |
| 1 | transient - renormalised to 2 before the initializer runs | the mode-transition pass on the way out of the field, which also snapshots the player's X and Z |
Retail never enters a scene without a door operand, but the port's scene picker can. A picker entry first tries the scene’s door arrivals - where other scenes’ scene-change ops land the party (engine) - and only when none qualifies does it synthesise a seat: retail's own fallback coordinate when it is standable, inside the scene's main walkable region and not on a teleport pad; otherwise a door-arrival anchor, then the region's centroid.
- The main region is not simply the largest. On the three kingdom maps the collision grid leaves the sea open - the coast is a closed wall ring and retail only arrives by door onto land - and the sea is about four times the continent. The rule excludes a region that reaches three or more map edges, which is exactly those three seas.
- A door arrival is never nudged. A standable tile is taken exactly as given, so every authored arrival lands byte-exact on its operand; only a derived tile the grid does not cover is moved to the nearest open sub-cell.
The camera turn is authored, not computed
The pad remap rotates the held direction by an eighth-turn index. Nothing in retail derives that index from the camera. Its writers across the whole disc are two, and both are content: a field-VM op that sets it to a literal, and the tile-board walker. A scene author picks the octant to match the fixed camera the same script installs; a bare tile crossing never changes it.
- The op turns the player with it. Under one condition the same arm rewrites the facing by the delta it applied, so the character keeps facing the same way on screen across a camera cut.
- The port departs here on purpose. Its camera can be dragged, so an authored constant goes stale the moment the view moves; it rounds the live camera azimuth to the nearest of the eight steps instead. On every axis-aligned camera the two agree.
- The slide is measured. A probe on the resolver's return in a retail town shows it answering the held direction on 261 of 276 calls and widening it on 15 - one held-left run down a wall, where “left” becomes “left and up”. Six of those rows are the pin the port's resolver is tested against.
The four base steps
| Step | When |
|---|---|
| 5 | Forced slow: the per-scene flag set on exactly the three kingdom maps. With the overworld's three-quarter scale that is 3 units per vsync, and the player never runs there |
| 12 | Run |
| 24 | A debug turbo behind the debug-mode word, a dev word and a pad bit. Retail boots the dev word to zero and only ever clears it, so the turbo is unreachable on a retail disc even with the debug-mode byte poked; the port carries the arm behind the same mirrors |
| 8 | Plain walk |
The walk clip is chosen the same way the step is: no direction held is the idle base, a direction under the plain walk step is the walk base, and any other step is the run base. The run button word is one of four button masks the new-game init seeds into the save block; that init is their only writer anywhere, so retail has no way to rebind them.
NPCs: facing and glide speed
Initial facing
The placement record has no facing byte. At spawn the installer pre-runs the record's leading ops (a 0x24 / 0x25 marker up to the first 0x21), and two of them write the heading from the 8-direction LUT at SCUS 0x80073F04: the NPC move-to-tile op 4C 51 (operand +3, low nibble) and op 0x38's simple path. Town prologues route that leg through story-flag tests, so the fall-through branch is the fresh-game facing. Rim Elm's side-by-side villagers at tiles (29,22) / (30,22) derive indices 6 and 2 - they face each other.
Heading space: retail 0 = Z−, 0x400 = X−, 0x800 = Z+, 0xC00 = X+. After spawn, walking snaps the heading and the rotate ops ramp it; the last writer in the frame draws (motion VM).
Glide speed
An NPC's pace is not the player's walk step - the walk kernel never reads the speed multiplier. Both NPC walk kernels carry the base step in the walk op's own operands, on the ladder numerator >> (2 + bits) units per vsync.
| Op | Where bits lives | Numerator |
|---|---|---|
0x37 glide | (op0 >> 5 & 4) | (op1 >> 6) | 0x80 |
0x41 glide | same | 0x40 - half speed |
0x47 walk-to-tile | b2 & 7; high nibble = approach mode | 0x80 |
motion-stream steps 03 / 19 / 20 | operand byte 1, low nibble | 0x80 |
motion-stream 06 / 18 | 4-bit selector over the operands' high bits | 0x80 |
Base steps run 32 / 16 / 8 / 4 / 2 / 1 for bits 0..5. There is no synthesised motion bytecode: the field VM parks the op's address on the actor and the walk kernel reads the record bytes in place each frame.
In the port
- Faithful path.
World::step_field_locomotionrings the held pad around the same eight-direction compass, hands the remapped mask to the wall-slide resolver and walks the result through the same per-axis 2-unit collision with the three-probe edge and the actor boxes. Wall-press captures rest byte-exact. Retail picks its rotation step from scene data; a free-orbiting camera has none, so the port rounds the live camera azimuth to the nearest eighth. - Enhancements. Free-angle movement (
options::precise_movement, default off): continuous azimuth, true 45° diagonals at unit speed, analog stick angle.--no-edge-collision/--no-solid-npcsfall back to a centre test and walk-through NPCs. - Floor.
World::sample_field_floor_heightcarries both height models. - Run button. The run mask defaults to the retail
Cross | R1plus Square; which key is which pad button is a host binding (legaia-engine config set --binding W=R1).
How we know
| Function / data | Address | What it proves | Dump |
|---|---|---|---|
| Locomotion controller | FUN_801D01B0 | Gate, remap, speed, 2-unit step loop; found by a write-watchpoint on the player X/Z (stores at 0x801D0684 / 06E4 / 0744 / 07B4) | autorun_player_pos_watch.lua, field overlay dump |
| Wall collision | FUN_801CFE4C / FUN_801D56C4 | Biased sub-cell mapping, quadrant bit select, probe tables DAT_801F21B4 / 2214 | field overlay dump |
| Actor collision | FUN_801CFC40 | Static ±80 / moving ±40 boxes, contact classes, +0x98 link | field overlay dump |
| Wall slide | FUN_80046494 | Three-point probe, perpendicular sweep, diagonals untouched | funcs/80046494.txt |
| Pad remap | FUN_800467E8 | Eighth-turn rotation over the 8-direction ring DAT_800766FC | funcs/800467e8.txt |
| Frame scalar | FUN_80016B6C | DAT_1F800393 picked from a VSync(1) measurement with per-mode floors | funcs/80016b6c.txt |
| Floor sampler | FUN_80019278 | Bilinear vs override on cell bit 0x800; LUT at scratchpad 0x1F80035C | funcs/80019278.txt |
| Settle / hop | FUN_801D1BA0 / FUN_801D1878 | Step-delta globals 0x8007BDE0 / E4, hop arc | field overlay dump |
| Walk-on dispatch | FUN_801D1EC4 → FUN_801D5630 → FUN_8003BDE0 | Tile compare, trigger scan with the +0x12000 fallback, gate check | field overlay dump, funcs/8003bde0.txt |
| Object spawn | FUN_8003A55C / FUN_8003AEB0 | Object record fields, bind lookup, footprint bit, LUT fill | funcs/8003a55c.txt |
| Grid source | FUN_8001F7C0 via CD-DMA FUN_8005D9A0 | The only writer of the live grid is the DMA load; scripts paint deltas | funcs/8001f7c0.txt |
| Wall paint | 0x801E1C64 (op 4C 7s) | Rect [col0, col1+1) × [row0+1, row1+2); subs 0/1 six bytes, 2/3 seven | overlay_0897_801de840.txt |
| Wall-press captures | PCSX-Redux states | X− press pins the 47-unit edge; Z− press pins the authored Z bias | capture states |
Full write-up, including the hop-arc controller, the door-swing clip binding, the timed warp and the scripted-scene actor programs: docs/subsystems/field-locomotion.md.
Details
Player actor fields
The player actor pointer is _DAT_8007C364. Read +0x14/16/18 as 16-bit: a 32-bit read of +0x14 folds the next word into X and looks like position drift.
| Offset | Meaning |
|---|---|
+0x10 | flags: 0x80000 movement disabled, 0x1000000 interact requested |
+0x14 / 16 / 18 | world X / Y / Z (s16, 128 units per tile); Y is the ground-follow height, not a yaw or tilt |
+0x26 | heading, eight angles |
+0x72 | speed multiplier, 0x1000 = 1.0 |
+0x94 | encounter-record pointer |
+0x98 | interaction-target actor |
Object records and the two spawners
| Offset | Type | Meaning |
|---|---|---|
+0x00 | u16 | X sub-tile offset: world_x = col × 128 + this + 0x40 |
+0x02 | u16 | Y offset added to the tile's LUT floor height |
+0x04 | u16 | Z sub-tile offset: world_z = row × 128 − (this − 0x40) |
+0x06 / 07 | i8 | Footprint column / row delta to the anchor tile |
+0x08..0x0C | u16 × 3 | Rotation triple, 4096 per turn (+0x0A = yaw) |
+0x10 | u16 | Mesh index into the scene TMD pack |
+0x12 | u16 | flags; 0x4 = placed |
A placed record becomes an actor through exactly one of two sweeps. The scene-init sweep resolves the record's bind (the gate-0 trigger on its footprint anchor) and skips unbound records; the sub-area window rebuild spawns from the tile words with no bind lookup. The sets are complementary (Rim Elm: 37 bound + 9 unbound = 46), so every placed record draws. A bound record with a clip id is posed by that clip's frame 0 - the cupboard's hinge doors hang through the floor without it. Parser legaia_scene_formats::field_objects.
Scene entry: which .MAP, which script, which encounters
- The
.MAPis the PROT slot atdefine − 2from the scene's CDNAME block; its0x12000footprint identifies it. - The entry script is MAN partition-1 record 0 on channel
0xFB. Its wall paints sit behind story-flag tests: map03 paints tile (66, 102) solid behind flag0x6C2and clears three rects behind0x378. - The same MAN carries the random-encounter table; towns have real tables too - Rim Elm declares 7 formations, gated by its region records.
- On entry the player is seated from the destination-entry pair
_DAT_80073EF4 / EF8: the door's operand on a scene change, the saved coordinates on a return from battle, a minigame or an FMV.(0xA40, 0, 0xA40)is where the ambient particle emitter spawns, not Vahn's seat.
The scripted-scene actor
Some field actors are small programs rather than NPCs - the hop-arc controller, door swings, one-shot props. FUN_801D4A60 is a 38-state jump table on the actor's +0x54 whose entry state computes +0x54 = +0x54 + 1 + (+0x50 × 10): +0x50 is a program selector, and the table is four short programs of 5, 5, 6 and 7 states entered at 1 / 11 / 21 / 31. Spawner FUN_801D5A24(program).
The submode return state: a parked word nothing reads
Opening a field sub-screen (op 0x49) parks a state in the scene struct: the pre-enter handler slot, then the submode return constant 7, into scene[+0x40]. Nothing reads it. A disassembly sweep of the executable and every overlay image finds no load of +0x40 through the scene pointer, and a read watch across a field-to-minigame transition sees neither word read while the pointer still names the scene. The return is carried by the driver’s own handler slot, so the port collapses enter and return into one step and keeps no copy of the parked word.
Open: what the port does not model yet
- Touch counters that keep the engaged flag raised until every overlapping touch is dismissed; the engaged flag, the parked-script resume and the facing save / restore are modelled.
- Terrain slow - the controller’s half-speed test on a cell flag under one scene control byte.
- NPC walk clips - a script-walked scene NPC glides in its current pose.
- Static facing fallback - a placement whose prologue the entry pre-run does not execute takes the fall-through leg’s facing.
- A prop that seats itself still draws where it stood: the hosts hide placed objects by bind record, and one record binds many placements, so the per-placement seat moves only the collider.
- Travel-art part stages are counted, not drawn.
History: readings this page replaced
Walls were once thought to be authored entirely by scripts (a static search for CPU stores cannot see the DMA load). The +0x8000 region was read as a terrain-flag grid; it is the object map. The object table was read as NPC spawns rather than buildings. Base step 5 was read as a scripted slow-walk; it is the kingdom overworld's step. The 4C 51 operand was read as a glide speed; it is a facing nibble. See do-not-re-walk.