tests/demo/demo_main.py died immediately on `mcrfpy.sceneUI`, removed with the
pre-Scene-object API. Four bugs, not the one filed:
1. screens/base.py called mcrfpy.sceneUI(name). The Scene object it had already
constructed owns the UI collection: scene.children.
2. demo_main.py called the removed mcrfpy.setScene().
3. run_interactive() referenced `menu`, a local of create_menu() -- NameError on
every interactive launch, independent of the sceneUI bug.
4. run_headless() assigned a DemoScreen (not a Scene) to mcrfpy.current_scene ->
TypeError, so the headless screenshot path was broken too.
The screens themselves had rotted the same way as the unit suite: add_layer() no
longer takes keyword arguments or a name (it takes a layer object), layer.set()
takes a position tuple, and mcrfpy.Animation is no longer exported (targets animate
themselves). Fixed in grid_demo and animation_demo, including the code samples the
demos display -- the demos are documentation, so a stale sample is a stale doc.
The headless path also no longer needs a Timer at all. Rendering costs zero
simulation time (#341/#350), so it just activates each scene and screenshots it,
rather than waiting for frames that headless never runs.
Six showcase scripts wrote to a hardcoded /opt/goblincorps/... absolute path outside
the repo, so they raised PermissionError for anyone else. Replaced with
tests/demo/docs_output.py: $MCRF_DOCS_REPO, else a side-by-side mcrogueface.github.io
checkout, else tests/demo/screenshots/.
The point of the issue was the gate, so: run_tests.py now runs demo_main.py as a
smoke test. Verified it catches the original bug -- reintroducing the sceneUI call
turns the demo suite red. CLAUDE.md tells newcomers to read tests/demo/screens/ for
correct API usage; nothing was checking that it ran.
The 19 standalone showcase scripts under screens/ are not on the demo_main path and
are not adopted here; follow-up issue to come.
closes #372
104 lines
2.9 KiB
Python
104 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple Tutorial Screenshot Generator
|
|
|
|
This creates ONE screenshot - the part01 tutorial showcase.
|
|
Run with: xvfb-run -a ./build/mcrogueface --headless --exec tests/demo/simple_showcase.py
|
|
|
|
NOTE: In headless mode, automation.screenshot() is SYNCHRONOUS - it renders
|
|
and captures immediately. No timer dance needed!
|
|
"""
|
|
import mcrfpy
|
|
import docs_output
|
|
from mcrfpy import automation
|
|
import sys
|
|
import os
|
|
|
|
# Output
|
|
OUTPUT_PATH = os.path.join(docs_output.image_dir("tutorials"), "part_01_grid_movement.png") # #372
|
|
|
|
# Tile sprites from the labeled tileset
|
|
PLAYER_KNIGHT = 84
|
|
FLOOR_STONE = 42
|
|
WALL_STONE = 30
|
|
TORCH = 72
|
|
BARREL = 73
|
|
SKULL = 74
|
|
|
|
def main():
|
|
"""Create the part01 showcase screenshot."""
|
|
# Ensure output dir exists
|
|
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
|
|
|
# Create scene
|
|
scene = mcrfpy.Scene("showcase")
|
|
|
|
# Load texture
|
|
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
|
|
|
# Create grid - bigger zoom for visibility
|
|
grid = mcrfpy.Grid(
|
|
pos=(50, 80),
|
|
size=(700, 480),
|
|
grid_size=(12, 9),
|
|
texture=texture,
|
|
zoom=3.5
|
|
)
|
|
grid.fill_color = mcrfpy.Color(20, 20, 30)
|
|
scene.children.append(grid)
|
|
|
|
# Fill with floor
|
|
for y in range(9):
|
|
for x in range(12):
|
|
grid.at(x, y).tilesprite = FLOOR_STONE
|
|
|
|
# Add wall border
|
|
for x in range(12):
|
|
grid.at(x, 0).tilesprite = WALL_STONE
|
|
grid.at(x, 0).walkable = False
|
|
grid.at(x, 8).tilesprite = WALL_STONE
|
|
grid.at(x, 8).walkable = False
|
|
for y in range(9):
|
|
grid.at(0, y).tilesprite = WALL_STONE
|
|
grid.at(0, y).walkable = False
|
|
grid.at(11, y).tilesprite = WALL_STONE
|
|
grid.at(11, y).walkable = False
|
|
|
|
# Add player entity - a knight!
|
|
player = mcrfpy.Entity(
|
|
grid_pos=(6, 4),
|
|
texture=texture,
|
|
sprite_index=PLAYER_KNIGHT
|
|
)
|
|
grid.entities.append(player)
|
|
|
|
# Add decorations
|
|
for pos, sprite in [((2, 2), TORCH), ((9, 2), TORCH), ((2, 6), BARREL), ((9, 6), SKULL)]:
|
|
entity = mcrfpy.Entity(grid_pos=pos, texture=texture, sprite_index=sprite)
|
|
grid.entities.append(entity)
|
|
|
|
# Center camera on player
|
|
grid.center = (6 * 16 + 8, 4 * 16 + 8)
|
|
|
|
# Add title
|
|
title = mcrfpy.Caption(text="Part 1: The '@' and the Dungeon Grid", pos=(50, 20))
|
|
title.fill_color = mcrfpy.Color(255, 255, 255)
|
|
title.font_size = 28
|
|
scene.children.append(title)
|
|
|
|
subtitle = mcrfpy.Caption(text="Creating a grid, placing entities, handling input", pos=(50, 50))
|
|
subtitle.fill_color = mcrfpy.Color(180, 180, 200)
|
|
subtitle.font_size = 16
|
|
scene.children.append(subtitle)
|
|
|
|
# Activate scene
|
|
scene.activate()
|
|
|
|
# In headless mode, screenshot() is synchronous - renders then captures!
|
|
result = automation.screenshot(OUTPUT_PATH)
|
|
print(f"Screenshot saved: {OUTPUT_PATH} (result: {result})")
|
|
sys.exit(0)
|
|
|
|
|
|
# Run it
|
|
main()
|