From d6b852a1b6e3c898071755a5546161406517f1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sat, 7 Nov 2026 15:27:08 +0100 Subject: [PATCH] testlevel chapters 2-31 overwrite game memory (coin counter and flag rows end after world 8: progress, language, options): level projects, play-tests and the arena use chapters 1 and 32-49, old settings migrate; level tally counts the level's own coins and secrets --- CHANGELOG.md | 82 +++++ docs/manual/01-introduction.md | 2 +- docs/manual/03-editor-window.md | 2 +- docs/manual/14-level-settings.md | 2 +- docs/manual/17-sharing.md | 11 +- docs/manual/18-mods.md | 6 +- notes/img/mods/testlevel_ch49_tally.jpg | Bin 0 -> 47834 bytes notes/img/mods/testlevel_ch9_map_broken.jpg | Bin 0 -> 35997 bytes notes/img/mods/testlevel_chapter_option.jpg | Bin 0 -> 92235 bytes notes/levelbau.md | 28 ++ notes/levelbau.md.orig | 306 ++++++++++++++++++ notes/llamamod-app.md | 4 +- notes/mod-arena.md | 18 +- notes/strat-opcodes.md | 22 +- notes/testlevel-chapters.md | 75 +++++ .../environment/LevelSettingsDialog.java | 10 +- .../editor/tools/playtest/PlaytestRunner.java | 3 +- .../llamamod/editor/ui/NewLevelDialog.java | 17 +- .../llamamod/editor/ui/ProjectController.java | 3 +- .../llamamod/format/stpcmerge/TextMerge.java | 37 +++ .../llamamod/mods/arena/ArenaImport.java | 34 +- .../llamamod/mods/arena/ArenaMod.java | 31 +- .../llamamod/mods/levels/LevelProjectMod.java | 31 +- .../mods/levels/LevelProjectSource.java | 85 ++--- .../llamamod/mods/levels/LevelTally.java | 58 ++++ .../llamamod/mods/testworld/Gallery.java | 33 +- .../llamamod/mods/testworld/TestWorldMod.java | 3 +- .../llamamod/save/TestLevelChapters.java | 76 +++++ .../tools/playtest/PlaytestLifecycleTest.java | 5 +- .../llamamod/mods/arena/ArenaTest.java | 37 ++- .../llamamod/mods/arena/ArenaTestMod.java | 11 +- .../mods/levels/LevelProjectModTest.java | 58 +++- .../mods/testworld/TestWorldGoldenTest.java | 8 +- .../mods/testworld/TestWorldTallyTest.java | 35 ++ .../llamamod/save/TestLevelChaptersTest.java | 57 ++++ 35 files changed, 1015 insertions(+), 175 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 notes/img/mods/testlevel_ch49_tally.jpg create mode 100644 notes/img/mods/testlevel_ch9_map_broken.jpg create mode 100644 notes/img/mods/testlevel_chapter_option.jpg create mode 100644 notes/levelbau.md.orig create mode 100644 notes/testlevel-chapters.md create mode 100644 src/main/java/com/eliasmueller/llamamod/mods/levels/LevelTally.java create mode 100644 src/main/java/com/eliasmueller/llamamod/save/TestLevelChapters.java create mode 100644 src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldTallyTest.java create mode 100644 src/test/java/com/eliasmueller/llamamod/save/TestLevelChaptersTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2015a8b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,82 @@ +# Changelog + +## 0.2.1 – changes since 0.1.0 + +The first test release was 0.1.0 (launcher edition). Everything below is new for testers. +There are two downloads: **full** (launcher + level editor) and **launcher** (mods only). + +### Launcher + +- **Clearer texts everywhere.** Mod descriptions and options are explained in plain words, + directly under each option instead of only in tooltips. +- **Setup wizard** + - Wrong files, truncated ISO downloads and too little disk space are caught when you + pick the source, not after you click Set up. + - Copy progress shows megabytes ("123 of 568 MB"). + - Cancel tells you what happened. +- **Play** + - While the game runs, Play becomes **Quit game**, so a hung game can be stopped from LlamaMod. + - A crash now says "The game crashed" with advice, instead of only "The game has ended". + - Wine: a too-old Wine version gives a warning, and a wrong Wine command is explained. +- **Safer applying** + - Two LlamaMod windows (or the command line) can no longer break each other's apply. + - If LlamaMod is closed in the middle of applying mods, it finishes or rolls back the + apply on the next start and tells you. + - Settings changed in one window show up in the other. + - Old cached mod results are rebuilt after an update. +- **Mods** + - **Mirror mode:** pick the worlds with check boxes. + - **Practice and Free camera:** if two hotkeys use the same key, Apply refuses and says + which ones clash. + - **Boot to level:** a custom start position belongs to its level and no longer breaks + other levels. + - **Intro skip and Boot to level** can now be used together. + - **Test World:** a new gallery with all 253 characters, enemies, animals and objects + of the game, standing still on pedestals (the animal pen is gone). + - **New levels and the Alligator Arena only use TestLevel chapters 1 and 32–49.** On other + chapters the game stored the level's coins over your progress and other game data (chapter 9 + broke the world map). Levels and settings on old chapters move to a free one automatically. + - The level tally shows the real coin and secret totals of new levels, the Test World and the + arena instead of the Village's "55". +- **Save games** + - Backups can no longer overwrite each other. + - Import… opens in the backups folder. + - The table fits small (1280×720) screens. +- **Layout:** works on small and laptop screens; the dark theme is easier to read. + +### Level editor (full edition) + +- **Play-test** + - Shows "Game running" and a **Stop game** button. + - Shift+F5 shows a crosshair where the llama will start. + - Play-test copies are removed when the game ends, and your mod selection comes back. +- **Readable names.** "Coin 3", "Floor 2", "Waypoint 4" instead of internal ids, in the + outliner, inspector, status bar and messages; the outliner filter finds them. +- **Error messages.** Damaged files give one plain sentence with optional details. +- **Geometry** + - Painting or erasing tiles in one drag is one undo step. + - New tile option **No collision** (walk-through decoration). +- **Meshes and export** + - **Export level as glTF/GLB** (textures, vertex colours; optionally only the selection, + or collision as a separate mesh). + - Imported and exported models are **no longer mirrored** compared to Blender. Meshes in + older projects keep their look; switch them to "right" handedness in the inspector to + fix them. +- **Objects** + - **Guards** and **alligators** need a waypoint box around them, or the game removes + them. The editor now sets this up automatically and warns when it is wrong. It also + says which objects can trigger a guard's event bit. + - The start camera no longer starts inside the llama's head on some base levels. Moving + the player start moves its camera and intro fly-by too. + - Snap to floor follows the game's own rule. +- **Object scripts** + - Custom object types can be edited and deleted (with undo). + - **Script language with compiler:** if/else, loops, switch, events, named parameters. + - The script view is much easier to read. + - Opcodes have checked descriptions. +- Narrow docks, small windows and long labels wrap instead of being cut off. + +### Known limits + +- Tested on Linux (Wine) and in a Windows VM. Real Windows hardware is not tested yet. +- The game draws objects only within about 12 units of the llama. diff --git a/docs/manual/01-introduction.md b/docs/manual/01-introduction.md index 83b4137..10b7296 100644 --- a/docs/manual/01-introduction.md +++ b/docs/manual/01-introduction.md @@ -76,7 +76,7 @@ created. Object types of other levels can be added later with **Assets → All w The game has eight worlds – **Village, Jungle at Night, River, Jungle by Day, Mountain, City, Catacombs, Lab** – each with several chapters. A ninth, hidden world called **TestLevel** has 49 -empty chapter slots. New levels go into one of these TestLevel chapters, so they never replace an +empty chapter slots; 19 of them (1 and 32–49) are safe to use. New levels go into one of these TestLevel chapters, so they never replace an original level: each project gets its own chapter (shown in the New level dialog and in [Level settings](14-level-settings.md)). In the game they appear on the world-9 map as "TestLevel – chapter *n*". diff --git a/docs/manual/03-editor-window.md b/docs/manual/03-editor-window.md index dd4270c..ffeb280 100644 --- a/docs/manual/03-editor-window.md +++ b/docs/manual/03-editor-window.md @@ -29,7 +29,7 @@ list of recent projects. | **Level name** | The name LlamaMod and the loading picture show. | | **Author** | Your name (remembered for the next level). | | **Base level** | The original level that provides object types, textures, sky, fog and music, shown with its loading picture. Default: Village – Chapter 1. See [Base levels](01-introduction.md#base-levels). | -| **TestLevel chapter** | The chapter of world 9 (TestLevel) the level replaces when it is enabled. The next free chapter is suggested. | +| **TestLevel chapter** | The chapter of world 9 (TestLevel, 32–49 or 1) the level replaces when it is enabled. The next free chapter is suggested. | | **Start with** | **Empty** (nothing but the base level's sky, fog and music), **Flat floor** (a 16×14 grass floor, player start and an exit) or **Island** (a grass island with trees in the sea, player start and an exit). | Every new level starts clean: of the base level it keeps only the **start zone** (its fog, colours diff --git a/docs/manual/14-level-settings.md b/docs/manual/14-level-settings.md index 7c4af4e..887bdf3 100644 --- a/docs/manual/14-level-settings.md +++ b/docs/manual/14-level-settings.md @@ -25,7 +25,7 @@ nothing. | **Description** | A few sentences for the mod list. | | **Project** | *new level* or *changes to an original level* (read only). | | **Base level** | The original level the project is built on – it provides the object code, textures, sounds, sky and music (read only). | -| **TestLevel chapter** | New levels only: the chapter of world 9 (TestLevel, 1–49) the level replaces when its mod is on. | +| **TestLevel chapter** | New levels only: the chapter of world 9 (TestLevel: 32–49, or 1) the level replaces when its mod is on. Chapters 2–31 are not offered: the game would store this level's coins over other game data. | Below the chapter field the dialog lists which chapters other level projects in your `levels` folder already use, so you can pick a free one. Each level needs its own chapter; chapter 1 is diff --git a/docs/manual/17-sharing.md b/docs/manual/17-sharing.md index a87058d..ea80b89 100644 --- a/docs/manual/17-sharing.md +++ b/docs/manual/17-sharing.md @@ -39,14 +39,17 @@ Then tick the level in LlamaMod, click **Apply** and **▶ Play**. To get rid of ## Chapters and conflicts -Each new level occupies one of the 49 TestLevel chapters. The editor picks a free chapter for +Each new level occupies one of the TestLevel chapters **32–49** (or chapter 1). The editor picks a free chapter for new projects, but levels from different people may use the same one. If two active level mods use the same chapter – or two mods change the same original level – LlamaMod stops applying and names the files involved. Fix it in the mod's options: every new-level mod has a -**TestLevel chapter** option (1–49). +**TestLevel chapter** option (1, 32–49). -> **Note:** Chapter 1 of the TestLevel is also used by the built-in **Test World** mod. Prefer -> chapters 2 and above for your own levels. +> **Note:** Chapter 1 of the TestLevel is also used by the built-in **Test World** mod. The game +> keeps coins and flags for chapters 2–31 of world 9 in memory that belongs to other things (your +> progress, the text language …), so LlamaMod does not use them: levels saved with such a chapter +> by an older version move to a free chapter from 32 on. With more than 18 new levels, some share a +> chapter – only one of them can be on at a time. ## Updating a shared level diff --git a/docs/manual/18-mods.md b/docs/manual/18-mods.md index 198c9ef..9ddb0ff 100644 --- a/docs/manual/18-mods.md +++ b/docs/manual/18-mods.md @@ -38,7 +38,7 @@ option: | Option | Values | Meaning | |---|---|---| -| TestLevel chapter | 1 – 49 | the chapter of world 9 the level occupies; each level needs its own (chapter 1 is also used by the Test World mod) | +| TestLevel chapter | 1, 32 – 49 | the chapter of world 9 the level occupies; each level needs its own (chapter 1 is also used by the Test World mod). Other chapters would overwrite game data such as your progress, so older levels on chapters 2–31 move to a free one of 32–49 | Edited original levels have no options – they replace their chapter. See [Sharing levels](17-sharing.md). @@ -61,7 +61,7 @@ not want with **Remove…**. ## The Alligator Arena minigame -The **Alligator Arena** mod (Levels) puts a small minigame on a TestLevel chapter (default 8): +The **Alligator Arena** mod (Levels) puts a small minigame on a TestLevel chapter (32–49, default 49): a walled sand pit where alligators come in waves (wave 1: one, then two at a time). Face an alligator and **kick** (Action) or **jump and press Action in the air** (the llama dives onto it). Every hit costs it one life: it blinks, backs off and coins pop out – they count on the @@ -107,7 +107,7 @@ A minigame on a TestLevel chapter: alligators come in waves into a walled pit. E | Waves | 1 – 5 | 3 | Wave n brings n alligators (at most 2 at once). | | Llama energy | 1 – 5 | 4 | Energy cap of the llama in the arena (5 = normal). | | Coins per hit | 0 – 5 | 1 | Coins that pop out per hit (5 more at the end). | -| TestLevel chapter | 2 – 8 | 8 | Chapter of world 9 the arena replaces. | +| TestLevel chapter | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 | 49 | Chapter of world 9 the arena replaces (32–49; chapter 1 is the Test World's). | #### Test World diff --git a/notes/img/mods/testlevel_ch49_tally.jpg b/notes/img/mods/testlevel_ch49_tally.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1dcb9af5728d6054dcdef54d4e03f8362a7f28b GIT binary patch literal 47834 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 literal 0 HcmV?d00001 diff --git a/notes/img/mods/testlevel_ch9_map_broken.jpg b/notes/img/mods/testlevel_ch9_map_broken.jpg new file mode 100644 index 0000000000000000000000000000000000000000..147eee34863263447280e04768e072a38cc37427 GIT binary patch literal 35997 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 literal 0 HcmV?d00001 diff --git a/notes/img/mods/testlevel_chapter_option.jpg b/notes/img/mods/testlevel_chapter_option.jpg new file mode 100644 index 0000000000000000000000000000000000000000..64fe74e7b3712245ec018a3c5bf7f66c4e7fc0d0 GIT binary patch literal 92235 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�IwclzH}CxW}eLq5F1C&J1> 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 #wL-^y(zh5?d(v8@CsGX1pq>&k+^T0)eM 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%0AVr5_}aKWTdwu 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 literal 0 HcmV?d00001 diff --git a/notes/levelbau.md b/notes/levelbau.md index 2045b5c..6c60b79 100644 --- a/notes/levelbau.md +++ b/notes/levelbau.md @@ -68,6 +68,31 @@ stein, stein_kante, stein_moos, platte, holz_dunkel, holz_maser, holzbalken, rin inka, muster_lila, …`), a direct index (int) or a flat colour (`'meer'`, `'rot'`, … or `(r,g,b)`; appended to the texture table as a flat-colour entry (flags 0x105, page −1)). Contact sheet with indices: `notes/img/texturen_dorf.png`. + +**Texture names on other bases** (2026-11-07). The `TEX` indices are village (t1l1m001) indices; on another +base the same index is a different picture (village 2: index 1 = dark red floor). Names are therefore +resolved per base by picture: `levelbuild.BaseTextures` holds a fingerprint (FNV-1a over page format, +size and pixels of the entry's rectangle) of every named village texture; a name maps to the village index +if the base shows that picture there (village 1: always → outputs unchanged), else to the first base entry +with the same picture (flags 0x104 preferred). Coverage of the 47 names: t1l1m002 38 (missing: the three +flower textures, `wood_grain*`, `wood_brown*`, `wood_rings`, `wood_edge`), t1l1m003 29, t1l1m004 28, +t1l2m001 9, t1l3m001/t1l5m001 0–2. +* Java builder (`LevelBuilder`, test world/arena/jump lab, `fromCd`): a missing picture is **copied from the + village** (CD file loader `wads/t1l1m001.wad`): packed onto a new 256×256 page (same page flags, 1 px + clamped border) and appended as an entry after the base table; flat colours and copies share the + appended range in order of first use. Without a loader (editor brushes: `BrushCompiler` passes none) + the name is rejected: "texture 'flowers' is a village texture this base level does not have …" + (the editor shows that as a brush warning and only offers the names the scene's base has). + `LevelBuilder.baseTextureNames()` lists the names a base has. +* Python `tools/levelbuild.py --base`: same matching (md5 of RGBA rectangle); missing names raise a + KeyError (no copying). +* In game (apptest, display :22, 2026-11-07): Java-built level on t1l1m002 (grass box, stone box, sand, + trees, plus copied `flowers`, `wood_rings`, `wood_grain`), `--editor-import` → level mod, Village + chapter 2: grass green, stone grey, trees bark + leaves, copied textures correct + (`notes/img/editor/village2_levelbuild_textures.jpg`, `…_textures2.jpg`). Before: grass = dark red + (`village2_levelbuild_start_camera.jpg`). +* Editor (display :22): on a t1l1m002 scene the brush swatches and the texture grid show the base's own + pictures for the names, without the names it lacks (`notes/img/editor/village2_texture_picker.jpg`). **Every triangle shows a whole texture tile** (the engine has no free UVs) – so everything is split into 1×1 pieces; narrow/flat walls show the texture squashed. @@ -190,6 +215,9 @@ loop: if g21 == 3 ; game state "running" 49 chapter slots, table 0x572420) and calls `lama-mod an`. For this, `lama-mod` now also takes symlinks from mod folders (`find … -type f -o -type l`). Checked: chapter 2 on the map is selectable and loads the test world. +* **Only chapters 1 and 32–49 are safe** for world 9: the per-chapter coin counter and flag byte of + the other chapters overwrite game globals (chapter 9 destroys the furthest progress), see + [testlevel-chapters.md](testlevel-chapters.md). * The selection map shows no background for world 9 (only text) and 772 as the coin target (coin table 0x5722f4 only has worlds 1–8) – cosmetic. diff --git a/notes/levelbau.md.orig b/notes/levelbau.md.orig new file mode 100644 index 0000000..6f7059d --- /dev/null +++ b/notes/levelbau.md.orig @@ -0,0 +1,306 @@ +# Level building with `tools/levelbuild.py` + +As of: 2026-11-06 (exit + parkour reworked). Builds complete level WADs from a level description (Python script). +The base is always an original level (default: village `t1l1m001.wad`): its strat code/object models +(STPC), textures (TEXT), sound etc. are taken over; **geometry (TRAK), tile grid, +vertex colours, objects and waypoints (MAP) are generated completely from scratch.** +Result checked in game: `out/testwelt.wad` (= installed test world, see below). + +## Invocation + +``` +python3 tools/levelbuild.py levels/testwelt.py out/testwelt.wad # build +python3 tools/levelbuild.py levels/testwelt.py out/testwelt.wad --install # + install as TestLevel +python3 tools/levelbuild.py levels/testwelt.py out/t.wad --set start=ziel # pass variables to the script +python3 tools/levelbuild.py --types # placeable strat types of the base level +python3 tools/levelbuild.py --textures tex.png # texture contact sheet + name list +python3 tools/texsheet.py WAD out.png [--all] [--only 1,2,3] [--size 96] +``` +`--install` copies to `~/.wine-lama/mods/testlevel/wads/T1L9M001.wad` (creates +`.orig` once) and creates the chapter symlinks 2..49 (see "Exit"). Test in game: `tools/lamatest.py run out/x.wad outdir` (see testharness.md). + +## Level script + +```python +LEVEL_ARGS = dict(name='Testwelt', seed=7) # NAME chunk, random seed (texture rotation) + +def build(L): # L = levelbuild.Level + L.sea(0, 0, 88, 72, y=-2.0, kill=(6, 4, 80, 66)) # sea + death plane + L.box(14, 12, 26, 24, -0.4, 0, top='stein', side='stein') + L.stairs(26, 18, 29, 22, 0, 1.5, 3, '+x') + L.player(20, 14.5, rot=0) + L.checkpoint(17, 18.5) + L.exit(70.5, 12.5) + L.strat('Bunny', 66, 34) + L.coins([(27, 20), (28, 20)]) + +def report(L): # optional, after building + ... +``` +`L.opts` contains the `--set` values (strings). + +### Coordinates +* Game coordinates as for strats: **x towards east, y up, z = grid row (positive, + "south")**, 1 unit = 1 grid cell. Village grid: 96×96 → everything must lie within x,z ∈ [0,96). +* Angles in **degrees**, 0° = facing +z, 90° = facing +x (checked in game). +* Internally converted into export space (z mirrored); winding order is corrected + automatically (`_orient`), you only specify the desired outward normal. + +### Geometry building blocks +| Method | Description | +|---|---| +| `floor(x0,z0,x1,z1,y, tex, rot='random', coll=True)` | horizontal surface (subdivided into 1×1 tiles) | +| `box(x0,z0,x1,z1,y0,y1, top, side, side_top=None, bottom=None, sides='nesw', side_coll=None)` | cuboid; `side_top` = texture of the topmost wall row (e.g. `'graskante'`), `side_coll=False` = walls without collision (low plinths walkable) | +| `platform(x,z,w,d,y, top, side, thick=0.5, side_top='graskante', bottom='erde')` | floating platform, top edge y | +| `pillar(x,z,y0,y1,size, side, top)` | pillar around a centre point | +| `ramp(x0,z0,x1,z1, ya, yb, '+x', top, side, base)` | ramp, rises in direction `+x/-x/+z/-z` | +| `stairs(x0,z0,x1,z1, y0, y1, n, '+x', top, side, base, walkable=True)` | stairs; `walkable`: invisible slope as collision | +| `fence(pts, y, height)` | fence (posts + rail) along a point list | +| `tree(x,z,y, height, crown)` | blocky tree | +| `sea(x0,z0,x1,z1, y, tex='meer', kill=(...))` | water surface without collision + death plane | +| `quad([BL,BR,TR,TL], tex, coll, rot)` | single quad (corners seen from the front) | +| `collider(pts, normal)` | invisible collision surface | +| `obj(path, offset, scale, tex)` | OBJ file (game coordinates), `usemtl tex_N` or texture name | +| `height_at(x,z)` | height of the topmost walkable surface | + +Textures: name from `TEX` (village: `gras, gras2, gras_dunkel, blumen, sand, erde, graskante, +stein, stein_kante, stein_moos, platte, holz_dunkel, holz_maser, holzbalken, rinde, kiste, +inka, muster_lila, …`), a direct index (int) or a flat colour (`'meer'`, `'rot'`, … or +`(r,g,b)`; appended to the texture table as a flat-colour entry (flags 0x105, page −1)). +Contact sheet with indices: `notes/img/texturen_dorf.png`. + +**Texture names on other bases** (2026-11-07). The `TEX` indices are village (t1l1m001) indices; on another +base the same index is a different picture (village 2: index 1 = dark red floor). Names are therefore +resolved per base by picture: `levelbuild.BaseTextures` holds a fingerprint (FNV-1a over page format, +size and pixels of the entry's rectangle) of every named village texture; a name maps to the village index +if the base shows that picture there (village 1: always → outputs unchanged), else to the first base entry +with the same picture (flags 0x104 preferred). Coverage of the 47 names: t1l1m002 38 (missing: the three +flower textures, `wood_grain*`, `wood_brown*`, `wood_rings`, `wood_edge`), t1l1m003 29, t1l1m004 28, +t1l2m001 9, t1l3m001/t1l5m001 0–2. +* Java builder (`LevelBuilder`, test world/arena/jump lab, `fromCd`): a missing picture is **copied from the + village** (CD file loader `wads/t1l1m001.wad`): packed onto a new 256×256 page (same page flags, 1 px + clamped border) and appended as an entry after the base table; flat colours and copies share the + appended range in order of first use. Without a loader (editor brushes: `BrushCompiler` passes none) + the name is rejected: "texture 'flowers' is a village texture this base level does not have …" + (the editor shows that as a brush warning and only offers the names the scene's base has). + `LevelBuilder.baseTextureNames()` lists the names a base has. +* Python `tools/levelbuild.py --base`: same matching (md5 of RGBA rectangle); missing names raise a + KeyError (no copying). +* In game (apptest, display :22, 2026-11-07): Java-built level on t1l1m002 (grass box, stone box, sand, + trees, plus copied `flowers`, `wood_rings`, `wood_grain`), `--editor-import` → level mod, Village + chapter 2: grass green, stone grey, trees bark + leaves, copied textures correct + (`notes/img/editor/village2_levelbuild_textures.jpg`, `…_textures2.jpg`). Before: grass = dark red + (`village2_levelbuild_start_camera.jpg`). +* Editor (display :22): on a t1l1m002 scene the brush swatches and the texture grid show the base's own + pictures for the names, without the names it lacks (`notes/img/editor/village2_texture_picker.jpg`). +**Every triangle shows a whole texture tile** (the engine has no free UVs) – so +everything is split into 1×1 pieces; narrow/flat walls show the texture squashed. + +### Lighting (vertex colours) +`L.sun = dict(azimuth=215, elevation=50, color=(1,.93,.8), strength=.62, sky=(.5,.55,.66), +ground=(.3,.28,.24), ao=.55, ao_radius=1.3, shadow=.35)` – per vertex: +sky/ground hemisphere light + sun (Lambert) with **shadows** (ray towards the sun against all +cuboids) + **ambient occlusion** (24 rays, range `ao_radius`). Cuboids, pillars, +stairs, platforms register as shadow casters (`solids`). 1.0 = texture colour +(vertex colour 0x80), max. 2.0. `tint=(r,g,b)` multiplies per building block. + +### Objects (strats) +`L.strat(name, x, z, y=None, rot=None, params=None, wp='relative', variant=0, hidden=False)` +* `y=None`: place on the topmost walkable surface at (x,z). +* Type fields (nvars, slots, flags, extra memory) and parameters are copied from the **first + original instance** of the type (`params=[…]` overrides; count must match). +* `variant`: types with several code entry points (e.g. SmashPot: 2 spawn stubs). +* **Waypoints** (`wp`): `'relative'` = move the original's waypoint chain along (offset to the + object is kept, y is set to the floor if there is one), `'here'` = all points on + the object, `'none'` = none (−1), or your own list `[(x,z[,y[,deg[,unk2]]]), …]` (other extra + fields of the original waypoints are taken over). Chains are renumbered (no references into the + old level). +* `player(x,z,rot)` (strat `Llama`), `checkpoint(x,z)` (`CheckPointPost`), `exit(x,z)` + (`LevelDoor`), `coins([(x,z),…])`. +* Types that are in the village code but not placed there, via `Strats.BORROWED` with type fields + from another level: currently `RubyIdol` (from t1l1m002). +* `poolExtra` is set to max(original, number of strats + 80); the original's parameter pool + is kept, new parameters are appended. + +### What `build()` generates +1. All triangles are distributed onto tiles by (grid cell, height band of 10 units from y = −3); + tile origin = cell centre/band centre (collision d must be within ±8). +2. **Collision**: every quad with `coll=True` becomes a collision polygon; floors only in their + own cell, walls/slopes additionally in neighbouring cells up to 0.3 distance (the engine only checks + the tiles of the cell the player stands in). Unused helper vertices make sure + the tile box encloses the collision polygons. +3. Identical tile models are merged (vertex colours are stored per tile in MAP). +4. **Death plane**: original model 0 of the village (invisible, material 0x1e) in every cell + of the `kill` area that is not completely covered by floor. +5. MAP: zone grid = 0, zone 0 sees everything (mask 0xffffffff), no tile swaps (alt block + empty), tile blocks A/B/models/vertex colours consistent, strats + waypoints new, light/ + environment zones (90-byte block) and rest unchanged. +6. TEXT: appended flat colours. NAME: `LEVEL_ARGS['name']`. +7. Cross-check: MAP is fully parsed again after writing. + +## Findings from the game tests (lamatest, Xvfb) +* **Scale**: llama ≈ 0.6 units tall, walks ≈ 1.1 units/s (coin measuring track, see below; the + 0.85 noted earlier was too low), needs ~1 s to accelerate. A jump (X) clears + 0.35 high edges, 0.5 not. **With a run-up it flies ~1.3 far** (horizontally as fast as + when walking), onto +0.2 it lands after ~1.05; from standing much shorter (0.5 gap not + cleared). → Parkour: gaps 0.3 with +0.2, platforms before/after ≥ 1.5 long (after + landing there must be room for the next take-off), larger heights via stairs/ramps. + Measuring level: `levels/sprunglabor.py` (`--set spur=a..g|m`; `m` = coin measuring track: a coin every 0.25 → + coin counter × 0.25 = distance walked; `f` = long jump over water). +* **No step logic**: even a 0.12 high edge *with wall collision* blocks the llama. + Without wall collision (`side_coll=False`) it walks up onto low plinths; stairs with + an invisible slope (`stairs(walkable=True)`) and ramps are walkable (checked). +* **Controls are camera-relative** (Up = camera view direction). The start camera is behind + the llama; if there is an obstacle right behind it (e.g. a post), it moves aside → + keep the area behind start points clear. +* Rot 0° = facing +z, 90° = facing +x. +* **Death plane works**: falling into the sea → health gone, respawn at the last + checkpoint (or start). +* Player waypoint chain = start camera positions (the game takes the first node within 2.5 cells + of the llama, see `editor.model.cmd.PlayerStart`): `player()` gives the llama ONE node 2 cells + behind it, 0.5 above, unk2 = 0 (default camera distance/height) – like the editor's player start. + Until 2026-11-07 it copied the prototype's 14-node chain (first node 8 behind = out of range, the + rest on the llama): on village 2 bases (node 2 has unk2 ≠ 0) that put the camera into the llama's + head. Waypoint lists take an optional 5th value unk2: `[(x,z,y,deg,unk2)]`. + In game (test world, apptest 2026-11-07): the start camera is behind the llama at the default + distance (`notes/img/editor/testworld_start_camera.jpg`). Village 2 base (levelbuild floor + player on + t1l1m002, `--editor-import` as an edit project, boot-to-level 1/2): camera behind the llama, not in its + head (`notes/img/editor/village2_levelbuild_start_camera.jpg`). +* **LevelDoor** (exit) – solved, see section "Exit (LevelDoor)" below. +* Sizes that work: TRAK 0.6–1.0 MB, up to 1100 models, 5200 tiles, WAD up to 6.9 MB + (originals: TRAK ≤ 1 MB, ≤ 5800 tiles, WAD ≤ 6.85 MB). Test world: 628 models, + 2474 tiles, TRAK 646 KB, WAD 6.3 MB. + +## Exit (LevelDoor) – how it works + +Strat bytecode (village, STPC 0x430332, 3 parameters, invisible, no model), decoded via the +VM `FUN_0054d180` (opcode < 0x45: table 0x5796bc with 16-bit operand, otherwise 0x578ff4+op·4): +``` +g65,g66,g67 = param 0,1,2 ; tribe, world, chapter of the next level (20.12) +loop: if g21 == 3 ; game state "running" + and player.floor.material == 16 ; property 0x11 = *(strat+0xc0)+4 = material byte of the + ; collision polygon below the player (FUN_00403020) + and floor(player.x) == floor(door.x) and floor(player.z) == floor(door.z) ; same grid cell + -> op 0x192 (FUN_0054d620): level change(param), g1 += 1, end + yield +``` +* So the door is only a trigger; in the village there is a floor polygon with + **material 16** in the doorway (8 of them in the village, also in all other levels). Our test world had + material 0 everywhere → nothing happened. **Fix:** mosaic in the gate with `mat=16` (`box(..., mat=TUER_MAT)`), + door in the same grid cell, the player's path leads right through it. +* **Only tiles of the door cell count** (cell list, FUN_004036d0), not the height (t1l3m002–4: floor + 3 units below the door). **Village 1 & co.:** the door cell initially contains *no* material 16 – + it is in the **swap model** of the tile (MAP tile swap, WFPC 0x10000): the RubyIdolDoor in + the same cell swaps tile 420 from model 48 to 49 when opening (op 0x11a/0x11b), and only + model 49 has the exit pad (42.25–42.75 / 49.23–49.50, y 3.25). Checked in game + (2026-11-07): model 49 inserted + door removed, llama at 42.5/3.5/49.37 → immediately the summary + "Dorf Kapitel 1"; original (door closed) → nothing. Affects 8 of the 31 original doors (Ruby, + PressurePad, Lab doors); the pad at 43.5/48.45 (cell 43,48), on the other hand, is a house entrance + (zone teleport). t1l3m003: 2 of the 4 doors (cells 71,48/71,49) have no pad at all. +* Second path (strat code at 0x4252aa, player logic [likely]): floor material 16 and cell = + g104/g105 (set by the LevelDoor at start, Llama sets them to −1) → level change(g101..g103). +* Parameters in the originals: (1, world, chapter+1) = next chapter; in the last chapter of a + world **(7,7,7)** = "world completed" → back to the selection map, unlock the next world. +* `FUN_0054d620`: remembers the current level (0x585008..14), sets the next (0x584eb4/b8/bc), shows the + summary (coins x/55, secrets) with "EINGABETASTE = speichern / ESC = weiter" (Enter = save / Esc = continue). Progress + 0x585018/19 (max. world/chapter, in KSV +0x42/+0x43): if you are in the highest unlocked chapter, + the next chapter is unlocked; with (7,7,7) the next world. +* **(7,7,7) in world 9 crashes**: unlocks "world 10" → after saving, the + map cursor is on world 10, world name pointer 0x57bd40[9] = NULL → page fault at 0x41B5CC. +* **Solution for the test world: parameters (1,9,1)** – after the summary chapter 1 is reloaded (no + crash, tested with saving). Side effect: chapter 2 is unlocked (and from + chapter n, chapter n+1). If its WAD is missing, the selection crashes ("Couldnt load wad"). + That is why `levelbuild.py --install` (function `kapitel_links()`) creates in the mod + **symlinks T1L9M002..049.wad → T1L9M001.wad and T192..T1949.COR → T191.COR** (world 9 has + 49 chapter slots, table 0x572420) and calls `lama-mod an`. For this, `lama-mod` now also takes + symlinks from mod folders (`find … -type f -o -type l`). Checked: chapter 2 on the map is + selectable and loads the test world. +* The selection map shows no background for world 9 (only text) and 772 as the coin target + (coin table 0x5722f4 only has worlds 1–8) – cosmetic. + +## The test world (`levels/testwelt.py` → `out/testwelt.wad`, installed as T1L9M001.wad) +**Rebuilt compactly on 2026-11-06** ("everything closer together"): L-shaped island, main part 32.5×23.5 +(x 11..43.5, z 9.5..33) + strip below the parkour end (x 43.5..49, z 9.5..20.5), previously +42×34 (x 10..52, z 8..42). Gallery, enclosure and parkour adjoin each other directly, no empty +meadows any more. NAME chunk "Testwelt" (not shown anywhere in the game). +Preview images (phone, 1280 px): `notes/img/vorschau/` (01–03 Blender renderings with +labels, 04–11 game shots, 12 montage). Older images `notes/img/testwelt_*.jpg` still show +the old, sprawling layout. + +| Area | old | new | +|---|---|---| +| Start square | x 14..22, z 11..19 (8×8) | x 15..21, z 12..19 (6×7) | +| Gallery | z 22..38, columns 2.5, rows z 25/28.5/32/35.5 | x 14.5..32.5, z 20.5..32, columns 1.9, rows z 22.5/25.2/27.9/30.6 (spacing 2.7) | +| Enclosure | x 39..49, z 24..35 | x 33..42, z 20.8..30 (directly east of the gallery) | +| Parkour | x 22..53.6 | x 21..52.6 (only shifted by −1 in x, same dimensions) | +| Exit | LevelDoor (51.5/15.5) | LevelDoor (50.5/15.5), cell 50/15, mosaic with material 16 | + +* **Start square** (x 15..21, z 12..19): stone square with Inca mosaic, 4 Inca pillars, flower beds, + trees, 2 coins, checkpoint (16.1/17.4). The llama starts at (18, 14.6) facing south + (gallery directly behind it, parkour stairs on the left of the picture). +* **Gallery** (z 20.5..32): 4 rows, each object on a flat plinth (0.9 wide, 0.08 high, + walkable), gap 1.0 between plinths, sand path (0.6) in front of each row, coloured post at the + start of the row (yellow/grey/blue/red), main path north–south at x 15.6..16.3: + 1. Collectables: Coin, ChargeBooster, RubyIdol, HealthFull, HealthGenerator, ChacaChargePotion, Secret + 2. Crates/pots: SmashPot (2 variants), Jar, ObjectDontDropInBox, WaterReboundPoints, CheatTeleport + 3. Characters: TipoVillage, ChacaVillage, PachaGivingInfo, KidOnLlama, Wompy + 4. Mechanics: PressurePadDoor, RubyIdolDoor, SwitchPlatformPadVillage, AppearingPlatformsVillage, + RisingColumns, SinkingStatue, FallingTree, CheckPointPost +* **Enclosure** (x 33..42, z 20.8..30, fence, entrance on the west side z 20.8..24): Bunny ×2, Ladybird, + FlockOfBirds, VillagerHerder; viewing pedestal (0.6) with stairs on the west side between + gallery rows 2 and 3. +* **Parkour** (starts at the east edge of the square, **everything on the line z = 15.5 going east**, + so it can be done without rotating the camera – rotating via A/S or Left/Right was not + reproducible in scripts). Geometry is written in the script as "old x + PX" (PX = −1): + stairs 0→0.8 · **stop step** +0.3 (no gap: the llama runs into it and stands still) · + 2 gaps of 0.3 with +0.2 each onto 1.5 long wooden blocks · beam 0.5 wide · middle platform (1.5) with + checkpoint · sand ramp 1.5→2.4 · **stop step** +0.3 · 3 stepping stones (1.5 long, gaps 0.3, + +0.2 each) · narrow beam 0.45 · stairs 3.3→4.0 · goal platform (4.0; x 47.6..52.6, juts out over + the coast) with pillars, parapet and gate; below it a mosaic with material 16, LevelDoor at + (50.5 / 15.5). 18 coins along the way. Below the parkour there is meadow (falling down is harmless). +* **Scripted run** (start → goal → exit → summary), old version 4/4, compact version 2/2 + runs successful (summary 18/55 coins); the .seq is time-based/relative and stayed unchanged: + ``` + python3 tools/levelbuild.py levels/testwelt.py out/testwelt_parkour.wad --set start=parkour + lamatest.py run out/testwelt_parkour.wad OUTDIR --seq "$(cat levels/testwelt_parkour.seq)" + ``` + (`run:T:x@t,…` holds Up for T s and presses X at fixed times; the stop steps reset the + timing error. Shots (`shot@`) block ~0.4 s → never shortly before a jump.) +* Checked in game: loading without a crash (many runs), all 31 gallery objects appear + (coin/secret/… react: coin counter, "GEHEIMNIS!"), the llama walks through gallery/enclosure, + up the stairs, jumps onto platform B and onto stepping stone 1, up the ramp, stairs to the + goal platform, sea = respawn, complete parkour by script, exit → summary → chapter 1 again. + Images: current `notes/img/vorschau/` (compact layout); `notes/img/testwelt_*.jpg` = + old sprawling layout (1–6 + montage even still with the old parkour). +* Start point for tests is switchable: `--set start=platz|reihe1..4|weg1..4|gehege|podest|parkour|pk1..pk5|ziel|ziel2` + (`podest` = on the viewing pedestal, looking west over the gallery); + exit parameters `--set tuer=1,9,1`. + +### Village types not placed +| Type | Reason | +|---|---| +| Llama | is the player (start) | +| LevelDoor | exit at the end of the parkour (needs a floor with material 16, see above) | +| CameraSide | camera zone, invisible | +| CutSceneCameraDirectional | cutscene camera, invisible | +| CutSceneTrigger | starts a cutscene | +No placeable type crashed the game (no bisection needed). +Note: mechanics objects (doors, pillars, statue) belong to specific geometry / +tile swaps in the village; in the gallery they stand "bare" (e.g. a floating RubyIdolDoor sign). +Types that only exist in the code (BouncingCoin, Sparkles, RubyIdolStatue …) have no +original instance with type fields and are not offered (exception: RubyIdol via `Strats.BORROWED`). + +## Preview renderings (Blender) +`/usr/bin/blender` (Debian 3.0.1): **Cycles renders only black** (even the default scene), OCIO +is missing ("Color management: fallback", only the "Standard" view). **Eevee works**, but needs an +X display → own `Xvfb :7` (never :0, :5 belongs to lamatest). Procedure (scripts in +`tools/vorschau/`: `strats_json.py` (call from levelbau/), `render_level.py`, `label.py`): +1. `geom_export.py out/testwelt.wad GE` → level.obj/mtl + page_NN.png +2. Strat positions: run the level script with `levelbuild.Level` (without `L.build`), `L.strats` as JSON +3. `DISPLAY=:7 STRATS=strats.json VIEWS=schraeg,schraeg2,oben blender -b -P render_level.py -- GE r 64`: + own OBJ reader (the 3.0 importer does not know vertex colours), OBJ (= game space since + 2026-11-07) → Blender z up (y_blender = −game z, winding kept), material = texture (closest) × vertex colour, a little emission, + sun + sky, markers for strats (coins gold, llama start orange cone, gallery objects + in row colour, animals white, exit purple ring); writes PNG + JSON per view with + image coordinates of the area names. +4. PIL: scale to 1280 px, labels (Start/Galerie/Gehege/Parkour/Ziel), JPG q85. diff --git a/notes/llamamod-app.md b/notes/llamamod-app.md index 1d96ad8..ab8e7ed 100644 --- a/notes/llamamod-app.md +++ b/notes/llamamod-app.md @@ -313,7 +313,9 @@ Decoded from FUN_0041c640 (load) / FUN_0041c820 (save); details in `save/Ksv.jav | 0xD0–0xD9 | options: effects volume, music volume, 0x80, speaker mode, 6 more option bytes | Chapters per world (table 0x572420): 4, 3, 4, 3, 3, 3, 5, 5, 49. World 9 has no row in the -per-chapter arrays. The KSV files shipped on the CD are empty (0x00 then 0xCC). Selecting an empty +per-chapter arrays. Its coin counter/flag byte for chapters 2–31 land on other globals (chapter 9 = +furthest progress): mods use only chapters 1 and 32–49 (`save/TestLevelChapters`, +[testlevel-chapters.md](testlevel-chapters.md)). The KSV files shipped on the CD are empty (0x00 then 0xCC). Selecting an empty slot and "new game" immediately writes a zeroed file. ## Tests done (2026-11-06) diff --git a/notes/mod-arena.md b/notes/mod-arena.md index b074e49..cc5ca7b 100644 --- a/notes/mod-arena.md +++ b/notes/mod-arena.md @@ -11,11 +11,12 @@ Registered in `ModRegistry` (one line). Test-only: `ArenaTestMod` (src/test, scr | `waves` | 3 (1–5) | wave n has min(n, 2) alligators (3 waves = 5 alligators) | | `energy` | 4 (1–5) | llama energy cap (5 = game normal) | | `coins` | 1 (0–5) | EnemyCoins per hit (+5 on defeat, +5 bonus at the exit) | -| `chapter` | 8 (2–8) | TestLevel chapter; a chapter already written by another mod → apply error | +| `chapter` | 49 (32–49) | TestLevel chapter; a chapter already written by another mod → apply error; a stored 2–8 falls back to 49 | -Chapter ≤ 8 on purpose: `levelRecord` (coin counter) lives at `0x584F1C[world·6 + chapter]`; for -world 9 chapter 9 that is 0x585018 = furthest progress (unverified risk for level projects on -chapters ≥ 9, see "Issues"). +Only chapters 32–49: the coin counter `0x584F1C[world·6 + chapter]` and the flag byte +`0x584EC8[…]` of world-9 chapters 2–31 overwrite other globals (chapter 8 = last played map, +chapter 9 = furthest progress – verified, see [testlevel-chapters.md](testlevel-chapters.md)). +The tally total (LNFO) is 2 placed coins + alligators · ((lives − 1) · coins + 5) + 5 bonus. Script options (`Arena`, tests/`ArenaTestMod`): `probe=true` (event probe 1.6 ahead of the llama), `master=false` (no alligators), `start=x,z,rot`, `debug=true` (HUD coin counter shows @@ -72,7 +73,9 @@ per type (nVars/slots/typeFlags/extra), then `LevelBuilder` builds the level. * `spawn` copies the strat's matrix/translation block, not `x/y/z`: after `self.x = …` the child appears at the old place unless the spawner yields first. * Energy 0: the game's "Weiter / Beenden" screen; Weiter restarts the level (all strats fresh). -* The level tally after the exit shows "Münzen gesammelt 0 / 55" (not our coin counter). +* The level tally after the exit showed "Münzen gesammelt 0 / 55" on chapter 8: 0 = the coin slot + after endLevel wrote the last played map into it, 55 = LNFO of the Village base. Fixed: chapter + 32–49 and recounted LNFO ("2 / 12" for 1 alligator with 1 life). ## Verified in game (apptest :24, Wine 6, load 3–9, 2026-11-07) @@ -101,7 +104,6 @@ mod's chapter → IOException; probe option. ## Issues / limits -* Loading picture and tally use the TestLevel texts ("TestLevel Kapitel 8"). +* Loading picture and tally use the TestLevel texts ("TestLevel Kapitel 49"). * The exit target is the arena itself (1, 9, chapter): it restarts after the tally. -* Level projects on TestLevel chapters ≥ 9 may write the coin counter over furthest progress - (0x585018) – not verified. +* Exiting chapter 49 unlocks "chapter 50" (progress 9/50); the map caps the cursor at 49 (checked). diff --git a/notes/strat-opcodes.md b/notes/strat-opcodes.md index 0fd3d0e..939ffd7 100644 --- a/notes/strat-opcodes.md +++ b/notes/strat-opcodes.md @@ -35,6 +35,16 @@ properties). Language/syntax: `notes/strat-language.md`. Disassembler/assembler: getter FUN_00550e60 and setter FUN_005509f0 share the numbering; ids without a setter are read-only (a write would go to address 0). +## Spawning and collision (engine facts, found by the arena, 2026-11-07) + +* `spawn` (0xe0, FUN_0054bfc0) takes the same fields as a MAP + strat record: params = MAP parameter count, vars = local variables, slots, typeFlags = MAP type flags + (collision setup), `extra` = words of the collision-bone area. +* `spawn` copies the spawner's matrix/translation block (+0x20…), not its `x/y/z` properties: after + `self.x = …` the child still appears at the old place. Set the position, **yield**, then spawn. +* A MAP-placed type that uses `collisionOn` needs collision type flags in its MAP record (0x28000 like + Coin); with typeFlags 0 the game crashes (page fault in the heap allocator FUN_0041ae20). + ## Sources * groove.exe handler tables, decompiled with Ghidra 12.2 (headless, all 0x1b2 + 0x45 entries and @@ -642,7 +652,7 @@ ops = operands (inline words for long ops, the 16-bit argument for short ops); p | 143 | `camScale` | yes | yes | low | DAT_006D9D30, camera value initialised to 1.0 (zoom/fov factor?). | | 144 | `levelHeaderC` | yes | no | low | Level header (DAT_00584648)+0x0C ushort << 12. | | 145 | `slopeSteepness` | yes | no | medium | 1.0 - floor normal y (DAT_0057DDA4) << 12: 0 on flat ground (FUN_004054c0). | -| 147 | `levelTableByte` | yes | no | low | Byte table 0x585024[map + (chapter + tribe*10)*4] << 12 (per-level constant). | +| 147 | `levelTableByte` | yes | no | low | Byte table 0x585024[map + (world + tribe*10)*4] << 12 (FUN_0041af40(tribe, world, map)); bss, never written by the game (always 0) – but world-9 coin counters of TestLevel chapters 22–31 land in its tribe-1 part (see testlevel-chapters.md). | | 149 | `flagEC_1` | yes | no | low | Flag +0xEC & 0x1. | | 150 | `flagEC_8000000` | yes | no | low | Flag +0xEC & 0x8000000. | | 151 | `cheat5` | yes | no | medium | Cheat toggle bits DAT_00584710 & 0x20 (button-sequence cheats, FUN_0041a0xx). | @@ -660,10 +670,10 @@ ops = operands (inline words for long ops, the 16-bit argument for short ops); p | 165 | `instanceData1` | yes | yes | low | Map-instance record (+0xF0)+0x28 -> word 1. | | 166 | `floorCollisionOn` | yes | no | medium | Flag +0xEC & 0x8000 (floor collision, ops 0xa5/0xa6). | | 169 | `lives` | yes | yes | low | DAT_00584E6C (fixed, starts at 5.0, saved per slot) - lives or max health. | -| 171 | `saveProgressA` | yes | no | low | DAT_00585008 << 12 (saved progress byte). | -| 172 | `progressWorld` | yes | no | low | DAT_0058500C << 12 (saved; world of the furthest/selected level, 8 = finale). | -| 173 | `progressChapter` | yes | no | low | DAT_00585010 << 12 (saved; chapter, 6 with world 8 = last level). | -| 174 | `saveProgressB` | yes | no | low | DAT_00585014 << 12 (saved progress byte). | +| 171 | `saveProgressA` | yes | no | low | DAT_00585008 << 12: last played tribe (KSV +0x3E, set by endLevel). | +| 172 | `progressWorld` | yes | no | low | DAT_0058500C << 12: last played world (KSV +0x3F, set by endLevel; 8 = finale). | +| 173 | `progressChapter` | yes | no | low | DAT_00585010 << 12: last played chapter (KSV +0x40; 6 with world 8 = last level). | +| 174 | `saveProgressB` | yes | no | low | DAT_00585014 << 12: last played map (KSV +0x41). | | 175 | `unk_6da308` | yes | no | low | DAT_006DA308, no other reference. | | 178 | `cheat4` | yes | no | medium | DAT_00584710 & 0x10 (cheat toggle). | | 179 | `hudFlags` | yes | yes | medium | DAT_006DA2E0 HUD element enable mask (default 0x3F000; 0x2000 timer, 0x8000/0x10000/0x20000/0x40000 counters). | @@ -676,7 +686,7 @@ ops = operands (inline words for long ops, the 16-bit argument for short ops); p | 187 | `hitDamage` | yes | yes | medium | +0x140: damage dealt on contact (copied into the other strat's +0x144). | | 188 | `damageTaken` | yes | no | medium | +0x144: damage received from the last contact (player cleared to 0 each frame). | | 189 | `health2` | yes | yes | medium | DAT_00584E70 = health (same as 92, settable). | -| 190 | `levelRecord` | yes | yes | low | Per-level saved word DAT_00584F1C[chapter + world*6] (best/collected value for the current level; HUD shows it). | +| 190 | `levelRecord` | yes | yes | medium | Coin counter of the current level: u32 DAT_00584F1C[chapter + world*6] (coins << 12; HUD and tally show it, zeroed on level start, KSV keeps worlds 0–8 as u16). World 9 has no row: TestLevel chapter c hits 0x584FF4 + 4c (chapter 9 = furthest progress 0x585018) – only chapters 1 and 32–49 are free (testlevel-chapters.md). | | 191 | `counterC` | yes | yes | low | DAT_00584FFC: persistent HUD counter (icon 0x150), saved. | | 192 | `counterA` | yes | yes | low | DAT_00584FF4: persistent HUD counter (icon 0x184, displayed up to 999) - probably coins. | | 201 | `distToPlayerSq` | yes | no | high | +0x148: cached squared distance to the player ((d>>6)^2 sum), updated each frame when tracking (op 0x30). | diff --git a/notes/testlevel-chapters.md b/notes/testlevel-chapters.md new file mode 100644 index 0000000..4865139 --- /dev/null +++ b/notes/testlevel-chapters.md @@ -0,0 +1,75 @@ +# TestLevel chapters: which ones are safe (2026-11-07) + +World 9 ("TestLevel") has 49 chapter slots (table 0x572420), but the game's per-chapter arrays only +have rows for worlds 0–8. Indexing them with world 9 runs into other globals. Code: +`save/TestLevelChapters` (usable = chapter **1** and **32–49**). + +## The arrays (index world·6 + chapter) + +| Array | Where it is used | +|---|---| +| u32 coins 0x584F1C (coins << 12) | `levelRecord` (strat var 0xBE) get/set, HUD (FUN_00426bf0), tally (FUN_0040dd60), endLevel (FUN_0054d620: "all coins" check), **zeroed on every level start** (FUN_0041cba0), KSV keeps 54 entries as u16 | +| u8 flags 0x584EC8 (bit 0 collectible, bit 1 gold coin) | collectible picked up (HUD, strat var 0xFA rising) → \|=1; endLevel → \|=2 when coins == static total 0x5722F4[world·5 + chapter] (garbage for world 9, often 0) or cheat 0x584710&8 → \|=3; gold count FUN_0041cd70 (worlds 1–8 only) | + +Coin total table 0x5722F4 and the selection map only know worlds 1–8 (map shows garbage targets +for world 9 – cosmetic). + +## What world-9 chapter c hits + +Coin slot 0x584FF4 + 4c, flag byte 0x584EFE + c (Ghidra references 0x584EF0–0x585240): + +| c | coin slot | flag byte | +|---|---|---| +| 0 | 0x584FF4 counter var 0xC0 (HUD) | free | +| **1** | 0x584FF8 free (only cleared by new game) | free | +| 2 | 0x584FFC counter var 0xBF (HUD icon 0x150) | free | +| 3 | 0x585000 HUD flag (FUN_00427c40) | free | +| 4 | 0x585004 var 0x80 | free | +| 5–8 | 0x585008–14 last played tribe/world/chapter/map (strat vars 0xAB–0xAE, KSV +0x3E–41) | free | +| 6–9 | | 0x584F04 **language index** (u32; \|=2 → text table out of range) | +| 9 | 0x585018 **furthest progress** (world, chapter bytes; KSV +0x42/43) | | +| 10–13 | 0x58501C/20 free | 0x584F08 option (KSV +0xD6) | +| 12 | 0x585024 start of byte table (var 0x93, tribe 0) | | +| 14–21 | 0x585028–48 free | 0x584F0C / 0x584F10 (var 0x5B) | +| 22–25 | 0x58504C–58 table 0x585024, tribe 1 (read by var 0x93 in original levels) | free | +| 26–29 | table, tribe 1 | 0x584F18 option (KSV +0xD8) | +| 30–31 | table, tribe 1 (world 8/9) | coin slots of world 0 (never read) | +| **32–49** | table, tribes 2/3 (never used: tribe is 1, 4 only in the attract demo) | coin slots of world 0 | + +## Verified in game (apptest :24, 2026-11-07) + +* **Chapter 9** (arena test mod, 1 alligator, 2 coins collected): pause → "Beenden und zur + Auswahlkarte" → the selection map shows world "°p", "Kapitel 0", coin target 14384: furthest + progress 0x585018 was replaced by the coin counter (2 << 12 → world 0, chapter 32). + ![chapter 9: broken map](img/mods/testlevel_ch9_map_broken.jpg) +* **Chapter 49** (same run): tally "Münzen gesammelt 2 / 12, Geheimnisse 0 / 0" (LNFO recounted), + save written with progress (9, 50) – the map caps the cursor at chapter 49, no crash; map fine. + ![chapter 49 tally](img/mods/testlevel_ch49_tally.jpg) +* Before (arena on chapter 8, arena agent): tally "0 / 55": the collected value is the coin slot + after endLevel copied the level ids into 0x585008–14 (chapter 8 = last played map = 0), the total + is LNFO of the Village base. + +## Tally totals (LNFO) + +Chunk LNFO = u32 coins, u32 secrets → 0x5FF070 / 0x5FF060 (FUN_0042AB10; the tally shows the values +of the level just finished, kept in 0x5FF064 / 0x5FF06C). Originals: e.g. t1l3m004 40 = 20 Coin + +4 Coin5·5; levels with CoinLine/CoinCircle/enemies count those too. `mods/levels/LevelTally` recounts +Coin (1) + Coin5 (5) and Secret strats for new level projects and the Test World (20 coins, 0 +secrets); the arena adds its dynamic coins (alligator hit/death coins + 5 exit bonus). + +## Migration + +* Level projects: stored chapter 1 or 32–49 kept (first file wins), others get the first free of + 32–49; more than 18 projects share chapters (only one of them can be on – apply error otherwise). + Play-tests/exports of an old project: `migrate` (2..19 → 32..49, 20..31 → 32..43). +* The chapter options are choices (level: 1, 32–49; arena: 32–49, default 49); a stored old value is + invalid and falls back to the default (ModOptions). +* Editor: New level / Level settings spinners only offer usable chapters. + +![chapter option](img/mods/testlevel_chapter_option.jpg) + +## Not fixed + +* The map lets the player pick any unlocked world-9 chapter; empty ones are linked to an installed + world-9 WAD (ModManager), so playing e.g. chapter 9 from the map still clobbers progress. Unlocking + chapter 32+ unlocks 2–31 on the map too. diff --git a/src/main/java/com/eliasmueller/llamamod/editor/tools/environment/LevelSettingsDialog.java b/src/main/java/com/eliasmueller/llamamod/editor/tools/environment/LevelSettingsDialog.java index 2988e8f..00612b2 100644 --- a/src/main/java/com/eliasmueller/llamamod/editor/tools/environment/LevelSettingsDialog.java +++ b/src/main/java/com/eliasmueller/llamamod/editor/tools/environment/LevelSettingsDialog.java @@ -17,6 +17,7 @@ import com.eliasmueller.llamamod.editor.model.cmd.Command; import com.eliasmueller.llamamod.editor.model.cmd.SetProp; import com.eliasmueller.llamamod.editor.ui.EditorContext; import com.eliasmueller.llamamod.mods.levels.LevelProjects; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; @@ -38,6 +39,7 @@ import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.SpinnerListModel; /** Level → Settings: name, chapter, grid size, death plane, WFPC flags, object pool (one undo step). */ final class LevelSettingsDialog { @@ -80,9 +82,13 @@ final class LevelSettingsDialog { "Base level", new JLabel(st.base() + " (object code, textures, sounds)"), "Fixed when the project is created (the project stores only the differences to it)"); - JSpinner chapter = Ui.number(Math.max(1, st.targetChapter()), 1, 49, 1, "0", v -> {}); + JSpinner chapter = new JSpinner(new SpinnerListModel(TestLevelChapters.all())); + chapter.setValue(TestLevelChapters.migrate(st.targetChapter())); JLabel used = new JLabel(usedChapters(ctx, st.targetChapter())); - f.row("TestLevel chapter", chapter, "New levels replace this chapter of world 9 (TestLevel)"); + f.row( + "TestLevel chapter", + chapter, + "New levels replace this chapter of world 9 (TestLevel): 32–49, or 1 (the Test World's)"); f.full(used); chapter.setEnabled(st.isNewLevel()); diff --git a/src/main/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestRunner.java b/src/main/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestRunner.java index 6584c26..c56d911 100644 --- a/src/main/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestRunner.java +++ b/src/main/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestRunner.java @@ -17,6 +17,7 @@ import com.eliasmueller.llamamod.platform.Config; import com.eliasmueller.llamamod.platform.GameLauncher; import com.eliasmueller.llamamod.platform.Json; import com.eliasmueller.llamamod.platform.Progress; +import com.eliasmueller.llamamod.save.TestLevelChapters; import com.eliasmueller.llamamod.save.Worlds; import java.io.IOException; import java.nio.file.Files; @@ -50,7 +51,7 @@ public final class PlaytestRunner { /** World/chapter of a project: TestLevel chapter, or the original's place; null if not on the map. */ public static int[] target(Document d) { if (d.isNewLevel()) { - return new int[] {9, Math.max(1, d.scene().settings().targetChapter())}; + return new int[] {9, TestLevelChapters.migrate(d.scene().settings().targetChapter())}; } Matcher m = Pattern.compile("t1l(\\d)m(\\d{3})").matcher(d.base().toLowerCase(Locale.ROOT)); if (m.matches()) { diff --git a/src/main/java/com/eliasmueller/llamamod/editor/ui/NewLevelDialog.java b/src/main/java/com/eliasmueller/llamamod/editor/ui/NewLevelDialog.java index 9723cb6..3e56d91 100644 --- a/src/main/java/com/eliasmueller/llamamod/editor/ui/NewLevelDialog.java +++ b/src/main/java/com/eliasmueller/llamamod/editor/ui/NewLevelDialog.java @@ -4,6 +4,7 @@ import com.eliasmueller.llamamod.editor.io.ProjectIO; import com.eliasmueller.llamamod.editor.io.ProjectInfo; import com.eliasmueller.llamamod.editor.io.Templates; import com.eliasmueller.llamamod.mods.levels.LevelProjects; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; @@ -28,7 +29,7 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.JTextField; -import javax.swing.SpinnerNumberModel; +import javax.swing.SpinnerListModel; import javax.swing.UIManager; /** New level: name, base level (object types, textures, sky, music), template, TestLevel chapter. */ @@ -51,7 +52,8 @@ final class NewLevelDialog extends JDialog { base.setSelectedItem(l); } } - JSpinner chapter = new JSpinner(new SpinnerNumberModel(nextFreeChapter(w), 1, 49, 1)); + JSpinner chapter = new JSpinner(new SpinnerListModel(TestLevelChapters.all())); + chapter.setValue(nextFreeChapter(w)); PicturePanel pic = new PicturePanel(300, 170); Runnable updatePic = () -> { OriginalLevels.Level l = (OriginalLevels.Level) base.getSelectedItem(); @@ -75,7 +77,8 @@ final class NewLevelDialog extends JDialog { row, "TestLevel chapter", chapter, - "The level replaces this chapter of world 9 (TestLevel) when it is enabled"); + "The level replaces this chapter of world 9 (TestLevel) when it is enabled (32–49; 1 is the Test" + + " World's)"); JPanel templates = new JPanel(); templates.setLayout(new BoxLayout(templates, BoxLayout.Y_AXIS)); ButtonGroup g = new ButtonGroup(); @@ -161,7 +164,7 @@ final class NewLevelDialog extends JDialog { return row + 1; } - /** First TestLevel chapter from 2 that no project in the levels folder uses. */ + /** First usable TestLevel chapter from 32 that no project in the levels folder uses. */ static int nextFreeChapter(EditorWindow w) { Set used = new HashSet<>(); for (Path p : LevelProjects.list(w.env().levelsDir())) { @@ -174,11 +177,7 @@ final class NewLevelDialog extends JDialog { // skip broken files } } - int c = 2; - while (used.contains(c) && c < 49) { - c++; - } - return c; + return TestLevelChapters.pick(TestLevelChapters.FIRST, used); } Result result() { diff --git a/src/main/java/com/eliasmueller/llamamod/editor/ui/ProjectController.java b/src/main/java/com/eliasmueller/llamamod/editor/ui/ProjectController.java index c737fec..44e454c 100644 --- a/src/main/java/com/eliasmueller/llamamod/editor/ui/ProjectController.java +++ b/src/main/java/com/eliasmueller/llamamod/editor/ui/ProjectController.java @@ -15,6 +15,7 @@ import com.eliasmueller.llamamod.editor.model.cmd.AddNode; import com.eliasmueller.llamamod.editor.model.cmd.Batch; import com.eliasmueller.llamamod.editor.model.cmd.Command; import com.eliasmueller.llamamod.platform.Json; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; @@ -592,7 +593,7 @@ public final class ProjectController { ? String.format( Locale.ROOT, "T1L9M%03d", - Math.max(1, d.scene().settings().targetChapter())) + TestLevelChapters.migrate(d.scene().settings().targetChapter())) : d.base(); fc.setSelectedFile(new File(fc.getCurrentDirectory(), stem + ".wad")); if (fc.showSaveDialog(w) != JFileChooser.APPROVE_OPTION) { diff --git a/src/main/java/com/eliasmueller/llamamod/format/stpcmerge/TextMerge.java b/src/main/java/com/eliasmueller/llamamod/format/stpcmerge/TextMerge.java index 1842644..907bf99 100644 --- a/src/main/java/com/eliasmueller/llamamod/format/stpcmerge/TextMerge.java +++ b/src/main/java/com/eliasmueller/llamamod/format/stpcmerge/TextMerge.java @@ -1,6 +1,8 @@ package com.eliasmueller.llamamod.format.stpcmerge; import com.eliasmueller.llamamod.format.TextChunk; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -87,6 +89,41 @@ public final class TextMerge { } } + /** + * TEXT chunk {@code orig} with the pages, entries and animations of {@code tex} (usually + * {@code Textures.of(orig)} extended by {@link #merge}); palettes and the rest are kept. + */ + public static byte[] rebuild(byte[] orig, Textures tex) { + Textures old = Textures.of(orig); + int head = 8 + 8 * old.entries.size(); + for (Anim a : old.anims) { + head += 3 + a.data().length; + } + TextChunk tc = TextChunk.parse(orig); + byte[] rest = Arrays.copyOfRange(tc.tail, head, tc.tail.length); + ByteBuffer b = ByteBuffer.allocate(8 + 8 * tex.entries.size() + tex.anims.size() * 64 + rest.length + 1024) + .order(ByteOrder.LITTLE_ENDIAN); + b.putInt(tex.entries.size()); + for (TextChunk.TextureEntry e : tex.entries) { + b.putShort((short) e.flags()) + .put((byte) e.page()) + .put((byte) e.page2()) + .put((byte) e.u0()) + .put((byte) e.u1()) + .put((byte) e.v0()) + .put((byte) e.v1()); + } + b.putInt(tex.anims.size()); + for (Anim a : tex.anims) { + b.putShort((short) a.texture()).put((byte) a.type()).put(a.data()); + } + b.put(rest); + tc.pages.clear(); + tc.pages.addAll(tex.pages); + tc.tail = Arrays.copyOf(b.array(), b.position()); + return tc.build(); + } + /** Result: map[srcIndex] = target index (−1 = not needed), counts of added items. */ public record Result(int[] map, int addedPages, int addedEntries, int addedAnims, int reusedEntries) {} diff --git a/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaImport.java b/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaImport.java index ec233e7..6014edd 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaImport.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaImport.java @@ -3,7 +3,6 @@ package com.eliasmueller.llamamod.mods.arena; import com.eliasmueller.llamamod.format.Bytes; import com.eliasmueller.llamamod.format.Level; import com.eliasmueller.llamamod.format.MapChunk; -import com.eliasmueller.llamamod.format.TextChunk; import com.eliasmueller.llamamod.format.Wad; import com.eliasmueller.llamamod.format.stpcmerge.DialogMerge; import com.eliasmueller.llamamod.format.stpcmerge.SoundMerge; @@ -106,10 +105,6 @@ final class ArenaImport { StpcMerge.Analysis tgt = new StpcMerge.Analysis(new StpcImage(wad.require("STPC"), lv.wfpc()), troots); byte[] text = wad.require("TEXT"); TextMerge.Textures tex = TextMerge.Textures.of(text); - int origTail = 8 + 8 * tex.entries.size(); - for (TextMerge.Anim a : tex.anims) { - origTail += 3 + a.data().length; - } List snd = SoundMerge.parse(wad.get("SMPC")); DialogMerge.Texts texts = wad.get("LGPC") == null ? null : DialogMerge.Texts.parse(wad.get("LGPC")); DialogMerge.Speech speech = DialogMerge.Speech.parse(wad.get("SRPC")); @@ -143,40 +138,13 @@ final class ArenaImport { Wad out = new Wad(wad.chunks()); out.set("STPC", res.stpc()); out.set("MAP", map.build()); - out.set("TEXT", text(text, tex, origTail)); + out.set("TEXT", TextMerge.rebuild(text, tex)); if (wad.get("SMPC") != null) { out.set("SMPC", SoundMerge.build(snd)); } return new Merged(out.toBytes(), codes, res.stats()); } - /** TEXT chunk with the (extended) pages, entries and animations of tex; the rest is kept. */ - private static byte[] text(byte[] orig, TextMerge.Textures tex, int origTailHead) { - TextChunk tc = TextChunk.parse(orig); - byte[] rest = Arrays.copyOfRange(tc.tail, origTailHead, tc.tail.length); - ByteBuffer b = ByteBuffer.allocate(8 + 8 * tex.entries.size() + tex.anims.size() * 64 + rest.length + 1024) - .order(ByteOrder.LITTLE_ENDIAN); - b.putInt(tex.entries.size()); - for (TextChunk.TextureEntry e : tex.entries) { - b.putShort((short) e.flags()) - .put((byte) e.page()) - .put((byte) e.page2()) - .put((byte) e.u0()) - .put((byte) e.u1()) - .put((byte) e.v0()) - .put((byte) e.v1()); - } - b.putInt(tex.anims.size()); - for (TextMerge.Anim a : tex.anims) { - b.putShort((short) a.texture()).put((byte) a.type()).put(a.data()); - } - b.put(rest); - tc.pages.clear(); - tc.pages.addAll(tex.pages); - tc.tail = Arrays.copyOf(b.array(), b.position()); - return tc.build(); - } - private static void putI32(byte[] d, int o, int v) { ByteBuffer.wrap(d, o, 4).order(ByteOrder.LITTLE_ENDIAN).putInt(v); } diff --git a/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaMod.java b/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaMod.java index c11f3cb..7c5030c 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaMod.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/arena/ArenaMod.java @@ -5,6 +5,8 @@ import com.eliasmueller.llamamod.levelbuild.LevelBuilder; import com.eliasmueller.llamamod.mods.Mod; import com.eliasmueller.llamamod.mods.ModContext; import com.eliasmueller.llamamod.mods.ModOption; +import com.eliasmueller.llamamod.mods.levels.LevelTally; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; @@ -53,7 +55,29 @@ public final class ArenaMod implements Mod { ModOption.integer( "energy", "Llama energy", "Energy cap of the llama in the arena (5 = normal).", 4, 1, 5), ModOption.integer("coins", "Coins per hit", "Coins that pop out per hit (5 more at the end).", 1, 0, 5), - ModOption.integer("chapter", "TestLevel chapter", "Chapter of world 9 the arena replaces.", 8, 2, 8)); + chapterOption()); + } + + static final int DEFAULT_CHAPTER = TestLevelChapters.LAST; + + /** TestLevel chapters 32–49 ({@link TestLevelChapters}); a stored 2–8 falls back to 49. */ + static ModOption chapterOption() { + ModOption.Choice[] cs = new ModOption.Choice[TestLevelChapters.LAST - TestLevelChapters.FIRST + 1]; + for (int c = TestLevelChapters.FIRST; c <= TestLevelChapters.LAST; c++) { + cs[c - TestLevelChapters.FIRST] = ModOption.of(Integer.toString(c), Integer.toString(c)); + } + return ModOption.choice( + "chapter", + "TestLevel chapter", + "Chapter of world 9 the arena replaces (32–49; chapter 1 is the Test World's).", + Integer.toString(DEFAULT_CHAPTER), + cs); + } + + /** Coins the arena can give beyond its placed ones: hit and death coins of every alligator, exit bonus. */ + static int dynamicCoins(ArenaScripts.Settings s) { + int perGator = (s.lives() - 1) * s.coinsPerHit() + 5; + return ArenaScripts.totalAlligators(s.waves()) * perGator + 5; } /** After Test World (50) and level projects (60), so a clash with them is reported here. */ @@ -95,7 +119,8 @@ public final class ArenaMod implements Mod { } ctx.progress(0.0, "building alligator arena"); byte[] base = ctx.read(LevelBuilder.BASE_WAD); - byte[] wad = new Arena(settings(ctx)) + ArenaScripts.Settings settings = settings(ctx); + byte[] wad = new Arena(settings) .buildWad( base, rel -> { @@ -106,7 +131,7 @@ public final class ArenaMod implements Mod { } }, scriptOpts); - ctx.write(target, wad); + ctx.write(target, LevelTally.counted(wad, dynamicCoins(settings))); ctx.progress(0.9, "loading picture"); ctx.write("T19" + chapter + ".COR", loadingPicture(chapter)); ctx.progress(1.0, "arena done"); diff --git a/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectMod.java b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectMod.java index fdd349d..ab3aed1 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectMod.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectMod.java @@ -10,6 +10,7 @@ import com.eliasmueller.llamamod.mods.ModContext; import com.eliasmueller.llamamod.mods.ModOption; import com.eliasmueller.llamamod.mods.boottolevel.BootToLevelMod; import com.eliasmueller.llamamod.mods.boottolevel.StartOverride; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -129,14 +130,22 @@ public final class LevelProjectMod implements Mod { if (!info.isNewLevel()) { return List.of(); } - return List.of(ModOption.integer( - "chapter", - "TestLevel chapter", - "Chapter of world 9 (TestLevel) the level replaces. Chapter 1 is also used by the Test World mod;" - + " each level needs its own chapter. Unlock it on the Save games tab.", - defaultChapter, - 1, - 49)); + return List.of(chapterOption( + "Chapter of world 9 (TestLevel) the level replaces: 32–49, or 1 (the Test World's chapter). Each" + + " level needs its own chapter. Unlock it on the Save games tab.", + defaultChapter)); + } + + /** + * TestLevel chapter choice: the usable chapters ({@link TestLevelChapters}); a stored value + * outside them (2–31 from older versions) falls back to {@code def}. + */ + private static ModOption chapterOption(String description, int def) { + ModOption.Choice[] cs = TestLevelChapters.all().stream() + .map(c -> ModOption.of( + Integer.toString(c), c == TestLevelChapters.TEST_WORLD ? "1 (Test World)" : "" + c)) + .toArray(ModOption.Choice[]::new); + return ModOption.choice("chapter", "TestLevel chapter", description, Integer.toString(def), cs); } /** CD path of the WAD this mod writes. */ @@ -160,7 +169,8 @@ public final class LevelProjectMod implements Mod { throw new IOException("Level \"" + name() + "\": " + target + " is already changed by another mod (" + (info.isNewLevel() ? "Test World or another level on TestLevel chapter " + chapter - + "). Choose a free chapter in the options of \"" + name() + "\"" + + "). Choose a free chapter in the options of \"" + name() + + "\" or turn one of them off" : "two mods change the same level). Turn one of them off") + "."); } @@ -174,6 +184,9 @@ public final class LevelProjectMod implements Mod { throw new IOException("Level \"" + name() + "\": " + bad + ". Move the Llama onto walkable ground in" + " the level editor."); } + if (info.isNewLevel()) { + wad = LevelTally.counted(wad); + } ctx.write(target, wad); if (info.isNewLevel()) { ctx.progress(0.9, "loading picture"); diff --git a/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectSource.java b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectSource.java index d46a51f..95f6d5b 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectSource.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelProjectSource.java @@ -5,18 +5,21 @@ import com.eliasmueller.llamamod.editor.io.ProjectInfo; import com.eliasmueller.llamamod.mods.Mod; import com.eliasmueller.llamamod.mods.ModSource; import com.eliasmueller.llamamod.platform.AppPaths; +import com.eliasmueller.llamamod.save.TestLevelChapters; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** * One mod per {@code .llamalevel} file in {@code /levels/} (category "levels"). New levels - * get a default TestLevel chapter: their stored target, or the next free one if another project - * already uses it. Temporary play-test copies in {@code levels/.playtest/} are listed too, with + * get a default TestLevel chapter: their stored target, or the next free usable one if another + * project already uses it or it is not usable ({@link TestLevelChapters}; old projects on 2–31). Temporary play-test copies in {@code levels/.playtest/} are listed too, with * ids {@code playtest-…}. */ public final class LevelProjectSource implements ModSource { @@ -32,57 +35,57 @@ public final class LevelProjectSource implements ModSource { @Override public List mods() { - List out = new ArrayList<>(); - Set used = new HashSet<>(); List levels = LevelProjects.list(dir); List tests = LevelProjects.list(LevelProjects.playtestDir(dir)); Set bases = new HashSet<>(); levels.forEach(p -> bases.add(LevelProjectMod.baseId(p, false))); tests.forEach(p -> bases.add(LevelProjectMod.baseId(p, true))); - Set ids = new HashSet<>(); - for (Path p : levels) { - add(p, false, used, bases, ids, out); + List all = new ArrayList<>(); + levels.forEach(p -> read(p, false, all)); + tests.forEach(p -> read(p, true, all)); + // usable stored chapters first (first file wins), so a moved old project never displaces them + Map chapters = new HashMap<>(); + Set used = new HashSet<>(); + for (Entry e : all) { + int c = e.info.targetChapter(); + if (e.info.isNewLevel() && !e.playtest && TestLevelChapters.usable(c) && used.add(c)) { + chapters.put(e, c); + } } - for (Path p : tests) { - add(p, true, used, bases, ids, out); + List out = new ArrayList<>(); + Set ids = new HashSet<>(); + for (Entry e : all) { + int chapter = 0; + if (e.info.isNewLevel() && e.playtest) { + chapter = TestLevelChapters.migrate(e.info.targetChapter()); + } else if (e.info.isNewLevel()) { + chapter = chapters.computeIfAbsent(e, k -> TestLevelChapters.pick(k.info.targetChapter(), used)); + used.add(chapter); + } + // names differing only in case or punctuation share an id: later ones get a suffix no other file has + String base = LevelProjectMod.baseId(e.file, e.playtest); + String id = base; + if (!ids.add(id)) { + int i = 2; + while (bases.contains(base + "-" + i) || ids.contains(base + "-" + i)) { + i++; + } + id = base + "-" + i; + ids.add(id); + } + out.add(new LevelProjectMod(e.file, e.data, e.info, chapter, id, e.playtest)); } return out; } - private static void add( - Path p, boolean playtest, Set used, Set bases, Set ids, List out) { - byte[] d; - ProjectInfo info; + private record Entry(Path file, boolean playtest, byte[] data, ProjectInfo info) {} + + private static void read(Path p, boolean playtest, List out) { try { - d = Files.readAllBytes(p); - info = ProjectIO.info(d); + byte[] d = Files.readAllBytes(p); + out.add(new Entry(p, playtest, d, ProjectIO.info(d))); } catch (IOException | RuntimeException e) { - return; + // unreadable or broken: not listed } - int chapter = 0; - if (info.isNewLevel() && playtest) { - chapter = Math.max(1, Math.min(49, info.targetChapter())); - } else if (info.isNewLevel()) { - chapter = info.targetChapter(); - if (chapter < 1 || chapter > 49 || used.contains(chapter)) { - chapter = 2; - while (used.contains(chapter) && chapter < 49) { - chapter++; - } - } - used.add(chapter); - } - // names differing only in case or punctuation share an id: later ones get a suffix no other file has - String base = LevelProjectMod.baseId(p, playtest); - String id = base; - if (!ids.add(id)) { - int i = 2; - while (bases.contains(base + "-" + i) || ids.contains(base + "-" + i)) { - i++; - } - id = base + "-" + i; - ids.add(id); - } - out.add(new LevelProjectMod(p, d, info, chapter, id, playtest)); } } diff --git a/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelTally.java b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelTally.java new file mode 100644 index 0000000..bf5be0c --- /dev/null +++ b/src/main/java/com/eliasmueller/llamamod/mods/levels/LevelTally.java @@ -0,0 +1,58 @@ +package com.eliasmueller.llamamod.mods.levels; + +import com.eliasmueller.llamamod.format.Level; +import com.eliasmueller.llamamod.format.MapChunk; +import com.eliasmueller.llamamod.format.Strats; +import com.eliasmueller.llamamod.format.Wad; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; +import java.util.Map; + +/** + * The level tally's totals ("Münzen gesammelt n / total", "Geheimnisse entdeckt n / total"): chunk + * LNFO = u32 coins, u32 secrets (→ 0x5ff070 / 0x5ff060, FUN_0042ab10). Levels built on a base keep + * the base's numbers (Village: 55, 2) unless they are recounted here. + */ +public final class LevelTally { + private LevelTally() {} + + /** + * Placed coins (Coin = 1, Coin5 = 5) and Secret strats of a level WAD. Other coin sources + * (CoinLine, CoinCircle, enemies' EnemyCoins …) are not counted. + */ + public static int[] count(byte[] wad) { + Level lv = Level.parse(wad); + Map> t = Strats.levelTypes(lv.wad().require("STPC"), lv.wfpc(), lv.map()); + int coins = n(t, "Coin") + 5 * n(t, "Coin5"); + return new int[] {coins, n(t, "Secret")}; + } + + private static int n(Map> t, String type) { + List l = t.get(type); + return l == null ? 0 : l.size(); + } + + /** {@code wad} with LNFO set to {@link #count} plus {@code extraCoins}; unchanged without LNFO. */ + public static byte[] counted(byte[] wad, int extraCoins) { + int[] c = count(wad); + return withTotals(wad, c[0] + extraCoins, c[1]); + } + + public static byte[] counted(byte[] wad) { + return counted(wad, 0); + } + + /** {@code wad} with LNFO = (coins, secrets); unchanged if it has no LNFO chunk. */ + public static byte[] withTotals(byte[] wad, int coins, int secrets) { + Wad w = Wad.parse(wad); + byte[] old = w.get("LNFO"); + if (old == null || old.length < 8) { + return wad; + } + byte[] ln = old.clone(); + ByteBuffer.wrap(ln).order(ByteOrder.LITTLE_ENDIAN).putInt(0, coins).putInt(4, secrets); + w.set("LNFO", ln); + return w.toBytes(); + } +} diff --git a/src/main/java/com/eliasmueller/llamamod/mods/testworld/Gallery.java b/src/main/java/com/eliasmueller/llamamod/mods/testworld/Gallery.java index 44e5590..63cedeb 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/testworld/Gallery.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/testworld/Gallery.java @@ -412,10 +412,6 @@ public final class Gallery { StpcMerge.Analysis tgt = new StpcMerge.Analysis(ti, roots); byte[] text = wad.require("TEXT"); TextMerge.Textures tex = TextMerge.Textures.of(text); - int origTail = 8 + 8 * tex.entries.size(); - for (TextMerge.Anim a : tex.anims) { - origTail += 3 + a.data().length; - } List snd = SoundMerge.parse(wad.get("SMPC")); DialogMerge.Texts texts = wad.get("LGPC") == null ? null : DialogMerge.Texts.parse(wad.get("LGPC")); DialogMerge.Speech speech = DialogMerge.Speech.parse(wad.get("SRPC")); @@ -463,34 +459,7 @@ public final class Gallery { Wad out = new Wad(wad.chunks()); out.set("STPC", res.stpc()); out.set("MAP", map.build()); - out.set("TEXT", text(text, tex, origTail)); + out.set("TEXT", TextMerge.rebuild(text, tex)); return new Merged(out.toBytes(), codes, res.stats()); } - - /** TEXT chunk with the (extended) pages, entries and animations of tex; the rest is kept. */ - private static byte[] text(byte[] orig, TextMerge.Textures tex, int origTailHead) { - TextChunk tc = TextChunk.parse(orig); - byte[] rest = Arrays.copyOfRange(tc.tail, origTailHead, tc.tail.length); - ByteBuffer b = ByteBuffer.allocate(8 + 8 * tex.entries.size() + tex.anims.size() * 64 + rest.length + 1024) - .order(java.nio.ByteOrder.LITTLE_ENDIAN); - b.putInt(tex.entries.size()); - for (TextChunk.TextureEntry e : tex.entries) { - b.putShort((short) e.flags()) - .put((byte) e.page()) - .put((byte) e.page2()) - .put((byte) e.u0()) - .put((byte) e.u1()) - .put((byte) e.v0()) - .put((byte) e.v1()); - } - b.putInt(tex.anims.size()); - for (TextMerge.Anim a : tex.anims) { - b.putShort((short) a.texture()).put((byte) a.type()).put(a.data()); - } - b.put(rest); - tc.pages.clear(); - tc.pages.addAll(tex.pages); - tc.tail = Arrays.copyOf(b.array(), b.position()); - return tc.build(); - } } diff --git a/src/main/java/com/eliasmueller/llamamod/mods/testworld/TestWorldMod.java b/src/main/java/com/eliasmueller/llamamod/mods/testworld/TestWorldMod.java index c900b22..9273c8a 100644 --- a/src/main/java/com/eliasmueller/llamamod/mods/testworld/TestWorldMod.java +++ b/src/main/java/com/eliasmueller/llamamod/mods/testworld/TestWorldMod.java @@ -4,6 +4,7 @@ import com.eliasmueller.llamamod.format.CorEncoder; import com.eliasmueller.llamamod.levelbuild.LevelBuilder; import com.eliasmueller.llamamod.mods.Mod; import com.eliasmueller.llamamod.mods.ModContext; +import com.eliasmueller.llamamod.mods.levels.LevelTally; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -59,7 +60,7 @@ public final class TestWorldMod implements Mod { } }, Map.of()); - ctx.write(OUT_WAD, wad); + ctx.write(OUT_WAD, LevelTally.counted(wad)); ctx.progress(0.9, "loading picture"); ctx.write(LOADING_PICTURE, loadingPicture()); ctx.progress(1.0, "test world done"); diff --git a/src/main/java/com/eliasmueller/llamamod/save/TestLevelChapters.java b/src/main/java/com/eliasmueller/llamamod/save/TestLevelChapters.java new file mode 100644 index 0000000..6d2585f --- /dev/null +++ b/src/main/java/com/eliasmueller/llamamod/save/TestLevelChapters.java @@ -0,0 +1,76 @@ +package com.eliasmueller.llamamod.save; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * TestLevel (world 9) chapters a mod level may occupy. The game has per-chapter arrays only for + * worlds 1–8: the coin counter u32 0x584F1C[world·6 + chapter] and the flag byte + * 0x584EC8[world·6 + chapter]. For world 9 both run into other globals, which the game zeroes on + * level start and overwrites with coins or flags: + * + *

