""" Camera system for viewport scrolling. Provides abstraction for camera motion in different modes: - Vertical: traditional upward scroll - Horizontal: left/right movement - Omni: combination of both - Floating: sinusoidal/bobbing motion """ import math from collections.abc import Callable from dataclasses import dataclass, field from enum import Enum, auto class CameraMode(Enum): VERTICAL = auto() HORIZONTAL = auto() OMNI = auto() FLOATING = auto() @dataclass class Camera: """Camera for viewport scrolling. Attributes: x: Current horizontal offset (positive = scroll left) y: Current vertical offset (positive = scroll up) mode: Current camera mode speed: Base scroll speed custom_update: Optional custom update function """ x: int = 0 y: int = 0 mode: CameraMode = CameraMode.VERTICAL speed: float = 1.0 custom_update: Callable[["Camera", float], None] | None = None _time: float = field(default=0.0, repr=False) def update(self, dt: float) -> None: """Update camera position based on mode. Args: dt: Delta time in seconds """ self._time += dt if self.custom_update: self.custom_update(self, dt) return if self.mode == CameraMode.VERTICAL: self._update_vertical(dt) elif self.mode == CameraMode.HORIZONTAL: self._update_horizontal(dt) elif self.mode == CameraMode.OMNI: self._update_omni(dt) elif self.mode == CameraMode.FLOATING: self._update_floating(dt) def _update_vertical(self, dt: float) -> None: self.y += int(self.speed * dt * 60) def _update_horizontal(self, dt: float) -> None: self.x += int(self.speed * dt * 60) def _update_omni(self, dt: float) -> None: speed = self.speed * dt * 60 self.y += int(speed) self.x += int(speed * 0.5) def _update_floating(self, dt: float) -> None: base = self.speed * 30 self.y = int(math.sin(self._time * 2) * base) self.x = int(math.cos(self._time * 1.5) * base * 0.5) def reset(self) -> None: """Reset camera position.""" self.x = 0 self.y = 0 self._time = 0.0 @classmethod def vertical(cls, speed: float = 1.0) -> "Camera": """Create a vertical scrolling camera.""" return cls(mode=CameraMode.VERTICAL, speed=speed) @classmethod def horizontal(cls, speed: float = 1.0) -> "Camera": """Create a horizontal scrolling camera.""" return cls(mode=CameraMode.HORIZONTAL, speed=speed) @classmethod def omni(cls, speed: float = 1.0) -> "Camera": """Create an omnidirectional scrolling camera.""" return cls(mode=CameraMode.OMNI, speed=speed) @classmethod def floating(cls, speed: float = 1.0) -> "Camera": """Create a floating/bobbing camera.""" return cls(mode=CameraMode.FLOATING, speed=speed) @classmethod def custom(cls, update_fn: Callable[["Camera", float], None]) -> "Camera": """Create a camera with custom update function.""" return cls(custom_update=update_fn)