Refine start menu difficulty and preview workflow
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user