270 lines
13 KiB
Markdown
270 lines
13 KiB
Markdown
# levelbau – level modding kit for *The Emperor's New Groove* (PC, 2000)
|
||
|
||
German title: *Ein Königreich für ein Lama*. PC port by Argonaut, built on the Croc 2 engine.
|
||
|
||
This kit came out of an AI-assisted reverse-engineering project. It contains:
|
||
|
||
* **Format documentation** for the level files (`*.wad`): geometry, collision, object
|
||
placement, textures, waypoints, visibility zones, and what makes the level exit work.
|
||
* **Python tools** that read, export and rebuild those files byte-exactly.
|
||
* **A level builder** (`tools/levelbuild.py`). You describe a level as a short Python script
|
||
(boxes, ramps, stairs, fences, trees, sea, objects) and it writes a complete, playable WAD.
|
||
* **An example level, "Testwelt"** (`levels/testwelt.py`). It goes into the hidden, empty
|
||
*TestLevel* slot (world 9, chapter 1) and has its own geometry, a gallery with every
|
||
object type on pedestals, an animal pen and a small parkour with a working exit.
|
||
* **A debug patch generator** (`tools/debugviz.py`). It makes collision visible in every
|
||
level, including invisible walls, along with invisible trigger and camera objects.
|
||
* **An automated in-game tester** (`tools/lamatest.py`). On Linux it runs the game under
|
||
Wine on a virtual display, loads the test level, takes screenshots and detects crashes.
|
||
|
||
Nothing from the game itself is included: no game files, no executable, no decompiled
|
||
code, no built WADs. Every tool works on **your own copy** of the game.
|
||
|
||
> Most notes in `notes/` are written in **German**. Any AI assistant can read and translate
|
||
> them, and that is the recommended way to use this kit (see the last section).
|
||
|
||
---
|
||
|
||
## Requirements
|
||
|
||
* Your own copy of the game. Development used the **German CD version**. Other language
|
||
versions should use the same format, but this hasn't been tested.
|
||
* Python 3.8+ with `numpy` and `Pillow`. Optional: `capstone` (disassembler helper).
|
||
* For the automated in-game tests only: Linux, Wine, Xvfb, xdotool, ImageMagick, tesseract
|
||
with German language data, and PulseAudio (`pactl`).
|
||
* Optional: Blender (preview renders), and Ghidra (to regenerate the decompile, see below).
|
||
|
||
The builder and the export tools are plain Python, so they should also work on Windows.
|
||
The test harness is Linux/Wine-only.
|
||
|
||
### Paths – adjust these
|
||
|
||
The tools were written for one Linux/Wine setup and some paths are hard-coded at the top of
|
||
the scripts:
|
||
|
||
| Script | Variable | Meaning |
|
||
|---|---|---|
|
||
| `tools/levelbuild.py` | `WADS` | folder with the original level WADs (the `wads` folder of the game CD) |
|
||
| `tools/levelbuild.py` | `MODWAD` | where `--install` puts the built level (`T1L9M001.wad`) |
|
||
| `tools/debugviz.py` | `CD_WADS` | same as `WADS` |
|
||
| `tools/lamatest.py` | `PREFIX`, `LAMA_MOD` | Wine prefix and the overlay script |
|
||
|
||
The game reads its level data from the CD drive, in the `wads` folder of the CD. To mod it,
|
||
make a copy of the CD data (or an overlay of symlinks, see `tools/lama-mod`), point the CD
|
||
drive or `CDPath` at that copy, and put modified WADs into its `wads` folder.
|
||
|
||
---
|
||
|
||
## Folder layout
|
||
|
||
```
|
||
README.md this file
|
||
notes/ format documentation and findings (German)
|
||
geometry.md TRAK/MAP geometry, collision polygons, coordinate spaces, zones, limits
|
||
objects.md MAP chunk layout, strat (object) entries, waypoints, player start, exit
|
||
strat_types_table.md every object type in every original level, with counts
|
||
textures.md TEXT chunk: texture pages, RLE16, texture table, animations
|
||
levelbau.md how to use levelbuild.py, measured player physics, exit logic
|
||
testharness.md the automated Wine/Xvfb tester
|
||
debugviz.md debug patch: colour legend, what is shown, limits
|
||
img/ screenshots and preview renders
|
||
tools/
|
||
geomlib.py core library: WAD, TRAK, STPC models, MAP geometry, TEXT, collision
|
||
geom_export.py WAD -> OBJ/MTL (+ textures, vertex colours, collision.obj, objects.obj)
|
||
geom_import.py OBJ -> new geometry inside a WAD copy
|
||
mapchunk.py MAP chunk parser/writer (byte-exact on all original levels)
|
||
strats.py, strat_table.py, stpcwalk.py, map_dump.py object types and strat code helpers
|
||
textchunk.py, text_export.py, texsheet.py textures: export/import PNG, contact sheets
|
||
levelbuild.py the level builder (see notes/levelbau.md)
|
||
debugviz.py debug patch generator (collision/trigger visualisation)
|
||
lamatest.py automated in-game test on a virtual display (Linux/Wine)
|
||
lama-mod shell script: builds a CD overlay with modded WADs (Wine setup)
|
||
vorschau/ Blender preview render scripts
|
||
disx86.py small disassembler helper for groove.exe (needs capstone)
|
||
wadtool.py minimal WAD chunk lister/extractor/replacer (used by several tools)
|
||
cor2png.py, png2cor.py loading/concept pictures (*.COR, LZ-compressed 640x480) <-> PNG
|
||
asf2wav.py music (*.ASF, 4-bit ADPCM) -> WAV
|
||
levels/
|
||
testwelt.py the example test world
|
||
testwelt_parkour.seq scripted run through the parkour (used with lamatest.py)
|
||
sprunglabor.py "jump lab": test lanes used to measure jump height/width
|
||
re/ghidra_scripts/
|
||
DumpAll.java Ghidra headless script: decompiles every function to one C file
|
||
```
|
||
|
||
---
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
# export a level to OBJ (+ collision and object markers)
|
||
python3 tools/geom_export.py /path/to/cd/wads/t1l1m001.wad out_village --collision --objects
|
||
# -> level.obj/.mtl, texture pages, collision.obj, objects.obj, preview images
|
||
|
||
# build the example test world
|
||
python3 tools/levelbuild.py levels/testwelt.py testwelt.wad
|
||
python3 tools/levelbuild.py --types # object types you can place
|
||
python3 tools/levelbuild.py --textures tex.png # texture sheet with the names you can use
|
||
|
||
# build the debug patch for all levels
|
||
python3 tools/debugviz.py --help
|
||
```
|
||
|
||
### Playing the test level
|
||
|
||
The game has a hidden world 9, *TestLevel*, with 49 chapter slots, but the retail game ships
|
||
no data for it. Put your built WAD into the CD's `wads` folder as `T1L9M001.wad`. Also add a
|
||
loading picture `T191.COR`, for example a copy of another `T1xx.COR`.
|
||
|
||
To reach world 9, edit a save file (`king0.ksv`…`king2.ksv`, 256 bytes):
|
||
|
||
* `+0x04/+0x05` = map cursor (world, chapter)
|
||
* `+0x3f/+0x40` = current world/chapter
|
||
* `+0x42/+0x43` = highest unlocked world/chapter
|
||
|
||
Set them to world 9, chapter 1.
|
||
|
||
When you leave through the exit, the game unlocks the next chapter. `levelbuild.py --install`
|
||
therefore links chapters 2–49 to the same file, so that picking one on the map doesn't crash.
|
||
|
||
### Debug patch: visible collision and invisible walls
|
||
|
||
```bash
|
||
python3 tools/debugviz.py --all debug_wads --src /path/to/cd/wads # all 33 level WADs
|
||
python3 tools/debugviz.py t1l1m001.wad t1l1m001_debug.wad # a single level
|
||
python3 tools/debugviz_check.py debug_wads /path/to/cd/wads # offline sanity check
|
||
```
|
||
|
||
Put the resulting WADs into the CD's `wads` folder in place of the originals, keeping the
|
||
same file names. On the Wine setup, `lama-mod debug an|aus` toggles them. The patch only
|
||
adds render geometry. Collision, objects, waypoints and zones stay byte-identical, so
|
||
gameplay is unchanged.
|
||
|
||
Legend (full version in `notes/debugviz.md`):
|
||
|
||
| Colour | Meaning |
|
||
|---|---|
|
||
| green / yellow-green wireframe | walkable floor / slope |
|
||
| blue | wall |
|
||
| orange | ceiling / overhang |
|
||
| **red, semi-transparent fill** | **invisible collision**: invisible walls and floors |
|
||
| magenta lines | death plane ("no floor") |
|
||
| violet fill | level exit floor (material 16) |
|
||
| other colours | special collision materials |
|
||
|
||
| Marker | Meaning |
|
||
|---|---|
|
||
| white arrow | player start |
|
||
| violet cube | exit |
|
||
| turquoise cube | checkpoint |
|
||
| cyan cube | camera |
|
||
| yellow cube | trigger / cutscene |
|
||
| orange cube | force or special zone |
|
||
| gold cube | secret zone |
|
||
| grey cube | other invisible helper object |
|
||
| pink wire box | ambience / music / fog zone |
|
||
|
||
Cubes sit on a stick, and a horizontal line shows the object's facing. Wire boxes show
|
||
trigger areas.
|
||
|
||
Status: 19 of 33 levels were loaded in the game without problems. The rest
|
||
(catacombs/roller coaster world 7, lab world 8, two others) passed the offline check only.
|
||
Collision of moving objects (platforms, doors) is not shown.
|
||
|
||
---
|
||
|
||
## LlamaMod and the level editor
|
||
|
||
Besides the Python kit, this repository contains **LlamaMod** (Java 25, Swing; `src/`), a
|
||
one-click mod manager for the game on **Windows and Linux**, and a full **3D level editor**
|
||
built into it. Nothing from the game is included: the wizard installs the game from your own
|
||
CD/ISO into LlamaMod's data folder, and all mods are built from that copy.
|
||
|
||
* **Mods** – tick mods, click *Apply*/*Play*: debug wireframe, Test World, randomizer, fog,
|
||
mirror mode, intro skip, cheats, practice tools, free camera, *boot to level* … Mods may change
|
||
level files and `groove.exe` (code caves); the original files are never touched.
|
||
Developer notes: `notes/llamamod-app.md`, one `notes/mod-*.md` per mod.
|
||
* **Level editor** (*Level Editor* button, or `LlamaMod --editor`): create new levels from
|
||
templates, brushes and the base level's tile models and objects, or edit the original
|
||
levels; import OBJ/glTF meshes and PNG/JPG textures (texture manager, paint-on-faces,
|
||
animations); waypoints, zones, fog, lights, visibility regions; **Play (F5)** builds the
|
||
level and starts the game directly in it (Escape returns to the editor).
|
||
* **Sharing**: a level is a small `.llamalevel` file with only your changes. Others import it in
|
||
LlamaMod (*Mods → Levels → Import level*) and it is built from their own game copy; new levels
|
||
appear in the hidden *TestLevel* world (chapters 1–49).
|
||
|
||
User guide (English): **`notes/editor-guide.md`**. In the editor: *Help → Getting started*
|
||
and *Help → Shortcut sheet*. Technical notes: `notes/editor-design.md`, `editor-api.md`,
|
||
`editor-format.md`, `editor-render.md`, `editor-ui.md`, `editor-tools-*.md`,
|
||
`editor-playtest.md`.
|
||
|
||
```bash
|
||
./gradlew build fatJar # tests + build/libs/llamamod-<ver>-all.jar
|
||
./gradlew packageLinux # build/dist/LlamaMod-<ver>-full-linux-x64.tar.gz (own Java runtime)
|
||
./gradlew packageWindows # build/dist/LlamaMod-<ver>-full-windows-x64.zip (LlamaMod.exe + runtime)
|
||
./gradlew packageAll # both systems, full and launcher (no editor) edition
|
||
java -jar build/libs/llamamod-*-all.jar # LlamaMod
|
||
java -jar build/libs/llamamod-*-all.jar --editor # the level editor alone
|
||
```
|
||
|
||

|
||
|
||
---
|
||
|
||
## Key facts
|
||
|
||
* **The world is made of tiles.** TRAK holds about 100–650 small models: render triangles
|
||
plus collision polygons. MAP places them on a W×H grid (96×96 in the village) with height
|
||
and rotation. MAP also stores baked vertex colours and a 32-zone visibility table. There is
|
||
no BSP and no single world mesh.
|
||
* **Objects are "strats".** These are compiled scripts in the STPC chunk. In MAP, an object's
|
||
type is simply its **byte offset into STPC**. A level can therefore only use the object
|
||
types its own STPC contains. The builder uses the village (`t1l1m001`) as its base.
|
||
* **Coordinates and scale.** y points up. Fixed point 20.12 for objects, 4096 = 360° for
|
||
angles, and render space vs. game space have z mirrored. The llama is about 0.6 units
|
||
tall, runs about 1.1 units/s, can step up at most about 0.35 and jumps about 1.3 units
|
||
far with a run-up.
|
||
* **The level exit (LevelDoor)** is an invisible trigger. It fires only when the llama
|
||
stands in its grid cell **on floor with collision material 16**. Its parameters are
|
||
(tribe, world, chapter) of the next level.
|
||
* **Texture pixels** are PS1-style 15-bit BGR, RLE16-compressed, on 256×256 pages. The
|
||
value 0x0000 means transparent.
|
||
|
||
The parsers rebuild every original WAD byte-for-byte, so they can be trusted as format
|
||
specs.
|
||
|
||
---
|
||
|
||
## Building on this with an AI assistant
|
||
|
||
This whole kit was made by an AI coding agent (Claude Code) working through the steps
|
||
below. Any capable assistant that can run code should be able to continue.
|
||
|
||
1. **Give it the notes first.** Ask it to read `notes/*.md` (they are German; it can
|
||
translate). They contain offsets, field meanings, confidence levels and pitfalls.
|
||
2. **Regenerate the decompile** for questions about game logic. Install Ghidra and a full
|
||
JDK (version 25 for Ghidra 12.x), then run
|
||
`analyzeHeadless <projdir> groove -import groove.exe -scriptPath re/ghidra_scripts -postScript DumpAll.java groove_decomp.c`.
|
||
This writes all functions as C (about 2 MB) for the assistant to search with grep.
|
||
Loader functions worth knowing: MAP `0x42ac50`, TRAK `0x42aac0`, STPC `0x42ab50`, strat
|
||
interpreter `0x54d180`.
|
||
3. **Always test in the game.** Have the assistant load and screenshot every build. On
|
||
Linux, `tools/lamatest.py run <wad> <outdir>` does this unattended in about 75 s. On
|
||
Windows, test by hand or adapt the script.
|
||
4. **Change one thing at a time** and keep the original WADs untouched. The game crashes on
|
||
inconsistent data: tiles outside the grid, a vertex-colour block that doesn't match the
|
||
vertex counts, or missing chapters.
|
||
|
||
### Ideas for what to try next
|
||
|
||
* Use object types from other levels. You would have to merge and relocate their STPC
|
||
code and their textures.
|
||
* ~~Import custom textures~~ – done in the level editor (texture manager, import, painting).
|
||
* ~~Make a real visual editor~~ – done: the LlamaMod level editor (see above).
|
||
* Add custom music or sounds (SMPC/AMPC/SRPC are sound banks).
|
||
|
||
---
|
||
|
||
This is a non-commercial fan project, not affiliated with Disney or Argonaut. Please share
|
||
only tools and your own content, not the game's files.
|