Add Ctrl+Return cheat to instantly win the current level
During gameplay, pressing Ctrl+Return triggers an immediate level clear: the current level is marked as won (level_clear, or run_complete on the last DAT level) and the state transitions to VICTORY, showing the normal 'Level Clear!' dialog so the player can then advance with Return. Implementation: - engine/sdl2.py: mainloop detects Ctrl+Return via SDLK_RETURN + KMOD_CTRL and dispatches a 'cheat_win_level' action (bypassing keybindings). - engine/controls.py: trigger() now also accepts direct action names that are registered in the dispatcher (not key-event names), and a new cheat_win_level handler forwards to the game. - rats.py: MiceMaze.cheat_win_level() performs the win, guarded so it only fires while a level is actively being played. No keybinding files need editing; the cheat is wired through the engine layer directly.
This commit is contained in:
@@ -211,6 +211,7 @@ class KeyBindings:
|
|||||||
"toggle_pause": self.toggle_pause,
|
"toggle_pause": self.toggle_pause,
|
||||||
"toggle_full_screen": self.toggle_full_screen,
|
"toggle_full_screen": self.toggle_full_screen,
|
||||||
"quit_game": self.quit_game,
|
"quit_game": self.quit_game,
|
||||||
|
"cheat_win_level": self.cheat_win_level,
|
||||||
"menu_up": self.game.menu_up,
|
"menu_up": self.game.menu_up,
|
||||||
"menu_down": self.game.menu_down,
|
"menu_down": self.game.menu_down,
|
||||||
"menu_left": self.game.menu_left,
|
"menu_left": self.game.menu_left,
|
||||||
@@ -298,6 +299,17 @@ class KeyBindings:
|
|||||||
if not self.bindings:
|
if not self.bindings:
|
||||||
self.initialize_keybindings()
|
self.initialize_keybindings()
|
||||||
|
|
||||||
|
# Direct action dispatch: if the action name itself is a registered
|
||||||
|
# dispatcher entry (e.g. cheat actions injected by the engine layer),
|
||||||
|
# invoke it without requiring a keybinding entry.
|
||||||
|
direct_method = self.action_dispatcher.get(action)
|
||||||
|
if direct_method is not None and action not in ("spawn_rat",):
|
||||||
|
# Only treat as direct when the action isn't also a normal key event
|
||||||
|
# name. Key events look like "keydown_*" / "keyup_*" / etc.
|
||||||
|
if not action.startswith(("keydown_", "keyup_", "joybutton", "joyhat", "controller", "mousemove")):
|
||||||
|
direct_method()
|
||||||
|
return None
|
||||||
|
|
||||||
value = None
|
value = None
|
||||||
for section_name in self._binding_sections_for_action():
|
for section_name in self._binding_sections_for_action():
|
||||||
value = self.bindings.get(section_name, {}).get(action)
|
value = self.bindings.get(section_name, {}).get(action)
|
||||||
@@ -319,6 +331,11 @@ class KeyBindings:
|
|||||||
method()
|
method()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def cheat_win_level(self):
|
||||||
|
"""Cheat handler: win the current level instantly (Ctrl+Return)."""
|
||||||
|
if hasattr(self.game, "cheat_win_level"):
|
||||||
|
self.game.cheat_win_level()
|
||||||
|
|
||||||
def spawn_rat(self):
|
def spawn_rat(self):
|
||||||
self.game.unit_manager.spawn_rat()
|
self.game.unit_manager.spawn_rat()
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -949,8 +949,12 @@ class GameWindow:
|
|||||||
keycode = event.key.keysym.sym
|
keycode = event.key.keysym.sym
|
||||||
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
|
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
|
||||||
key = key.replace(" ", "_")
|
key = key.replace(" ", "_")
|
||||||
# Check for Right Ctrl key to trigger white flash
|
# Cheat: Ctrl+Return instantly wins the current level.
|
||||||
self.trigger(f"keydown_{key}")
|
if keycode == sdl2.SDLK_RETURN and (event.key.keysym.mod & sdl2.KMOD_CTRL):
|
||||||
|
self.trigger("cheat_win_level")
|
||||||
|
else:
|
||||||
|
# Check for Right Ctrl key to trigger white flash
|
||||||
|
self.trigger(f"keydown_{key}")
|
||||||
elif event.type == sdl2.SDL_KEYUP:
|
elif event.type == sdl2.SDL_KEYUP:
|
||||||
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
|
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
|
||||||
key = key.replace(" ", "_")
|
key = key.replace(" ", "_")
|
||||||
|
|||||||
@@ -344,6 +344,29 @@ class MiceMaze:
|
|||||||
print(f"[flow] level loaded directly into PLAYING: points={self.points}")
|
print(f"[flow] level loaded directly into PLAYING: points={self.points}")
|
||||||
|
|
||||||
|
|
||||||
|
def cheat_win_level(self):
|
||||||
|
"""Cheat: instantly win the current level (Ctrl+Return).
|
||||||
|
|
||||||
|
Only active while a level is actually being played; ignored in menus,
|
||||||
|
end screens, or once the level has already been cleared.
|
||||||
|
"""
|
||||||
|
if self.state_machine.current_state != GameState.PLAYING:
|
||||||
|
return
|
||||||
|
if self.game_end[0]:
|
||||||
|
return
|
||||||
|
print(f"[cheat] win_level triggered: level={self.current_level + 1} points={self.points}")
|
||||||
|
self.render_engine.stop_sound()
|
||||||
|
self.render_engine.play_sound("VICTORY.WAV")
|
||||||
|
if self._is_last_dat_level():
|
||||||
|
self.render_engine.play_sound("WELLDONE.WAV", tag="effects")
|
||||||
|
self.game_end = (True, config.GAME_END_RUN_COMPLETE)
|
||||||
|
self.state_machine.transition_to(GameState.VICTORY)
|
||||||
|
self._record_run_result(completed=True)
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
|
||||||
def advance_level(self):
|
def advance_level(self):
|
||||||
print(f"[flow] advance_level called from level={self.current_level + 1} points={self.points}")
|
print(f"[flow] advance_level called from level={self.current_level + 1} points={self.points}")
|
||||||
if not self._is_dat_campaign():
|
if not self._is_dat_campaign():
|
||||||
|
|||||||
Reference in New Issue
Block a user