v1.1: Stufen Easy/Normal/Hard mit Auswahlmenü (Hard = altes Tempo). Koffer behalten beim Drehen ihre Textur. Name/Punkteliste entfernt, nur Stufe + Prozent. Endlos: Ergebnis -> "Nochmals?" -> Stufenauswahl, Ende nur per Esc. storage.py und net.py gelöscht, 48 Tests.
This commit is contained in:
1 parent
de550e1d4d
commit
d970185e75
15 files changed
+613
-342
No files matched your search
@@ -1,30 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Einstiegspunkt des Habegger Loader Games.
|
||||
|
||||
Ablauf: Tutorialbild (nur beim Programmstart) → Stufenauswahl → Ampel-Start →
|
||||
Runde → Ergebnis ("Nochmals?") → Stufenauswahl → …
|
||||
|
||||
Beendet wird ausschliesslich mit ``Esc`` (bzw. Controller-Taste B).
|
||||
|
||||
Beispiele::
|
||||
|
||||
./run.sh # Fenster, letzter Spielername
|
||||
./run.sh # Fenster
|
||||
./run.sh --fullscreen # Vollbild (Messe-/Eventbetrieb)
|
||||
./run.sh --name "Anna" # Name direkt setzen
|
||||
./run.sh --smoke 10 # Selbsttest ohne Eingabegeräte
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import random
|
||||
import sys
|
||||
|
||||
import pygame
|
||||
|
||||
from loader_game import config, storage
|
||||
from loader_game import config
|
||||
from loader_game.assets import Assets
|
||||
from loader_game.game import Game, State
|
||||
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.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
|
||||
|
||||
|
||||
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Habegger Loader Game")
|
||||
parser.add_argument(
|
||||
@@ -32,11 +45,6 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
action="store_true",
|
||||
help="Vollbild starten (Standard: Fenster; F11 schaltet jederzeit um)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
default=None,
|
||||
help="Spielername (sonst der zuletzt gespeicherte aus runtime/)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--seed",
|
||||
type=int,
|
||||
@@ -48,8 +56,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="SEKUNDEN",
|
||||
help="Selbsttest: bestätigt das Intro automatisch, spielt ohne Eingaben "
|
||||
"und beendet sich nach N Sekunden",
|
||||
help="Selbsttest: spielt ohne Eingaben und beendet sich nach N Sekunden",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
@@ -98,15 +105,72 @@ def main(argv: list[str] | None = None) -> int:
|
||||
for warning in warnings:
|
||||
print("Sequenz-Warnung:", warning, file=sys.stderr)
|
||||
|
||||
name = (args.name or storage.load_last_player() or config.DEFAULT_PLAYER_NAME).strip()
|
||||
name = name[: config.MAX_PLAYER_NAME_LENGTH] or config.DEFAULT_PLAYER_NAME
|
||||
storage.save_last_player(name)
|
||||
|
||||
rng = random.Random(args.seed)
|
||||
intro: Intro | None = Intro(assets)
|
||||
game: Game | None = None
|
||||
clock = pygame.time.Clock()
|
||||
difficulty: Difficulty = DEFAULT_DIFFICULTY
|
||||
|
||||
phase = Phase.INTRO
|
||||
after_intro = Phase.MENU # nach dem Tutorial kommt die Stufenauswahl
|
||||
intro: Intro | None = Intro(assets, show_instruction=True)
|
||||
menu: DifficultyMenu | None = None
|
||||
game: Game | None = None
|
||||
|
||||
def apply_action(action: Action) -> bool:
|
||||
"""Verarbeitet eine Aktion in der aktuellen Phase.
|
||||
|
||||
Rückgabe ``False`` = Programm beenden.
|
||||
"""
|
||||
nonlocal phase, intro, menu, game, difficulty, after_intro
|
||||
|
||||
if action is Action.BACK:
|
||||
return False
|
||||
|
||||
if phase is Phase.INTRO and intro is not None:
|
||||
intro.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
|
||||
return True
|
||||
|
||||
if phase is Phase.PLAY and game is not None:
|
||||
result = game.handle(action)
|
||||
if result is HandleResult.QUIT:
|
||||
return False
|
||||
if result is HandleResult.NEW_ROUND:
|
||||
menu = DifficultyMenu(assets, selected=difficulty)
|
||||
phase = Phase.MENU
|
||||
return True
|
||||
|
||||
def advance(dt: float) -> None:
|
||||
"""Weiterrechnen und Phasenwechsel ohne Eingabe."""
|
||||
nonlocal phase, intro, menu, game
|
||||
|
||||
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)
|
||||
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)
|
||||
elif phase is Phase.MENU and menu is not None:
|
||||
menu.draw(screen)
|
||||
elif phase is Phase.PLAY and game is not None:
|
||||
game.draw(screen)
|
||||
pygame.display.flip()
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
running = True
|
||||
smoke_remaining = args.smoke
|
||||
smoke_timer = 0.0
|
||||
@@ -114,6 +178,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
while running:
|
||||
dt = min(clock.tick(config.FPS) / 1000.0, 0.1)
|
||||
|
||||
# ---------------------------------------------------------- Eingabe
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
@@ -122,47 +187,39 @@ def main(argv: list[str] | None = None) -> int:
|
||||
pygame.display.toggle_fullscreen()
|
||||
continue
|
||||
for action in actions_from_event(event):
|
||||
if intro is not None:
|
||||
intro.handle(action)
|
||||
if action is Action.BACK:
|
||||
running = False
|
||||
elif game is not None:
|
||||
running = game.handle(action)
|
||||
if not apply_action(action):
|
||||
running = False
|
||||
break
|
||||
if not running:
|
||||
break
|
||||
|
||||
if intro is not None:
|
||||
if intro.update(dt):
|
||||
intro = None
|
||||
game = Game(assets, sequences, name=name, rng=rng)
|
||||
elif smoke_remaining is not None and intro.phase == "instruction":
|
||||
intro.handle(Action.PRIMARY)
|
||||
elif game is not None:
|
||||
game.update(dt)
|
||||
advance(dt)
|
||||
|
||||
# Selbsttest: ohne Eingabegeräte mitspielen
|
||||
if smoke_remaining is not None:
|
||||
smoke_remaining -= dt
|
||||
if smoke_remaining <= 0:
|
||||
running = False
|
||||
elif game is not None and intro is None:
|
||||
else:
|
||||
smoke_timer -= dt
|
||||
if smoke_timer <= 0:
|
||||
smoke_timer = 0.25
|
||||
action = _demo_action(game, rng)
|
||||
if action is not None:
|
||||
running = game.handle(action)
|
||||
smoke_timer = 0.3
|
||||
if phase is Phase.PLAY and game is not None:
|
||||
demo = _demo_action(game, rng)
|
||||
if demo is not None and not apply_action(demo):
|
||||
running = False
|
||||
elif not apply_action(Action.PRIMARY):
|
||||
running = False
|
||||
|
||||
if intro is not None:
|
||||
intro.draw(screen)
|
||||
elif game is not None:
|
||||
game.draw(screen)
|
||||
pygame.display.flip()
|
||||
draw()
|
||||
|
||||
if game is not None:
|
||||
percent = game.percent if game.state is State.GAME_OVER else game.trailer.load_percent()
|
||||
percent = (
|
||||
game.percent if game.state is State.FINISHED else game.trailer.load_percent()
|
||||
)
|
||||
print(
|
||||
f"Spiel beendet: Zustand={game.state.value}, "
|
||||
f"Beladung={percent:.0f}%, Koffer im Auflieger={len(game.trailer.stack)}"
|
||||
f"Spiel beendet: Stufe={difficulty.label}, Beladung={percent:.0f}%, "
|
||||
f"Koffer im Auflieger={len(game.trailer.stack)}"
|
||||
)
|
||||
|
||||
pygame.quit()
|
||||
|
||||
Reference in new issue
Block a user