v1.3: Ablauf gefixt. Tutorial und Startsequenz waren in einer Klasse - jetzt getrennt. Reihenfolge immer: Anleitung -> Moduswahl -> Startsequenz -> Spiel -> Abschluss (10s) -> Anleitung. 57 Tests.

This commit is contained in:
Project Bridge committed 2026-11-01 11:49:44 +00:00
1 parent e3a0c5a57d
commit 441bdb440c
5 files changed
+136 -90

No files matched your search

+54 -46
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
"""Einstiegspunkt des Habegger Loader Games.
Ablauf: Tutorialbild (nur beim Programmstart) → Stufenauswahl → Ampel-Start →
Runde → Ergebnis ("Nochmals?") → Stufenauswahl → …
Ablauf – immer in dieser Reihenfolge, auch nach jedem Ergebnis::
Anleitung → Moduswahl → Startsequenz (Ampel + Reissverschluss) → Spiel
→ Abschluss (Prozent, Spruch, 10 s Cooldown) → Anleitung → …
Beendet wird ausschliesslich mit ``Esc`` (bzw. Controller-Taste B).
@@ -27,15 +29,16 @@ from loader_game.assets import Assets
from loader_game.difficulty import DEFAULT_DIFFICULTY, Difficulty
from loader_game.game import Game, HandleResult, State
from loader_game.input_map import Action, actions_from_event, controller_hint_lines
from loader_game.intro import Intro
from loader_game.intro import StartSequence, Tutorial
from loader_game.menu import DifficultyMenu
from loader_game.sequences import load_sequences
class Phase(enum.Enum):
INTRO = "intro" # Tutorial und/oder Ampel-Start
MENU = "menu" # Stufenauswahl
PLAY = "play" # laufende Runde
TUTORIAL = "tutorial" # Anleitungsbild, wartet auf A/Space
MENU = "menu" # Stufenauswahl
START = "start" # Ampel + Reissverschluss
PLAY = "play" # laufende Runde
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
@@ -100,7 +103,6 @@ def main(argv: list[str] | None = None) -> int:
for line in controller_hint_lines():
print(line)
print(assets.audio_info())
sequences, warnings = load_sequences()
@@ -112,10 +114,10 @@ def main(argv: list[str] | None = None) -> int:
round_number = 0
previous_sequence: int | None = None
phase = Phase.INTRO
after_intro = Phase.MENU # nach dem Tutorial kommt die Stufenauswahl
intro: Intro | None = Intro(assets, show_instruction=True)
phase = Phase.TUTORIAL
tutorial: Tutorial | None = Tutorial(assets)
menu: DifficultyMenu | None = None
start: StartSequence | None = None
game: Game | None = None
def apply_action(action: Action) -> bool:
@@ -123,22 +125,25 @@ def main(argv: list[str] | None = None) -> int:
Rückgabe ``False`` = Programm beenden.
"""
nonlocal phase, intro, menu, game, difficulty, after_intro
nonlocal phase, tutorial, menu, start, game, difficulty
if action is Action.BACK:
return False
if phase is Phase.INTRO and intro is not None:
intro.handle(action)
if phase is Phase.TUTORIAL and tutorial is not None:
tutorial.handle(action)
return True
if phase is Phase.MENU and menu is not None:
selected = menu.handle(action)
if selected is not None:
difficulty = selected
intro = Intro(assets, show_instruction=False)
after_intro = Phase.PLAY
phase = Phase.INTRO
start = StartSequence(assets)
phase = Phase.START
return True
if phase is Phase.START and start is not None:
start.handle(action)
return True
if phase is Phase.PLAY and game is not None:
@@ -146,47 +151,50 @@ def main(argv: list[str] | None = None) -> int:
if result is HandleResult.QUIT:
return False
if result is HandleResult.NEW_ROUND:
# Wie beim Programmstart: Anleitung -> Stufenauswahl -> Ampel
intro = Intro(assets, show_instruction=True)
after_intro = Phase.MENU
menu = None
phase = Phase.INTRO
tutorial = Tutorial(assets)
phase = Phase.TUTORIAL
return True
def advance(dt: float) -> None:
"""Weiterrechnen und Phasenwechsel ohne Eingabe."""
nonlocal phase, intro, menu, game, round_number, previous_sequence
nonlocal phase, tutorial, menu, start, game, round_number, previous_sequence
if phase is Phase.TUTORIAL and tutorial is not None:
if tutorial.update(dt):
tutorial = None
menu = DifficultyMenu(assets, selected=difficulty)
phase = Phase.MENU
elif phase is Phase.START and start is not None:
if start.update(dt):
start = None
game = Game(
assets,
sequences,
difficulty=difficulty,
rng=rng,
previous_sequence=previous_sequence,
)
previous_sequence = game.sequence_index
round_number += 1
first = ", ".join(spec.label for spec in game.sequence[:5])
print(
f"Runde {round_number}: Sequenz {game.sequence_index + 1}/{len(sequences)} "
f"({len(game.sequence)} Koffer, Start: {first} …), "
f"Stufe {difficulty.label}"
)
phase = Phase.PLAY
if phase is Phase.INTRO and intro is not None:
if intro.update(dt):
intro = None
if after_intro is Phase.PLAY:
game = Game(
assets,
sequences,
difficulty=difficulty,
rng=rng,
previous_sequence=previous_sequence,
)
previous_sequence = game.sequence_index
round_number += 1
start = ", ".join(spec.label for spec in game.sequence[:5])
print(
f"Runde {round_number}: Sequenz {game.sequence_index + 1}/{len(sequences)} "
f"({len(game.sequence)} Koffer, Start: {start} …), "
f"Stufe {difficulty.label}"
)
else:
menu = DifficultyMenu(assets, selected=difficulty)
phase = after_intro
elif phase is Phase.PLAY and game is not None:
game.update(dt)
def draw() -> None:
if phase is Phase.INTRO and intro is not None:
intro.draw(screen)
if phase is Phase.TUTORIAL and tutorial is not None:
tutorial.draw(screen)
elif phase is Phase.MENU and menu is not None:
menu.draw(screen)
elif phase is Phase.START and start is not None:
start.draw(screen)
elif phase is Phase.PLAY and game is not None:
game.draw(screen)
pygame.display.flip()