diff --git a/mod_pygame/moveimg.py b/mod_pygame/moveimg.py index 99a1eb4..9ec663a 100644 --- a/mod_pygame/moveimg.py +++ b/mod_pygame/moveimg.py @@ -11,8 +11,6 @@ Если скачать не получилось то будут нарисованы заглушки. Зависимости: pygame pillow - - TODO: отбработка выхода из лабиринта """ import os @@ -21,7 +19,7 @@ import tempfile import urllib.request from abc import ABC, abstractmethod from contextlib import contextmanager -from random import choice, randrange +from random import choice, randrange, sample from typing import NamedTuple, Optional import PIL @@ -285,6 +283,9 @@ class Hero(GameObject): self.rect = self.surface.get_rect() self.rect.topleft = coords self.active = True + self.looking_right = False + # картинка изначально влево, а надо бы начинать со взгляда вправо + self.flip() def draw(self): self.parent.surface.blit(self.surface, self.rect) @@ -320,36 +321,57 @@ class Hero(GameObject): has_collision = self._check_collision(coords) super().set_coords(coords) + def flip(self): + self.looking_right = not self.looking_right + self._surface = pygame.transform.flip(self.surface, True, False) + + def move(self, direction: Coords, step: int = 1): + if direction.x != 0: + going_right = direction.x > 0 + if self.looking_right != going_right: + self.flip() + self.coords += direction * step + self.scene.coins.collect(self.rect) + + def move_left(self, step: int = 1): + self.move(Coords(-1, 0), step) + + def move_right(self, step: int = 1): + self.move(Coords(1, 0), step) + + def move_up(self, step: int = 1): + self.move(Coords(0, -1), step) + + def move_down(self, step: int = 1): + self.move(Coords(0, 1), step) + def handle_event(self, event: pygame.event.Event): if not self.active: return - up, down = Coords(0, -1), Coords(0, 1) - left, right = Coords(-1, 0), Coords(1, 0) - - da, db = 30, 5 + wide, short = 30, 5 if event.type == pygame.KEYDOWN: match event.key: case pygame.K_UP: - self.coords += up * da + self.move_up(wide) case pygame.K_DOWN: - self.coords += down * da + self.move_down(wide) case pygame.K_LEFT: - self.coords += left * da + self.move_left(wide) case pygame.K_RIGHT: - self.coords += right * da + self.move_right(wide) case pygame.K_w: - self.coords += up * db + self.move_up(short) case pygame.K_s: - self.coords += down * db + self.move_down(short) case pygame.K_a: - self.coords += left * db + self.move_left(short) case pygame.K_d: - self.coords += right * db + self.move_right(short) class WallBlock(GameObject): - """объект елемента стены""" + """объект элемента стены""" def __init__( self, @@ -395,6 +417,102 @@ class Walls(GameObject): return True return False +class Coin(GameObject): + """объект монетки""" + + def __init__( + self, + coords: Coords, + parent: GameObject, + assets: dict | None = None, + ): + super().__init__(coords, parent, assets) + self._surface = pygame.image.load(self.assets["coin.png"]) + self.rect = self.surface.get_rect() + self.rect.topleft = coords + # уменьшаем размер монетки + new_rect = self.rect.scale_by(0.7, 0.7) + self._surface = pygame.transform.scale(self._surface, new_rect.size) + self.rect = new_rect + + def draw(self): + self.parent.surface.blit(self.surface, self.rect) + +class Coins(GameObject): + """объект коллекции монеток""" + + def __init__( + self, + parent: GameObject, + maze: list[list[int]], + box_sz: Coords, + count: int, + assets: dict | None = None, + ): + super().__init__(Coords.zero(), parent, assets) + self.box_sz = box_sz + self._capacity = count + + free_points = [] + excluded = Coords(0, 1), get_maze_sz(maze) - Coords(1, 2) + for i, row in enumerate(maze): + for j, item in enumerate(row): + p = Coords(j, i) + if item < 1 and p not in excluded: + free_points.append(p) + continue + + coin_points = sample(free_points, min(count, len(free_points))) + self.coins = [ + Coin(point.transform(box_sz), self, self.assets) + for point in coin_points + ] + self.collected_coins = [] + + # Надпись, если все монетки собраны + font = pygame.font.SysFont("Arial", 30) + text = "Все монетки собраны!" + self.done_txt = font.render(text, 1, "#050366e3") + self.done_txt_rect = self.done_txt.get_rect() + self.done_txt_rect.topleft = Coords(10, 10) + + @property + def capacity(self): + return self._capacity + + @property + def coins_left(self): + return len(self.coins) + + @property + def coins_collected(self): + return self.capacity - self.coins_left + + @property + def all_collected(self): + return self.coins_left == 0 + + def draw(self): + for coin in self.collected_coins: + coin.draw() + for coin in self.coins: + coin.draw() + if self.all_collected: + self.parent.surface.blit(self.done_txt, self.done_txt_rect) + + def add_to_collected(self, coin: Coin): + last_pos = Coords(10, 10) + if self.collected_coins: + last_pos = Coords(*self.collected_coins[-1].rect.topright) + last_pos -= Coords(coin.rect.width // 2, 0) + coin.coords = last_pos + self.collected_coins.append(coin) + + def collect(self, rect: pygame.Rect): + mined = [*filter(lambda coin: coin.rect.colliderect(rect), self.coins)] + for coin in mined: + self.coins.remove(coin) + self.add_to_collected(coin) class EndLevel(GameObject): def __init__(self, scene: GameObject): @@ -417,10 +535,19 @@ class EndLevel(GameObject): self.hint_rect.center = self.parent.rect.center self.hint_rect = self.hint_rect.move(Coords(0, 300)) + # Надпись для хорошего финала + text = "Все монетки собраны!" + self.goodtxt = font_hint.render(text, 1, "#96081ba4") + self.goodtxt_rect = self.goodtxt.get_rect() + self.goodtxt_rect.center = self.parent.rect.center + self.goodtxt_rect = self.goodtxt_rect.move(Coords(0, -100)) + def draw(self): if not self.active: return - self.parent.surface.blit(self.image, self.rect) + if self.scene.coins.all_collected: + self.parent.surface.blit(self.image, self.rect) + self.parent.surface.blit(self.goodtxt, self.goodtxt_rect) self.parent.surface.blit(self.surface, self.rect) self.parent.surface.blit(self.hint, self.hint_rect) @@ -439,7 +566,7 @@ class Scene(GameObject): # кнопки для выхода из игры exit_keys = (pygame.K_ESCAPE, pygame.K_q) - def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords): + def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords, coins_count: int): super().__init__(Coords.zero(), None, assets) self.maze = maze_gen(*maze_sz) maze_sz = get_maze_sz(self.maze) @@ -447,6 +574,7 @@ class Scene(GameObject): box_sz = screen_sz // get_maze_sz(self.maze) self.box_sz = box_sz resize_img(self.assets, "brick.png", box_sz) + resize_img(self.assets, "coin.png", box_sz) hero_sz = Coords(*map(int, box_sz * 0.8)) resize_img(self.assets, "ghost.png", hero_sz) @@ -461,12 +589,15 @@ class Scene(GameObject): self.done = False self.level_completed = False self.maze = maze_gen(6, 6) + self.walls = Walls(self, self.maze, box_sz) + self.coins = Coins(self, self.maze, box_sz, coins_count) + self.end = EndLevel(self) self.end.active = False self.want_new_level = False self.exit_rect = self.get_exit_rect() - # для тестирования экрана конца уровня + # #для тестирования экрана конца уровня # self.hero.coords = Coords(*self.exit_rect.topleft) + Coords( # -self.box_sz.x // 2, 5 # ) @@ -495,9 +626,9 @@ class Scene(GameObject): if self.level_completed: self.end.draw() else: - # pygame.draw.rect(self._surface, pygame.Color("#42c53d25"), self.get_exit_rect()) self.hero.draw() self.walls.draw() + self.coins.draw() def handle_event(self, event: pygame.event.Event): if self.done: @@ -525,11 +656,12 @@ def game(assets): pygame.init() screen_sz = Coords(1000, 1000) maze_sz = Coords(6, 6) + coins_count = 10 pygame.display.set_caption("Движение рисунка на Pygame") want_new_level = True while want_new_level: - scene = Scene(assets, screen_sz, maze_sz) + scene = Scene(assets, screen_sz, maze_sz, coins_count) scene.event_loop() want_new_level = scene.want_new_level @@ -542,6 +674,7 @@ def main(): "ghost.png", "brick.png", "win.png", + "coin.png", ] with get_assets(assets) as assets: game(assets)