Cavern (Challacade)ยถ

Jetpack platformer, engine test only (BY-NC-ND assets).

๐Ÿ“„ Docs only

Upstream: https://github.com/challacade/cavern

Tags: port tier-1

Cavern: SimVX Port (Engine Test Only)ยถ

SimVX port of Challacade/cavern, a 2D adventure platformer originally written in Lร–VE 2D.

DO NOT REDISTRIBUTE. Source code is MIT, but the art and audio assets are licensed CC BY-NC-ND 4.0 by Challacade. This port exists solely as a SimVX engine integration test. See ../LICENSE_NOTICE.md for the full rules.

Runยถ

# Desktop
uv run python ported_games/cavern/simvx_port/main.py

# Skip the menu and drop straight into gameplay
uv run python ported_games/cavern/simvx_port/main.py --game

# Headless capture (writes screenshots/frame_30.png, frame_60.png, frame_120.png)
uv run python ported_games/cavern/simvx_port/main.py --test

# Web export (outputs simvx_port/web/index.html)
uv run simvx export web ported_games/cavern/simvx_port/main.py \
    -o ported_games/cavern/simvx_port/web/index.html

All commands must run from /home/fezzik/dev/simvx (the workspace root); uv wonโ€™t resolve workspace-only packages from a port subdirectory.

Controlsยถ

Action

Binding

Move

WASD / Arrow keys

Jetpack thrust up

W / Up / Space

Aim weapon

Mouse

Fire blaster

Left mouse button

Back to menu

Esc

Whatโ€™s faithful to upstreamยถ

  • Three rooms (rm1, rm2, rm3) parsed from upstreamโ€™s STI Lua exports; same wall geometry, transitions, and spike-enemy placements.

  • Player sprite stack: jetpack + body + helmet + arm, with Cavernโ€™s exact artwork.

  • Aim-with-mouse + LMB-fire blaster; camera follows player and clamps to room bounds.

  • Music (cavern.ogg, menu.ogg) and sfx (laser.wav, itemGet.wav, blip.wav) wired through AudioStreamPlayer.

Whatโ€™s reduced (engine-test scope)ยถ

  • Only the blaster weapon (rocket launcher / spear gun / aquaPack are wired as pickup labels but donโ€™t change behaviour).

  • Jetpack approximation: simple gravity + thrust integrator instead of Lร–VEโ€™s Box2D solver. Feel is similar; tunables in nodes/config.py.

  • Single enemy type (Spike); Cavernโ€™s other enemies (flyer, fish, starfish, egg, boss) are out of scope.

  • No water / drowning / aquaPack mechanic (Cavernโ€™s underwater rooms behave like dry rooms here).

  • No breakables, vines, or boss room.

  • Menu is keyboard / mouse driven; the upstream rmMainMenu bespoke layout isnโ€™t used.

Filesยถ

simvx_port/
โ”œโ”€โ”€ main.py              entry point + test/capture mode
โ”œโ”€โ”€ pyproject.toml       PEP 621 + [tool.simvx] root="CavernRoot"
โ”œโ”€โ”€ nodes/
โ”‚   โ”œโ”€โ”€ lua_map.py       STI-Lua map parser (Walls/Transitions/Enemies/...)
โ”‚   โ”œโ”€โ”€ config.py        tuning constants
โ”‚   โ”œโ”€โ”€ room.py          Room + Pickup nodes (one per active map)
โ”‚   โ”œโ”€โ”€ player.py        Player, Bullet
โ”‚   โ”œโ”€โ”€ enemies.py       Spike, EnemyProjectile, spawn_enemy()
โ”‚   โ”œโ”€โ”€ hud.py           in-game HUD (health pips + room label + bottom strip)
โ”‚   โ”œโ”€โ”€ menu.py          MainMenu scene
โ”‚   โ”œโ”€โ”€ game.py          CavernGame: owns room + camera + collisions
โ”‚   โ””โ”€โ”€ root.py          CavernRoot scene container (web-export entry)
โ”œโ”€โ”€ assets/
โ”‚   โ”œโ”€โ”€ sprites/         player, enemies, environment, items
โ”‚   โ”œโ”€โ”€ sounds/          laser, itemGet, blip
โ”‚   โ”œโ”€โ”€ music/           cavern.ogg, menu.ogg
โ”‚   โ””โ”€โ”€ maps/            rm1.lua, rm2.lua, rm3.lua, rmMainMenu.lua
โ”œโ”€โ”€ screenshots/
โ””โ”€โ”€ web/

Sourceยถ

 1#!/usr/bin/env python3
 2"""Cavern (Challacade): Jetpack platformer, engine test only (BY-NC-ND assets).
 3
 4# /// simvx
 5# tags = ["port", "tier-1"]
 6# upstream = "https://github.com/challacade/cavern"
 7# web = { width = 1152, height = 768, responsive = true, disabled = true, reason = "Blocked on audio refactor (uses old 'Music' bus name; upstream art also under BY-NC-ND)." }
 8# ///
 9
10Source code MIT (Challacade); art and audio assets are CC BY-NC-ND 4.0.
11Engine test only; see ``ported_games/cavern/LICENSE_NOTICE.md``.
12
13Run desktop : uv run python ported_games/cavern/simvx_port/main.py
14Headless    : uv run python ported_games/cavern/simvx_port/main.py --test
15Web export  : uv run simvx export web ported_games/cavern/simvx_port/main.py \
16                  -o ported_games/cavern/simvx_port/web/index.html
17"""
18
19# /// script
20# requires-python = ">=3.13"
21# dependencies = ["simvx-core", "simvx-graphics", "numpy", "pillow"]
22# ///
23
24from __future__ import annotations
25
26import sys
27from pathlib import Path
28
29_PORT_DIR = Path(__file__).resolve().parent
30sys.path.insert(0, str(_PORT_DIR))
31
32from simvx.graphics import App  # noqa: E402
33
34from nodes.root import CavernRoot  # noqa: E402
35
36WINDOW_W = 1152
37WINDOW_H = 768
38
39
40def main() -> None:
41    test_mode = "--test" in sys.argv
42    skip_intro = "--game" in sys.argv
43
44    app = App(
45        width=WINDOW_W, height=WINDOW_H,
46        title="Cavern (SimVX engine test)",
47        visible=not test_mode,
48    )
49
50    start = "game" if skip_intro else "menu"
51
52    if test_mode:
53        from simvx.graphics import save_png
54        out = _PORT_DIR / "screenshots"
55        out.mkdir(exist_ok=True)
56        capture_frames = [30, 60, 120]
57        # Capture both menu and gameplay: 30 = menu, 60/120 = gameplay.
58        captured = app.run_headless(
59            CavernRoot(start_scene="game"),
60            frames=150,
61            capture_frames=capture_frames,
62        )
63        for idx, n in enumerate(capture_frames):
64            if idx < len(captured):
65                save_png(out / f"frame_{n}.png", captured[idx])
66                print(f"wrote frame_{n}.png")
67        app.quit()
68        return
69
70    app.run(CavernRoot(start_scene=start))
71
72
73if __name__ == "__main__":
74    main()