2024-04-06 10:30:07 +00:00
|
|
|
|
import pygame
|
|
|
|
|
from coords import Coords, Direction
|
|
|
|
|
from common import DrawableGameObject, EventHandler
|
|
|
|
|
|
|
|
|
|
|
2024-04-06 20:56:26 +00:00
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2024-04-06 10:30:07 +00:00
|
|
|
|
class Hero(DrawableGameObject, EventHandler):
|
|
|
|
|
"""объект главного героя"""
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
coords: Coords,
|
|
|
|
|
parent: DrawableGameObject,
|
|
|
|
|
assets: dict | None = None,
|
|
|
|
|
):
|
|
|
|
|
super().__init__(coords, parent, assets)
|
2024-04-07 17:37:02 +00:00
|
|
|
|
self._surface = pygame.image.load(self.assets["ghost.png"]).convert_alpha()
|
2024-04-06 10:30:07 +00:00
|
|
|
|
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
|
2024-04-06 20:56:26 +00:00
|
|
|
|
self.auto_move_target = None
|
|
|
|
|
|
2024-04-06 10:30:07 +00:00
|
|
|
|
# картинка изначально влево, а надо бы начинать со взгляда вправо
|
|
|
|
|
self.flip()
|
|
|
|
|
|
|
|
|
|
def draw(self):
|
2024-04-06 20:56:26 +00:00
|
|
|
|
self.auto_move()
|
2024-04-06 10:30:07 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-04-06 20:56:26 +00:00
|
|
|
|
self.auto_move_target = None
|
|
|
|
|
|
2024-04-06 10:30:07 +00:00
|
|
|
|
# уменьшение шага
|
|
|
|
|
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
|
|
|
|
|
|
2024-04-06 20:56:26 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-04-06 10:30:07 +00:00
|
|
|
|
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)
|
2024-04-06 20:56:26 +00:00
|
|
|
|
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
|
2024-04-06 10:30:07 +00:00
|
|
|
|
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)
|