phantomcastle wasm: control by mouse
This commit is contained in:
parent
9482dd6c0d
commit
6ee4776711
@ -65,6 +65,21 @@ class Coords(NamedTuple):
|
|||||||
def transform(self, ref: "Coords"):
|
def transform(self, ref: "Coords"):
|
||||||
return self * ref
|
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
|
@classmethod
|
||||||
def zero(cls):
|
def zero(cls):
|
||||||
return cls(0, 0)
|
return cls(0, 0)
|
||||||
@ -125,7 +140,10 @@ def maze_gen(row=4, col=4):
|
|||||||
for pattern, replacement in upd.items():
|
for pattern, replacement in upd.items():
|
||||||
for i in range(len(maze) - len(pattern) + 1):
|
for i in range(len(maze) - len(pattern) + 1):
|
||||||
for j in range(len(maze[0]) - len(pattern[0]) + 1):
|
for j in range(len(maze[0]) - len(pattern[0]) + 1):
|
||||||
if all(maze[i + k][j : j + len(v)] == list(map(int, v)) for k, v in enumerate(pattern)):
|
if all(
|
||||||
|
maze[i + k][j : j + len(v)] == list(map(int, v))
|
||||||
|
for k, v in enumerate(pattern)
|
||||||
|
):
|
||||||
for k, v in filter(lambda x: x[1], enumerate(replacement)):
|
for k, v in filter(lambda x: x[1], enumerate(replacement)):
|
||||||
maze[i + k][j : j + len(v)] = list(map(int, v))
|
maze[i + k][j : j + len(v)] = list(map(int, v))
|
||||||
|
|
||||||
@ -153,6 +171,18 @@ class Direction(Enum):
|
|||||||
case Direction.DOWN:
|
case Direction.DOWN:
|
||||||
return Coords(0, 1)
|
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):
|
class SurfaceWithRect(NamedTuple):
|
||||||
surface: pygame.Surface
|
surface: pygame.Surface
|
||||||
@ -165,7 +195,9 @@ class SurfaceWithRect(NamedTuple):
|
|||||||
class DrawableGameObject(ABC):
|
class DrawableGameObject(ABC):
|
||||||
"""обобщение игрового элемента"""
|
"""обобщение игрового элемента"""
|
||||||
|
|
||||||
coords = property(lambda self: self.get_coords(), lambda self, c: self.set_coords(c))
|
coords = property(
|
||||||
|
lambda self: self.get_coords(), lambda self, c: self.set_coords(c)
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -221,13 +253,17 @@ class DrawableGameObject(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class KeysHandler(ABC):
|
class EventHandler(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def handle_keys(self, keys_pressed):
|
def handle_keys(self, keys_pressed):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def handle_event(self):
|
||||||
|
pass
|
||||||
|
|
||||||
class Hero(DrawableGameObject, KeysHandler):
|
|
||||||
|
class Hero(DrawableGameObject, EventHandler):
|
||||||
"""объект главного героя"""
|
"""объект главного героя"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -246,6 +282,7 @@ class Hero(DrawableGameObject, KeysHandler):
|
|||||||
self.looking_right = False
|
self.looking_right = False
|
||||||
self._speed = 1
|
self._speed = 1
|
||||||
self.direction = Direction.RIGHT
|
self.direction = Direction.RIGHT
|
||||||
|
self.mouse_active = False
|
||||||
# картинка изначально влево, а надо бы начинать со взгляда вправо
|
# картинка изначально влево, а надо бы начинать со взгляда вправо
|
||||||
self.flip()
|
self.flip()
|
||||||
|
|
||||||
@ -280,7 +317,9 @@ class Hero(DrawableGameObject, KeysHandler):
|
|||||||
# проверка колизии
|
# проверка колизии
|
||||||
has_collision = self._check_collision(coords)
|
has_collision = self._check_collision(coords)
|
||||||
if not has_collision:
|
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:
|
while has_collision and coords != self.coords:
|
||||||
@ -290,6 +329,7 @@ class Hero(DrawableGameObject, KeysHandler):
|
|||||||
coords = coords_new
|
coords = coords_new
|
||||||
has_collision = self._check_collision(coords)
|
has_collision = self._check_collision(coords)
|
||||||
super().set_coords(coords)
|
super().set_coords(coords)
|
||||||
|
self.scene.coins.collect(self)
|
||||||
|
|
||||||
def flip(self):
|
def flip(self):
|
||||||
self.looking_right = not self.looking_right
|
self.looking_right = not self.looking_right
|
||||||
@ -310,7 +350,30 @@ class Hero(DrawableGameObject, KeysHandler):
|
|||||||
def move(self, direction: Direction, step: int = 1):
|
def move(self, direction: Direction, step: int = 1):
|
||||||
self.update_direction(direction)
|
self.update_direction(direction)
|
||||||
self.coords += direction.as_coords() * step * self.speed // 3
|
self.coords += direction.as_coords() * step * self.speed // 3
|
||||||
self.scene.coins.collect(self)
|
|
||||||
|
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
|
||||||
|
self.handle_mouse_event(event)
|
||||||
|
|
||||||
def handle_keys(self, keys_pressed):
|
def handle_keys(self, keys_pressed):
|
||||||
if not self.active:
|
if not self.active:
|
||||||
@ -351,6 +414,7 @@ class WallBlock(DrawableGameObject):
|
|||||||
# уменьшаем размер монетки
|
# уменьшаем размер монетки
|
||||||
sf = Coords(1, 1)
|
sf = Coords(1, 1)
|
||||||
self._surface, self.rect = self.scene.scale_box(self.surface, self.rect, sf)
|
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):
|
def draw(self):
|
||||||
self.parent.surface.blit(self.surface, self.rect)
|
self.parent.surface.blit(self.surface, self.rect)
|
||||||
@ -438,7 +502,9 @@ class Coins(DrawableGameObject):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
coin_points = sample(free_points, min(count, len(free_points)))
|
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.coins = [
|
||||||
|
Coin(point.transform(box_sz), self, self.assets) for point in coin_points
|
||||||
|
]
|
||||||
self.collected_coins = []
|
self.collected_coins = []
|
||||||
|
|
||||||
# Надпись, если все монетки собраны
|
# Надпись, если все монетки собраны
|
||||||
@ -487,7 +553,7 @@ class Coins(DrawableGameObject):
|
|||||||
self.add_to_collected(coin)
|
self.add_to_collected(coin)
|
||||||
|
|
||||||
|
|
||||||
class EndLevelMenu(DrawableGameObject, KeysHandler):
|
class EndLevelMenu(DrawableGameObject, EventHandler):
|
||||||
def __init__(self, scene: DrawableGameObject):
|
def __init__(self, scene: DrawableGameObject):
|
||||||
super().__init__(Coords.zero(), scene, scene.assets)
|
super().__init__(Coords.zero(), scene, scene.assets)
|
||||||
self._surface, self.rect = self._create_end_game_label()
|
self._surface, self.rect = self._create_end_game_label()
|
||||||
@ -557,21 +623,37 @@ class EndLevelMenu(DrawableGameObject, KeysHandler):
|
|||||||
self.stats_label = self._create_stats_label()
|
self.stats_label = self._create_stats_label()
|
||||||
self.stats_label.draw_to(self.parent.surface)
|
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_keys(self, keys_pressed):
|
def handle_keys(self, keys_pressed):
|
||||||
if not self.active:
|
if not self.active:
|
||||||
return
|
return
|
||||||
if keys_pressed[pygame.K_n]:
|
if keys_pressed[pygame.K_n]:
|
||||||
self.parent.want_new_level = True
|
self.request_new_level()
|
||||||
self.parent.done = True
|
|
||||||
|
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
|
||||||
|
self.handle_mouse_event(event)
|
||||||
|
|
||||||
|
|
||||||
class Scene(DrawableGameObject, KeysHandler):
|
class Scene(DrawableGameObject, EventHandler):
|
||||||
"""основной игровой объект"""
|
"""основной игровой объект"""
|
||||||
|
|
||||||
# кнопки для выхода из игры
|
# кнопки для выхода из игры
|
||||||
exit_keys = (pygame.K_ESCAPE, pygame.K_q)
|
exit_keys = (pygame.K_ESCAPE, pygame.K_q)
|
||||||
|
|
||||||
def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords, coins_count: int):
|
def __init__(
|
||||||
|
self, assets: dict, screen_sz: Coords, maze_sz: Coords, coins_count: int
|
||||||
|
):
|
||||||
super().__init__(Coords.zero(), None, assets)
|
super().__init__(Coords.zero(), None, assets)
|
||||||
self.maze = maze_gen(*maze_sz)
|
self.maze = maze_gen(*maze_sz)
|
||||||
maze_sz = get_maze_sz(self.maze)
|
maze_sz = get_maze_sz(self.maze)
|
||||||
@ -630,7 +712,9 @@ class Scene(DrawableGameObject, KeysHandler):
|
|||||||
self.walls.draw()
|
self.walls.draw()
|
||||||
self.coins.draw()
|
self.coins.draw()
|
||||||
|
|
||||||
def scale_box(self, surface: pygame.Surface, rect: pygame.Rect, scale_factor: Coords):
|
def scale_box(
|
||||||
|
self, surface: pygame.Surface, rect: pygame.Rect, scale_factor: Coords
|
||||||
|
):
|
||||||
rect.size = self.box_sz
|
rect.size = self.box_sz
|
||||||
rect.scale_by_ip(*scale_factor)
|
rect.scale_by_ip(*scale_factor)
|
||||||
surface = pygame.transform.scale(surface, rect.size)
|
surface = pygame.transform.scale(surface, rect.size)
|
||||||
@ -644,10 +728,18 @@ class Scene(DrawableGameObject, KeysHandler):
|
|||||||
self.check_level_completed()
|
self.check_level_completed()
|
||||||
self.end.handle_keys(keys_pressed)
|
self.end.handle_keys(keys_pressed)
|
||||||
|
|
||||||
|
def handle_event(self, event: pygame.event.Event):
|
||||||
|
if self.done:
|
||||||
|
return
|
||||||
|
self.hero.handle_event(event)
|
||||||
|
self.check_level_completed()
|
||||||
|
self.end.handle_event(event)
|
||||||
|
|
||||||
async def event_loop(self):
|
async def event_loop(self):
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
while not self.done:
|
while not self.done:
|
||||||
pygame.event.pump()
|
for event in pygame.event.get():
|
||||||
|
self.handle_event(event)
|
||||||
self.handle_keys(pygame.key.get_pressed())
|
self.handle_keys(pygame.key.get_pressed())
|
||||||
self.draw()
|
self.draw()
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
Loading…
Reference in New Issue
Block a user