From efa19e339867a5eb359a6b204504acad7f74aea4 Mon Sep 17 00:00:00 2001
From: Dmitry <b4tm4n@mail.ru>
Date: Fri, 29 Mar 2024 22:29:54 +0300
Subject: [PATCH] pygame moveimg objects

---
 mod_pygame/moveimg.py | 169 +++++++++++++++++++++++++++++++++---------
 1 file changed, 136 insertions(+), 33 deletions(-)

diff --git a/mod_pygame/moveimg.py b/mod_pygame/moveimg.py
index f8a994e..c7be425 100644
--- a/mod_pygame/moveimg.py
+++ b/mod_pygame/moveimg.py
@@ -8,16 +8,17 @@
     Зависимости: pygame pillow
 """
 
-import pygame
 import os
 import shutil
 import tempfile
 import urllib.request
+from abc import ABC, abstractmethod
 from contextlib import contextmanager
-from typing import NamedTuple
+from typing import NamedTuple, Optional
 
 import PIL
-from PIL import Image, ImageDraw
+import pygame
+from PIL import ImageDraw
 
 
 def download_asset(asset, path):
@@ -117,6 +118,10 @@ class Coords(NamedTuple):
     def transform(self, ref: "Coords"):
         return self * ref
 
+    @classmethod
+    def zero(cls):
+        return cls(0, 0)
+
 
 def resize_img(assets: dict, name: str, sz: Coords):
     """
@@ -143,41 +148,138 @@ def choose_plural(amount, declensions):
     return f"{amount} {declensions[i]}"
 
 
-def draw_background_image(screen, filename):
-    background = pygame.image.load(filename)
-    screen.blit(background, (0, 0))
+class GameObject(ABC):
+    """обобщение игрового элемента"""
+
+    coords = property(
+        lambda self: self.get_coords(), lambda self, c: self.set_coords(c)
+    )
+
+    def __init__(
+        self,
+        coords: Coords,
+        parent: Optional["GameObject"] = None,
+        assets: dict | None = None,
+    ):
+        self.parent = parent
+        self._coords = coords
+        self.assets = assets or (parent.assets if parent else None)
+        self._surface = None
+
+    @property
+    def surface(self) -> pygame.Surface | None:
+        return self._surface or (self.parent.surface if self.parent else None)
+
+    def get_coords(self) -> Coords:
+        return self._coords
+
+    def set_coords(self, coords: Coords):
+        if self.parent:
+            if self.parent.surface:
+                if not (
+                    coords.x >= 0
+                    and coords.x < self.parent.surface.get_width()
+                    and coords.y >= 0
+                    and coords.y < self.parent.surface.get_height()
+                ):
+                    return
+        self._coords = coords
+
+    @abstractmethod
+    def draw(self):
+        pass
+
+    @abstractmethod
+    def handle_event(self, event: pygame.event.Event):
+        pass
+
+
+class Hero(GameObject):
+    """объект главного героя"""
+
+    def __init__(
+        self,
+        coords: Coords,
+        parent: GameObject,
+        assets: dict | None = None,
+    ):
+        super().__init__(coords, parent, assets)
+        screen_sz = Coords(*parent.surface.get_size())
+        ghost_sz = screen_sz // 10
+        resize_img(self.assets, "ghost.png", ghost_sz)
+
+        self._surface = pygame.image.load(self.assets["ghost.png"])
+        self.rect = self.surface.get_rect()
+        self.coords = coords
+
+    def set_coords(self, coords: Coords):
+        super().set_coords(coords)
+        self.rect.topleft = self.coords
+
+    def draw(self):
+        self.parent.surface.blit(self.surface, self.rect)
+
+    def handle_event(self, event: pygame.event.Event):
+        delta = 10
+        if event.type == pygame.KEYDOWN:
+            match event.key:
+                case pygame.K_UP | pygame.K_w:
+                    self.coords += Coords(0, -1) * delta
+                case pygame.K_DOWN | pygame.K_s:
+                    self.coords += Coords(0, 1) * delta
+                case pygame.K_LEFT | pygame.K_a:
+                    self.coords += Coords(-1, 0) * delta
+                case pygame.K_RIGHT | pygame.K_d:
+                    self.coords += Coords(1, 0) * delta
+
+
+class Scene(GameObject):
+    """основной игровой объект"""
+
+    # кнопки для выхода из игры
+    exit_keys = (pygame.K_ESCAPE, pygame.K_q)
+
+    def __init__(self, assets: dict, sz: Coords):
+        super().__init__(Coords.zero(), None, assets)
+        self._surface = pygame.display.set_mode(sz)
+        self.hero = Hero(Coords(100, 100), self)
+        resize_img(assets, "bg1k.png", sz)
+        self.background = pygame.image.load(self.assets["bg1k.png"])
+        self.done = False
+
+    def draw(self):
+        if self.done:
+            return
+        self.surface.blit(self.background, self.coords)
+        self.hero.draw()
+
+    def handle_event(self, event: pygame.event.Event):
+        if self.done:
+            return
+        if (
+            event.type == pygame.QUIT
+            or event.type == pygame.KEYDOWN
+            and event.key in self.exit_keys
+        ):
+            self.done = True
+        if not self.done:
+            self.hero.handle_event(event)
+
+    def event_loop(self):
+        while not self.done:
+            event = pygame.event.wait()
+            self.handle_event(event)
+            self.draw()
+            pygame.display.flip()
 
 
 def game(assets):
     pygame.init()
-    width, height = screen_sz = Coords(1000, 1000)
-    screen = pygame.display.set_mode(screen_sz)
+    screen_sz = Coords(1000, 1000)
     pygame.display.set_caption("Движение рисунка на Pygame")
 
-    ghost_sz = screen_sz // 10
-
-    resize_img(assets, "bg1k.png", screen_sz)
-    speed = [2, 2]
-
-    resize_img(assets, "ghost.png", ghost_sz)
-    ghost = pygame.image.load(assets["ghost.png"])
-    ghostrect = ghost.get_rect()
-
-    running = True
-
-    while running:
-        for event in pygame.event.get():
-            if event.type == pygame.QUIT: 
-                running = False
-        ghostrect = ghostrect.move(speed)
-        if ghostrect.left < 0 or ghostrect.right > width:
-            speed[0] = -speed[0]
-        if ghostrect.top < 0 or ghostrect.bottom > height:
-            speed[1] = -speed[1]
-
-        draw_background_image(screen, assets["bg1k.png"])
-        screen.blit(ghost, ghostrect)
-        pygame.display.flip()
+    scene = Scene(assets, screen_sz)
+    scene.event_loop()
 
     pygame.quit()
 
@@ -190,4 +292,5 @@ def main():
     with get_assets(assets) as assets:
         game(assets)
 
-main()
\ No newline at end of file
+
+main()