# Mod "practice" – Practice tools (GAMEPLAY) Package `mods.practice` (`PracticeMod`, `SavePositionPatch`, `ExtraCheckpoints`, plus the helpers `Asm` (extra x86 encodings on top of `exe/X86`) and `Hotkeys` (key choices), also used by `freecam`). ## Options | Key | Default | Meaning | |---|---|---| | `saveKey` | F5 | store the llama's position (choices F1–F5, F7–F9, F11, F12, Insert, Delete, Home, End; F6 = game screenshot, F10 = system key are left out) | | `loadKey` | F8 | put the llama back to the stored position | | `checkpoints` | true | add extra CheckPointPost strats to the level WADs | | `spacing` | 10 (4–40) | minimum distance (tiles) between two checkpoints | ## Save / restore position (exe) Hook: `call FUN_0054c4a0` (strat update) at **0x54b77e** inside the tick loop of the frame function FUN_0054b590 is redirected to a cave: 1. poll `GetAsyncKeyState` (via `importAddress`, the game does not import it) with edge detection; 2. save: player strat `*(0x6d9e1c)` +0x30/+0x34/+0x38 (position, 20.12 fixed, z positive), +0x24 (yaw), and the current world/chapter `0x584eb8`/`0x584ebc`; 3. restore (only if a position was saved in the same world/chapter and the player exists): write position into +0x30.. **and** the previous-tick copy +0x50.. (the wall collision FUN_00403ee0 sweeps from previous to current position, so the jump must not look like movement), yaw into +0x24/+0x44; 4. call the original strat update; 5. for 4 ticks after a restore the position is written again after the update ("pin"), so the remaining fall speed of the llama script is used up on the floor collision instead of moving the llama away. Strat structure facts used (0x154 bytes per strat, FUN_0054c4a0/FUN_0054bc30): +0x04 next, +0x20/24/28 rotation (angle<<12, 4096 = 360°), +0x30/34/38 position, +0x40..+0x5f copy of +0x20..+0x3f from the previous tick, +0x70 3×3 matrix, +0x94/98/9c render position copy, +0xc0 collision block, +0xe8 flags (0x8000 floor, 0x40000 wall collision). Velocity is not a fixed field – movement comes from strat-script opcodes (e.g. FUN_00550690 = move along yaw), so it lives in the llama's script variables. The stored position survives deaths and restarts of the same level; it is ignored in another level. Keys are polled with GetAsyncKeyState, so they also react when the game window is not focused. ## Extra checkpoints (WAD) `ExtraCheckpoints.process`: for every `t[1-8]l*m*.wad` that already contains `CheckPointPost` instances (STPC code + type constants available): * candidates = `Coin`/`Coin5` positions (the designers' route), sorted by distance from `Llama`; * a candidate becomes a checkpoint if it is ≥ `spacing` from the start, every original and every added checkpoint, and walkable floor (collision polygon with ny ≥ 0.7, material ≠ 30/17) lies 0.8 below … 0.2 above the coin (`Floor.below`, world-space collision of all tiles); * new strat = copy of the first CheckPointPost (code, nVars 12, slots 1, flags 0x28000, extra 0, parameter value 0x333 appended to the parameter pool), yaw = direction from the nearest previous checkpoint, `poolExtra` +1 per post (max 40 per level); * each original checkpoint owns exactly one path node = respawn point (position + yaw, `next` 0, ~0.3–0.5 tiles beside the post); new ones get a node 0.4 tiles ahead (or behind/sideways if there is no floor ahead). The floor finder reproduces the height of 68 of 102 original posts within 0.05 (the rest stand on moving platforms/strat models). With spacing 10, 32 checkpoints are added in 17 levels (e.g. Village 1: +2, jungle day 3: +5). Levels without CheckPointPost (Village 3/4, river, chase, roller coaster, lab 2/6) are unchanged. ## Tests `PracticeModTest`: instruction encodings (checked against nasm), all WADs: original posts on floor, strat/node/poolExtra counts after processing. `FreeCamModTest` applies practice + freecam to the real exe (no overlapping patches) and writes `build/tmp/practice-freecam.exe` for objdump. ## Verified in game (2026-11-06, Wine 6, apptest, Village 1 = t1l1m001) * F5 → walk away → F8: the llama is back at the stored spot (`img/mods/practice-teleport.jpg`); memory check with winedbg: player +0x30.. equals the stored position after F8. * After restoring, the camera is not snapped; it swings back behind the llama on its own (it may look from an unusual angle for a moment). * Together with `freecam`: no patch conflicts; pause menu, leaving to the map and re-entering the level, falling off the island (respawn) and game over → no crash; the stored position stays valid after the level is restarted. * Extra checkpoints: levels load with the added posts; a new post renders at its place (`img/mods/practice-extra-checkpoint.jpg`, Village 1, (42.91, 2.9, 50.84), seen with the free camera). ## Limits / not verified * Activation of an added checkpoint and respawning at its path node were **not** verified in game (falling off the island does not use checkpoints; reaching a real death in the test harness was not attempted). The strat record, parameter and node layout are copied 1:1 from the level's own CheckPointPost, so it is expected to behave like the original ones. * Posts are placed at coins with floor below; a coin on a moving platform or behind a one-way section can give a post the llama only reaches from one side.