from abc import ABC, abstractmethod from typing import NamedTuple, Optional FONT_NAME = "Arial" from sys import platform IS_WASM = platform == "emscripten" import pygame from coords import Coords class SurfaceWithRect(NamedTuple): surface: pygame.Surface rect: pygame.Rect def draw_to(self, target: pygame.Surface): target.blit(self.surface, self.rect) class DrawableGameObject(ABC): """обобщение игрового элемента""" coords = property( lambda self: self.get_coords(), lambda self, c: self.set_coords(c) ) def __init__( self, coords: Coords, parent: Optional["DrawableGameObject"] = None, assets: dict | None = None, ): self.parent = parent self.rect = pygame.Rect(coords, coords) self.assets = assets or (parent.assets if parent else None) self._surface = None self._mask = None def __str__(self): return f"{self.__class__.__name__}({self.coords})" @property def surface(self) -> pygame.Surface | None: return self._surface or (self.parent.surface if self.parent else None) @property def mask(self) -> pygame.Mask | None: if not self._mask: self._mask = pygame.mask.from_surface(self.surface.convert_alpha()) return self._mask def overlap(self, rect: pygame.Rect, mask: pygame.Mask) -> bool: if not self.rect.colliderect(rect): return False offset = Coords(*self.rect.topleft) - Coords(*rect.topleft) overlap = mask.overlap(self.mask, offset) return overlap is not None @property def scene(self): return self.parent.scene if self.parent else self def get_coords(self) -> Coords: return Coords(*self.rect.topleft) def set_coords(self, coords: Coords): new_rect = self.rect.copy() new_rect.topleft = coords if self.parent: if self.parent.rect: if not self.parent.rect.contains(new_rect): return self.rect = new_rect @abstractmethod def draw(self): pass class EventHandler(ABC): @abstractmethod def handle_keys(self, keys_pressed): pass @abstractmethod def handle_event(self): pass