2024-04-06 10:30:07 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2024-04-06 19:50:23 +00:00
|
|
|
class SingletonMeta(type):
|
|
|
|
_instances = {}
|
|
|
|
|
|
|
|
def __call__(cls, *args, **kwargs):
|
|
|
|
if cls not in cls._instances:
|
|
|
|
cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
|
|
|
|
return cls._instances[cls]
|
|
|
|
|
|
|
|
|
2024-04-06 10:30:07 +00:00
|
|
|
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
|