]> Untitled Git - axy/ft/pacman.git/commitdiff
pacgum collision and chases added
authorThomas Rimelen <trimelen@k0r4p2.42mulhouse.fr>
Mon, 5 Oct 2026 13:52:47 +0000 (15:52 +0200)
committerThomas Rimelen <trimelen@k0r4p2.42mulhouse.fr>
Mon, 5 Oct 2026 13:52:47 +0000 (15:52 +0200)
src/pacman/game/__init__.py

index a21efe0735df88a177511e4ec64bc7203b7514a9..a621720486f99ed8b0c550b6627e5d309e71f1cf 100644 (file)
@@ -1,7 +1,7 @@
 from collections.abc import Callable
 from dataclasses import dataclass
 from enum import Enum, auto
-from random import random
+from random import random, shuffle
 from time import time
 
 from mazegenerator import MazeGenerator  # type: ignore[import-untyped]
@@ -10,7 +10,7 @@ from pygame.math import clamp
 
 from pacman.ecs import minimal_plugins
 from pacman.ecs.assets import Assets
-from pacman.ecs.coord import Coord2D, CoordAbs2D
+from pacman.ecs.coord import Coord2D, CoordAbs2D, PropagateTransform
 from pacman.ecs.graphics import (
     AnimationSet,
     GraphicsSystems,
@@ -26,6 +26,20 @@ from pacman.ecs.update import PostUpdate, PreUpdate, Update
 from pacman.ecs.world import Component, Entity, Query, Resource, Systems, World
 
 
+@dataclass
+class Score(Resource):
+    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
+
+
 class Maze(Component):
     def __init__(self, x: int, y: int) -> None:
         self._cells: dict[tuple[int, int], Entity] = {}
@@ -151,7 +165,7 @@ def spawn_pacgums(
 
 
 def setup_maze(world: World) -> None:
-    world.add_system(create_maze)(MazeGenerator())
+    world.add_system(create_maze)(MazeGenerator((12, 12)))
 
 
 @dataclass
@@ -206,6 +220,7 @@ def move_to_target(
     entities: Query[tuple[TargetTile, InMotion, Coord2D]],
     cells: Query[tuple[Cell, Coord2D]],
     maze_q: Query[Maze],
+    status: GameStatus
 ) -> None:
     if (maze := maze_q.single()) is None:
         return
@@ -229,6 +244,10 @@ def move_to_target(
                 )
             ),
         )
+        if status.flee:
+            lst = candidates[:-1]
+            shuffle(lst)
+            candidates = lst + [candidates[-1]]
         nxt, nxt_dir = candidates[0]
         motion.src = motion.pos
         motion.pos = nxt
@@ -314,7 +333,7 @@ class GhostVariant(Enum):
             case GhostVariant.A:
                 return player
             case GhostVariant.B:
-                if Vector2(player).distance_squared_to(pos) >= 64:
+                if Vector2(player).distance_squared_to(pos) >= 16:
                     return player
                 return slf.home
             case GhostVariant.C:
@@ -326,7 +345,7 @@ class GhostVariant(Enum):
                 x, y = player
                 x_target, y_target = (x + 2 * dx, y + 2 * dy)
                 x_blinky, y_blinky = blinky_pos
-                return ((x_target - x_blinky) * 2, (y_target - y_blinky) * 2)
+                return (x_target * 2 - x_blinky, y_target * 2 - y_blinky)
 
 
 @dataclass
@@ -412,19 +431,56 @@ def move_pacman(
             motion.direction = direction
 
 
-@dataclass
-class ChaseStatus(Resource):
-    tick: int = 0
-    cycle_chase: int = 60 * 10
-    cycle_wander: int = 60 * 10
-    chase: bool = False
+def collect_pacgum(
+    world: World,
+    pacgums: Query[tuple[CoordAbs2D, Entity], PacGum],
+    pacmans: Query[CoordAbs2D, Pacman],
+    score: Score,
+) -> None:
+    collided = [
+        pacgum
+        for coord, pacgum in pacgums
+        for pos in pacmans
+        if pos._vec.xy.distance_squared_to(coord._vec.xy) < 100
+    ]
+    score.score += len(collided)
+    world.despawn_many(collided)
+
+
+def collect_superpacgum(
+    world: World,
+    pacgums: Query[tuple[CoordAbs2D, Entity], SuperPacGum],
+    pacmans: Query[CoordAbs2D, Pacman],
+    ghosts: Query[InMotion, Ghost],
+    score: Score,
+    status: GameStatus,
+) -> None:
+    collided = [
+        pacgum
+        for coord, pacgum in pacgums
+        for pos in pacmans
+        if pos._vec.xy.distance_squared_to(coord._vec.xy) < 100
+    ]
+    score.score += len(collided * 10)
+    if len(collided) > 0:
+        status.flee = True
+        status.tick = 60 * 8
+        for ghost in ghosts:
+            tmp = ghost.src
+            ghost.src = ghost.pos
+            ghost.pos = tmp
+    world.despawn_many(collided)
+
+
+def level_is_finished(pacgums: Query[SuperPacGum | PacGum]) -> bool:
+    return next(iter(pacgums), None) is None
 
 
 def update_ghost_targets(
     ghosts: Query[tuple[Ghost, TilePos, TargetTile]],
     pacman_q: Query[tuple[InMotion, TilePos], Pacman],
     cells: Query[Cell],
-    chase: ChaseStatus,
+    chase: GameStatus,
 ) -> None:
     player = pacman_q.single()
     if player is None:
@@ -446,16 +502,23 @@ def update_ghost_targets(
         )
 
 
-def update_chase_status(status: ChaseStatus) -> None:
-    status.tick += 1
-    status.chase = status.tick > status.cycle_wander
-    if status.tick < (status.cycle_wander + status.cycle_chase):
-        return
-    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_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
+
+        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(
@@ -474,7 +537,8 @@ def update_ghost_anim(
 
 def setup_resources(world: World) -> None:
     world.spawn_resource(Inputs({}))
-    world.spawn_resource(ChaseStatus())
+    world.spawn_resource(GameStatus())
+    world.spawn_resource(Score())
 
 
 def update_inputs(inputs: Inputs, events: WindowEvents) -> None:
@@ -485,6 +549,10 @@ def update_inputs(inputs: Inputs, events: WindowEvents) -> None:
             del inputs.inputs[key]
 
 
+def victory() -> None:
+    return
+
+
 def run_game() -> None:
     World().with_plugins(graphics_plugins, minimal_plugins).with_systems(
         StartupSchedule,
@@ -512,4 +580,6 @@ def run_game() -> None:
         Systems(update_pacman_anim, update_ghost_anim)
         .before(GraphicsSystems)
         .in_set(PostUpdate),
+        Systems(collect_pacgum, collect_superpacgum).after(PropagateTransform),
+        Systems(victory).run_if(level_is_finished),
     ).run_main()