90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
"""Der Koffer: Geometrie und Einlaufbewegung.
|
||
|
||
Bewusst ohne ``pygame``-Abhängigkeit, damit die Regeln testbar bleiben.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
from . import config
|
||
from .difficulty import DEFAULT_DIFFICULTY, Difficulty
|
||
|
||
|
||
@dataclass
|
||
class Case:
|
||
"""Ein Koffer im Auflieger.
|
||
|
||
``units_w`` / ``units_h`` beschreiben die **aktuelle** Grundfläche in
|
||
Kasten-Einheiten. Eine Drehung tauscht die beiden Werte.
|
||
|
||
``texture`` merkt sich die Orientierung, in der der Koffer eingelaufen ist.
|
||
Die Grafik bleibt dadurch immer dieselbe – ein gedrehter Koffer wird nur um
|
||
90° gedreht gezeichnet und bekommt keine andere Textur.
|
||
"""
|
||
|
||
units_w: int
|
||
units_h: int
|
||
x: float
|
||
y: float
|
||
color: tuple[int, int, int] = config.CASE_COLORS[0]
|
||
texture: tuple[int, int] | None = None
|
||
placed: bool = False
|
||
snapped: bool = False
|
||
allow_snap: bool = False
|
||
can_rotate: bool = True
|
||
|
||
def __post_init__(self) -> None:
|
||
if self.texture is None:
|
||
self.texture = (self.units_w, self.units_h)
|
||
|
||
# -- Geometrie ------------------------------------------------------
|
||
@property
|
||
def width(self) -> float:
|
||
return self.units_w * config.UNIT_LENGTH
|
||
|
||
@property
|
||
def height(self) -> float:
|
||
return self.units_h * config.UNIT_HEIGHT
|
||
|
||
@property
|
||
def right(self) -> float:
|
||
return self.x + self.width
|
||
|
||
@property
|
||
def bottom(self) -> float:
|
||
return self.y + self.height
|
||
|
||
@property
|
||
def is_rotated(self) -> bool:
|
||
"""Steht der Koffer quer zu seiner Textur?"""
|
||
return (self.units_w, self.units_h) != self.texture
|
||
|
||
# -- Bewegung -------------------------------------------------------
|
||
def move(
|
||
self,
|
||
dt: float,
|
||
wall_x: float,
|
||
difficulty: Difficulty = DEFAULT_DIFFICULTY,
|
||
) -> None:
|
||
"""Bewegt den Koffer nach links, mit Abbremsen kurz vor der Barriere."""
|
||
if self.placed:
|
||
return
|
||
distance = self.x - wall_x
|
||
speed = (
|
||
difficulty.speed_fast
|
||
if distance > config.FAST_TO_SLOW_DISTANCE
|
||
else difficulty.speed_slow
|
||
)
|
||
self.x -= speed * dt
|
||
if self.x <= wall_x + config.SNAP_THRESHOLD:
|
||
self.allow_snap = True
|
||
|
||
def rotate_footprint(self) -> None:
|
||
"""Tauscht Breite und Höhe (nach einer 90°-Drehung)."""
|
||
self.units_w, self.units_h = self.units_h, self.units_w
|
||
|
||
def fits_trailer_height(self) -> bool:
|
||
"""Passt der Koffer nach einer Drehung noch in die Höhe des Aufliegers?"""
|
||
return self.width <= config.TRAILER_HEIGHT
|