diff --git a/mod_pygame/phantom_castle.py b/mod_pygame/phantom_castle.py index 4b91c6e..0062f97 100644 --- a/mod_pygame/phantom_castle.py +++ b/mod_pygame/phantom_castle.py @@ -145,6 +145,21 @@ class Coords(NamedTuple): def transform(self, ref: "Coords"): return self * ref + def dir_norm(self): + """нормализация вектора, но только для получения направления + x, y могут быть только -1, 0, 1 + может быть только направление по горизонтали либо по вертикали + либо без направления + """ + src = self + if self.x and self.y: + src = ( + self.__class__(self.x, 0) + if abs(self.x) > abs(self.y) + else self.__class__(0, self.y) + ) + return self.__class__(*((n > 0) - (n < 0) for n in src)) + @classmethod def zero(cls): return cls(0, 0) @@ -236,6 +251,18 @@ class Direction(Enum): case Direction.DOWN: return Coords(0, 1) + @staticmethod + def from_coords(coords: Coords) -> Optional["Direction"]: + match coords.dir_norm(): + case Coords(-1, 0): + return Direction.LEFT + case Coords(1, 0): + return Direction.RIGHT + case Coords(0, -1): + return Direction.UP + case Coords(0, 1): + return Direction.DOWN + class SurfaceWithRect(NamedTuple): surface: pygame.Surface @@ -331,6 +358,7 @@ class Hero(DrawableGameObject, EventHandler): self.looking_right = False self._speed = 1 self.direction = Direction.RIGHT + self.mouse_active = False # картинка изначально влево, а надо бы начинать со взгляда вправо self.flip() @@ -365,7 +393,9 @@ class Hero(DrawableGameObject, EventHandler): # проверка колизии has_collision = self._check_collision(coords) if not has_collision: - return super().set_coords(coords) + super().set_coords(coords) + self.scene.coins.collect(self) + return # уменьшение шага while has_collision and coords != self.coords: @@ -375,6 +405,7 @@ class Hero(DrawableGameObject, EventHandler): 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 @@ -395,31 +426,53 @@ class Hero(DrawableGameObject, EventHandler): def move(self, direction: Direction, step: int = 1): self.update_direction(direction) self.coords += direction.as_coords() * step * self.speed // 3 - self.scene.coins.collect(self) + + def handle_keyboard_event(self, event: pygame.event.Event): + wide, short = 3, 1 + if event.type != pygame.KEYDOWN: + return + match event.key: + case pygame.K_UP: + self.move(Direction.UP, wide) + case pygame.K_DOWN: + self.move(Direction.DOWN, wide) + case pygame.K_LEFT: + self.move(Direction.LEFT, wide) + case pygame.K_RIGHT: + self.move(Direction.RIGHT, wide) + case pygame.K_w: + self.move(Direction.UP, short) + case pygame.K_s: + self.move(Direction.DOWN, short) + case pygame.K_a: + self.move(Direction.LEFT, short) + case pygame.K_d: + self.move(Direction.RIGHT, short) + + 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) + 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 - - wide, short = 3, 1 - if event.type == pygame.KEYDOWN: - match event.key: - case pygame.K_UP: - self.move(Direction.UP, wide) - case pygame.K_DOWN: - self.move(Direction.DOWN, wide) - case pygame.K_LEFT: - self.move(Direction.LEFT, wide) - case pygame.K_RIGHT: - self.move(Direction.RIGHT, wide) - case pygame.K_w: - self.move(Direction.UP, short) - case pygame.K_s: - self.move(Direction.DOWN, short) - case pygame.K_a: - self.move(Direction.LEFT, short) - case pygame.K_d: - self.move(Direction.RIGHT, short) + self.handle_keyboard_event(event) + self.handle_mouse_event(event) class WallBlock(DrawableGameObject): @@ -438,6 +491,7 @@ class WallBlock(DrawableGameObject): # уменьшаем размер монетки sf = Coords(1, 1) self._surface, self.rect = self.scene.scale_box(self.surface, self.rect, sf) + self._mask = pygame.mask.Mask(self.rect.size, fill=True) def draw(self): self.parent.surface.blit(self.surface, self.rect) @@ -646,13 +700,26 @@ class EndLevelMenu(DrawableGameObject, EventHandler): self.stats_label = self._create_stats_label() self.stats_label.draw_to(self.parent.surface) + def request_new_level(self): + self.scene.want_new_level = True + self.scene.done = True + + def handle_keyboard_event(self, event: pygame.event.Event): + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_n: + self.request_new_level() + + def handle_mouse_event(self, event: pygame.event.Event): + if event.type == pygame.MOUSEBUTTONDOWN and self.keys_hint.rect.collidepoint( + event.pos + ): + self.request_new_level() + def handle_event(self, event: pygame.event.Event): if not self.active: return - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_n: - self.parent.want_new_level = True - self.parent.done = True + self.handle_keyboard_event(event) + self.handle_mouse_event(event) class Scene(DrawableGameObject, EventHandler):