From: Thomas Rimelen Date: Wed, 7 Oct 2026 14:26:05 +0000 (+0200) Subject: oranised + collide_ghost added X-Git-Url: https://git.uwuaxy.net/Project%20Management.png?a=commitdiff_plain;h=14a95d322a3cc985a456056d7ee04c5d4f2e8dea;p=axy%2Fft%2Fpacman.git oranised + collide_ghost added --- diff --git a/src/pacman/game/__init__.py b/src/pacman/game/__init__.py index a621720..830c98d 100644 --- a/src/pacman/game/__init__.py +++ b/src/pacman/game/__init__.py @@ -25,6 +25,41 @@ from pacman.ecs.schedule import StartupSchedule, UpdateSchedule from pacman.ecs.update import PostUpdate, PreUpdate, Update from pacman.ecs.world import Component, Entity, Query, Resource, Systems, World +_DIR_OFFSETS = [(0, -1), (1, 0), (0, 1), (-1, 0)] + + +class GhostVariant(Enum): + A = auto() + B = auto() + C = auto() + D = auto() + + def compute_target_tile( + self, + player: tuple[int, int], + player_dir: int, + pos: tuple[int, int], + slf: "Ghost", + blinky_pos: tuple[int, int], + ) -> tuple[int, int]: + match self: + case GhostVariant.A: + return player + case GhostVariant.B: + if Vector2(player).distance_squared_to(pos) >= 16: + return player + return slf.home + case GhostVariant.C: + dx, dy = _DIR_OFFSETS[player_dir] + x, y = player + return (x + 2 * dx, y + 2 * dy) + case GhostVariant.D: + dx, dy = _DIR_OFFSETS[player_dir] + x, y = player + x_target, y_target = (x + 2 * dx, y + 2 * dy) + x_blinky, y_blinky = blinky_pos + return (x_target * 2 - x_blinky, y_target * 2 - y_blinky) + @dataclass class Score(Resource): @@ -40,6 +75,63 @@ class GameStatus(Resource): flee: bool = False +@dataclass +class Inputs(Resource): + inputs: dict[int, float] + + def get_dir(self) -> int | None: + lookup = {K_w: 0, K_d: 1, K_s: 2, K_a: 3} + res = sorted( + ((k, v) for k, v in self.inputs.items() if k in lookup), + key=lambda e: e[1], + reverse=True, + ) + if len(res) == 0: + return None + return lookup[res[0][0]] + + +class PacGum(Component): + pass + + +class SuperPacGum(Component): + pass + + +@dataclass +class Pacman(Component): + pass + + +@dataclass +class Ghost(Component): + variant: GhostVariant + home: tuple[int, int] + + +@dataclass +class TargetTile(Component): + pos: tuple[int, int] + + +@dataclass +class TilePos(Component): + pos: Entity + + +@dataclass +class InMotion(Component): + src: Entity + pos: Entity + direction: int + + +@dataclass +class MoveSpeed(Component): + pixels_per_frame: float + + class Maze(Component): def __init__(self, x: int, y: int) -> None: self._cells: dict[tuple[int, int], Entity] = {} @@ -47,9 +139,6 @@ class Maze(Component): self._y = y -_DIR_OFFSETS = [(0, -1), (1, 0), (0, 1), (-1, 0)] - - @dataclass class Cell(Component): n: int @@ -63,14 +152,6 @@ class Cell(Component): return [(1 & (self.n >> i)) == 1 for i in range(4)] -class PacGum(Component): - pass - - -class SuperPacGum(Component): - pass - - def create_maze( world: World, assets: Assets[Sprite2D] ) -> Callable[[MazeGenerator], Entity]: @@ -168,28 +249,6 @@ def setup_maze(world: World) -> None: world.add_system(create_maze)(MazeGenerator((12, 12))) -@dataclass -class TargetTile(Component): - pos: tuple[int, int] - - -@dataclass -class TilePos(Component): - pos: Entity - - -@dataclass -class InMotion(Component): - src: Entity - pos: Entity - direction: int - - -@dataclass -class MoveSpeed(Component): - pixels_per_frame: float - - def move_in_motion( entities: Query[tuple[InMotion, Coord2D, MoveSpeed]], positions: Query[Coord2D], @@ -220,7 +279,7 @@ def move_to_target( entities: Query[tuple[TargetTile, InMotion, Coord2D]], cells: Query[tuple[Cell, Coord2D]], maze_q: Query[Maze], - status: GameStatus + status: GameStatus, ) -> None: if (maze := maze_q.single()) is None: return @@ -254,27 +313,6 @@ def move_to_target( motion.direction = nxt_dir -@dataclass -class Inputs(Resource): - inputs: dict[int, float] - - def get_dir(self) -> int | None: - lookup = {K_w: 0, K_d: 1, K_s: 2, K_a: 3} - res = sorted( - ((k, v) for k, v in self.inputs.items() if k in lookup), - key=lambda e: e[1], - reverse=True, - ) - if len(res) == 0: - return None - return lookup[res[0][0]] - - -@dataclass -class Pacman(Component): - pass - - def spawn_pacman( world: World, assets: Assets[Sprite2D], maze_q: Query[Maze] ) -> None: @@ -292,7 +330,7 @@ def spawn_pacman( TilePos, ], ( - Coord2D(0, 0, -10), + Coord2D(3 * 64, 3 * 64, -10), CoordAbs2D(), AnimationSet( [ @@ -308,52 +346,13 @@ def spawn_pacman( ), Pacman(), ShouldRender(), - InMotion(maze._cells[(0, 0)], maze._cells[(0, 0)], 1), + InMotion(maze._cells[(3, 3)], maze._cells[(3, 3)], 1), MoveSpeed(3.0), - TilePos(maze._cells[(0, 0)]), + TilePos(maze._cells[(3, 3)]), ), ) -class GhostVariant(Enum): - A = auto() - B = auto() - C = auto() - D = auto() - - def compute_target_tile( - self, - player: tuple[int, int], - player_dir: int, - pos: tuple[int, int], - slf: "Ghost", - blinky_pos: tuple[int, int], - ) -> tuple[int, int]: - match self: - case GhostVariant.A: - return player - case GhostVariant.B: - if Vector2(player).distance_squared_to(pos) >= 16: - return player - return slf.home - case GhostVariant.C: - dx, dy = _DIR_OFFSETS[player_dir] - x, y = player - return (x + 2 * dx, y + 2 * dy) - case GhostVariant.D: - dx, dy = _DIR_OFFSETS[player_dir] - x, y = player - x_target, y_target = (x + 2 * dx, y + 2 * dy) - x_blinky, y_blinky = blinky_pos - return (x_target * 2 - x_blinky, y_target * 2 - y_blinky) - - -@dataclass -class Ghost(Component): - variant: GhostVariant - home: tuple[int, int] - - def spawn_ghosts( world: World, assets: Assets[Sprite2D], maze_q: Query[Maze] ) -> None: @@ -457,9 +456,9 @@ def collect_superpacgum( ) -> None: collided = [ pacgum - for coord, pacgum in pacgums - for pos in pacmans - if pos._vec.xy.distance_squared_to(coord._vec.xy) < 100 + for pacgum_pos, pacgum in pacgums + for pacman_pos in pacmans + if pacman_pos._vec.xy.distance_squared_to(pacgum_pos._vec.xy) < 100 ] score.score += len(collided * 10) if len(collided) > 0: @@ -472,6 +471,18 @@ def collect_superpacgum( world.despawn_many(collided) +def collide_ghost( + pacmans: Query[CoordAbs2D, Pacman], ghosts: Query[CoordAbs2D, Ghost] +) -> None: + for pacman_pos in pacmans: + for ghost_pos in ghosts: + if ( + pacman_pos._vec.xy.distance_squared_to(ghost_pos._vec.xy) + < 3600 + ): + pass + + def level_is_finished(pacgums: Query[SuperPacGum | PacGum]) -> bool: return next(iter(pacgums), None) is None @@ -529,9 +540,9 @@ def update_pacman_anim( def update_ghost_anim( - pacman_q: Query[tuple[InMotion, AnimationSet], Ghost], + ghost_q: Query[tuple[InMotion, AnimationSet], Ghost], ) -> None: - for motion, anim in pacman_q: + for motion, anim in ghost_q: anim.idx = motion.direction @@ -580,6 +591,8 @@ def run_game() -> None: Systems(update_pacman_anim, update_ghost_anim) .before(GraphicsSystems) .in_set(PostUpdate), - Systems(collect_pacgum, collect_superpacgum).after(PropagateTransform), + Systems(collect_pacgum, collect_superpacgum, collide_ghost).after( + PropagateTransform + ), Systems(victory).run_if(level_is_finished), ).run_main()