+ * coin slot 0x584FF4 + 4c: c=0 counter var 0xC0, 1 unused, 2 counter var 0xBF, 3 HUD flag 0x585000,
+ *   4 var 0x80, 5..8 last played tribe/world/chapter/map (0x585008..14), 9 furthest progress
+ *   (0x585018: zeroed on level start, coins make it garbage), 10..21 unused, 22..31 byte table
+ *   0x585024 (strat var 0x93, tribe 1), 32..49 the same table for tribes 2/3 (never used).
+ * flag byte 0x584EFE + c: 0..5 unused, 6..9 language index 0x584F04 (|=2 → wrong texts/crash),
+ *   10..13 option 0x584F08, 14..21 0x584F0C/0x584F10, 22..25 unused, 26..29 option 0x584F18,
+ *   30..49 coin slots of world 0 (never read).
+ * 
+ * + * Safe: chapter 1 (Test World) and 32–49. + */ +public final class TestLevelChapters { + private TestLevelChapters() {} + + /** The Test World's chapter. */ + public static final int TEST_WORLD = 1; + + /** First and last chapter for other mod levels. */ + public static final int FIRST = 32; + + public static final int LAST = 49; + + public static boolean usable(int chapter) { + return chapter == TEST_WORLD || chapter >= FIRST && chapter <= LAST; + } + + /** All usable chapters: 1, 32..49. */ + public static List all() { + List out = new ArrayList<>(); + out.add(TEST_WORLD); + for (int c = FIRST; c <= LAST; c++) { + out.add(c); + } + return out; + } + + /** + * A usable chapter for a stored one: unchanged if usable, else 2..19 → 32..49, 20..31 → 32..43, + * anything else → 32 (deterministic, for play-tests and exports of old projects). + */ + public static int migrate(int chapter) { + if (usable(chapter)) { + return chapter; + } + return chapter >= 2 && chapter < FIRST ? FIRST + (chapter - 2) % (LAST - FIRST + 1) : FIRST; + } + + /** + * {@code wanted} if usable and not in {@code used}, else the first free chapter of 32..49; when + * all are taken, {@link #migrate}({@code wanted}) (shared: only one of those levels can be on). + */ + public static int pick(int wanted, Collection used) { + if (usable(wanted) && !used.contains(wanted)) { + return wanted; + } + for (int c = FIRST; c <= LAST; c++) { + if (!used.contains(c)) { + return c; + } + } + return migrate(wanted); + } +} diff --git a/src/test/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestLifecycleTest.java b/src/test/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestLifecycleTest.java index 4372807..6fb1a1d 100644 --- a/src/test/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestLifecycleTest.java +++ b/src/test/java/com/eliasmueller/llamamod/editor/tools/playtest/PlaytestLifecycleTest.java @@ -76,7 +76,10 @@ class PlaytestLifecycleTest { assertNotNull(t); assertTrue(t.playtest()); assertTrue(t.name().endsWith("(play-test)")); - assertEquals("4", t.options().getFirst().defaultValue(), "own chapter, not shifted by the saved level"); + assertEquals( + "34", + t.options().getFirst().defaultValue(), + "own chapter (old 4 → 34), not shifted by the saved level"); assertFalse(mod("level-mine").playtest()); cfg.enabledMods = new ArrayList<>(List.of("fog", "playtest-mine")); diff --git a/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTest.java b/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTest.java index 1a68ae9..dca9139 100644 --- a/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTest.java +++ b/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.eliasmueller.llamamod.format.Bytes; import com.eliasmueller.llamamod.format.Level; import com.eliasmueller.llamamod.format.MapChunk; import com.eliasmueller.llamamod.format.Strats; @@ -13,8 +14,11 @@ import com.eliasmueller.llamamod.format.stpcmerge.StratCode; import com.eliasmueller.llamamod.format.strat.StratCompiler; import com.eliasmueller.llamamod.format.strat.StratListing; import com.eliasmueller.llamamod.mods.GameData; +import com.eliasmueller.llamamod.mods.ModOption; +import com.eliasmueller.llamamod.mods.ModOptions; import com.eliasmueller.llamamod.mods.ModRegistry; import com.eliasmueller.llamamod.mods.TestModContext; +import com.eliasmueller.llamamod.platform.Config; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -89,11 +93,11 @@ class ArenaTest { void builtLevel() throws IOException { Path cd = GameData.cd(); ArenaMod mod = new ArenaMod(); - TestModContext ctx = new TestModContext(cd, mod, Map.of("lives", "7", "chapter", "5"), null); + TestModContext ctx = new TestModContext(cd, mod, Map.of("lives", "7", "chapter", "40"), null); mod.apply(ctx); - byte[] wad = ctx.written.get("wads/T1L9M005.wad"); + byte[] wad = ctx.written.get("wads/T1L9M040.wad"); assertNotNull(wad, "written: " + ctx.written.keySet()); - assertNotNull(ctx.written.get("T195.COR")); + assertNotNull(ctx.written.get("T1940.COR")); Level lv = Level.parse(wad); Map> t = types(lv); @@ -101,6 +105,9 @@ class ArenaTest { assertEquals(1, t.get("ArenaMaster").size()); assertEquals(1, t.get("CheckPointPost").size()); assertEquals(2, t.get("Coin").size()); + // tally total: 2 placed + 5 alligators · (6 hit coins + 5 death coins) + 5 exit bonus + assertEquals(2 + 5 * (6 + 5) + 5, Bytes.u32(lv.wad().require("LNFO"), 0)); + assertEquals(0, Bytes.u32(lv.wad().require("LNFO"), 4)); assertTrue( !t.containsKey("LevelDoor") && !t.containsKey("ArenaGator"), t.keySet().toString()); @@ -140,9 +147,9 @@ class ArenaTest { Path cd = GameData.cd(); ArenaMod mod = new ArenaMod(); TestModContext ctx = new TestModContext(cd, mod, Map.of(), null); - ctx.write("wads/T1L9M008.wad", new byte[] {1, 2, 3}); + ctx.write("wads/T1L9M049.wad", new byte[] {1, 2, 3}); IOException e = assertThrows(IOException.class, () -> mod.apply(ctx)); - assertTrue(e.getMessage().contains("chapter 8"), e.getMessage()); + assertTrue(e.getMessage().contains("chapter 49"), e.getMessage()); } @Test @@ -151,9 +158,27 @@ class ArenaTest { ArenaMod mod = new ArenaMod(); TestModContext ctx = new TestModContext(cd, mod, Map.of(), null); ArenaMod.apply(ctx, Map.of("probe", "true", "master", "false")); - Level lv = Level.parse(ctx.written.get("wads/T1L9M008.wad")); + Level lv = Level.parse(ctx.written.get("wads/T1L9M049.wad")); Map> t = types(lv); assertEquals(1, t.get("ArenaProbe").size()); assertTrue(!t.containsKey("ArenaMaster"), new ArrayList<>(t.keySet()).toString()); } + + @Test + void chapterIsUsableOnly() { + ModOption o = new ArenaMod() + .options().stream() + .filter(x -> x.key().equals("chapter")) + .findFirst() + .orElseThrow(); + assertEquals("49", o.defaultValue()); + assertEquals("32", o.normalize("32")); + for (String bad : List.of("1", "8", "9", "31", "50")) { + assertThrows(IllegalArgumentException.class, () -> o.normalize(bad), bad); + } + // a stored chapter from the old range 2..8 falls back to the default + Config cfg = new Config(); + cfg.modOptions.put("arena", Map.of("chapter", "8")); + assertEquals("49", ModOptions.resolve(new ArenaMod(), cfg).get("chapter")); + } } diff --git a/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTestMod.java b/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTestMod.java index 8ff7379..56ea9fd 100644 --- a/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTestMod.java +++ b/src/test/java/com/eliasmueller/llamamod/mods/arena/ArenaTestMod.java @@ -12,7 +12,7 @@ import java.util.Map; /** * The arena with level script options for in-game tests (not registered): {@code * APPTEST_CP=build/classes/java/test APPTEST_JAVA_OPTS=-Dllamamod.extraMods=}, option - * {@code script} = {@code key=value;key=value} (probe=true, start=x,z,rot). + * {@code script} = {@code key=value;key=value} (probe=true, start=x,z,rot); {@code chapter} 1–49. */ public final class ArenaTestMod implements Mod { @Override @@ -32,7 +32,14 @@ public final class ArenaTestMod implements Mod { @Override public List options() { - List o = new ArrayList<>(new ArenaMod().options()); + List o = new ArrayList<>(); + for (ModOption x : new ArenaMod().options()) { + // any chapter, to reproduce the per-chapter memory clashes of unusable ones + o.add( + x.key().equals("chapter") + ? ModOption.integer("chapter", x.label(), x.description(), ArenaMod.DEFAULT_CHAPTER, 1, 49) + : x); + } o.add(ModOption.string("script", "Script options", "key=value;…", "")); return o; } diff --git a/src/test/java/com/eliasmueller/llamamod/mods/levels/LevelProjectModTest.java b/src/test/java/com/eliasmueller/llamamod/mods/levels/LevelProjectModTest.java index c39c248..58dd55a 100644 --- a/src/test/java/com/eliasmueller/llamamod/mods/levels/LevelProjectModTest.java +++ b/src/test/java/com/eliasmueller/llamamod/mods/levels/LevelProjectModTest.java @@ -10,10 +10,13 @@ import com.eliasmueller.llamamod.editor.io.ProjectIO; import com.eliasmueller.llamamod.editor.model.Strat; import com.eliasmueller.llamamod.editor.model.cmd.Edits; import com.eliasmueller.llamamod.editor.model.cmd.History; +import com.eliasmueller.llamamod.format.Bytes; import com.eliasmueller.llamamod.format.Level; import com.eliasmueller.llamamod.mods.GameData; import com.eliasmueller.llamamod.mods.Mod; +import com.eliasmueller.llamamod.mods.ModOptions; import com.eliasmueller.llamamod.mods.TestModContext; +import com.eliasmueller.llamamod.platform.Config; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -38,10 +41,10 @@ class LevelProjectModTest { ProjectIO.save(edit, dir.resolve("village edit.llamalevel"), cd); Project a = Project.create(cd, "new", "t1l1m001"); a.scene().settings().prop("name").setRaw(a.scene().settings(), "Level A"); - a.scene().settings().prop("targetChapter").setRaw(a.scene().settings(), 3); + a.scene().settings().prop("targetChapter").setRaw(a.scene().settings(), 40); ProjectIO.save(a, dir.resolve("a.llamalevel"), cd); Project b = Project.create(cd, "new", "t1l1m001"); - b.scene().settings().prop("targetChapter").setRaw(b.scene().settings(), 3); + b.scene().settings().prop("targetChapter").setRaw(b.scene().settings(), 40); ProjectIO.save(b, dir.resolve("b.llamalevel"), cd); Files.writeString(dir.resolve("broken.llamalevel"), "no zip"); @@ -52,17 +55,20 @@ class LevelProjectModTest { LevelProjectMod ma = (LevelProjectMod) mods.get(0); LevelProjectMod mb = (LevelProjectMod) mods.get(1); LevelProjectMod me = (LevelProjectMod) mods.get(2); - assertEquals("3", ma.options().getFirst().defaultValue()); - assertEquals("2", mb.options().getFirst().defaultValue(), "chapter 3 is taken: next free"); + assertEquals("40", ma.options().getFirst().defaultValue()); + assertEquals("32", mb.options().getFirst().defaultValue(), "chapter 40 is taken: first free usable"); assertTrue(me.options().isEmpty()); assertEquals(Mod.LEVELS, me.category()); TestModContext ctx = new TestModContext(cdDir, ma, Map.of(), null); ma.apply(ctx); - assertTrue(ctx.written.containsKey("wads/T1L9M003.wad")); - assertTrue(ctx.written.containsKey("T193.COR")); - Level lv = Level.parse(ctx.written.get("wads/T1L9M003.wad")); + assertTrue(ctx.written.containsKey("wads/T1L9M040.wad")); + assertTrue(ctx.written.containsKey("T1940.COR")); + Level lv = Level.parse(ctx.written.get("wads/T1L9M040.wad")); assertEquals(0, lv.map().strats.size()); + byte[] lnfo = lv.wad().get("LNFO"); + assertEquals(0, Bytes.u32(lnfo, 0), "tally: no coins (not the base's 55)"); + assertEquals(0, Bytes.u32(lnfo, 4), "tally: no secrets"); TestModContext ec = new TestModContext(cdDir, me, Map.of(), null); me.apply(ec); @@ -78,7 +84,7 @@ class LevelProjectModTest { IOException e = assertThrows(IOException.class, () -> me.apply(twice)); assertTrue(e.getMessage().contains("already changed"), e.getMessage()); TestModContext both = new TestModContext(cdDir, ma, Map.of(), null); - both.written.put("wads/T1L9M003.wad", new byte[] {1}); + both.written.put("wads/T1L9M040.wad", new byte[] {1}); assertThrows(IOException.class, () -> ma.apply(both)); } @@ -100,4 +106,40 @@ class LevelProjectModTest { assertTrue(!v1.equals(v2), "cache key must change with the project file"); assertEquals(new LevelProjectSource(dir).mods().getFirst().version(), v2); } + + @Test + void oldChaptersMoveToUsableOnes(@TempDir Path dir) throws IOException { + Bases cd = Bases.fromCd(GameData.cd()); + for (String[] f : new String[][] {{"a", "5"}, {"b", "9"}, {"c", "1"}, {"d", "33"}}) { + Project p = Project.create(cd, "new", "t1l1m001"); + p.scene().settings().prop("targetChapter").setRaw(p.scene().settings(), Integer.parseInt(f[1])); + ProjectIO.save(p, dir.resolve(f[0] + ".llamalevel"), cd); + } + List mods = new LevelProjectSource(dir).mods(); + assertEquals( + List.of("32", "34", "1", "33"), + mods.stream().map(m -> m.options().getFirst().defaultValue()).toList(), + "2..31 → first free of 32..49; 1 and 33 stay"); + // a chapter stored in the launcher config by an older version falls back to the default + Config cfg = new Config(); + cfg.modOptions.put("level-a", Map.of("chapter", "5")); + assertEquals("32", ModOptions.resolve(mods.getFirst(), cfg).get("chapter")); + cfg.modOptions.put("level-a", Map.of("chapter", "45")); + assertEquals("45", ModOptions.resolve(mods.getFirst(), cfg).get("chapter")); + } + + @Test + void moreProjectsThanChaptersShareThem(@TempDir Path dir) throws IOException { + Bases cd = Bases.fromCd(GameData.cd()); + Project p = Project.create(cd, "new", "t1l1m001"); + p.scene().settings().prop("targetChapter").setRaw(p.scene().settings(), 40); + for (int i = 0; i < 20; i++) { + ProjectIO.save(p, dir.resolve(String.format("p%02d.llamalevel", i)), cd); + } + List ch = new LevelProjectSource(dir) + .mods().stream().map(m -> m.options().getFirst().defaultValue()).toList(); + assertEquals("40", ch.get(0)); + assertEquals(18, ch.stream().distinct().count(), ch.toString()); + assertTrue(ch.stream().allMatch(c -> Integer.parseInt(c) >= 32), ch.toString()); + } } diff --git a/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldGoldenTest.java b/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldGoldenTest.java index 2f995cc..e3a984a 100644 --- a/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldGoldenTest.java +++ b/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldGoldenTest.java @@ -3,10 +3,13 @@ package com.eliasmueller.llamamod.mods.testworld; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import com.eliasmueller.llamamod.format.Bytes; +import com.eliasmueller.llamamod.format.Level; import com.eliasmueller.llamamod.levelbuild.LevelBuilder; import com.eliasmueller.llamamod.levelbuild.LevelScript; import com.eliasmueller.llamamod.mods.GameData; import com.eliasmueller.llamamod.mods.ModContext; +import com.eliasmueller.llamamod.mods.levels.LevelTally; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -135,7 +138,10 @@ class TestWorldGoldenTest { public void progress(double fraction, String message) {} }); assertEquals(List.of("T191.COR", "wads/T1L9M001.wad"), List.copyOf(written.keySet())); - assertEquals(TEST_WORLD_SHA, sha256(written.get("wads/T1L9M001.wad"))); + // the mod only recounts the tally (LNFO: 20 coins, no secrets instead of the Village's 55, 2) + byte[] wad = written.get("wads/T1L9M001.wad"); + assertEquals(20, Bytes.u32(Level.parse(wad).wad().require("LNFO"), 0)); + assertEquals(TEST_WORLD_SHA, sha256(LevelTally.withTotals(wad, 55, 2))); assertEquals( "c55e3babf4855540f14daa60b979a2534e3732037854d9ad785f69f370d095d0", sha256(written.get("T191.COR"))); } diff --git a/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldTallyTest.java b/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldTallyTest.java new file mode 100644 index 0000000..0f439ae --- /dev/null +++ b/src/test/java/com/eliasmueller/llamamod/mods/testworld/TestWorldTallyTest.java @@ -0,0 +1,35 @@ +package com.eliasmueller.llamamod.mods.testworld; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.eliasmueller.llamamod.format.Bytes; +import com.eliasmueller.llamamod.format.Level; +import com.eliasmueller.llamamod.format.MapChunk; +import com.eliasmueller.llamamod.format.Strats; +import com.eliasmueller.llamamod.mods.GameData; +import com.eliasmueller.llamamod.mods.TestModContext; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** The tally of the Test World counts its own coins, not the Village base's 55. */ +class TestWorldTallyTest { + @Test + void lnfoCountsPlacedCoins() throws IOException { + TestWorldMod mod = new TestWorldMod(); + TestModContext ctx = new TestModContext(GameData.cd(), mod, Map.of(), null); + mod.apply(ctx); + Level lv = Level.parse(ctx.written.get(TestWorldMod.OUT_WAD)); + Map> t = Strats.levelTypes(lv.wad().require("STPC"), lv.wfpc(), lv.map()); + int coins = t.getOrDefault("Coin", List.of()).size() + + 5 * t.getOrDefault("Coin5", List.of()).size(); + byte[] lnfo = lv.wad().require("LNFO"); + System.out.println( + "TALLY test world " + Bytes.u32(lnfo, 0) + "/" + Bytes.u32(lnfo, 4) + " types " + t.keySet()); + assertTrue(coins > 0); + assertEquals(coins, Bytes.u32(lnfo, 0)); + assertEquals(t.getOrDefault("Secret", List.of()).size(), Bytes.u32(lnfo, 4)); + } +} diff --git a/src/test/java/com/eliasmueller/llamamod/save/TestLevelChaptersTest.java b/src/test/java/com/eliasmueller/llamamod/save/TestLevelChaptersTest.java new file mode 100644 index 0000000..77ca3bd --- /dev/null +++ b/src/test/java/com/eliasmueller/llamamod/save/TestLevelChaptersTest.java @@ -0,0 +1,57 @@ +package com.eliasmueller.llamamod.save; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class TestLevelChaptersTest { + /** Coin slot 0x584F1C + 4·(54 + c) and flag byte 0x584EC8 + 54 + c of world 9 (see the class comment). */ + @Test + void usableChaptersAvoidTheGlobalsBehindTheArrays() { + for (int c = 0; c <= 50; c++) { + int coin = 0x584F1C + 4 * (9 * 6 + c); + int flag = 0x584EC8 + 9 * 6 + c; + boolean coinFree = c == 1 + || coin >= 0x58501C && coin < 0x585024 + || coin >= 0x585028 && coin < 0x58504C + || coin >= 0x585074 && coin <= 0x5850BB; + boolean flagFree = flag < 0x584F04 || flag >= 0x584F14 && flag < 0x584F18 || flag >= 0x584F1C; + assertEquals(coinFree && flagFree && c >= 1 && c <= 49, TestLevelChapters.usable(c), "chapter " + c); + } + assertEquals(19, TestLevelChapters.all().size()); + assertEquals(0x585018, 0x584F1C + 4 * (9 * 6 + 9), "chapter 9 = furthest progress"); + } + + @Test + void migrate() { + assertEquals(1, TestLevelChapters.migrate(1)); + assertEquals(40, TestLevelChapters.migrate(40)); + assertEquals(32, TestLevelChapters.migrate(2)); + assertEquals(38, TestLevelChapters.migrate(8)); + assertEquals(49, TestLevelChapters.migrate(19)); + assertEquals(32, TestLevelChapters.migrate(20)); + assertEquals(43, TestLevelChapters.migrate(31)); + assertEquals(32, TestLevelChapters.migrate(0)); + assertEquals(32, TestLevelChapters.migrate(60)); + for (int c = -1; c < 60; c++) { + assertTrue(TestLevelChapters.usable(TestLevelChapters.migrate(c))); + } + } + + @Test + void pick() { + assertEquals(40, TestLevelChapters.pick(40, Set.of())); + assertEquals(1, TestLevelChapters.pick(1, Set.of(32))); + assertEquals(33, TestLevelChapters.pick(40, Set.of(32, 40))); + assertEquals(32, TestLevelChapters.pick(5, Set.of())); + Set full = new HashSet<>(TestLevelChapters.all()); + assertEquals(38, TestLevelChapters.pick(8, full), "all taken: shared"); + assertEquals(45, TestLevelChapters.pick(45, full)); + assertFalse(List.of(1).contains(TestLevelChapters.pick(1, Set.of(1)))); + } +}