McRogueFace/tests/procgen_interactive/main.py

200 lines
6 KiB
Python

"""Interactive Procedural Generation Demo Launcher
Run with: ./mcrogueface ../tests/procgen_interactive/main.py
"""
import mcrfpy
import sys
# Demo classes
from .demos.cave_demo import CaveDemo
from .demos.dungeon_demo import DungeonDemo
from .demos.terrain_demo import TerrainDemo
from .demos.town_demo import TownDemo
class DemoLauncher:
"""Main menu for selecting demos."""
DEMOS = [
("Cave (Cellular Automata)", CaveDemo,
"Cellular automata cave generation with noise, smoothing, and region detection"),
("Dungeon (BSP)", DungeonDemo,
"Binary Space Partitioning with room extraction and corridor connections"),
("Terrain (Multi-layer)", TerrainDemo,
"FBM noise elevation with water level, mountains, erosion, and biomes"),
("Town (Voronoi)", TownDemo,
"Voronoi districts with Bezier roads and building placement"),
]
def __init__(self):
"""Build the menu scene."""
self.scene = mcrfpy.Scene("procgen_menu")
self.current_demo = None
self._build_menu()
def _build_menu(self):
"""Create the menu UI."""
ui = self.scene.children
# Background
bg = mcrfpy.Frame(
pos=(0, 0),
size=(1024, 768),
fill_color=mcrfpy.Color(25, 28, 35)
)
ui.append(bg)
# Title
title = mcrfpy.Caption(
text="Interactive Procedural Generation",
pos=(512, 60),
font_size=32,
fill_color=mcrfpy.Color(220, 220, 230)
)
ui.append(title)
subtitle = mcrfpy.Caption(
text="Educational demos for exploring generation techniques",
pos=(512, 100),
font_size=16,
fill_color=mcrfpy.Color(150, 150, 160)
)
ui.append(subtitle)
# Demo buttons
button_y = 180
button_width = 400
button_height = 80
for i, (name, demo_class, description) in enumerate(self.DEMOS):
# Button frame
btn = mcrfpy.Frame(
pos=(312, button_y),
size=(button_width, button_height),
fill_color=mcrfpy.Color(45, 48, 55),
outline=2,
outline_color=mcrfpy.Color(80, 85, 100)
)
# Demo name
name_caption = mcrfpy.Caption(
text=name,
pos=(20, 15),
font_size=20,
fill_color=mcrfpy.Color(200, 200, 210)
)
btn.children.append(name_caption)
# Description (wrap manually for now)
desc_text = description[:55] + "..." if len(description) > 55 else description
desc_caption = mcrfpy.Caption(
text=desc_text,
pos=(20, 45),
font_size=12,
fill_color=mcrfpy.Color(120, 120, 130)
)
btn.children.append(desc_caption)
# Click handler
demo_idx = i
btn.on_click = lambda p, b, a, idx=demo_idx: self._on_demo_click(idx, b, a)
btn.on_enter = lambda p, btn=btn: self._on_btn_enter(btn)
btn.on_exit = lambda p, btn=btn: self._on_btn_exit(btn)
ui.append(btn)
button_y += button_height + 20
# Instructions
instructions = [
"Click a demo to start exploring procedural generation",
"Each demo shows step-by-step visualization of the algorithm",
"",
"Controls (in demos):",
" Left/Right arrows: Navigate steps",
" Middle-drag: Pan viewport",
" Scroll wheel: Zoom in/out",
" Number keys: Toggle layer visibility",
" R: Reset view",
" Escape: Return to this menu",
]
instr_y = 580
for line in instructions:
cap = mcrfpy.Caption(
text=line,
pos=(312, instr_y),
font_size=12,
fill_color=mcrfpy.Color(100, 100, 110)
)
ui.append(cap)
instr_y += 18
# Keyboard handler
self.scene.on_key = self._on_key
def _on_btn_enter(self, btn):
"""Handle button hover enter."""
btn.fill_color = mcrfpy.Color(55, 60, 70)
btn.outline_color = mcrfpy.Color(100, 120, 180)
def _on_btn_exit(self, btn):
"""Handle button hover exit."""
btn.fill_color = mcrfpy.Color(45, 48, 55)
btn.outline_color = mcrfpy.Color(80, 85, 100)
def _on_demo_click(self, idx, button, action):
"""Handle demo button click."""
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.RELEASED:
self._launch_demo(idx)
def _launch_demo(self, idx):
"""Launch a demo by index."""
_, demo_class, _ = self.DEMOS[idx]
self.current_demo = demo_class()
self.current_demo.activate()
def _on_key(self, key, action):
"""Handle keyboard input."""
# Only process on key press
if action != mcrfpy.InputState.PRESSED:
return
# Convert key to string for easier comparison
key_str = str(key) if not isinstance(key, str) else key
# Number keys to launch demos directly
if key_str.startswith("Key.NUM") or (len(key_str) == 1 and key_str.isdigit()):
try:
num = int(key_str[-1])
if 1 <= num <= len(self.DEMOS):
self._launch_demo(num - 1)
except (ValueError, IndexError):
pass
elif key == mcrfpy.Key.ESCAPE:
sys.exit(0)
def show(self):
"""Show the menu."""
mcrfpy.current_scene = self.scene
# Global launcher instance
_launcher = None
def show_menu():
"""Show the demo menu (called from demos to return)."""
global _launcher
if _launcher is None:
_launcher = DemoLauncher()
_launcher.show()
def main():
"""Entry point for the demo system."""
show_menu()
if __name__ == "__main__":
main()