8.0 KiB
Test harness tools/lamatest.py
Starts "Ein Königreich für ein Lama" unattended and invisibly (Xvfb :5, sound into a null sink), loads TestLevel chapter 1 (T1L9M001.wad) and takes screenshots. Tested on 2026-11-06 with Wine 6.0.3, llvmpipe.
Commands
lamatest.py run <wad> <outdir> [n] # all in one: install, start, tolevel, tour, stop
lamatest.py start # Xvfb :5 + null sink lama_stumm + groove.exe
lamatest.py tolevel # menu -> slot with TestLevel -> chapter 1 -> gameplay
lamatest.py tour <outdir> [n] # n camera shots (look around) + n walking shots + llama view
lamatest.py shot out.png # full screen 1920x1080
lamatest.py key <xdotool-key> [s] # hold key for s seconds (default 0.12)
lamatest.py keys Up:1.5 a:0.5 x # sequence, name[:hold time], also sleep=2
lamatest.py seq <outdir> "TOKENS" # --seq tokens (shot:, run:, rj: …) in the running game
lamatest.py status | stop | restore-wad
run options (added 2026-11-06)
lamatest.py run <wad> <outdir> [n] [--seq "TOKENS"] [--tour] [--keep]
-
WAD restore:
runremembers the previously installedT1L9M001.wad($LAMATEST_DIR/vorher_T1L9M001.wad) and restores it after the run – i.e. not.orig. With--keepthe tested WAD stays installed. (restore-wadstill explicitly restores.orig.) -
--seqreplaces the default tour with a key sequence (with--tourthe tour runs as well):Up:1.5,a:0.5,x… key[:hold time] as withkeys;sleep=1shot:NAME→seq_NN_NAME.jpg,fp:NAME→ shot in llama view (Return held)dialog→ click away dialog/letterboxUp+x:0.8→ hold keys simultaneouslyrj:T1:T2→ running jump (hold Up, X after T1 s, Up for another T2 s)dj:T→ double X with run-up,hop:N:T→ hold Up, jump N× every T srun:T:x@1.3,x@2.9,shot@5.0,Left@3.0-3.4→ hold Up for T s, following a schedule (s from start) tap X / take a shot (seq_NN_runT.jpg) / hold a key from–to;run/Left:T:…holds another key instead of Up. Real time: a shot blocks ~0.4 s (delays subsequent events and slows the game down) → no shots shortly before jumps.
-
Controls are camera-relative (Up = in the camera's view direction, Left/Right sideways). A/S rotate the camera, S:0.45 ≈ look 90° to the left.
-
Measurements (llama): ~1.1 units/s walking, jump 0.35 high, ~1.3 far with run-up – see levelbau.md. Timed runs scatter by ~0.3 units (start delay, frame rate); longer distances need "stops" (a wall/step where the llama comes to a halt).
-
Camera: after running into a wall or a fall the camera rotates unpredictably → Up then walks in a different direction. A/S rotation (S:0.45) is only roughly 90°.
-
Complete test world parkour as a script:
levels/testwelt_parkour.seq(levelbau.md). -
LAMATEST_SHOT=1280(environment):shot:/fp:shots at 1280×720, quality 85 instead of 960×540 (preview images). -
"EINGEFROREN?" (frozen?) for a
--seqwithout movement (e.g. onlysleep=3 shot:x) is a false alarm – the standing llama does not change the image; with camera rotation/walking you get "OK". -
Good viewing angles in the game are hard: the camera hangs low behind the llama; elevated start points (pedestal, parkour checkpoint
pk2+A:0.6) show several areas.
Helper script for series: $CLAUDE_JOB_DIR/tmp/lb_run.sh WAD NAME "SEQ" (montage lb_mNAME.jpg,
WAD path absolute!), rs.sh NAME "SEQ" (restart with installed WAD + seq, montage i/NAME.jpg).
Caution: no pgrep -f groove.exe in wait loops – it matches your own shell
(the command line contains "groove.exe") and lamatest takes it for a foreign instance.
State/PIDs/wine.log: $LAMATEST_DIR (default /tmp/lamatest-$USER).
run exit codes: 0 OK, 3 crash, 4 timeout/hang, 5 image frozen.
Output in <outdir>: report.txt, wine.log (stderr, truncated to 2 MB),
00_geladen.png (full) + .jpg, tour_*.jpg (960x540, ~30 KB), on failure
zz_absturz.jpg / zz_timeout.jpg. One run ≈ 75 s, ≈ 1.2 MB.
Safety rules (implemented in the script)
- Aborts if a groove.exe with a different DISPLAY is running (Elias' instance).
- xdotool only with DISPLAY=:5.
- Sound: own null sink, the app is never muted;
stopunloads the sink. - Save games (
*.ksvin the game folder) are backed up onstartand restored onstopif changed (newly created ones are deleted). stop:wineserver -konly if no foreign groove.exe is running, otherwise only own PIDs.- The test world exit saves progress (chapter 2 unlocked) – restored on
stoptogether with the save games. - WAD: the first
runbacks up the original asmods/testlevel/wads/T1L9M001.wad.orig;restore-wadrestores it. The overlay (lama-mod an) consists of symlinks to the mod files → overwriting~/.wine-lama/mods/testlevel/wads/T1L9M001.wadtakes effect immediately;lama-mod anis only called if the overlay link is missing.
tolevel sequence (measured)
| Step | Key | Detection | t (from start) |
|---|---|---|---|
| Logo → main menu | – | OCR crop 700x300+1150+650, threshold 75 % → only the highlighted item "Spiel starten" is readable | ~6 s |
| "Spiel starten" (start game) | Return | – | |
| Save slot selection (carousel, starts at slot 2, no wrap) | Left ×3, then Right until "TestLevel" | OCR crop 1600x280+160+740 (fuzzy, 2 thresholds) | ~20 s |
| Load slot 3 | Return | selection map = >75 % beige pixels | ~25 s |
| Activate chapter 1 (cursor from KSV +4/+5) | Return | ||
| Camera fly-through (letterbox: top+bottom row black) | Return skips | +1.5 s | |
| Gameplay | – | bright, no letterbox, golden HUD top left (or >25 s) 3× in a row | ~29 s total, loading ~4 s |
Without skipping, the camera fly-through in the village takes ~20 s. Loading screen = T191.COR ("TestLevel Kapitel 1 … LADEVORGANG").
Fallback: if no menu is detected, Return after 40 s (skips the demo). Caution: Return on a misdetected screen can start a different game – but save games are restored at the end.
PC controls (keyboard, from "Optionen → Tastatur konfigurieren", verified)
| Action | Key |
|---|---|
| Up / Down (walk forward / backward) | Up / Down |
| Left / Right (turn) | Left / Right |
| Jump (also advances dialog) | X |
| Action | Z |
| Power | Space |
| Llama view (first person, hold) | Return |
| Rotate camera left / right | A / S (hold 0.5 s ≈ 90°) |
| Pause menu | Escape |
Registry HKCU\Software\Disney Interactive\Emperors New Groove\1.0,
"InputDevice Wine Keyboard" = 14 DIK codes (4 hex digits each):
CB Left, CD Right, C8 Up, D0 Down, 1E A, 1F S, 39 Space, 2D X, 1C Return,
19 P, C9 PgUp, D1 PgDn, 2C Z, 18 O. The registry order ≠ menu order;
P/PgUp/PgDn/O probably pause/inventory (according to exe strings: PAUSE GAME, CYCLE
INVENTORY UP/DOWN, USE AN ITEM, TURN 180, SELECT) – not verified.
Other values there: CameraMode, ControlType, ScreenW/H (1920x1142), CDPath=L:.
Pause menu (Escape): graphics/sound options, configure keyboard/joystick, more options, continue, "Beenden und zur Auswahlkarte wechseln" (quit to selection map; confirmation Ja/Nein, default Nein → Up, Return), quit game. Main menu submenus: Escape = back.
Observations on the village 1 copy
- Walking forward in the village immediately starts the Pacha dialog (letterbox with
subtitles, ~9× X to click through).
tourdetects this, photographs the dialog once and clicks it away. - The llama can fall off the cliff during the tour (respawn) – irrelevant for visual checks.
Crash detection
Per poll: groove.exe process on :5 gone / window "Groove" gone / window with
"Programmfehler", "error", "exception" … (Wine crash dialog). The report contains the
line wine: Unhandled page fault … at address XXXXXXXX (look up the address in Ghidra).
Tested: WAD truncated to 300 KB and WAD with 2000 random
bytes → both crash during the loading screen, detected after ~5 s (exit 3).
Harmless log noise (gstreamer/MPEG-1 missing, ole, ChangeDisplaySettings,
MIDIMAP) is filtered out of the report.
Dependencies
Xvfb, xdotool, ImageMagick (import/convert), tesseract with deu, pactl, wine.