diff --git a/notes/img/mods/devflag_aux_moonjump.jpg b/notes/img/mods/devflag_aux_moonjump.jpg new file mode 100644 index 0000000..5d1428f Binary files /dev/null and b/notes/img/mods/devflag_aux_moonjump.jpg differ diff --git a/notes/img/mods/devflag_river.jpg b/notes/img/mods/devflag_river.jpg new file mode 100644 index 0000000..90e7065 Binary files /dev/null and b/notes/img/mods/devflag_river.jpg differ diff --git a/notes/img/mods/devflag_teleport.jpg b/notes/img/mods/devflag_teleport.jpg new file mode 100644 index 0000000..6f0cb8c Binary files /dev/null and b/notes/img/mods/devflag_teleport.jpg differ diff --git a/notes/mod-cheats.md b/notes/mod-cheats.md index affd652..178af14 100644 --- a/notes/mod-cheats.md +++ b/notes/mod-cheats.md @@ -63,7 +63,7 @@ diagnostics) → `jmp 0x41C050`. The cave writes only game globals, so it needs | F4 in game | requests state 0x0C (world map) – the normal "back to map" | no | | F7 in game | toggles 0x584708 → start row of the strat debug print (0x6D9DB8 = 0x20 / 0x48); nothing visible in retail strats | no | | F6 | screenshot `capture%d.tga` | no | -| `CheatTeleport`, `CheatCharge` strats | present in most level WADs; strat-only logic, not decoded | no | +| `CheatTeleport`, `CheatCharge` strats | present in most level WADs; gated by property 258, see "Developer switch (property 258)" | no | Level select / unlock-all is the save editor's job (`--unlock`), not duplicated here. @@ -115,3 +115,61 @@ the bit makes it fall back. Same write as the llama: normal jump, no effect (pro To enable on PC a patch is required, e.g. make the pad poll map a key to padAux bit 0x800. Which PS button of pad 2 feeds bit 0x800 is not known (no PS binary analysed) – hypothesis only. + +## Developer switch (property 258) + +Strat property 258 (0x102) is a hard-wired developer flag: getter FUN_00550e60, jump table +0x552684[0x102] → 0x55231B `mov eax,[esp+14h]; push 0; push eax; call 0x54BBD0` (the `push 0` byte is +at **0x552320**; the same handler also serves the unread ids 0x75, 0x9E, 0xC1, 0xEE). It is read +101× in the 34 level WADs (all units disassembled; `pushProp` only, no player/parent/... variants). +Every use is a logical test (`and`, `not`, `jz`), so any non-zero value enables it. + +Keys (in-game `padHeld` 0x5FCF20, measured with gdb while holding the key): A 0x400, S 0x800 +(camera left/right), Return 0x1000, Z 0x2000 (action), X 0x4000 (jump), Space 0x8000 (power), +P 0x08 (pause). No keyboard key gives 0x100/0x200. `padAux` (prop 88, 0x5FCF58) and +`padAuxPressed` (prop 90, 0x5FCF60 = edge of padAux, computed every frame at 0x425C1E) are never +set on PC (see above). Strat constants are 20.12: `0.25` = 0x400, `0.5` = 0x800, `0.75` = 0xC00, +`10.0` = 0xA000, `256r` = raw 0x100. + +| Reader strat | Levels | Gate | Bits / keys | Effect | Status | +|---|---|---|---|---|---| +| CheatTeleport (+CheatCharge, same unit) | placed in 22 levels: 1-1..1-4, 2-1, 2-2, 4-1..4-3, 5-1..5-3, 6-1..6-3, 7-2, 7-4, 8-1..8-5 (code also in 3-1..3-4, not placed) | entry `if (!prop258) remove` at level start | hold **A+S** (padHeld 0xC00) | teleports the llama to the next node of the strat's own path (hand-placed warp list, 1–18 nodes per level, often next to checkpoints, doors, puzzles; wraps around); sets `g0 = 14` | verified 1-1 (4 nodes, cycled twice) | +| CheatTeleport | same | prop258 | padAuxPressed 0x400 | +1 heart (if alive), +5 purple fruit, hudDigit = charge | verified (fruit 0 → 5, HUD shows it) | +| CheatTeleport | same | prop258 (+ prop 0 == 0) | padAux 0x100 held | +1 coin per frame (prop 190, coins of this level) | verified (HUD 307 coins after 0.5 s) | +| CheatTeleport | same | prop258 (+ prop 0 == 0) | padAux 0x200 held | +1 pink idol per frame, max 99 (prop 250) | verified (0 → 65 → 99) | +| CheatTeleport | same | prop258 | hold Space+Z (0xA000) | only `print` debug lists (player/camera state dump: "gFreezePlayer", "gCamAng", "teleported to location (" …) that the PC VM skips → nothing visible | static | +| CheatTeleport | same | prop258 **and prop 0 ≠ 0** | padAux 0x100 held = debug menu; then padPressed Space/Z = previous/next music track (spawns unit 48 = track display, `musicPlay`), X = lose heart, Return = +1 idol, A = +1 coin (+10 charge every 5), S = full charge, 0x100 = **`endLevel(g101,g102,g103)`** (finish level), 0x200 = +1 counterC; combo becomes 0xF00 | unreachable even with prop258 = 1 (prop 0 is also hard-wired 0, handler 0x552192) | static | +| Llama | 23 levels (all with a llama) | prop258 | padAux 0x800 held | moonjump/fly: starts a jump on the ground without pressing X (no cooldown check) and sets vertical speed 0.0698/frame every frame in the air → rises until released | verified 1-1: y 8.25 → 20.6 in ~2 s without touching a key | +| LlamaChase | 2-3 (night chase) | prop258 | padAux 0x800 | same moonjump; also allows jumping anywhere (normally only on floor material 11) | static (chase intro did not start under load) | +| KuscoBunny | 7-4, 8-1, 8-2 (potion) | prop258 | padAux 0x800 | rabbit jumps/rises with speed 0.045 while held | static | +| KuscoFrog | 4-3, 8-3 (potion) | prop258 only for take-off | padAux 0x800 | air rise has **no** gate (moonjump agent, verified); with prop258 the bit also starts the jump | static | +| KuscoAndPachaTogetherOnATree (river log) | 3-1..3-4 | prop258 | hold **Space** (0x8000) | damage handling skipped (invincible), +1 heart every frame, collision flag 0x40000 off (passes through banks), horizontal movement ×8, no current/steering term | verified 3-1: with Space the log shot through the river bank out of the level | +| RiverWhirlpool | 3-2 (36×) | prop258 | hold Space | whirlpool does not pull the log | static | +| CondorManDoor | 5-2 | prop258 | padHeld 0x100 (no PC key) | a hit always takes the "straight hit" branch (`v16 = 1`, later `otherPiece()` on impact; hypothesis: the door breaks) instead of needing the angle within 96 | static | +| EndGameTriggerBox | 8-6 (finale, 15×) | prop258 | hold Space+Z during the box's wait time | `endLevel(1,1,1)` at once (hypothesis: skips the rest of the finale) | static | + +How it was verified (apptest `devflag-a`, :25, German exe): `set {char}0x552320 = 1` via the winedbg +gdb proxy at the main menu (CheatTeleport removes itself at level start, so the patch must be in +before the level loads), then `set {int}0x5FCF58 = 0x100/0x200/0x400/0x800` one at a time. +`img/mods/devflag_teleport.jpg` (1-1 start → three A+S warps), `devflag_aux_moonjump.jpg` (before / +after padAux 0x400+0x100: fruit 5 and 307 coins on the HUD / bit 0x800: llama flying above the +village), `devflag_river.jpg` (3-1 log normal / after Space boost in the bank / outside the level). +Note: the exe run by LlamaMod relocates the per-level array 0x584F1C to 0x6DB940 (seen at 0x550BC2), +so `levelRecord`/coins are not at the address of `re/groove.exe`. + +Property 0 is a second hard-wired 0 read by strats (131×: Camera, CameraSide, CheatTeleport, birds, +butterflies, ladybirds, moths, Bunny, Panther, Yzma…). Ambient critters with a parameter set remove +themselves when it is non-zero, the camera uses alternative heights/distances when it is 0, and +CheatTeleport switches its combo to 0xF00 – **hypothesis**: a platform/hardware flag (PS button +layout or low-memory mode), not a debug flag. Other constant getter cases: 0x5E/0x60 = 84, +0x5F/0x61 = 123, 0x82/0x83 = 42 (stick thresholds), and 0, 0x13, 0x15, 0x47, 0x75, 0x76, 0x77, +0x98, 0x9E, 0xC1, 0xD8, 0xED, 0xEE, 0x102 = 0; of the zero ids only 0 and 0x102 are read by any strat. +No exe code writes padAux, and no other dev key handler was found beyond the table above (F4, F6, F7). + +Suggested "Developer mode" option for the cheats mod (not implemented): patch byte 0x552320 to 1 +(or point jump-table entry 0x552A8C at a cave that pushes 0x1000, leaving the shared handler alone), +and map keys to padAux in the pad poll: at the end of FUN_004256C0 (0x425B88, where padAux is only +cleared when >1 pad) jump to a cave that builds padAux from the DIK state array 0x58226C + 4·DIK, +e.g. F1 → 0x800 (moonjump), F2 → 0x400 (heart + fruit), F3 → 0x200 (idols), F5 → 0x100 (coins). +A+S teleport and Space (river) work with the normal keys once prop 258 is 1. Note that F4/F6/F7 +are taken; the frog's ungated moonjump would also react to the 0x800 key.