"""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 @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 – es gibt also keinen zusätzlichen ``rotated``-Schalter, der mit den Massen kollidieren könnte (das war im Altstand eine Fehlerquelle). """ units_w: int units_h: int x: float y: float color: tuple[int, int, int] = config.CASE_COLORS[0] placed: bool = False snapped: bool = False allow_snap: bool = False can_rotate: bool = True # -- 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 # -- Bewegung ------------------------------------------------------- def move(self, dt: float, wall_x: float) -> None: """Bewegt den Koffer nach links, mit Abbremsen kurz vor der Barriere.""" if self.placed: return distance = self.x - wall_x speed = ( config.CASE_SPEED_FAST if distance > config.FAST_TO_SLOW_DISTANCE else config.CASE_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