94 lines
4.8 KiB
Markdown
94 lines
4.8 KiB
Markdown
# 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.
|
||
|
||
The Python tools have a few **hard-coded paths** at the top of the scripts (`WADS`, `MODWAD` in
|
||
`levelbuild.py`, `CD_WADS` in `debugviz.py`, `PREFIX` in `lamatest.py`) that point into the original
|
||
author's Wine setup; use `--base <cd>/wads/t1l1m001.wad` or adjust them. `--install` only works with
|
||
that setup – with LlamaMod, use a Java level or the editor instead.
|
||
|
||
## 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).
|