Files

164 lines
9.5 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.
# Objects, waypoints, zones and lights
Everything that is not static geometry lives in the second half of the MAP chunk: object
instances ("strats"), their parameters, waypoint chains and environment zones. Lights are in their
own chunk, LGHT. Full byte layouts: [notes/objects.md](../../notes/objects.md).
## Strats: objects are programs
An object – coin, enemy, door, camera, the player – is an instance of a **strat**: a script in the
level's STPC code. A MAP record says *where* and *which code*; the code does the rest. There is no
type id: **the type is the code's byte offset in STPC**. Type names come from the code itself
(a `debugName "Coin"` instruction at the entry point). How the code works: [Strats](../strats/README.md).
About 300 types are placed in the original levels, about 200 more are only spawned by other
strats (splinters, effects, heads). The full list of which type appears in which level, with
instance counts, is [notes/strat_types_table.md](../../notes/strat_types_table.md)
(`tools/strat_table.py`).
## The MAP object blocks
After the geometry blocks ([Geometry](geometry.md#map-the-tile-grid-certain)):
| Block | Format |
|---|---|
| parameter pool | `u32 n, n × u32` – all object parameters of the level, referenced by index |
| environment zones | `u32 n, n × 90 bytes` (below) |
| waypoints | `u32 n, n × 34 bytes {u32 next, u32, u16 rx, ry, rz, s32 x, y, z, u32, u32}`; `next ≠ 0` = the following entry belongs to the same chain |
| strats | `u32 nStrats, u32 poolExtra`, nStrats × 58-byte records |
| optional tail | paths (WFPC 0x200000, unused by the CD levels), a buffer size (WFPC 0x10), a u16 |
### Strat record (58 bytes, *certain* unless noted)
| Offset | Field |
|---|---|
| 0 | rotation x, y, z (u16 each, 4096 = 360°; x/z almost always 0) |
| 6 | position x, y, z (s32, 20.12 fixed point, game space) |
| 18 | **code offset in STPC = the type** |
| 22 / 26 | parameter count / index into the parameter pool (−1 if none) |
| 30 | number of strat variables (fixed per type) |
| 34 | 16-byte slots for event handlers (fixed per type, *likely*) |
| 38 | type flags: collision and physics setup (fixed per type) |
| 42 | extra memory words (collision bones; fixed per type) |
| 46 | first waypoint index or −1 |
| 54 | flags; bit 1 = not created at level start |
`poolExtra` is the number of strat slots beyond 30: the engine allocates `poolExtra + 30` slots,
which must cover all placed objects **plus everything spawned at run time**.
The type fields (variables, slots, flags, extra) come from the compiler and must be copied from an
original instance of the same type; a type that uses collision but has type flags 0 crashes the
game. Builders and the editor do this automatically.
### Parameters
The *n* parameters of an instance are pushed onto the new strat's stack and become its
**variables v0 … v(n−1)**. Values are 20.12 fixed point by convention (0x1000 = 1.0), booleans
0 / 0x1000, counts and world/chapter numbers `n << 12`. The editor knows the meaning of the
parameters of many types (schemas in
[`editor/tools/objects/Schemas.java`](../../src/main/java/com/eliasmueller/llamamod/editor/tools/objects/Schemas.java),
findings in [notes/editor-object-schemas.md](../../notes/editor-object-schemas.md)); the others are
shown raw with the values used in the original levels.
### Waypoints
A waypoint chain is a list of positions used for patrol paths, moving platforms, camera paths –
and for some types (guards, box walkers) as a **pair of boxes** the object must stand in, or the
game removes it ("removing strat"). Guards also wait for an **event bit** (global variable 95) that
another object sets ([notes/editor-object-schemas.md](../../notes/editor-object-schemas.md#event-bits-global-95)).
The **player's waypoint chain is the start camera**: the camera takes the first node within 2.5
cells of the llama. A good start has one node about 2 cells behind the llama and 0.5 above it, and
nothing solid right behind the llama (the camera moves aside).
## Special objects
| Role | Type | Notes |
|---|---|---|
| player start | `Llama` | exactly one per level (`RollerCoaster`, `KuscoAndPachaTogetherOnATree`, `LlamaChase` in special levels) |
| checkpoint | `CheckPointPost` (spawns `CheckPointHead`), `CheckPointBox` | the respawn point after a fall |
| exit | `LevelDoor` | invisible trigger, see below; variants `VillageEndDoor`, `EndGameDoor` |
| cameras | `CameraSide`, `CutSceneCameraDirectional`, `CutSceneTrigger` | camera zones and cut scenes |
| always present | `CheatTeleport`, `Wompy`, `CutSceneTrigger` | |
### The exit (LevelDoor)
An exit is **two parts working together** (decompiled strat code; the details below were checked
in game in Jungle Day 2 with a debugger, the basic rule also in Village 1):
* The **LevelDoor** object (no model) writes its three parameters – the next level (tribe, world,
chapter) – to globals g101–g103 and its grid cell to g104/g105. It does **not** end the level
itself in the cases we tested (see the details below).
* **Kuzco's own script** ends the level when **all** of these hold:
```
floor material under Kuzco == 16 ; the remembered floor (see "floor test" below)
and floor(x), floor(z) == the cell in g104/g105 ; the exit's grid cell, any height
and Kuzco is at most ~0.6 above that floor's height
-> level change to g101..g103 (tally screen)
```
On a material-16 floor **outside** the exit cell the same check is a house door: fade-out and
the teleport of the environment zone of that cell.
So a normal exit needs **a floor with collision material 16 in the exit's grid cell**. In several
original levels that floor only exists after a tile swap opens the door (Village 1 and Jungle Day 2:
the RubyIdolDoor swaps the tile to a model that has the material-16 pad; checked in game in Village
1). The parameters of the originals are (1, world, chapter + 1); **(7, 7, 7)** in the last chapter of
a world means "world finished" – in TestLevel that crashes, so custom levels use (1, 9, chapter).
The tally shows coins x / LNFO total and secrets and unlocks the next chapter. The arena sets
g101–g105 itself to make its own exit ([Arena](../strats/arena.md)).
**Details checked in game (Jungle Day 2, t1l4m002, 2026-11-07):**
* The LevelDoor stores its parameters and cell in g101–g105, then checks global 0x21 **once**
(after 5 ticks); if it is not 3 the door removes itself. In Jungle Day 2 it was gone after the
start, so the Llama's check is what ends the level. That check needs floor material 16, the
door's cell, and the llama no more than 0.6 above the recorded floor height. Off the exit cell
the same material-16 check is the house door (fade-out and zone teleport).
* The floor test only looks at tiles in the player's cell and takes the **nearest upward floor
below the player, at any distance**. Material 17 is ignored for the llama. A llama hovering 3.7
units above a house-door pad records material 16.
* Each tick runs all strat scripts first and then the floor tests. A position change made after
the floor tests is seen by the next tick's scripts with the **old** floor record. So hovering
above any house-door pad and then moving the llama straight to floor height in the exit cell
ends the level, even though the exit cell has no material 16 yet (it appears only in the swap
model when the RubyIdolDoor opens). Moving there from normal ground does nothing.
## Environment zones (90 bytes, *certain* for the fields below)
A zone sets fog, colours, music and sky, and optionally teleports the player.
| Field | Meaning |
|---|---|
| cell x, y, z | the trigger cell |
| target x, y, z, angle | teleport target and facing |
| flags (+0x20) | flag 8 = **start zone** (exactly one per level: the level begins with its settings); flag 4 = do not move the player |
| sky model | STPC offset of a sky model (0 = none) |
| fog mode | 0 none, 1 darkness with a light around the player, 2 table fog, 3 vertex fog |
| fog start / end | two pairs (detail Minimum and Maximum); **fog end = far clip = draw distance** |
| colours | ambient colour for object lighting, fog/backdrop colour |
| music id | 1–39 (see [Music](audio.md#music)), 0 stops the music |
Teleports are triggered from strat code (opcode 0x100 looks up the zone of the calling object's
cell). In the original levels the trigger cells hold exit floors (material 16) or door objects –
this is how the village house doors work. The editor edits zones in the
*Environment* panel ([manual chapter 12](../manual/12-environment.md)).
## Lights
LGHT: `u32 n`, then per light a type, a colour (×2/255) and direction or position (game space).
Lights only light **object models** (ambient + all directional lights + the nearest point lights,
at most 6). Tiles use baked vertex colours, so changing a light does not change the ground – bake
the vertex colours again. Point lights are full up to radius *a* and fade to 0 at *b*; one byte
makes them flicker.
## Tools
| Task | Tool |
|---|---|
| list types, instances, parameters of a level | [`tools/strats.py`](../../tools/strats.py) `level.wad [--all]`, [`tools/map_dump.py`](../../tools/map_dump.py) `--strats --tiles --lights` |
| type × level table | [`tools/strat_table.py`](../../tools/strat_table.py) |
| place objects from a script | [`tools/levelbuild.py`](../../tools/levelbuild.py) `L.strat(...)`, Java [`LevelBuilder`](../../src/main/java/com/eliasmueller/llamamod/levelbuild/LevelBuilder.java) |
| place, edit, check objects visually | the level editor ([manual chapter 9](../manual/09-objects.md), [10](../manual/10-waypoints.md), [11](../manual/11-cameras-triggers.md)) |