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:
Project Bridge committed 2026-11-01 10:48:52 +00:00
1 parent de550e1d4d
commit d970185e75
15 files changed
+613 -342

No files matched your search

+39 -40
View File
@@ -8,15 +8,23 @@ from dataclasses import dataclass
from . import config, scene
from .case import Case
from .difficulty import DEFAULT_DIFFICULTY, Difficulty
from .input_map import Action
from .net import submit_score
from .sequences import CaseSpec, FALLBACK_SEQUENCE
from .trailer import Trailer
class State(enum.Enum):
PLAYING = "playing"
GAME_OVER = "game_over"
FINISHED = "finished"
class HandleResult(enum.Enum):
"""Was der Aufrufer nach einer Aktion tun soll."""
CONTINUE = "continue"
NEW_ROUND = "new_round" # zurück zur Stufenauswahl
QUIT = "quit" # Programm beenden (Esc / B)
@dataclass
@@ -45,14 +53,12 @@ class Game:
assets,
sequences,
*,
name: str = config.DEFAULT_PLAYER_NAME,
difficulty: Difficulty = DEFAULT_DIFFICULTY,
rng: random.Random | None = None,
submit_scores: bool = True,
) -> None:
self.assets = assets
self.rng = rng or random.Random()
self.name = name
self.submit_scores = submit_scores
self.difficulty = difficulty
self.state = State.PLAYING
self.trailer = Trailer()
@@ -64,8 +70,6 @@ class Game:
self.animation: Animation | None = None
self.shake = 0.0
self.percent = 0.0
self.status_line = ""
self.status_is_error = False
self._spawn()
@@ -98,20 +102,21 @@ class Game:
# ------------------------------------------------------------------
# Eingabe
# ------------------------------------------------------------------
def handle(self, action: Action) -> bool:
"""Verarbeitet eine Aktion. Rückgabe ``False`` = Spiel beenden."""
def handle(self, action: Action) -> HandleResult:
if action is Action.BACK:
return False
return HandleResult.QUIT
if self.state is State.GAME_OVER:
return action is not Action.PRIMARY
if self.state is State.FINISHED:
if action is Action.PRIMARY:
return HandleResult.NEW_ROUND
return HandleResult.CONTINUE
if self.animation is not None or self.current is None:
return True
return HandleResult.CONTINUE
case = self.current
if case.placed or not case.allow_snap:
return True
return HandleResult.CONTINUE
if action is Action.PRIMARY:
self._start_snap(case)
@@ -119,7 +124,7 @@ class Game:
self._start_rotate(case)
elif action is Action.UP:
self._start_stack(case)
return True
return HandleResult.CONTINUE
def _start_snap(self, case: Case) -> None:
self._stop_roll()
@@ -179,7 +184,7 @@ class Game:
self._finish()
return
case.move(dt, self.trailer.wall_x)
case.move(dt, self.trailer.wall_x, self.difficulty)
if not case.placed and case.x <= self.trailer.wall_x:
self._stack_with_gap(case, sound="fail")
return
@@ -258,25 +263,25 @@ class Game:
self._finish()
def _finish(self) -> None:
if self.state is State.GAME_OVER:
if self.state is State.FINISHED:
return
self.state = State.GAME_OVER
self.state = State.FINISHED
self._stop_roll()
self.percent = self.trailer.load_percent()
if self.percent >= 99.9:
self.percent = 100.0
if self.submit_scores:
ok, message = submit_score(self.name, self.percent)
if ok or config.SCORE_URL:
self.status_line = message
self.status_is_error = not ok
# ------------------------------------------------------------------
# Zeichnen
# ------------------------------------------------------------------
def draw(self, surface) -> None:
assets = self.assets
scene.draw_top_bar(surface, assets, self.trailer.load_percent())
scene.draw_top_bar(
surface,
assets,
self.trailer.load_percent(),
subtitle=config.SUBTITLE_TEMPLATE.format(difficulty=self.difficulty.label),
)
scene.draw_background(surface, assets)
offset = (0, 0)
@@ -304,22 +309,16 @@ class Game:
surface, assets, self.trailer.find_platform(case) is not None
)
if self.state is State.GAME_OVER:
scene.draw_overlay_lines(
surface,
assets,
self.game_over_lines(),
status=self.status_line,
status_color=config.COLOR_ERROR if self.status_is_error else config.COLOR_HINT_SNAP,
)
if self.state is State.FINISHED:
scene.draw_overlay_lines(surface, assets, self.result_lines())
def game_over_lines(self) -> list[str]:
percent = int(self.percent)
if self.percent >= config.GAME_OVER_GOOD_PERCENT:
first = config.GAME_OVER_GOOD.format(name=self.name, percent=percent)
else:
first = config.GAME_OVER_NORMAL.format(name=self.name, percent=percent)
return [first, config.GAME_OVER_HINT]
def result_lines(self) -> list[str]:
return [
config.RESULT_TEMPLATE.format(
difficulty=self.difficulty.label, percent=int(self.percent)
),
config.RESULT_HINT,
]
# ------------------------------------------------------------------
# Hilfen