Add non-regression test states and Bluetooth diagnostic script
- Introduced a new JSON file containing non-regression test states with detailed unit information, including positions, ages, and movement directions across multiple frames. - Added a shell script for Bluetooth diagnostics that checks system information, Bluetooth binaries, running processes, D-Bus status, Bluetooth controller details, and audio stack status, providing a comprehensive overview for troubleshooting.
This commit is contained in:
@@ -6,87 +6,15 @@ import os
|
||||
import json
|
||||
import time
|
||||
|
||||
from engine import maze, sdl2 as engine, controls, graphics, unit_manager, scoring
|
||||
from engine import maze, sdl2 as engine, controls, graphics, unit_manager, scoring, state_machine, config
|
||||
from engine.state_machine import GameState
|
||||
from engine.collision_system import CollisionSystem
|
||||
from units import points
|
||||
from engine.user_profile_integration import UserProfileIntegration
|
||||
from runtime_paths import bundle_path
|
||||
|
||||
|
||||
LEVEL_MUSIC_CONFIG = "level_music"
|
||||
START_MENU_MUSIC = "High_Score_Garden.mp3"
|
||||
RUN_COMPLETE_MUSIC = "Sunset_At_Pixel_Gardens.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
|
||||
GAME_END_LEVEL_CLEAR = "level_clear"
|
||||
GAME_END_DEFEAT = "defeat"
|
||||
GAME_END_RUN_COMPLETE = "run_complete"
|
||||
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),
|
||||
"header_fill": (255, 255, 255),
|
||||
"text": (24, 24, 24),
|
||||
"muted": (82, 82, 82),
|
||||
"hint_fill": (255, 255, 255),
|
||||
"track_fill": (212, 215, 216),
|
||||
"card_fill": (255, 255, 255),
|
||||
}
|
||||
START_MENU_AUDIO_STYLES = {
|
||||
"sound_volume": {
|
||||
"accent": (214, 146, 62),
|
||||
"fill": (251, 241, 225),
|
||||
},
|
||||
"music_volume": {
|
||||
"accent": (91, 122, 208),
|
||||
"fill": (230, 235, 248),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class MiceMaze(
|
||||
controls.KeyBindings,
|
||||
unit_manager.UnitManager,
|
||||
graphics.Graphics,
|
||||
scoring.Scoring
|
||||
):
|
||||
class MiceMaze:
|
||||
|
||||
# ==================== INITIALIZATION ====================
|
||||
|
||||
@@ -95,6 +23,13 @@ class MiceMaze(
|
||||
self.render_engine.show_loading_screen(message, detail=detail, progress=progress)
|
||||
|
||||
def __init__(self, maze_file, level_index=0):
|
||||
self._setup_initial_state(maze_file, level_index)
|
||||
self._setup_engine()
|
||||
self._setup_components()
|
||||
self._setup_game_assets()
|
||||
self._setup_initial_units()
|
||||
|
||||
def _setup_initial_state(self, maze_file, level_index):
|
||||
# Initialize user profile integration
|
||||
self.profile_integration = UserProfileIntegration()
|
||||
self.map_source = maze_file
|
||||
@@ -106,35 +41,55 @@ 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.difficulty = config.DEFAULT_DIFFICULTY
|
||||
self.initial_rat_count = config.BASE_INITIAL_RATS
|
||||
self.rat_speed_multiplier = 1.0
|
||||
self._apply_difficulty(self.profile_integration.get_setting('difficulty', DEFAULT_DIFFICULTY), persist=False)
|
||||
self._apply_difficulty(self.profile_integration.get_setting('difficulty', config.DEFAULT_DIFFICULTY), persist=False)
|
||||
|
||||
self.cell_size = 40
|
||||
self.full_screen = False
|
||||
self.loaded_theme_index = None
|
||||
self.start_menu_selection = 0
|
||||
|
||||
# Initialize render engine with profile-aware title
|
||||
self.points = 0
|
||||
self.units = {}
|
||||
self.unit_positions = {}
|
||||
self.unit_positions_before = {}
|
||||
self.scrolling_direction = None
|
||||
self.game_status = "start_menu"
|
||||
self.menu_screen = "start"
|
||||
self.game_end = (False, None)
|
||||
self.run_recorded = False
|
||||
self.scrolling = False
|
||||
self.sounds = {}
|
||||
self.background_texture = None
|
||||
self.combined_scores = None
|
||||
|
||||
def _setup_engine(self):
|
||||
player_name = self.profile_integration.get_profile_name()
|
||||
window_title = f"Mice! - {player_name}"
|
||||
|
||||
self.render_engine = engine.GameWindow(self.map.width, self.map.height,
|
||||
self.cell_size, window_title,
|
||||
key_callback=self.trigger)
|
||||
key_callback=lambda action: self.controls.trigger(action))
|
||||
self.render_engine.audio = self.audio
|
||||
self.startup_loading_active = True
|
||||
self._update_startup_loading("Loading Mice!", detail="Applying player settings", progress=0.08)
|
||||
|
||||
# Apply profile settings
|
||||
# Apply profile volumes
|
||||
if hasattr(self.render_engine, 'set_sound_volume'):
|
||||
self.render_engine.set_sound_volume(self.sound_volume)
|
||||
if hasattr(self.render_engine, 'set_music_volume'):
|
||||
self.render_engine.set_music_volume(self.music_volume)
|
||||
elif hasattr(self.render_engine, 'set_volume'):
|
||||
self.render_engine.set_volume(self.music_volume)
|
||||
self.initialize_keybindings()
|
||||
|
||||
def _setup_components(self):
|
||||
self.scoring = scoring.Scoring(self)
|
||||
self.unit_manager = unit_manager.UnitManager(self)
|
||||
self.graphics = graphics.Graphics(self)
|
||||
self.controls = controls.KeyBindings(self)
|
||||
self.state_machine = state_machine.StateMachine(self)
|
||||
self.collision_system = CollisionSystem(self.cell_size, self.map.width, self.map.height)
|
||||
|
||||
def _setup_game_assets(self):
|
||||
self._update_startup_loading("Loading Mice!", detail="Applying player settings", progress=0.08)
|
||||
self.controls.initialize_keybindings()
|
||||
|
||||
self._update_startup_loading("Loading configuration", detail="Reading bundled settings", progress=0.14)
|
||||
self.configs = self.get_config()
|
||||
@@ -144,46 +99,26 @@ class MiceMaze(
|
||||
self.current_level_music = None
|
||||
|
||||
self._update_startup_loading("Loading graphics", detail="Preparing common assets", progress=0.26)
|
||||
self.load_assets()
|
||||
self.graphics.load_assets()
|
||||
|
||||
self._update_startup_loading("Loading start menu", detail="Preparing menu animation", progress=0.9)
|
||||
self.start_menu_animation = self.render_engine.load_animation(START_MENU_ANIMATION)
|
||||
self.start_menu_animation = self.render_engine.load_animation(config.START_MENU_ANIMATION)
|
||||
self.start_menu_animation_started_at = time.monotonic()
|
||||
|
||||
def _setup_initial_units(self):
|
||||
self._update_startup_loading("Starting", detail="Opening intro screen", progress=0.98)
|
||||
self.render_engine.show_intro(bundle_path("assets", "Rat", "intro.png"))
|
||||
self.startup_loading_active = False
|
||||
|
||||
self.pointer = (random.randint(1, self.map.width-2), random.randint(1, self.map.height-2))
|
||||
self.scroll_cursor()
|
||||
self.points = 0
|
||||
self.units = {}
|
||||
self.graphics.scroll_cursor()
|
||||
|
||||
# Initialize optimized collision system with NumPy
|
||||
self.collision_system = CollisionSystem(
|
||||
self.cell_size,
|
||||
self.map.width,
|
||||
self.map.height
|
||||
)
|
||||
|
||||
# Keep old dictionaries for backward compatibility (can be removed later)
|
||||
self.unit_positions = {}
|
||||
self.unit_positions_before = {}
|
||||
|
||||
self.scrolling_direction = None
|
||||
self.game_status = "start_menu"
|
||||
self.menu_screen = "start"
|
||||
self.game_end = (False, None)
|
||||
self.run_recorded = False
|
||||
self.scrolling = False
|
||||
self.sounds = {}
|
||||
self.start_game()
|
||||
self.background_texture = None
|
||||
self.combined_scores = None
|
||||
|
||||
|
||||
def get_config(self):
|
||||
configs = {}
|
||||
conf_dir = bundle_path("conf")
|
||||
for file in os.listdir(conf_dir):
|
||||
for file in sorted(os.listdir(conf_dir)):
|
||||
if file.endswith(".json"):
|
||||
with open(os.path.join(conf_dir, file), encoding="utf-8") as f:
|
||||
configs[file[:-5]] = json.load(f)
|
||||
@@ -194,7 +129,7 @@ class MiceMaze(
|
||||
tracks = []
|
||||
for file_name in sorted(os.listdir(music_dir)):
|
||||
_, extension = os.path.splitext(file_name)
|
||||
if extension.lower() not in SUPPORTED_MUSIC_EXTENSIONS:
|
||||
if extension.lower() not in config.SUPPORTED_MUSIC_EXTENSIONS:
|
||||
continue
|
||||
if os.path.isfile(os.path.join(music_dir, file_name)):
|
||||
tracks.append(file_name)
|
||||
@@ -202,7 +137,7 @@ class MiceMaze(
|
||||
|
||||
def _resolve_level_music(self, level_index):
|
||||
level_number = level_index + 1
|
||||
level_music_config = self.configs.get(LEVEL_MUSIC_CONFIG, {})
|
||||
level_music_config = self.configs.get(config.LEVEL_MUSIC_CONFIG, {})
|
||||
configured_tracks = level_music_config.get("levels", {})
|
||||
configured_track = configured_tracks.get(str(level_number))
|
||||
|
||||
@@ -222,21 +157,21 @@ class MiceMaze(
|
||||
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:
|
||||
difficulty_key = config.DIFFICULTY_ALIASES.get(difficulty_key, difficulty_key)
|
||||
if difficulty_key in config.DIFFICULTY_OPTIONS_BY_KEY:
|
||||
return difficulty_key
|
||||
return DEFAULT_DIFFICULTY
|
||||
return config.DEFAULT_DIFFICULTY
|
||||
|
||||
def _difficulty_config(self):
|
||||
return DIFFICULTY_OPTIONS_BY_KEY[self.difficulty]
|
||||
return config.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]
|
||||
difficulty_config = 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"])),
|
||||
int(round(config.BASE_INITIAL_RATS * difficulty_config["starting_rats_multiplier"])),
|
||||
)
|
||||
self.rat_speed_multiplier = difficulty_config["speed_multiplier"]
|
||||
if persist:
|
||||
@@ -245,20 +180,20 @@ class MiceMaze(
|
||||
def _can_adjust_start_difficulty(self):
|
||||
if self.game_end[0]:
|
||||
return False
|
||||
return self.game_status == "start_menu" and self.menu_screen == "start"
|
||||
return self.state_machine.current_state == GameState.START_MENU
|
||||
|
||||
def _cycle_difficulty(self, delta):
|
||||
if not self._can_adjust_start_difficulty():
|
||||
return
|
||||
|
||||
current_index = 0
|
||||
for index, option in enumerate(DIFFICULTY_OPTIONS):
|
||||
for index, option in enumerate(config.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"])
|
||||
next_index = (current_index + delta) % len(config.DIFFICULTY_OPTIONS)
|
||||
self._apply_difficulty(config.DIFFICULTY_OPTIONS[next_index]["key"])
|
||||
|
||||
def _is_dat_campaign(self):
|
||||
return self.map.source_path.suffix.lower() == ".dat"
|
||||
@@ -273,7 +208,7 @@ class MiceMaze(
|
||||
|
||||
def _record_run_result(self, completed):
|
||||
if not self.run_recorded:
|
||||
self.save_score()
|
||||
self.scoring.save_score()
|
||||
self.profile_integration.update_game_stats(self.points, completed=completed)
|
||||
self.run_recorded = True
|
||||
self.combined_scores = self.profile_integration.get_device_leaderboard(5)
|
||||
@@ -282,9 +217,8 @@ class MiceMaze(
|
||||
if score is not None:
|
||||
self.points = max(0, int(score))
|
||||
self.current_level = max(0, self.total_levels - 1)
|
||||
self.game_status = "paused"
|
||||
self.menu_screen = None
|
||||
self.game_end = (True, GAME_END_RUN_COMPLETE)
|
||||
self.state_machine.transition_to(GameState.PAUSED)
|
||||
self.game_end = (True, config.GAME_END_RUN_COMPLETE)
|
||||
self.run_recorded = True
|
||||
self.combined_scores = self.profile_integration.get_device_leaderboard(5)
|
||||
print(
|
||||
@@ -325,8 +259,8 @@ class MiceMaze(
|
||||
self.background_texture = None
|
||||
|
||||
# Clear blood layer on game start/restart
|
||||
self.blood_layer_sprites.clear()
|
||||
self.cave_foreground_tiles.clear()
|
||||
self.graphics.blood_layer_sprites.clear()
|
||||
self.graphics.cave_foreground_tiles.clear()
|
||||
self.game_end = (False, None)
|
||||
self.game_status = "start_menu"
|
||||
self.menu_screen = "start"
|
||||
@@ -335,10 +269,10 @@ class MiceMaze(
|
||||
self.unit_positions_before.clear()
|
||||
self.points = 0
|
||||
self.pointer = (random.randint(1, self.map.width-2), random.randint(1, self.map.height-2))
|
||||
self.scroll_cursor()
|
||||
self.graphics.scroll_cursor()
|
||||
|
||||
for _ in range(self.initial_rat_count):
|
||||
self.spawn_rat()
|
||||
self.unit_manager.spawn_rat()
|
||||
|
||||
def load_level(self, level_index, preserve_points=True, show_menu=False, menu_screen=None):
|
||||
target_level_index = self._normalize_target_level(level_index)
|
||||
@@ -359,20 +293,20 @@ class MiceMaze(
|
||||
self.map.height
|
||||
)
|
||||
|
||||
if getattr(self, "loaded_theme_index", None) != next_theme_index:
|
||||
if self.graphics.loaded_theme_index != next_theme_index:
|
||||
print(
|
||||
f"[flow] theme switch needed: loaded_theme={getattr(self, 'loaded_theme_index', None)} "
|
||||
f"[flow] theme switch needed: loaded_theme={self.graphics.loaded_theme_index} "
|
||||
f"-> next_theme={next_theme_index}"
|
||||
)
|
||||
self.load_assets()
|
||||
self.graphics.load_assets()
|
||||
else:
|
||||
print(f"[flow] theme unchanged: reusing theme {next_theme_index}")
|
||||
self.units.clear()
|
||||
self.unit_positions.clear()
|
||||
self.unit_positions_before.clear()
|
||||
self.blood_stains = {}
|
||||
self.blood_layer_sprites.clear()
|
||||
self.cave_foreground_tiles.clear()
|
||||
self.graphics.blood_layer_sprites.clear()
|
||||
self.graphics.cave_foreground_tiles.clear()
|
||||
self.background_texture = None
|
||||
self.ammo = {
|
||||
"bomb": {"count": 2, "max": 8},
|
||||
@@ -383,7 +317,7 @@ class MiceMaze(
|
||||
self.combined_scores = None
|
||||
self.game_end = (False, None)
|
||||
self.pointer = (random.randint(1, self.map.width-2), random.randint(1, self.map.height-2))
|
||||
self.scroll_cursor()
|
||||
self.graphics.scroll_cursor()
|
||||
if not preserve_points:
|
||||
self.points = 0
|
||||
self.run_recorded = False
|
||||
@@ -395,16 +329,18 @@ class MiceMaze(
|
||||
flush=True,
|
||||
)
|
||||
for _ in range(self.initial_rat_count):
|
||||
self.spawn_rat()
|
||||
self.unit_manager.spawn_rat()
|
||||
|
||||
if show_menu:
|
||||
self.game_status = "start_menu"
|
||||
self.menu_screen = menu_screen or "level_intro"
|
||||
print(f"[flow] level loaded into menu state: menu_screen={self.menu_screen} points={self.points}")
|
||||
if menu_screen == "start":
|
||||
self.state_machine.transition_to(GameState.START_MENU)
|
||||
else:
|
||||
self.state_machine.transition_to(GameState.PLAYING)
|
||||
print(f"[flow] level loaded into state: {self.state_machine.current_state} points={self.points}")
|
||||
else:
|
||||
self.game_status = "game"
|
||||
self.menu_screen = None
|
||||
print(f"[flow] level loaded directly into gameplay: level={self.current_level + 1} points={self.points}")
|
||||
self.state_machine.transition_to(GameState.PLAYING)
|
||||
print(f"[flow] level loaded directly into PLAYING: points={self.points}")
|
||||
|
||||
|
||||
def advance_level(self):
|
||||
print(f"[flow] advance_level called from level={self.current_level + 1} points={self.points}")
|
||||
@@ -414,8 +350,8 @@ class MiceMaze(
|
||||
|
||||
if self._is_last_dat_level():
|
||||
print("[flow] advance_level -> reached end of DAT campaign")
|
||||
self.game_end = (True, GAME_END_RUN_COMPLETE)
|
||||
self.game_status = "paused"
|
||||
self.game_end = (True, config.GAME_END_RUN_COMPLETE)
|
||||
self.state_machine.transition_to(GameState.VICTORY)
|
||||
self._record_run_result(completed=True)
|
||||
return
|
||||
|
||||
@@ -425,14 +361,14 @@ class MiceMaze(
|
||||
|
||||
def reset_game(self):
|
||||
print(
|
||||
f"[flow] reset_game called: game_end={self.game_end} game_status={self.game_status} "
|
||||
f"[flow] reset_game called: game_end={self.game_end} state={self.state_machine.current_state} "
|
||||
f"menu_screen={self.menu_screen} points={self.points} level={self.current_level + 1}"
|
||||
)
|
||||
if self.game_end[0]:
|
||||
if self.game_end[1] == GAME_END_LEVEL_CLEAR:
|
||||
if self.game_end[1] == config.GAME_END_LEVEL_CLEAR:
|
||||
print("[flow] reset_game -> post-victory path")
|
||||
self.advance_level()
|
||||
elif self.game_end[1] == GAME_END_RUN_COMPLETE:
|
||||
elif self.game_end[1] == config.GAME_END_RUN_COMPLETE:
|
||||
print("[flow] reset_game -> DAT run complete, returning to start menu")
|
||||
self.start_menu_animation_started_at = time.monotonic()
|
||||
self.load_level(0, preserve_points=False, show_menu=True, menu_screen="start")
|
||||
@@ -442,18 +378,14 @@ class MiceMaze(
|
||||
self.load_level(0, preserve_points=False, show_menu=True, menu_screen="start")
|
||||
return
|
||||
|
||||
if self.game_status == "paused":
|
||||
if self.state_machine.current_state == GameState.PAUSED:
|
||||
print("[flow] reset_game -> unpausing current level")
|
||||
self.game_status = "game"
|
||||
self.state_machine.transition_to(GameState.PLAYING)
|
||||
return
|
||||
|
||||
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
|
||||
if self.state_machine.current_state == GameState.START_MENU:
|
||||
print(f"[flow] reset_game -> entering gameplay")
|
||||
self.state_machine.transition_to(GameState.PLAYING)
|
||||
return
|
||||
|
||||
print("[flow] reset_game -> hard reload current level")
|
||||
@@ -477,310 +409,7 @@ class MiceMaze(
|
||||
def _can_adjust_audio_menu(self):
|
||||
if self.game_end[0]:
|
||||
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]
|
||||
|
||||
def _draw_start_menu_slider(self, x, y, width, height, setting_name, label, value, selected):
|
||||
colors = START_MENU_COLORS
|
||||
style = START_MENU_AUDIO_STYLES[setting_name]
|
||||
accent = style["accent"]
|
||||
fill_color = style["fill"] if selected else colors["card_fill"]
|
||||
border_color = accent if selected else (156, 156, 156)
|
||||
text_color = colors["text"]
|
||||
render_engine = self.render_engine
|
||||
|
||||
render_engine.draw_rectangle(x, y, width, height, "start_menu_slider", filling=fill_color)
|
||||
render_engine.draw_rectangle(x, y, width, height, "start_menu_slider", outline=border_color)
|
||||
|
||||
if selected:
|
||||
render_engine.draw_rectangle(x + 12, y + 10, 8, height - 20, "start_menu_slider", filling=accent)
|
||||
|
||||
title_y = y + 10
|
||||
render_engine.draw_text(label, self._menu_font(render_engine.target_size[1] // 32), (x + 34, title_y), text_color)
|
||||
render_engine.draw_text(f"{value}%", self._menu_font(render_engine.target_size[1] // 34), (x + width - 84, title_y + 2), text_color)
|
||||
|
||||
track_x = x + 34
|
||||
track_y = y + height - 26
|
||||
track_width = width - 68
|
||||
track_height = 14
|
||||
filled_width = int(track_width * value / 100)
|
||||
knob_width = 14
|
||||
knob_x = track_x + int((track_width - knob_width) * value / 100)
|
||||
|
||||
render_engine.draw_rectangle(track_x, track_y, track_width, track_height, "start_menu_slider", filling=colors["track_fill"])
|
||||
render_engine.draw_rectangle(track_x, track_y, track_width, track_height, "start_menu_slider", outline=(128, 128, 128))
|
||||
if filled_width > 0:
|
||||
render_engine.draw_rectangle(track_x, track_y, filled_width, track_height, "start_menu_slider", filling=accent)
|
||||
render_engine.draw_rectangle(knob_x, track_y - 4, knob_width, track_height + 8, "start_menu_slider", filling=(255, 255, 255))
|
||||
render_engine.draw_rectangle(knob_x, track_y - 4, knob_width, track_height + 8, "start_menu_slider", outline=accent)
|
||||
|
||||
def _current_start_menu_animation_frame(self):
|
||||
animation = getattr(self, "start_menu_animation", None)
|
||||
if not animation or not animation["frames"]:
|
||||
return None, (0, 0)
|
||||
|
||||
if len(animation["frames"]) == 1 or animation["total_duration"] <= 0:
|
||||
return animation["frames"][0], animation["size"]
|
||||
|
||||
elapsed_ms = int((time.monotonic() - self.start_menu_animation_started_at) * 1000)
|
||||
current_offset = elapsed_ms % animation["total_duration"]
|
||||
accumulated = 0
|
||||
for index, duration in enumerate(animation["durations"]):
|
||||
accumulated += duration
|
||||
if current_offset < accumulated:
|
||||
return animation["frames"][index], animation["size"]
|
||||
|
||||
return animation["frames"][-1], animation["size"]
|
||||
|
||||
def _render_audio_menu(self, title, subtitle_lines, primary_action_text, hint_text, image_name="BMP_WEWIN"):
|
||||
colors = START_MENU_COLORS
|
||||
render_engine = self.render_engine
|
||||
target_width, target_height = render_engine.target_size
|
||||
compact_menu = target_height <= 540
|
||||
panel_x = max(48, target_width // 12)
|
||||
panel_y = max(34, target_height // 18)
|
||||
panel_width = target_width - panel_x * 2
|
||||
panel_height = target_height - panel_y * 2
|
||||
header_height = max(44, target_height // 13)
|
||||
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, panel_height, "start_menu", filling=colors["panel_fill"])
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, panel_height, "start_menu", outline=colors["panel_border"])
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, header_height, "start_menu", filling=colors["header_fill"])
|
||||
render_engine.draw_text(
|
||||
title,
|
||||
self._menu_font(target_height // 20),
|
||||
("center", panel_y + 16),
|
||||
colors["text"],
|
||||
)
|
||||
|
||||
image = self.assets[image_name]
|
||||
image_width, image_height = render_engine.get_image_size(image)
|
||||
image_y = panel_y + header_height + (14 if compact_menu else 18)
|
||||
render_engine.draw_image(
|
||||
target_width // 2 - image_width // 2 - render_engine.w_offset,
|
||||
image_y - render_engine.h_offset,
|
||||
image,
|
||||
"start_menu",
|
||||
)
|
||||
|
||||
info_y = image_y + image_height + 18
|
||||
line_gap = 18 if compact_menu else max(20, target_height // 34)
|
||||
for index, line in enumerate(subtitle_lines):
|
||||
render_engine.draw_text(
|
||||
line,
|
||||
self._menu_font(target_height // (34 if index == 0 else 36)),
|
||||
("center", info_y + line_gap * index),
|
||||
colors["text"] if index == 0 else colors["muted"],
|
||||
)
|
||||
|
||||
cta_y = info_y + line_gap * len(subtitle_lines) + 8
|
||||
render_engine.draw_text(
|
||||
primary_action_text,
|
||||
self._menu_font(target_height // 31),
|
||||
("center", cta_y),
|
||||
colors["text"],
|
||||
)
|
||||
|
||||
card_width = max(320, min(panel_width - 160, 720))
|
||||
card_height = 60 if compact_menu else max(68, target_height // 10)
|
||||
card_x = target_width // 2 - card_width // 2
|
||||
cards_y = cta_y + (16 if compact_menu else 26)
|
||||
card_gap = 12 if compact_menu else 16
|
||||
for index, (setting_name, label) in enumerate(START_MENU_AUDIO_OPTIONS):
|
||||
card_y = cards_y + index * (card_height + card_gap)
|
||||
self._draw_start_menu_slider(
|
||||
card_x,
|
||||
card_y,
|
||||
card_width,
|
||||
card_height,
|
||||
setting_name,
|
||||
label,
|
||||
getattr(self, setting_name),
|
||||
index == self.start_menu_selection,
|
||||
)
|
||||
|
||||
cards_bottom = cards_y + len(START_MENU_AUDIO_OPTIONS) * card_height + (len(START_MENU_AUDIO_OPTIONS) - 1) * card_gap
|
||||
if compact_menu:
|
||||
hint_y = min(cards_bottom + 8, panel_y + panel_height - 28)
|
||||
render_engine.draw_text(
|
||||
hint_text,
|
||||
self._menu_font(target_height // 42),
|
||||
("center", hint_y),
|
||||
colors["muted"],
|
||||
)
|
||||
else:
|
||||
hint_y = cards_bottom + 10
|
||||
hint_height = max(34, target_height // 18)
|
||||
hint_width = card_width
|
||||
hint_x = card_x
|
||||
render_engine.draw_rectangle(hint_x, hint_y, hint_width, hint_height, "start_menu", filling=colors["hint_fill"])
|
||||
render_engine.draw_rectangle(hint_x, hint_y, hint_width, hint_height, "start_menu", outline=(170, 170, 170))
|
||||
render_engine.draw_text(
|
||||
hint_text,
|
||||
self._menu_font(target_height // 40),
|
||||
("center", hint_y + 10),
|
||||
colors["muted"],
|
||||
)
|
||||
|
||||
def render_start_menu(self):
|
||||
colors = START_MENU_COLORS
|
||||
render_engine = self.render_engine
|
||||
target_width, target_height = render_engine.target_size
|
||||
compact_menu = target_height <= 540
|
||||
panel_x = max(48, target_width // 12)
|
||||
panel_y = max(34, target_height // 18)
|
||||
panel_width = target_width - panel_x * 2
|
||||
panel_height = target_height - panel_y * 2
|
||||
header_height = max(58, target_height // 10)
|
||||
title_font = self._menu_font(target_height // 15)
|
||||
body_font = self._menu_font(target_height // 28)
|
||||
meta_font = self._menu_font(target_height // 30)
|
||||
cta_font = self._menu_font(target_height // 22)
|
||||
hint_font = self._menu_font(target_height // 32)
|
||||
line_gap = 22 if compact_menu else max(26, target_height // 26)
|
||||
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, panel_height, "start_menu", filling=colors["panel_fill"])
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, panel_height, "start_menu", outline=colors["panel_border"])
|
||||
render_engine.draw_rectangle(panel_x, panel_y, panel_width, header_height, "start_menu", filling=colors["header_fill"])
|
||||
render_engine.draw_text(
|
||||
f"Welcome to Mice, {self.profile_integration.get_profile_name()}!",
|
||||
title_font,
|
||||
("center", panel_y + max(12, header_height // 5)),
|
||||
colors["text"],
|
||||
)
|
||||
|
||||
animation_frame, animation_size = self._current_start_menu_animation_frame()
|
||||
animation_bottom = panel_y + header_height + 18
|
||||
if animation_frame is not None:
|
||||
max_animation_width = panel_width - (70 if compact_menu else 110)
|
||||
display_width = min(animation_size[0], max_animation_width)
|
||||
scale_factor = display_width / animation_size[0]
|
||||
display_height = max(1, int(animation_size[1] * scale_factor))
|
||||
animation_x = target_width // 2 - display_width // 2
|
||||
animation_y = panel_y + header_height + (18 if compact_menu else 26)
|
||||
render_engine.draw_image(
|
||||
animation_x - render_engine.w_offset,
|
||||
animation_y - render_engine.h_offset,
|
||||
animation_frame,
|
||||
"start_menu",
|
||||
source_rect=(0, 0, animation_size[0], animation_size[1]),
|
||||
dest_size=(display_width, display_height),
|
||||
)
|
||||
animation_bottom = animation_y + display_height
|
||||
|
||||
player_profile = self.profile_integration.current_profile
|
||||
device_id = self.profile_integration.get_device_id()
|
||||
subtitle_lines = ["A game by Matteo, because he was bored."]
|
||||
if player_profile:
|
||||
if compact_menu:
|
||||
subtitle_lines.append(
|
||||
f"Best: {player_profile['best_score']} | Games: {player_profile['games_played']}"
|
||||
)
|
||||
else:
|
||||
subtitle_lines.append(f"Device: {device_id}")
|
||||
subtitle_lines.append(
|
||||
f"Best Score: {player_profile['best_score']} | Games: {player_profile['games_played']}"
|
||||
)
|
||||
elif compact_menu:
|
||||
subtitle_lines.append(f"Guest profile | {device_id}")
|
||||
else:
|
||||
subtitle_lines.append(f"Device: {device_id}")
|
||||
subtitle_lines.append("No profile loaded - playing as guest")
|
||||
|
||||
info_y = animation_bottom + (18 if compact_menu else 22)
|
||||
for index, line in enumerate(subtitle_lines):
|
||||
render_engine.draw_text(
|
||||
line,
|
||||
body_font if index == 0 else meta_font,
|
||||
("center", info_y + line_gap * index),
|
||||
colors["text"] if index == 0 else colors["muted"],
|
||||
)
|
||||
|
||||
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"],
|
||||
)
|
||||
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 = [
|
||||
f"Level {self.current_level + 1} | Points: {self.points}",
|
||||
f"Rats in maze: {self.count_rats()}",
|
||||
]
|
||||
self._render_audio_menu(
|
||||
title="Pause",
|
||||
subtitle_lines=subtitle_lines,
|
||||
primary_action_text="Press Return to resume",
|
||||
hint_text="Up/Down select Left/Right adjust Esc quits",
|
||||
image_name="BMP_PAUSE" if "BMP_PAUSE" in self.assets else "BMP_WEWIN",
|
||||
)
|
||||
return self.state_machine.current_state == GameState.PAUSED
|
||||
|
||||
def _apply_volume_setting(self, setting_name, value):
|
||||
clamped = max(0, min(100, int(value)))
|
||||
@@ -799,7 +428,7 @@ class MiceMaze(
|
||||
return
|
||||
if not self._can_adjust_audio_menu():
|
||||
return
|
||||
self.start_menu_selection = (self.start_menu_selection - 1) % len(START_MENU_AUDIO_OPTIONS)
|
||||
self.start_menu_selection = (self.start_menu_selection - 1) % len(config.START_MENU_AUDIO_OPTIONS)
|
||||
|
||||
def menu_down(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
@@ -807,12 +436,12 @@ class MiceMaze(
|
||||
return
|
||||
if not self._can_adjust_audio_menu():
|
||||
return
|
||||
self.start_menu_selection = (self.start_menu_selection + 1) % len(START_MENU_AUDIO_OPTIONS)
|
||||
self.start_menu_selection = (self.start_menu_selection + 1) % len(config.START_MENU_AUDIO_OPTIONS)
|
||||
|
||||
def _adjust_selected_volume(self, delta):
|
||||
if not self._can_adjust_audio_menu():
|
||||
return
|
||||
setting_name, _ = START_MENU_AUDIO_OPTIONS[self.start_menu_selection]
|
||||
setting_name, _ = config.START_MENU_AUDIO_OPTIONS[self.start_menu_selection]
|
||||
current_value = getattr(self, setting_name)
|
||||
self._apply_volume_setting(setting_name, current_value + delta)
|
||||
|
||||
@@ -820,48 +449,46 @@ class MiceMaze(
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(-1)
|
||||
return
|
||||
self._adjust_selected_volume(-VOLUME_STEP)
|
||||
self._adjust_selected_volume(-config.VOLUME_STEP)
|
||||
|
||||
def menu_right(self):
|
||||
if self._can_adjust_start_difficulty():
|
||||
self._cycle_difficulty(1)
|
||||
return
|
||||
self._adjust_selected_volume(VOLUME_STEP)
|
||||
self._adjust_selected_volume(config.VOLUME_STEP)
|
||||
|
||||
def update_background_music(self):
|
||||
if self.game_end[0] and self.game_end[1] == GAME_END_RUN_COMPLETE:
|
||||
self.render_engine.play_music(RUN_COMPLETE_MUSIC, loop=True)
|
||||
if self.game_end[0] and self.game_end[1] == config.GAME_END_RUN_COMPLETE:
|
||||
self.render_engine.play_music(config.RUN_COMPLETE_MUSIC, loop=True)
|
||||
return
|
||||
if self.game_status == "game" and not self.game_end[0]:
|
||||
if self.state_machine.current_state == GameState.PLAYING and not self.game_end[0]:
|
||||
if self.current_level_music:
|
||||
self.render_engine.play_music(self.current_level_music, loop=True)
|
||||
return
|
||||
if self.game_status == "start_menu" and not self.game_end[0]:
|
||||
if self.menu_screen == "start":
|
||||
self.render_engine.play_music(START_MENU_MUSIC, loop=True)
|
||||
return
|
||||
if self.menu_screen == "level_intro" and self.current_level_music:
|
||||
self.render_engine.play_music(self.current_level_music, loop=True)
|
||||
return
|
||||
if self.state_machine.current_state == GameState.START_MENU and not self.game_end[0]:
|
||||
self.render_engine.play_music(config.START_MENU_MUSIC, loop=True)
|
||||
return
|
||||
self.render_engine.pause_music()
|
||||
|
||||
def update_maze(self):
|
||||
self.update_background_music()
|
||||
|
||||
# Handle non-playing states via state machine
|
||||
if self.state_machine.current_state != GameState.PLAYING:
|
||||
self.state_machine.update()
|
||||
return
|
||||
|
||||
# Actual active gameplay logic
|
||||
if self.game_end[0]:
|
||||
if self.game_end[1] == config.GAME_END_DEFEAT:
|
||||
self.state_machine.transition_to(GameState.GAME_OVER)
|
||||
else:
|
||||
self.state_machine.transition_to(GameState.VICTORY)
|
||||
return
|
||||
|
||||
if self.game_over():
|
||||
return
|
||||
if self.game_status == "paused":
|
||||
self.render_pause_menu()
|
||||
return
|
||||
if self.game_status == "start_menu":
|
||||
if self.menu_screen == "level_intro":
|
||||
self.render_engine.dialog(
|
||||
f"Level {self.current_level + 1}",
|
||||
subtitle=f"Points: {self.points}\nPress Return to begin",
|
||||
image=self.assets["BMP_WEWIN"],
|
||||
)
|
||||
else:
|
||||
self.render_start_menu()
|
||||
return
|
||||
|
||||
self.render_engine.delete_tag("unit")
|
||||
self.render_engine.delete_tag("effect")
|
||||
self.render_engine.delete_tag("cave")
|
||||
@@ -873,7 +500,7 @@ class MiceMaze(
|
||||
|
||||
# First pass: Register all units in collision system BEFORE move
|
||||
# This allows bombs/gas to find victims during their move()
|
||||
for unit in self.units.values():
|
||||
for unit in sorted(self.units.values(), key=lambda u: int(u.id)):
|
||||
# Calculate bbox if not yet set (first frame)
|
||||
if not hasattr(unit, 'bbox') or unit.bbox == (0, 0, 0, 0):
|
||||
# Temporary bbox based on position
|
||||
@@ -895,7 +522,7 @@ class MiceMaze(
|
||||
self.unit_positions_before.setdefault(unit.position_before, []).append(unit)
|
||||
|
||||
# Second pass: move all units (can now access collision system)
|
||||
for unit in self.units.copy().values():
|
||||
for unit in sorted(self.units.values(), key=lambda u: int(u.id)):
|
||||
unit.move()
|
||||
|
||||
# Third pass: Update collision system with new positions after move
|
||||
@@ -903,7 +530,7 @@ class MiceMaze(
|
||||
self.unit_positions.clear()
|
||||
self.unit_positions_before.clear()
|
||||
|
||||
for unit in self.units.values():
|
||||
for unit in sorted(self.units.values(), key=lambda u: int(u.id)):
|
||||
# Register with updated positions/bbox from move()
|
||||
self.collision_system.register_unit(
|
||||
unit.id,
|
||||
@@ -916,23 +543,23 @@ class MiceMaze(
|
||||
self.unit_positions.setdefault(unit.position, []).append(unit)
|
||||
self.unit_positions_before.setdefault(unit.position_before, []).append(unit)
|
||||
# Fourth pass: check collisions and draw
|
||||
for unit in self.units.copy().values():
|
||||
for unit in sorted(self.units.values(), key=lambda u: int(u.id)):
|
||||
unit.collisions()
|
||||
unit.draw()
|
||||
|
||||
self.draw_cave_foreground()
|
||||
self.graphics.draw_cave_foreground()
|
||||
self.render_engine.draw_pointer(self.pointer[0] * self.cell_size, self.pointer[1] * self.cell_size)
|
||||
|
||||
|
||||
|
||||
self.render_engine.update_status(f"Mice: {self.count_rats()} - Points: {self.points}")
|
||||
self.render_engine.update_status(f"Mice: {self.unit_manager.count_rats()} - Points: {self.points}")
|
||||
self.refill_ammo()
|
||||
self.render_engine.update_ammo(self.ammo, self.assets)
|
||||
self.scroll()
|
||||
self.render_engine.update_ammo(self.ammo, self.graphics.assets)
|
||||
self.controls.scroll()
|
||||
self.render_engine.new_cycle(50, self.update_maze)
|
||||
|
||||
def run(self):
|
||||
self.render_engine.mainloop(update=self.update_maze, bg_update=self.draw_maze)
|
||||
self.render_engine.mainloop(update=self.update_maze, bg_update=self.graphics.draw_maze)
|
||||
|
||||
# ==================== GAME OVER LOGIC ====================
|
||||
|
||||
@@ -941,37 +568,36 @@ class MiceMaze(
|
||||
if self.combined_scores is None:
|
||||
self.combined_scores = self.profile_integration.get_device_leaderboard(5)
|
||||
|
||||
if self.game_end[1] == GAME_END_DEFEAT:
|
||||
if self.game_end[1] == config.GAME_END_DEFEAT:
|
||||
self.render_engine.dialog(
|
||||
"Game Over: Mice are too many!",
|
||||
image=self.assets.get("lose", self.assets["BMP_WEWIN"]),
|
||||
image_scale=0.48,
|
||||
image=self.graphics.assets.get("lose", self.graphics.assets["BMP_WEWIN"]),
|
||||
subtitle=f"Reached level: {self.current_level + 1}\nPress Return to go back to the start menu",
|
||||
scores=self.combined_scores,
|
||||
)
|
||||
elif self.game_end[1] == GAME_END_RUN_COMPLETE:
|
||||
elif self.game_end[1] == config.GAME_END_RUN_COMPLETE:
|
||||
self.render_engine.dialog(
|
||||
"THE END",
|
||||
image=self.assets.get("end", self.assets["BMP_WEWIN"]),
|
||||
image=self.graphics.assets.get("end", self.graphics.assets["BMP_WEWIN"]),
|
||||
current_score=self.points,
|
||||
style="run_complete",
|
||||
)
|
||||
else:
|
||||
self.render_engine.dialog(
|
||||
f"Level {self.current_level + 1} Clear! Points: {self.points}",
|
||||
image=self.assets.get("clear", self.assets["BMP_WEWIN"]),
|
||||
image=self.graphics.assets.get("clear", self.graphics.assets["BMP_WEWIN"]),
|
||||
subtitle="Press Return for the next level",
|
||||
scores=self.combined_scores
|
||||
)
|
||||
|
||||
|
||||
return True
|
||||
count_rats = self.count_rats()
|
||||
count_rats = self.unit_manager.count_rats()
|
||||
if count_rats > 200:
|
||||
self.render_engine.stop_sound()
|
||||
self.render_engine.play_sound("WEWIN.WAV")
|
||||
self.game_end = (True, GAME_END_DEFEAT)
|
||||
self.game_status = "paused"
|
||||
self.game_end = (True, config.GAME_END_DEFEAT)
|
||||
self.state_machine.transition_to(GameState.GAME_OVER)
|
||||
print(f"[flow] defeat reached: rats={count_rats} points={self.points} level={self.current_level + 1}")
|
||||
|
||||
self._record_run_result(completed=False)
|
||||
@@ -980,23 +606,22 @@ class MiceMaze(
|
||||
if not count_rats and not any(isinstance(unit, points.Point) for unit in self.units.values()):
|
||||
self.render_engine.stop_sound()
|
||||
self.render_engine.play_sound("VICTORY.WAV")
|
||||
self.game_status = "paused"
|
||||
|
||||
if self._is_last_dat_level():
|
||||
self.render_engine.play_sound("WELLDONE.WAV", tag="effects")
|
||||
self.game_end = (True, GAME_END_RUN_COMPLETE)
|
||||
self.game_end = (True, config.GAME_END_RUN_COMPLETE)
|
||||
self.state_machine.transition_to(GameState.VICTORY)
|
||||
self._record_run_result(completed=True)
|
||||
print(f"[flow] final DAT victory reached: points={self.points} level={self.current_level + 1}")
|
||||
else:
|
||||
self.game_end = (True, GAME_END_LEVEL_CLEAR)
|
||||
self.game_end = (True, config.GAME_END_LEVEL_CLEAR)
|
||||
self.state_machine.transition_to(GameState.VICTORY)
|
||||
self.combined_scores = self.profile_integration.get_device_leaderboard(5)
|
||||
print(f"[flow] victory reached: points={self.points} level={self.current_level + 1}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
def parse_args():
|
||||
def parse_debug_score(value):
|
||||
try:
|
||||
@@ -1033,4 +658,3 @@ if __name__ == "__main__":
|
||||
if args.debug_run_complete_dialog:
|
||||
solver.activate_debug_run_complete_dialog(score=args.debug_final_score)
|
||||
solver.run()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user