score: int = 0
-@dataclass
-class GameStatus(Resource):
- tick: int = 0
- cycle_chase: int = 60 * 10
- cycle_wander: int = 60 * 10
- chase: bool = False
- flee: bool = False
-
-
@dataclass
class Inputs(Resource):
inputs: dict[int, float]
pass
+@dataclass
+class GhostStatus(Component):
+ tick: int = 0
+ cycle_chase: int = 60 * 10
+ cycle_wander: int = 60 * 10
+ chase: bool = False
+ flee: bool = False
+
+
@dataclass
class Ghost(Component):
variant: GhostVariant
def setup_maze(world: World) -> None:
- world.add_system(create_maze)(MazeGenerator((12, 12)))
+ world.add_system(create_maze)(MazeGenerator((14, 14)))
def move_in_motion(
def move_to_target(
- entities: Query[tuple[TargetTile, InMotion, Coord2D]],
+ entities: Query[tuple[TargetTile, InMotion, Coord2D, GhostStatus]],
cells: Query[tuple[Cell, Coord2D]],
maze_q: Query[Maze],
- status: GameStatus,
) -> None:
if (maze := maze_q.single()) is None:
return
- for target, motion, pos in entities:
+ for target, motion, pos, status in entities:
cell, cell_pos = cells[motion.pos]
if cell_pos.xy.distance_squared_to(pos.xy) > 0.01:
continue
TilePos,
Ghost,
TargetTile,
+ GhostStatus,
],
[
(
)
]
for direction in "URDL"
- ],
+ ] + [[assets.load("./asset/enemy/ghost_blue.png")]],
6,
),
ShouldRender(),
TilePos(maze._cells[pos]),
Ghost(variant, pos),
TargetTile(pos),
+ GhostStatus(),
)
for variant, pos in [
(GhostVariant.A, (0, 0)),
world: World,
pacgums: Query[tuple[CoordAbs2D, Entity], SuperPacGum],
pacmans: Query[CoordAbs2D, Pacman],
- ghosts: Query[InMotion, Ghost],
+ ghosts: Query[tuple[InMotion, GhostStatus], Ghost],
score: Score,
- status: GameStatus,
) -> None:
collided = [
pacgum
]
score.score += len(collided * 10)
if len(collided) > 0:
- status.flee = True
- status.tick = 60 * 8
- for ghost in ghosts:
+ for ghost, status in ghosts:
tmp = ghost.src
ghost.src = ghost.pos
ghost.pos = tmp
+ status.flee = True
+ status.tick = 60 * 8
world.despawn_many(collided)
def update_ghost_targets(
- ghosts: Query[tuple[Ghost, TilePos, TargetTile]],
+ ghosts: Query[tuple[Ghost, TilePos, TargetTile, GhostStatus]],
pacman_q: Query[tuple[InMotion, TilePos], Pacman],
cells: Query[Cell],
- chase: GameStatus,
) -> None:
player = pacman_q.single()
if player is None:
return
player_motion, player_pos = player
blinky_pos = next(
- pos.pos for ghost, pos, _ in ghosts if ghost.variant == GhostVariant.A
+ pos.pos
+ for ghost, pos, _, _ in ghosts
+ if ghost.variant == GhostVariant.A
)
- for ghost, pos, target in ghosts:
- if not chase.chase:
+ for ghost, pos, target, status in ghosts:
+ if not status.chase:
target.pos = ghost.home
continue
target.pos = ghost.variant.compute_target_tile(
)
-def update_chase_status(status: GameStatus) -> None:
- if status.flee:
- if status.tick == 0:
- status.flee = False
- return
- status.tick -= 1
- else:
- status.tick += 1
- status.chase = status.tick > status.cycle_wander
- if status.tick < (status.cycle_wander + status.cycle_chase):
- return
+def update_chase_status(status_q: Query[GhostStatus]) -> None:
+ for status in status_q:
+ if status.flee:
+ if status.tick == 0:
+ status.flee = False
+ continue
+ status.tick -= 1
+ else:
+ status.tick += 1
+ status.chase = status.tick > status.cycle_wander
+ if status.tick < (status.cycle_wander + status.cycle_chase):
+ continue
+
+ status.tick = 0
+ if status.cycle_wander > 60 * 4:
+ status.cycle_wander -= 60
+ if status.cycle_chase > 60 * 7:
+ status.cycle_chase -= 60
+
+
+def update_ghost_speed(ghost: Query[tuple[MoveSpeed, GhostStatus]]) -> None:
+ for speed, status in ghost:
+ speed.pixels_per_frame = 2.5
+ if status.flee:
+ speed.pixels_per_frame = 1.5
- status.tick = 0
- if status.cycle_wander > 60 * 4:
- status.cycle_wander -= 60
- if status.cycle_chase > 60 * 7:
- status.cycle_chase -= 60
def update_pacman_anim(
def update_ghost_anim(
- ghost_q: Query[tuple[InMotion, AnimationSet], Ghost],
+ ghost_q: Query[tuple[InMotion, AnimationSet, GhostStatus], Ghost],
) -> None:
- for motion, anim in ghost_q:
- anim.idx = motion.direction
+ for motion, anim, status in ghost_q:
+ anim.idx = motion.direction if not status.flee else 4
def setup_resources(world: World) -> None:
world.spawn_resource(Inputs({}))
- world.spawn_resource(GameStatus())
world.spawn_resource(Score())
move_to_target,
move_in_motion,
update_chase_status,
+ update_ghost_speed,
)
.chain()
.in_set(Update),