# Levels from scripts Besides the editor, a level can be described as a short script that a **level builder** turns into a complete WAD: geometry made of boxes, ramps, stairs, trees and sea, objects placed by type name, lighting baked automatically. There are two builders with the same ideas: | Builder | Language | Used by | |---|---|---| | [`tools/levelbuild.py`](../../tools/levelbuild.py) | Python 3 (numpy, Pillow) | the original test world [`levels/testwelt.py`](../../levels/testwelt.py), the jump lab [`levels/sprunglabor.py`](../../levels/sprunglabor.py) | | [`levelbuild/LevelBuilder.java`](../../src/main/java/com/eliasmueller/llamamod/levelbuild/LevelBuilder.java) | Java (`LevelScript`) | the Test World mod ([`mods/testworld/`](../../src/main/java/com/eliasmueller/llamamod/mods/testworld/)), the [Alligator Arena](../strats/arena.md), the editor's brushes | Both start from a **base level** (default Village 1): its object code, textures and sound are kept; geometry, tile grid, vertex colours, objects and waypoints are generated from scratch. Full documentation of the Python builder, the measured physics and the test world: [notes/levelbau.md](../../notes/levelbau.md). ## A Python level ```python LEVEL_ARGS = dict(name='MyLevel', seed=7) def build(L): L.sea(0, 0, 60, 60, y=-2.0, kill=(6, 4, 54, 56)) # sea + death plane L.box(14, 12, 26, 24, -0.4, 0, top='stein', side='stein') # a stone plaza L.stairs(26, 18, 29, 22, 0, 1.5, 3, '+x') # stairs up to the east L.box(29, 17, 33, 23, 0, 1.5, top='gras', side='erde') L.player(20, 14.5, rot=0) # facing +z L.checkpoint(17, 18.5) L.coins([(27, 20), (28, 20)]) L.strat('Bunny', 31, 20) L.box(31, 21, 32, 22, 1.5, 1.56, top='platte', side='stein', mat=16) # exit floor L.exit(31.5, 21.5) # LevelDoor in the same cell ``` ```bash python3 tools/levelbuild.py my_level.py out/my_level.wad # build python3 tools/levelbuild.py --types # object types of the base python3 tools/levelbuild.py --textures tex.png # texture names and a contact sheet ``` Coordinates are game units: x east, y up, z = grid row ("south"); angles in degrees, 0° faces +z. Building blocks: `floor`, `box`, `platform`, `pillar`, `ramp`, `stairs`, `fence`, `tree`, `sea`, `quad`, `collider`, `obj` (an OBJ file), `height_at`. The exit needs a floor with collision material 16 in its cell – the test world uses `box(..., mat=16)` under the door. **Texture names**: the Python builder's own names are German (`gras`, `stein`, `erde`, `platte`, `meer` …, list: `--textures`); the English names of the Java builder (`grass`, `stone`, `earth`, `slab`, `sea` …) work as well and give the same result. The base level is `t1l1m001.wad` in the CD folder `$LLAMAMOD_CD/wads` (default `~/.wine-lama/iso-dk/Setup/Eng/Cd`), or `--base `. `--install` copies the WAD into the TestLevel mod of a hand-made Wine setup (`$LAMA_WINEPREFIX/mods/testlevel`, default `~/.wine-lama`) – with LlamaMod, use a Java level or the editor instead. All variables: [Tools: Paths](../tools.md#paths). ## A Java level Implement [`LevelScript`](../../src/main/java/com/eliasmueller/llamamod/levelbuild/LevelScript.java): ```java public final class MyLevel implements LevelScript { @Override public String levelName() { return "My Level"; } @Override public void build(LevelBuilder l) { l.sea(0, 0, 40, 40, -1.5, new double[] {7, 7, 33, 33}); l.floor(10, 10, 30, 30, 0, "grass"); l.box(18, 24, 22, 25, 0, 0.06, o().top("pattern_purple").mat(16)); // exit floor l.player(20, 12, 0); l.checkpoint(12, 12); l.exit(20.5, 24.5, null, null, new int[] {1, 9, 32}); l.coins(List.of(new double[] {20, 16}, new double[] {20, 18})); } } ``` `buildWad(baseWad, wads, opts)` returns the WAD. To ship it, write a small [mod](../developers.md#writing-a-mod) whose `apply` writes `wads/T1L9M0nn.wad` (chapter 1 or 32–49) and a loading picture – [`ArenaMod`](../../src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaMod.java) is a complete example. Texture names are English here (`grass`, `stone`, `sand`, `inca`, …) and are matched by picture on the chosen base. ## The editor from the command line The editor's project format can be scripted too: ```bash llamamod --editor-new my.llamalevel t1l1m001 --name "My level" # new project on a base llamamod --editor-import t1l2m001 jungle.llamalevel # an original level as a project llamamod --editor-build my.llamalevel out.wad # build the WAD llamamod --import-level my.llamalevel # install as a level mod ``` ## Seeing the result without the game * [`tools/geom_export.py`](../../tools/geom_export.py) `level.wad outdir --collision --objects` writes OBJ files, texture pages and two preview images. * [`tools/vorschau/`](../../tools/vorschau/) renders labelled overviews with Blender (Eevee). * In the game: the test harness, see [Tools](../tools.md#testing-in-the-game).