Files
llamamod/notes/mod-coaster.md
T

114 lines
8.2 KiB
Markdown
Raw 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.
# Llama Coaster (`coaster`, LEVELS)
Package `mods/coaster`: `CoasterMod` (options, chapter check, writes `wads/T1L9M0nn.wad` +
loading picture `T19nn.COR`), `CoasterLevel` (level script: track geometry + objects),
`CoasterTrack` (layouts → segments, corners, waypoint chain), `CoasterSpeed` (patches the
RollerCoaster strat's speed constants), `CoasterSim` (frame model of the strat's speed/jump logic,
used by the tests to check a layout). Tests: `CoasterTest`. Registered in `ModRegistry` (one line).
Docs: [docs/strats/roller-coaster.md](../docs/strats/roller-coaster.md), manual chapter 18.
| Option | Default | Meaning |
|---|---|---|
| `track` | `loop` | `loop` (~103 units/lap) or `grand` (~150 units, two jumps, two spin pads) |
| `laps` | 2 (1–3) | laps before the ride ends at the station |
| `speed` | `normal` | `slow` ×0.8, `normal` ×1, `fast` ×1.25 (cruise 614 raw and boost 20 raw/frame) |
| `coins` | on | CoinRollerCoaster along the straights (loop 27, grand 44; placed once, not per lap) |
| `chapter` | 48 (32–49) | TestLevel chapter; a chapter another mod already wrote → apply error |
## How the original rides work
**Roller coaster = Catacombs 1, 3, 5 (`t1l7m001/3/5`, world 7 – not world 8; Yzma's lab is
world 8 and has no ride).** Strat units of t1l7m001: `RollerCoaster` (unit 0, 1775 words),
`CameraFixedPointBox`, `RollerDoor`, `CoinRollerCoaster`, `WompyRollerCoaster`,
`FireworksTrigger`, `Camera`, `SpawnableRing`, `Sparkles`, `SpawnableNeedleBall`.
* **The cart is the player.** There is no `Llama` strat: `RollerCoaster` calls `isPlayer()` and
`lookAtMe()`, uses `model:0` (cart with Kuzco-llama and Pacha, scale 2) and its animations
(lean, cheer, look back, fall). The llama is part of the cart model, not attached at runtime.
* **Camera:** the strat spawns the shared `Camera` strat (`spawn<4,41,2,0x248000,0>(0.9, 1.0, …,
unit:Camera)`), the same chase camera as the Llama's; it follows `g29` = the cart's heading,
which the strat sets to the next waypoint's heading in corners (so the camera swings round
before the cart). `CameraFixedPointBox` (one per level) is a fixed-point camera zone.
* **Track following = waypoint chain + floor.** At start (`L_0200`) every waypoint x/z is rounded
to halves and its heading to a quarter turn (param2). The cart drives straight along its heading
at speed `v19` (min 0.15 = 614 raw), the floor height comes from the collision floor below it
(`noFloorPhysics`, `self.y = floorHeight`; a climb steeper than ~0.1 per frame loses the
floor). Within 0.75 in front of the current waypoint it turns by 90° on a small arc round the
waypoint (funnel: lateral offset clamped to 1.2·distance) and switches to the next one – so the
tracks are straight legs on the grid with 90° corners; only the geometry makes slopes.
Left/right input shifts the cart sideways (`v18`, ±half the speed); the rails are collision walls.
* **Floor materials** (collision material of the track bed): 5 boost (+20 raw speed and
"excess" per frame, decays back to cruise), 11 jump (vertical speed 163 raw, gravity 14 raw per
frame), 13 spin (small hop + half turn: the cart rides backwards until the next corner), 15 an
animation/sound pad, 16 the end (only when the current waypoint is the last one), 30 the
invisible death plane far below (fall → restart).
* **Start:** the first two waypoints have param 1.0 (station): the cart crawls (0.025 + nudges)
out of the station with letterbox borders and the start line, then picks up cruise speed.
* **End:** on material 16 with the last waypoint active the strat plays a sound and after 30
frames calls `endLevel(param1..3)` – the cart's own params 1–3 are the target (tribe, world,
chapter) → tally screen. `RollerDoor` are gates the cart passes, `FireworksTrigger` fires rockets
at its waypoint spots when the cart passes, `WompyRollerCoaster` is a spinning Wompy pick-up.
**River = Jungle river 1–4 (`t1l3m001…4`, world 3; world 2 is the jungle at night).** The player
strat is `KuscoAndPachaTogetherOnATree` (both on a log, also `isPlayer()` + the shared `Camera`).
It also rounds its waypoints to halves/quarter turns and turns at waypoints whose param is 1 or 2
(left/right), but it floats on water (`floorMaterial 5` = water there: rumble, splash particles)
and is pushed by current/force zones (`RiverCurrentBox`, many "kraft" zones); the player steers
freely between the banks, `CameraLift`/`RiverWaterfallCamera` give the scripted shots, and the
level ends at a normal `LevelDoor`. It is a steerable boat, not a rail ride, so the coaster mod
uses the RollerCoaster strat.
## Design
* **Base level = t1l7m001** (not the village + import): its STPC already has every unit the ride
needs and their textures, sounds and the ride music; importing `RollerCoaster` + `Camera` into
the village would only duplicate them. The original geometry and objects are replaced by
`LevelBuilder` output (`LevelScript.buildWad`), so `format.stpcmerge` is only used read-only
(`StpcImage`/`StratCode`) by `CoasterSpeed`.
* **Track:** straight legs along the axes, 2 wide (two 1-wide lanes, centre line on integers),
joined by flat 2 × 2 corner squares; heights change only on straights. Pieces: TRACK, BOOST (mat
5), JUMP (11), SPIN (13), END (16), STATION, GAP (no floor). Rails 0.35 high (inner face =
collision), underside, pillars every 5 units and under corners, a boarding platform with posts
left of the station, ground plane at y = 0 with material 30. Textures are t1l7m001 TEXT indices.
* **Leg 0** of every layout: run-in, END pad, brake run, STATION (5), first drop with a boost at
its top. The circuit must close (checked) and END must come before STATION on leg 0.
* **Waypoint chain** (`CoasterTrack.nodes`): node 0 one unit behind the cart and node 1 at the
station's end, both param 1.0 (crawl); then one node per corner, 0.5 before its centre, heading
after the turn, repeated per lap; last node 9 units past the END pad on leg 0, so the END pad is
live only in the last lap. RollerCoaster params = (0, 1, 9, chapter) ×4096 → the tally restarts
the ride, like the arena.
* **Speed** (`CoasterSpeed`): finds the six `pushShort 614` (cruise floor/cap) and two
`pushShort 20` (boost, after `pushVar v19/v22`) in the RollerCoaster unit with `StratListing`
and rewrites the 16-bit operands; nothing else changes.
* **Checking a layout offline** (`CoasterSim`, test `everyJumpLandsAndNoClimbLosesTheFloor`):
for every track × speed × laps every jump must clear its gap and land before the commit zone of
the next corner, no climb > 0.1 per frame, speed stays 0.02–0.45.
* Objects: `RollerCoaster` at the station start, a `RollerDoor` at the station exit, a
`FireworksTrigger` before the END pad with six rocket spots, CoinRollerCoaster every 1.5 units
on straights ≥ 4 long outside leg 0 (on a slight zig-zag). The tally total (LNFO) is the coin count.
* Loading/tally picture: drawn in Java (dark purple, a rail curve, title "Llama Coaster" in the
band y 265–335 that the game's tally text leaves free).
## Verified in game (apptest, :26, 2026-11-07)
* loop/normal 1 lap and 2 laps, loop/fast 1 lap, grand/fast 1 lap (chapter 40), grand/slow:
station crawl with letterbox and start line, drops, climbs, boost arrows, start hint "Kusco, bleib in der
Mitte der Kurve und wir werden uns automatisch umdrehen!", spin pad (cart turns backwards), jump over the gap and
landing, corners, coins counted (e.g. 14/27, 19/44), END pad → fireworks → tally with the
loading picture. No crash in any run.
* After the tally: **Enter** restarts the ride (same chapter); **Esc** asks "Möchtest du das
Spiel beenden?" (No → back to the tally). In the ride, Esc → *Beenden und zur Auswahlkarte
wechseln* → Yes → world-9 map on "TestLevel Kapitel 48". Same behaviour as the arena.
![ride](img/mods/coaster_ride.jpg)
![tracks (editor top view: loop, grand)](img/mods/coaster_tracks.jpg)
![tally](img/mods/coaster_tally.jpg)
## Limits
* Only 90° corners on the grid and straight slopes (the strat cannot follow curves or banking);
no loops/inversions (the cart has no roll).
* The tally coin count is not kept on the world map (it showed 0 after the ride) – same as the
other TestLevel mods; not investigated.
* The original levels' sky/fog/lighting of t1l7m001 stays (dark catacombs, fog end 23 cells).