# Randomizer mod (`randomizer`, GAMEPLAY) Code: `src/main/java/com/eliasmueller/llamamod/mods/randomizer/` (`RandomizerMod`, `Shuffler`), test `src/test/.../mods/randomizer/RandomizerModTest` (needs `-Dllamamod.cd=`). ## Options | Key | Type | Default | Effect | |---|---|---|---| | `seed` | SEED | random on first use | same seed = same layout | | `coins` | BOOL | on | `Coin`, `Coin5` | | `food` | BOOL | on | `Health`, `HealthFull`, `Grapes`, `HealthBanana` | | `mix` | BOOL | on | coins and food share one pool (otherwise each kind only among its own places) | | `pots` | BOOL | off | `SmashPot(spawn)`, `Jar` among their own places | | `enemies` | BOOL | off | enemies without patrol path, only among places of the same type | ## How it works Per level WAD `t1l[1-8]mNNN.wad`: parse MAP (`MapChunk`, TRAK vertex counts), name every strat from STPC (`Strats.nameOr`), sort eligible strats into pools, and permute the *places* inside each pool: only fields 0–5 of the 58-byte strat record (rotation x/y/z, position x/y/z) are exchanged. Type code, parameters, nVars/slots/flags/extra and the waypoint index stay with the strat, so the multiset of objects in a level and the set of occupied places are unchanged – every pickup ends on a spot where the original game had a pickup (reachable by construction). MAP is rebuilt with `MapChunk.build()`; all other chunks are untouched. PRNG: `java.util.Random(seed * 0x9E3779B97F4A7C15 + levelFileName.hashCode())`, pools in MAP order, `Collections.shuffle` – both are specified by the JDK, so a seed gives the same result everywhere. Never moved: * every type not in the lists above (Llama/start, LevelDoor, CheckPoint*, Camera*, CutScene*, triggers, Force*, switches/doors, RubyIdol (doors depend on it), ChargeBooster, CoinLine/Circle/ RollerCoaster, Secret, platforms …), * strats with a waypoint (index ≠ −1: patrol paths, secret areas – their coordinates are absolute) and strats with flag bit 1 (spawned later by an event), * pots within 3 tiles of a `Secret` (the village secret is "smash the pot cluster"). Pool sizes (all options on, seed 1): 10–65 pickups per level, up to 14 pots; enemies only in t1l4m002/3 (`VenusFlyTrap`, `AxeGuard`) because almost all enemies have patrol waypoints. Roller coaster levels (t1l7m001/3/5) and t1l8m006 are unchanged. Save game: coins per chapter are counted at pickup; the number of coins in a level is unchanged, so the per-chapter totals and the "all coins" flag behave as in the original. ## Verified in game (apptest, 2026-11-06) * Village t1l1m001, seed 1: level loads and plays, no crash (most swaps there are coin↔coin, so the start view looks the same). * Jungle t1l2m001, seed 2 (chosen because 3 food items land near the start): three of the four coins on the path ahead of the start are replaced by Health/HealthFull pickups, the coins moved elsewhere (`img/mods/randomizer-jungle-seed2.jpg`); level runs normally. * Unit test (`RandomizerModTest`, all 33 WADs): deterministic per seed, different seeds differ, pickup places are an exact permutation, type/params/waypoint fields unchanged, every other strat (Llama, LevelDoor, CheckPoint*, cameras, triggers …) byte-identical, strats with waypoints never move. ## Known limits * Not played to the level exit in game; correctness of the exit/checkpoints rests on them being untouched (unit test). * A coin that sat in the air in an arc may now be a food item there (still reachable, it was a pickup spot before); first coin parameter (0x1000 vs 0) stays with the strat. * Enemy shuffle has little effect: almost all enemies have patrol waypoints and are excluded.