Refine start menu difficulty and preview workflow
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# Project Guidelines
|
||||
|
||||
## UI Preview Tool
|
||||
|
||||
When editing the start menu, pause menu, or level intro UI, generate a real preview image before judging layout changes.
|
||||
|
||||
Use [tools/render_menu_preview.py](tools/render_menu_preview.py) instead of relying on mental layout or ad-hoc screenshots. The tool renders the actual SDL scene and saves a PNG from the real renderer.
|
||||
|
||||
Typical command:
|
||||
|
||||
```bash
|
||||
/home/enne2/dev/mice/.venv/bin/python tools/render_menu_preview.py \
|
||||
--output /tmp/mice_start_preview.png \
|
||||
--screen start \
|
||||
--difficulty normal \
|
||||
--resolution 1280x720
|
||||
```
|
||||
|
||||
Supported screens:
|
||||
|
||||
- `start`
|
||||
- `pause`
|
||||
- `level_intro`
|
||||
|
||||
Useful flags:
|
||||
|
||||
- `--difficulty easy|normal|hard`
|
||||
- `--resolution WIDTHxHEIGHT`
|
||||
- `--output /path/to/file.png`
|
||||
- `--seed N` for deterministic previews
|
||||
- `--animation-ms N` to choose the GIF frame timestamp for the start menu
|
||||
|
||||
Workflow when touching menu layout:
|
||||
|
||||
1. Edit the menu code.
|
||||
2. Run the preview tool for the relevant screen.
|
||||
3. Inspect the generated PNG.
|
||||
4. Iterate until spacing and readability are correct.
|
||||
|
||||
The preview tool is intended for fast visual feedback and should be preferred before launching a full interactive game session for menu-only changes.
|
||||
Binary file not shown.
Binary file not shown.
@@ -20,6 +20,10 @@
|
||||
"keybinding_start_menu": {
|
||||
"keydown_Return": "reset_game",
|
||||
"keydown_Escape": "quit_game",
|
||||
"keydown_Up": "menu_up",
|
||||
"keydown_Down": "menu_down",
|
||||
"keydown_Left": "menu_left",
|
||||
"keydown_Right": "menu_right",
|
||||
"keydown_M": "toggle_audio",
|
||||
"keydown_F": "toggle_full_screen"
|
||||
},
|
||||
|
||||
@@ -17,6 +17,10 @@ keybinding_game:
|
||||
keybinding_start_menu:
|
||||
controllerbuttondown_a: reset_game
|
||||
controllerbuttondown_start: reset_game
|
||||
controllerbuttondown_dpad_up: menu_up
|
||||
controllerbuttondown_dpad_down: menu_down
|
||||
controllerbuttondown_dpad_left: menu_left
|
||||
controllerbuttondown_dpad_right: menu_right
|
||||
controllerbuttondown_b: quit_game
|
||||
controllerbuttondown_back: quit_game
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ keybinding_game:
|
||||
keybinding_start_menu:
|
||||
keydown_Return: reset_game
|
||||
keydown_Escape: quit_game
|
||||
keydown_Up: menu_up
|
||||
keydown_Down: menu_down
|
||||
keydown_Left: menu_left
|
||||
keydown_Right: menu_right
|
||||
keydown_M: toggle_audio
|
||||
keydown_F: toggle_full_screen
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ keybinding_game:
|
||||
|
||||
keybinding_start_menu:
|
||||
joybuttondown_13: reset_game
|
||||
joybuttondown_8: menu_up
|
||||
joybuttondown_9: menu_down
|
||||
joybuttondown_10: menu_left
|
||||
joybuttondown_11: menu_right
|
||||
joybuttondown_16: toggle_pause
|
||||
joybuttondown_12: quit_game
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ keybinding_game:
|
||||
|
||||
keybinding_start_menu:
|
||||
joybuttondown_9: reset_game
|
||||
joyhatmotion_0_1: menu_up
|
||||
joyhatmotion_0_4: menu_down
|
||||
joyhatmotion_0_8: menu_left
|
||||
joyhatmotion_0_2: menu_right
|
||||
joybuttondown_10: toggle_pause
|
||||
joybuttondown_11: quit_game
|
||||
|
||||
|
||||
+46
-2
@@ -2,6 +2,7 @@ import os
|
||||
import random
|
||||
import ctypes
|
||||
from ctypes import *
|
||||
from pathlib import Path
|
||||
|
||||
import sdl2
|
||||
import sdl2.ext
|
||||
@@ -72,8 +73,15 @@ class GameWindow:
|
||||
|
||||
print(f"Screen size: {self.width}x{self.height}")
|
||||
|
||||
self.joystick_enabled = os.environ.get("MICE_DISABLE_JOYSTICK", "").strip().lower() not in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
"on",
|
||||
}
|
||||
|
||||
# SDL2 initialization
|
||||
sdl2.ext.init(joystick=True)
|
||||
sdl2.ext.init(joystick=self.joystick_enabled)
|
||||
sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO)
|
||||
|
||||
# Window and renderer setup
|
||||
@@ -119,7 +127,8 @@ class GameWindow:
|
||||
self._init_audio_system()
|
||||
self.audio = True
|
||||
# Input devices
|
||||
self.load_joystick()
|
||||
if self.joystick_enabled:
|
||||
self.load_joystick()
|
||||
|
||||
def _init_audio_system(self):
|
||||
"""Initialize audio devices for different audio channels"""
|
||||
@@ -579,6 +588,8 @@ class GameWindow:
|
||||
|
||||
def load_joystick(self):
|
||||
"""Initialize joystick and game controller support."""
|
||||
if not getattr(self, "joystick_enabled", True):
|
||||
return
|
||||
sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_GAMECONTROLLER)
|
||||
sdl2.SDL_JoystickEventState(sdl2.SDL_ENABLE)
|
||||
if hasattr(sdl2, "SDL_GameControllerEventState"):
|
||||
@@ -844,6 +855,39 @@ class GameWindow:
|
||||
"""Placeholder for cycle management (not implemented)"""
|
||||
pass
|
||||
|
||||
def capture_frame(self):
|
||||
"""Capture the current renderer output as a PIL image."""
|
||||
width, height = self.target_size
|
||||
pitch = width * 4
|
||||
pixel_buffer = (ctypes.c_ubyte * (pitch * height))()
|
||||
result = sdl2.SDL_RenderReadPixels(
|
||||
self.renderer.sdlrenderer,
|
||||
None,
|
||||
sdl2.SDL_PIXELFORMAT_RGBA32,
|
||||
pixel_buffer,
|
||||
pitch,
|
||||
)
|
||||
if result != 0:
|
||||
raise RuntimeError(f"Failed to capture frame: {sdl2.SDL_GetError()}")
|
||||
|
||||
return Image.frombuffer(
|
||||
"RGBA",
|
||||
(width, height),
|
||||
bytes(pixel_buffer),
|
||||
"raw",
|
||||
"RGBA",
|
||||
0,
|
||||
1,
|
||||
).copy()
|
||||
|
||||
def save_frame(self, path):
|
||||
"""Save the current renderer output to an image file."""
|
||||
output_path = Path(path).expanduser()
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
image = self.capture_frame()
|
||||
image.save(output_path)
|
||||
return output_path
|
||||
|
||||
def full_screen(self, flag):
|
||||
"""Toggle fullscreen mode"""
|
||||
sdl2.SDL_SetWindowFullscreen(self.window.window, flag)
|
||||
|
||||
@@ -17,11 +17,44 @@ LEVEL_MUSIC_CONFIG = "level_music"
|
||||
START_MENU_MUSIC = "High_Score_Garden.mp3"
|
||||
START_MENU_ANIMATION = "anim/start_mice.gif"
|
||||
SUPPORTED_MUSIC_EXTENSIONS = {".mp3", ".ogg", ".wav"}
|
||||
BASE_INITIAL_RATS = 5
|
||||
DEFAULT_DIFFICULTY = "easy"
|
||||
DIFFICULTY_ALIASES = {
|
||||
"medium": "normal",
|
||||
"normale": "normal",
|
||||
}
|
||||
START_MENU_AUDIO_OPTIONS = (
|
||||
("sound_volume", "Suono"),
|
||||
("music_volume", "Musica"),
|
||||
)
|
||||
VOLUME_STEP = 5
|
||||
DIFFICULTY_OPTIONS = (
|
||||
{
|
||||
"key": "easy",
|
||||
"label": "Easy",
|
||||
"starting_rats_multiplier": 1,
|
||||
"speed_multiplier": 1.0,
|
||||
"fill": (235, 246, 234),
|
||||
"accent": (88, 148, 82),
|
||||
},
|
||||
{
|
||||
"key": "normal",
|
||||
"label": "Normal",
|
||||
"starting_rats_multiplier": 2,
|
||||
"speed_multiplier": 1.5,
|
||||
"fill": (252, 242, 223),
|
||||
"accent": (204, 146, 44),
|
||||
},
|
||||
{
|
||||
"key": "hard",
|
||||
"label": "Hard",
|
||||
"starting_rats_multiplier": 3,
|
||||
"speed_multiplier": 2.0,
|
||||
"fill": (251, 229, 229),
|
||||
"accent": (188, 68, 68),
|
||||
},
|
||||
)
|
||||
DIFFICULTY_OPTIONS_BY_KEY = {option["key"]: option for option in DIFFICULTY_OPTIONS}
|
||||
START_MENU_COLORS = {
|
||||
"panel_fill": (255, 255, 255),
|
||||
"panel_border": (52, 52, 52),
|
||||
@@ -65,6 +98,10 @@ class MiceMaze(
|
||||
self.audio = self.profile_integration.get_setting('sound_enabled', True)
|
||||
self.sound_volume = self.profile_integration.get_setting('sound_volume', 50)
|
||||
self.music_volume = self.profile_integration.get_setting('music_volume', self.sound_volume)
|
||||
self.difficulty = DEFAULT_DIFFICULTY
|
||||
self.initial_rat_count = BASE_INITIAL_RATS
|
||||
self.rat_speed_multiplier = 1.0
|
||||
self._apply_difficulty(self.profile_integration.get_setting('difficulty', DEFAULT_DIFFICULTY), persist=False)
|
||||
|
||||
self.cell_size = 40
|
||||
self.full_screen = False
|
||||
@@ -166,9 +203,52 @@ class MiceMaze(
|
||||
fallback_track = random.choice(self.available_music_tracks)
|
||||
print(f"[audio] level {level_number} music={fallback_track} source=random")
|
||||
return fallback_track
|
||||
|
||||
def _normalize_difficulty(self, difficulty_key):
|
||||
difficulty_key = DIFFICULTY_ALIASES.get(difficulty_key, difficulty_key)
|
||||
if difficulty_key in DIFFICULTY_OPTIONS_BY_KEY:
|
||||
return difficulty_key
|
||||
return DEFAULT_DIFFICULTY
|
||||
|
||||
def _difficulty_config(self):
|
||||
return DIFFICULTY_OPTIONS_BY_KEY[self.difficulty]
|
||||
|
||||
def _apply_difficulty(self, difficulty_key, persist=True):
|
||||
normalized_difficulty = self._normalize_difficulty(difficulty_key)
|
||||
difficulty_config = DIFFICULTY_OPTIONS_BY_KEY[normalized_difficulty]
|
||||
self.difficulty = normalized_difficulty
|
||||
self.initial_rat_count = max(
|
||||
1,
|
||||
int(round(BASE_INITIAL_RATS * difficulty_config["starting_rats_multiplier"])),
|
||||
)
|
||||
self.rat_speed_multiplier = difficulty_config["speed_multiplier"]
|
||||
if persist:
|
||||
self.profile_integration.set_setting("difficulty", normalized_difficulty)
|
||||
|
||||
def _can_adjust_start_difficulty(self):
|
||||
if self.game_end[0]:
|
||||
return False
|
||||
return self.game_status == "start_menu" and self.menu_screen == "start"
|
||||
|
||||
def _cycle_difficulty(self, delta):
|
||||
if not self._can_adjust_start_difficulty():
|
||||
return
|
||||
|
||||
current_index = 0
|
||||
for index, option in enumerate(DIFFICULTY_OPTIONS):
|
||||
if option["key"] == self.difficulty:
|
||||
current_index = index
|
||||
break
|
||||
|
||||
next_index = (current_index + delta) % len(DIFFICULTY_OPTIONS)
|
||||
self._apply_difficulty(DIFFICULTY_OPTIONS[next_index]["key"])
|
||||
|
||||
def start_game(self):
|
||||
print(f"[flow] start_game: level={self.current_level + 1}")
|
||||
print(
|
||||
f"[flow] start_game: level={self.current_level + 1} "
|
||||
f"difficulty={self.difficulty} starting_rats={self.initial_rat_count} "
|
||||
f"rat_speed={int(self.rat_speed_multiplier * 100)}%"
|
||||
)
|
||||
self.start_menu_animation_started_at = time.monotonic()
|
||||
self.current_level_music = self._resolve_level_music(self.current_level)
|
||||
self._valid_positions = None
|
||||
@@ -208,7 +288,7 @@ class MiceMaze(
|
||||
self.pointer = (random.randint(1, self.map.width-2), random.randint(1, self.map.height-2))
|
||||
self.scroll_cursor()
|
||||
|
||||
for _ in range(5):
|
||||
for _ in range(self.initial_rat_count):
|
||||
self.spawn_rat()
|
||||
|
||||
def load_level(self, level_index, preserve_points=True, show_menu=False, menu_screen=None):
|
||||
@@ -258,8 +338,12 @@ class MiceMaze(
|
||||
self.run_recorded = False
|
||||
print("[flow] points reset for new run")
|
||||
|
||||
for spawn_index in range(5):
|
||||
print(f"[flow] spawning rat {spawn_index + 1}/5 for level={self.current_level + 1}", flush=True)
|
||||
print(
|
||||
f"[flow] spawning {self.initial_rat_count} rats for level={self.current_level + 1} "
|
||||
f"difficulty={self.difficulty}",
|
||||
flush=True,
|
||||
)
|
||||
for _ in range(self.initial_rat_count):
|
||||
self.spawn_rat()
|
||||
|
||||
if show_menu:
|
||||
@@ -302,6 +386,9 @@ class MiceMaze(
|
||||
|
||||
if self.game_status == "start_menu":
|
||||
print(f"[flow] reset_game -> leaving menu_screen={self.menu_screen} and entering gameplay")
|
||||
if self.menu_screen == "start":
|
||||
self.load_level(0, preserve_points=False, show_menu=False)
|
||||
return
|
||||
self.game_status = "game"
|
||||
self.menu_screen = None
|
||||
return
|
||||
@@ -329,6 +416,37 @@ class MiceMaze(
|
||||
return False
|
||||
return self.game_status == "paused"
|
||||
|
||||
def _draw_start_menu_difficulty_selector(self, x, y, width, height):
|
||||
colors = START_MENU_COLORS
|
||||
difficulty_config = self._difficulty_config()
|
||||
accent = difficulty_config["accent"]
|
||||
render_engine = self.render_engine
|
||||
center_x = x + width // 2
|
||||
compact_selector = height <= 72
|
||||
title_font = self._menu_font(render_engine.target_size[1] // 38)
|
||||
value_font = self._menu_font(render_engine.target_size[1] // 23)
|
||||
arrow_font = value_font
|
||||
section_gap = 4 if compact_selector else 6
|
||||
title_line_height = max(14, render_engine.target_size[1] // 32)
|
||||
value_line_height = max(18, render_engine.target_size[1] // 24)
|
||||
current_y = y + 2
|
||||
|
||||
render_engine.draw_text("Difficulty", title_font, ("center", current_y), colors["muted"])
|
||||
current_y += title_line_height + section_gap
|
||||
|
||||
arrow_offset = max(34, min(52, width // 7))
|
||||
arrow_y = current_y - (1 if compact_selector else 0)
|
||||
render_engine.draw_text("<", arrow_font, (center_x - arrow_offset, arrow_y), colors["muted"])
|
||||
render_engine.draw_text(">", arrow_font, (center_x + arrow_offset, arrow_y), colors["muted"])
|
||||
|
||||
render_engine.draw_text(
|
||||
difficulty_config["label"],
|
||||
value_font,
|
||||
("center", current_y),
|
||||
accent,
|
||||
)
|
||||
current_y += value_line_height
|
||||
|
||||
def _menu_font(self, size):
|
||||
clamped = max(10, min(69, int(size)))
|
||||
return self.render_engine.fonts[clamped]
|
||||
@@ -549,19 +667,44 @@ class MiceMaze(
|
||||
colors["text"] if index == 0 else colors["muted"],
|
||||
)
|
||||
|
||||
cta_y = info_y + line_gap * len(subtitle_lines) + 18
|
||||
difficulty_width = max(360, min(panel_width - 160, 760))
|
||||
difficulty_height = 54 if compact_menu else max(72, target_height // 9)
|
||||
difficulty_x = target_width // 2 - difficulty_width // 2
|
||||
difficulty_y = info_y + line_gap * len(subtitle_lines) + (12 if compact_menu else 18)
|
||||
self._draw_start_menu_difficulty_selector(
|
||||
difficulty_x,
|
||||
difficulty_y,
|
||||
difficulty_width,
|
||||
difficulty_height,
|
||||
)
|
||||
|
||||
cta_y = difficulty_y + difficulty_height + (12 if compact_menu else 24)
|
||||
render_engine.draw_text(
|
||||
"Press Return to start",
|
||||
cta_font,
|
||||
("center", cta_y),
|
||||
colors["text"],
|
||||
)
|
||||
render_engine.draw_text(
|
||||
"Esc quits M toggles audio",
|
||||
hint_font,
|
||||
("center", cta_y + line_gap + 8),
|
||||
colors["muted"],
|
||||
)
|
||||
if compact_menu:
|
||||
render_engine.draw_text(
|
||||
"Arrows change difficulty",
|
||||
hint_font,
|
||||
("center", cta_y + line_gap),
|
||||
colors["muted"],
|
||||
)
|
||||
render_engine.draw_text(
|
||||
"Esc quits M toggles audio",
|
||||
hint_font,
|
||||
("center", cta_y + line_gap + 18),
|
||||
colors["muted"],
|
||||
)
|
||||
else:
|
||||
render_engine.draw_text(
|
||||
"Arrows change difficulty Esc quits M toggles audio",
|
||||
hint_font,
|
||||
("center", cta_y + line_gap + 8),
|
||||
colors["muted"],
|
||||
)
|
||||
|
||||
def render_pause_menu(self):
|
||||
subtitle_lines = [
|
||||
@@ -588,11 +731,17 @@ class MiceMaze(
|
||||
self.profile_integration.set_setting(setting_name, clamped)
|
||||
|
||||
def menu_up(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(-1)
|
||||
return
|
||||
if not self._can_adjust_audio_menu():
|
||||
return
|
||||
self.start_menu_selection = (self.start_menu_selection - 1) % len(START_MENU_AUDIO_OPTIONS)
|
||||
|
||||
def menu_down(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(1)
|
||||
return
|
||||
if not self._can_adjust_audio_menu():
|
||||
return
|
||||
self.start_menu_selection = (self.start_menu_selection + 1) % len(START_MENU_AUDIO_OPTIONS)
|
||||
@@ -605,9 +754,15 @@ class MiceMaze(
|
||||
self._apply_volume_setting(setting_name, current_value + delta)
|
||||
|
||||
def menu_left(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(-1)
|
||||
return
|
||||
self._adjust_selected_volume(-VOLUME_STEP)
|
||||
|
||||
def menu_right(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(1)
|
||||
return
|
||||
self._adjust_selected_volume(VOLUME_STEP)
|
||||
|
||||
def update_background_music(self):
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Render a real Mice menu screen to a PNG preview")
|
||||
parser.add_argument("--output", default="/tmp/mice_menu_preview.png", help="Output PNG path")
|
||||
parser.add_argument("--screen", choices=("start", "pause", "level_intro"), default="start")
|
||||
parser.add_argument("--difficulty", choices=("easy", "normal", "hard", "medium"), default="hard")
|
||||
parser.add_argument("--level", type=int, default=0, help="0-based level index")
|
||||
parser.add_argument("--resolution", default="1280x720", help="Preview resolution, for example 1280x720")
|
||||
parser.add_argument("--seed", type=int, default=1337, help="Random seed for deterministic previews")
|
||||
parser.add_argument("--animation-ms", type=int, default=1200, help="Start-menu GIF timestamp in milliseconds")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def configure_environment(args):
|
||||
os.environ.setdefault("MICE_PROJECT_ROOT", str(PROJECT_ROOT))
|
||||
os.environ.setdefault("SDL_VIDEODRIVER", "dummy")
|
||||
os.environ.setdefault("SDL_AUDIODRIVER", "dummy")
|
||||
os.environ.setdefault("SDL_RENDER_DRIVER", "software")
|
||||
os.environ.setdefault("MICE_DISABLE_JOYSTICK", "1")
|
||||
os.environ["RESOLUTION"] = args.resolution
|
||||
|
||||
|
||||
def build_game(args):
|
||||
from engine import maze, sdl2 as engine
|
||||
from rats import MiceMaze
|
||||
|
||||
original_show_intro = engine.GameWindow.show_intro
|
||||
original_load_joystick = engine.GameWindow.load_joystick
|
||||
original_start_game = MiceMaze.start_game
|
||||
|
||||
def preview_start_game(self):
|
||||
self.start_menu_animation_started_at = time.monotonic()
|
||||
self.current_level_music = self._resolve_level_music(self.current_level)
|
||||
self._valid_positions = None
|
||||
self.run_recorded = False
|
||||
self.ammo = {
|
||||
"bomb": {"count": 2, "max": 8},
|
||||
"nuclear": {"count": 1, "max": 1},
|
||||
"mine": {"count": 2, "max": 4},
|
||||
"gas": {"count": 2, "max": 4},
|
||||
}
|
||||
self.blood_stains = {}
|
||||
self.background_texture = None
|
||||
self.blood_layer_sprites.clear()
|
||||
self.cave_foreground_tiles.clear()
|
||||
self.game_end = (False, None)
|
||||
self.game_status = "start_menu"
|
||||
self.menu_screen = "start"
|
||||
self.units.clear()
|
||||
self.unit_positions.clear()
|
||||
self.unit_positions_before.clear()
|
||||
self.points = 0
|
||||
self.pointer = (self.map.width // 2, self.map.height // 2)
|
||||
self.scroll_cursor()
|
||||
|
||||
engine.GameWindow.show_intro = lambda self, *a, **k: None
|
||||
engine.GameWindow.load_joystick = lambda self: None
|
||||
MiceMaze.start_game = preview_start_game
|
||||
try:
|
||||
game = MiceMaze(maze.get_default_map_source(), level_index=args.level)
|
||||
finally:
|
||||
engine.GameWindow.show_intro = original_show_intro
|
||||
engine.GameWindow.load_joystick = original_load_joystick
|
||||
MiceMaze.start_game = original_start_game
|
||||
|
||||
game.render_engine.audio = False
|
||||
game._apply_difficulty(args.difficulty, persist=False)
|
||||
game.start_menu_animation_started_at = time.monotonic() - args.animation_ms / 1000
|
||||
return game
|
||||
|
||||
|
||||
def render_screen(game, screen_name):
|
||||
renderer = game.render_engine.renderer
|
||||
renderer.clear()
|
||||
game.draw_maze()
|
||||
|
||||
if screen_name == "start":
|
||||
game.game_status = "start_menu"
|
||||
game.menu_screen = "start"
|
||||
game.render_start_menu()
|
||||
elif screen_name == "pause":
|
||||
game.game_status = "paused"
|
||||
game.menu_screen = None
|
||||
game.render_pause_menu()
|
||||
else:
|
||||
game.game_status = "start_menu"
|
||||
game.menu_screen = "level_intro"
|
||||
game.render_engine.dialog(
|
||||
f"Level {game.current_level + 1}",
|
||||
subtitle=f"Points: {game.points}\nPress Return to begin",
|
||||
image=game.assets["BMP_WEWIN"],
|
||||
)
|
||||
|
||||
renderer.present()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
configure_environment(args)
|
||||
random.seed(args.seed)
|
||||
|
||||
game = build_game(args)
|
||||
output_path = Path(args.output).expanduser()
|
||||
|
||||
try:
|
||||
render_screen(game, args.screen)
|
||||
saved_path = game.render_engine.save_frame(output_path)
|
||||
print(saved_path)
|
||||
finally:
|
||||
game.render_engine.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+1
-1
@@ -16,7 +16,7 @@ class Rat(Unit):
|
||||
def __init__(self, game, position=(0,0), id=None):
|
||||
super().__init__(game, position, id, collision_layer=CollisionLayer.RAT)
|
||||
# Specific attributes for rats
|
||||
self.speed = 0.10 # Rats are slower
|
||||
self.speed = 0.10 * getattr(self.game, "rat_speed_multiplier", 1.0)
|
||||
self.fight = False
|
||||
self.gassed = 0
|
||||
self.direction = "DOWN" # Default direction
|
||||
|
||||
Reference in New Issue
Block a user