import pygame from coords import Coords, Direction from common import DrawableGameObject, EventHandler def point_at(coords: Coords) -> tuple[pygame.Rect, pygame.Mask]: rect = pygame.Rect(coords.x, coords.y, 1, 1) mask = pygame.Mask((1, 1), fill=True) return rect, mask def collide_with_walls(coords: Coords, walls: DrawableGameObject) -> bool: rect, mask = point_at(coords) return walls.check_collision(rect, mask) def is_valid_point_to_move(coords: Coords, walls: DrawableGameObject) -> bool: return not collide_with_walls(coords, walls) class Hero(DrawableGameObject, EventHandler): """объект главного героя""" def __init__( self, coords: Coords, parent: DrawableGameObject, assets: dict | None = None, ): super().__init__(coords, parent, assets) self._surface = pygame.image.load(self.assets["ghost.png"]).convert_alpha() self.rect = self.surface.get_rect() sf = Coords(0.8, 0.8) self._surface, self.rect = self.scene.scale_box(self.surface, self.rect, sf) self.rect.topleft = coords self.active = True self.looking_right = False self._speed = 1 self.direction = Direction.RIGHT self.mouse_active = False self.auto_move_target = None # картинка изначально влево, а надо бы начинать со взгляда вправо self.flip() def draw(self): self.auto_move() self.parent.surface.blit(self.surface, self.rect) def _check_collision(self, coords): """Проверка пересечения со стенами""" new_rect = self.rect.copy() new_rect.topleft = coords return self.scene.walls.check_collision(new_rect, self.mask) @property def speed(self): return max(self._speed, 1) @speed.setter def speed(self, value): self._speed = min(value, 15) def _reduce_step(self, coords): """Уменьшение шага движения, с целью подойти вплотную к стене""" delta = coords - self.coords dx, dy = 0, 0 if abs(delta.x) > 1: dx = 1 * (delta.x < 0 or -1) if abs(delta.y) > 1: dy = 1 * (delta.y < 0 or -1) return coords + Coords(dx, dy) def set_coords(self, coords: Coords): # проверка колизии has_collision = self._check_collision(coords) if not has_collision: super().set_coords(coords) self.scene.coins.collect(self) return self.auto_move_target = None # уменьшение шага while has_collision and coords != self.coords: coords_new = self._reduce_step(coords) if coords_new == coords: return # не могу уменьшить шаг coords = coords_new has_collision = self._check_collision(coords) super().set_coords(coords) self.scene.coins.collect(self) def flip(self): self.looking_right = not self.looking_right self._surface = pygame.transform.flip(self.surface, flip_x=True, flip_y=False) def update_direction(self, direction: Direction): if direction in (Direction.LEFT, Direction.RIGHT): going_right = direction == Direction.RIGHT if self.looking_right != going_right: self.flip() if direction != self.direction: self.speed = 0 self.direction = direction else: self.speed += 1 def move(self, direction: Direction, step: int = 1): self.update_direction(direction) self.coords += direction.as_coords() * step * self.speed // 3 def auto_move(self): if self.auto_move_target is None: return rect, mask = point_at(self.auto_move_target) if self.overlap(rect, mask): self.auto_move_target = None return direction = Direction.from_coords(self.auto_move_target - self.coords) if direction: self.move(direction, step=1) else: self.auto_move_target = None def handle_mouse_event(self, event: pygame.event.Event): if event.type not in ( pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, ): return match event.type: case pygame.MOUSEBUTTONDOWN: self.mouse_active = self.rect.collidepoint(event.pos) if not self.mouse_active and is_valid_point_to_move( Coords(*event.pos), self.scene.walls ): self.auto_move_target = Coords(*event.pos) else: self.auto_move_target = None case pygame.MOUSEBUTTONUP: self.mouse_active = False case pygame.MOUSEMOTION if self.mouse_active: rel = Coords(*event.rel) direction = Direction.from_coords(rel) if direction: self.update_direction(direction) self.coords += rel def handle_event(self, event: pygame.event.Event): if not self.active: return self.handle_mouse_event(event) def handle_keys(self, keys_pressed): if not self.active: return wide, short = 3, 1 if keys_pressed[pygame.K_UP]: self.move(Direction.UP, wide) if keys_pressed[pygame.K_DOWN]: self.move(Direction.DOWN, wide) if keys_pressed[pygame.K_LEFT]: self.move(Direction.LEFT, wide) if keys_pressed[pygame.K_RIGHT]: self.move(Direction.RIGHT, wide) if keys_pressed[pygame.K_w]: self.move(Direction.UP, short) if keys_pressed[pygame.K_s]: self.move(Direction.DOWN, short) if keys_pressed[pygame.K_a]: self.move(Direction.LEFT, short) if keys_pressed[pygame.K_d]: self.move(Direction.RIGHT, short)