Files
llamamod/notes/mod-practice.md

93 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
Hotkeys are checked together with the other mods' hotkeys (`mods/Hotkeys.conflicts`): applying fails with a clear message if two enabled hotkey options share a key.