#!/usr/bin/env python3 """ Cookbook Screenshot Showcase - Visual examples for cookbook recipes! Generates beautiful screenshots for cookbook pages. Run with: xvfb-run -a ./build/mcrogueface --headless --exec tests/demo/cookbook_showcase.py In headless mode, automation.screenshot() is SYNCHRONOUS - no timer dance needed! """ import mcrfpy from mcrfpy import automation import sys import os # Output directory - in the docs site images folder OUTPUT_DIR = "/opt/goblincorps/repos/mcrogueface.github.io/images/cookbook" # Tile sprites from the labeled tileset TILES = { 'player_knight': 84, 'player_mage': 85, 'player_rogue': 86, 'player_warrior': 87, 'enemy_slime': 108, 'enemy_orc': 120, 'enemy_skeleton': 123, 'floor_stone': 42, 'wall_stone': 30, 'wall_brick': 14, 'torch': 72, 'chest_closed': 89, 'item_potion': 113, } def screenshot_health_bar(): """Create a health bar showcase.""" scene = mcrfpy.Scene("health_bar") # Dark background bg = mcrfpy.Frame(pos=(0, 0), size=(800, 600)) bg.fill_color = mcrfpy.Color(20, 20, 30) scene.children.append(bg) # Title title = mcrfpy.Caption(text="Health Bar Recipe", pos=(50, 30)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Nested frames for dynamic UI elements", pos=(50, 60)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Example health bars at different levels y_start = 120 bar_configs = [ ("Player - Full Health", 100, 100, mcrfpy.Color(50, 200, 50)), ("Player - Damaged", 65, 100, mcrfpy.Color(200, 200, 50)), ("Player - Critical", 20, 100, mcrfpy.Color(200, 50, 50)), ("Boss - 3/4 Health", 750, 1000, mcrfpy.Color(150, 50, 150)), ] for i, (label, current, maximum, color) in enumerate(bar_configs): y = y_start + i * 100 # Label lbl = mcrfpy.Caption(text=label, pos=(50, y)) lbl.fill_color = mcrfpy.Color(220, 220, 220) lbl.font_size = 18 scene.children.append(lbl) # Background bar bar_bg = mcrfpy.Frame(pos=(50, y + 30), size=(400, 30)) bar_bg.fill_color = mcrfpy.Color(40, 40, 50) bar_bg.outline = 2 bar_bg.outline_color = mcrfpy.Color(80, 80, 100) scene.children.append(bar_bg) # Fill bar (scaled to current/maximum) fill_width = int(400 * (current / maximum)) bar_fill = mcrfpy.Frame(pos=(50, y + 30), size=(fill_width, 30)) bar_fill.fill_color = color scene.children.append(bar_fill) # Text overlay hp_text = mcrfpy.Caption(text=f"{current}/{maximum}", pos=(60, y + 35)) hp_text.fill_color = mcrfpy.Color(255, 255, 255) hp_text.font_size = 16 scene.children.append(hp_text) scene.activate() output_path = os.path.join(OUTPUT_DIR, "ui_health_bar.png") automation.screenshot(output_path) print(f" -> {output_path}") def screenshot_fog_of_war(): """Create a fog of war showcase.""" scene = mcrfpy.Scene("fog_of_war") texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) grid = mcrfpy.Grid( pos=(50, 80), size=(700, 480), grid_size=(16, 12), texture=texture, zoom=2.8 ) grid.fill_color = mcrfpy.Color(0, 0, 0) # Black for unknown areas scene.children.append(grid) # Title title = mcrfpy.Caption(text="Fog of War Recipe", pos=(50, 20)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Visible, discovered, and unknown areas", pos=(50, 50)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Fill floor for y in range(12): for x in range(16): grid.at(x, y).tilesprite = TILES['floor_stone'] # Add walls for x in range(16): grid.at(x, 0).tilesprite = TILES['wall_stone'] grid.at(x, 11).tilesprite = TILES['wall_stone'] for y in range(12): grid.at(0, y).tilesprite = TILES['wall_stone'] grid.at(15, y).tilesprite = TILES['wall_stone'] # Interior walls (to break LOS) for y in range(3, 8): grid.at(8, y).tilesprite = TILES['wall_brick'] # Player (mage with light) player = mcrfpy.Entity(grid_pos=(4, 6), texture=texture, sprite_index=TILES['player_mage']) grid.entities.append(player) # Hidden enemies on the other side enemy1 = mcrfpy.Entity(grid_pos=(12, 4), texture=texture, sprite_index=TILES['enemy_orc']) grid.entities.append(enemy1) enemy2 = mcrfpy.Entity(grid_pos=(13, 8), texture=texture, sprite_index=TILES['enemy_skeleton']) grid.entities.append(enemy2) # Torch in visible area torch = mcrfpy.Entity(grid_pos=(2, 3), texture=texture, sprite_index=TILES['torch']) grid.entities.append(torch) grid.center = (4 * 16 + 8, 6 * 16 + 8) scene.activate() output_path = os.path.join(OUTPUT_DIR, "grid_fog_of_war.png") automation.screenshot(output_path) print(f" -> {output_path}") def screenshot_combat_melee(): """Create a melee combat showcase.""" scene = mcrfpy.Scene("combat_melee") texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) 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) # Title title = mcrfpy.Caption(text="Melee Combat Recipe", pos=(50, 20)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Bump-to-attack mechanics with damage calculation", pos=(50, 50)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Fill with dirt floor (battle arena feel) for y in range(9): for x in range(12): grid.at(x, y).tilesprite = 50 # dirt # Brick walls for x in range(12): grid.at(x, 0).tilesprite = TILES['wall_brick'] grid.at(x, 8).tilesprite = TILES['wall_brick'] for y in range(9): grid.at(0, y).tilesprite = TILES['wall_brick'] grid.at(11, y).tilesprite = TILES['wall_brick'] # Player knight engaging orc! player = mcrfpy.Entity(grid_pos=(4, 4), texture=texture, sprite_index=TILES['player_knight']) grid.entities.append(player) enemy = mcrfpy.Entity(grid_pos=(6, 4), texture=texture, sprite_index=TILES['enemy_orc']) grid.entities.append(enemy) # Fallen enemy (bones) bones = mcrfpy.Entity(grid_pos=(8, 6), texture=texture, sprite_index=75) # bones grid.entities.append(bones) # Potion for healing potion = mcrfpy.Entity(grid_pos=(3, 2), texture=texture, sprite_index=TILES['item_potion']) grid.entities.append(potion) grid.center = (5 * 16 + 8, 4 * 16 + 8) # Combat log UI log_frame = mcrfpy.Frame(pos=(50, 520), size=(700, 60)) log_frame.fill_color = mcrfpy.Color(30, 30, 40, 220) log_frame.outline = 1 log_frame.outline_color = mcrfpy.Color(60, 60, 80) scene.children.append(log_frame) msg1 = mcrfpy.Caption(text="You hit the Orc for 8 damage!", pos=(10, 10)) msg1.fill_color = mcrfpy.Color(255, 200, 100) msg1.font_size = 14 log_frame.children.append(msg1) msg2 = mcrfpy.Caption(text="The Orc hits you for 4 damage!", pos=(10, 30)) msg2.fill_color = mcrfpy.Color(255, 100, 100) msg2.font_size = 14 log_frame.children.append(msg2) scene.activate() output_path = os.path.join(OUTPUT_DIR, "combat_melee.png") automation.screenshot(output_path) print(f" -> {output_path}") def screenshot_dungeon_generator(): """Create a dungeon generator showcase.""" scene = mcrfpy.Scene("dungeon_gen") texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) grid = mcrfpy.Grid( pos=(50, 80), size=(700, 480), grid_size=(24, 16), texture=texture, zoom=2.0 ) grid.fill_color = mcrfpy.Color(10, 10, 15) scene.children.append(grid) # Title title = mcrfpy.Caption(text="Dungeon Generator Recipe", pos=(50, 20)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Procedural rooms connected by corridors", pos=(50, 50)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Fill with walls for y in range(16): for x in range(24): grid.at(x, y).tilesprite = TILES['wall_stone'] # Carve rooms rooms = [ (2, 2, 6, 5), # Room 1 (10, 2, 7, 5), # Room 2 (18, 3, 5, 4), # Room 3 (2, 9, 5, 5), # Room 4 (10, 10, 6, 5), # Room 5 (18, 9, 5, 6), # Room 6 ] for rx, ry, rw, rh in rooms: for y in range(ry, ry + rh): for x in range(rx, rx + rw): if x < 24 and y < 16: grid.at(x, y).tilesprite = TILES['floor_stone'] # Carve corridors (horizontal and vertical) # Room 1 to Room 2 for x in range(7, 11): grid.at(x, 4).tilesprite = 50 # dirt corridor # Room 2 to Room 3 for x in range(16, 19): grid.at(x, 4).tilesprite = 50 # Room 1 to Room 4 for y in range(6, 10): grid.at(4, y).tilesprite = 50 # Room 2 to Room 5 for y in range(6, 11): grid.at(13, y).tilesprite = 50 # Room 3 to Room 6 for y in range(6, 10): grid.at(20, y).tilesprite = 50 # Room 5 to Room 6 for x in range(15, 19): grid.at(x, 12).tilesprite = 50 # Add player in first room player = mcrfpy.Entity(grid_pos=(4, 4), texture=texture, sprite_index=TILES['player_knight']) grid.entities.append(player) # Add decorations grid.entities.append(mcrfpy.Entity(grid_pos=(3, 3), texture=texture, sprite_index=TILES['torch'])) grid.entities.append(mcrfpy.Entity(grid_pos=(12, 4), texture=texture, sprite_index=TILES['torch'])) grid.entities.append(mcrfpy.Entity(grid_pos=(19, 11), texture=texture, sprite_index=TILES['chest_closed'])) grid.entities.append(mcrfpy.Entity(grid_pos=(13, 12), texture=texture, sprite_index=TILES['enemy_slime'])) grid.entities.append(mcrfpy.Entity(grid_pos=(20, 5), texture=texture, sprite_index=TILES['enemy_skeleton'])) grid.center = (12 * 16, 8 * 16) scene.activate() output_path = os.path.join(OUTPUT_DIR, "grid_dungeon_generator.png") automation.screenshot(output_path) print(f" -> {output_path}") def screenshot_floating_text(): """Create a floating text/damage numbers showcase.""" scene = mcrfpy.Scene("floating_text") texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) grid = mcrfpy.Grid( pos=(50, 100), size=(700, 420), grid_size=(12, 8), texture=texture, zoom=3.5 ) grid.fill_color = mcrfpy.Color(20, 20, 30) scene.children.append(grid) # Title title = mcrfpy.Caption(text="Floating Text Recipe", pos=(50, 20)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Animated damage numbers and status messages", pos=(50, 50)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Fill floor for y in range(8): for x in range(12): grid.at(x, y).tilesprite = TILES['floor_stone'] # Walls for x in range(12): grid.at(x, 0).tilesprite = TILES['wall_stone'] grid.at(x, 7).tilesprite = TILES['wall_stone'] for y in range(8): grid.at(0, y).tilesprite = TILES['wall_stone'] grid.at(11, y).tilesprite = TILES['wall_stone'] # Player and enemy in combat player = mcrfpy.Entity(grid_pos=(4, 4), texture=texture, sprite_index=TILES['player_warrior']) grid.entities.append(player) enemy = mcrfpy.Entity(grid_pos=(7, 4), texture=texture, sprite_index=TILES['enemy_orc']) grid.entities.append(enemy) grid.center = (5.5 * 16, 4 * 16) # Floating damage numbers (as captions positioned over entities) # These would normally animate upward dmg1 = mcrfpy.Caption(text="-12", pos=(330, 240)) dmg1.fill_color = mcrfpy.Color(255, 80, 80) dmg1.font_size = 24 scene.children.append(dmg1) dmg2 = mcrfpy.Caption(text="-5", pos=(500, 260)) dmg2.fill_color = mcrfpy.Color(255, 100, 100) dmg2.font_size = 20 scene.children.append(dmg2) crit = mcrfpy.Caption(text="CRITICAL!", pos=(280, 200)) crit.fill_color = mcrfpy.Color(255, 200, 50) crit.font_size = 18 scene.children.append(crit) heal = mcrfpy.Caption(text="+8", pos=(320, 280)) heal.fill_color = mcrfpy.Color(100, 255, 100) heal.font_size = 20 scene.children.append(heal) scene.activate() output_path = os.path.join(OUTPUT_DIR, "effects_floating_text.png") automation.screenshot(output_path) print(f" -> {output_path}") def screenshot_message_log(): """Create a message log showcase.""" scene = mcrfpy.Scene("message_log") # Dark background bg = mcrfpy.Frame(pos=(0, 0), size=(800, 600)) bg.fill_color = mcrfpy.Color(20, 20, 30) scene.children.append(bg) # Title title = mcrfpy.Caption(text="Message Log Recipe", pos=(50, 30)) title.fill_color = mcrfpy.Color(255, 255, 255) title.font_size = 28 scene.children.append(title) subtitle = mcrfpy.Caption(text="Scrollable combat and event messages", pos=(50, 60)) subtitle.fill_color = mcrfpy.Color(180, 180, 200) subtitle.font_size = 16 scene.children.append(subtitle) # Message log frame log_frame = mcrfpy.Frame(pos=(50, 100), size=(700, 400)) log_frame.fill_color = mcrfpy.Color(30, 30, 40) log_frame.outline = 2 log_frame.outline_color = mcrfpy.Color(60, 60, 80) scene.children.append(log_frame) # Sample messages with colors messages = [ ("Welcome to the dungeon!", mcrfpy.Color(200, 200, 255)), ("You see a dark corridor ahead.", mcrfpy.Color(180, 180, 180)), ("A goblin appears!", mcrfpy.Color(255, 200, 100)), ("You hit the Goblin for 8 damage!", mcrfpy.Color(255, 255, 150)), ("The Goblin hits you for 3 damage!", mcrfpy.Color(255, 100, 100)), ("You hit the Goblin for 12 damage! Critical hit!", mcrfpy.Color(255, 200, 50)), ("The Goblin dies!", mcrfpy.Color(150, 255, 150)), ("You found a Healing Potion.", mcrfpy.Color(100, 200, 255)), ("An Orc blocks your path!", mcrfpy.Color(255, 150, 100)), ("You drink the Healing Potion. +15 HP", mcrfpy.Color(100, 255, 100)), ("You hit the Orc for 6 damage!", mcrfpy.Color(255, 255, 150)), ("The Orc hits you for 8 damage!", mcrfpy.Color(255, 100, 100)), ] for i, (msg, color) in enumerate(messages): caption = mcrfpy.Caption(text=msg, pos=(15, 15 + i * 30)) caption.fill_color = color caption.font_size = 16 log_frame.children.append(caption) # Scroll indicator scroll = mcrfpy.Caption(text="▼ More messages below", pos=(580, 370)) scroll.fill_color = mcrfpy.Color(100, 100, 120) scroll.font_size = 12 log_frame.children.append(scroll) scene.activate() output_path = os.path.join(OUTPUT_DIR, "ui_message_log.png") automation.screenshot(output_path) print(f" -> {output_path}") def main(): """Generate all cookbook screenshots!""" os.makedirs(OUTPUT_DIR, exist_ok=True) print("=== Cookbook Screenshot Showcase ===") print(f"Output: {OUTPUT_DIR}\n") showcases = [ ('Health Bar UI', screenshot_health_bar), ('Fog of War', screenshot_fog_of_war), ('Melee Combat', screenshot_combat_melee), ('Dungeon Generator', screenshot_dungeon_generator), ('Floating Text', screenshot_floating_text), ('Message Log', screenshot_message_log), ] for name, func in showcases: print(f"Generating {name}...") try: func() except Exception as e: print(f" ERROR: {e}") import traceback traceback.print_exc() print("\n=== All cookbook screenshots generated! ===") sys.exit(0) main()