]> Untitled Git - axy/ft/pacman.git/commitdiff
Fix library + despawn maze and childrens
authorThomas Rimelen <trimelen@k0r1p10.42mulhouse.fr>
Thu, 8 Oct 2026 12:35:25 +0000 (14:35 +0200)
committerThomas Rimelen <trimelen@k0r1p10.42mulhouse.fr>
Thu, 8 Oct 2026 12:35:25 +0000 (14:35 +0200)
src/pacman/ecs/hierarchy.py
src/pacman/ecs/world.py
src/pacman/game/__init__.py

index a50e97ce282fd23dc0d0b6d55b8535260ff7784b..116f53ac6c5eb56c2f5cc8f5a465e6c60c098d61 100644 (file)
@@ -153,3 +153,23 @@ class Hierarchy[Items, Filters = tuple[()]](FromWorld):
             if children is None:
                 continue
             queue.extend((child, e) for e in children.children)
+
+    def despawn_with_children(self, root: Entity) -> Callable[[World], None]:
+        queue = [root]
+        entities = set()
+        while len(queue) != 0:
+            nxt = queue.pop()
+            if nxt in entities:
+                continue
+            entities.add(nxt)
+            if (contents := self._nodes.get(nxt)) is None:
+                continue
+            _, children = contents
+            if children is None:
+                continue
+            queue.extend(children.children)
+
+        def inner(world: World) -> None:
+            world.despawn_many(entities)
+
+        return inner
index 28322e7cce726c70ba77971275c678539eeca305..34722c969a66eaa8323a1df28878c365cbe74dd6 100644 (file)
@@ -132,7 +132,7 @@ class Archetype:
 
     def add_components[T: Components](
         self, ty: type[T], components: list[tuple["Entity", T]]
-    ) -> None:
+    ) -> Callable[[], None]:
         dst, comps_ty = self._compute_add(ty)
         comps = [components_iter(e) for _, e in components]
         positions = [self._positions[e] for e, _ in components]
@@ -155,19 +155,22 @@ class Archetype:
         dst._extend(cb)
         self._discard(positions)
 
-        for k, lst in moved_comps:
-            hooks = self._world._components_cache[k][1]
-            hooks._remove(
-                zip((dst._entities[i] for i in itr), lst, strict=True)
-            )
-        for k in comps_ty:
-            lst = dst._components[k]
-            hooks = self._world._components_cache[k][1]
-            hooks._insert((dst._entities[i], lst[i]) for i in itr)
+        def res() -> None:
+            for k, lst in moved_comps:
+                hooks = self._world._components_cache[k][1]
+                hooks._remove(
+                    zip((dst._entities[i] for i in itr), lst, strict=True)
+                )
+            for k in comps_ty:
+                lst = dst._components[k]
+                hooks = self._world._components_cache[k][1]
+                hooks._insert((dst._entities[i], lst[i]) for i in itr)
+
+        return res
 
     def remove_components[T: Components](
-        self, ty: type[T], entities: Iterable["Entity"]
-    ) -> None:
+        self, ty: type[T], entities: list["Entity"]
+    ) -> Callable[[], None]:
         dst, comps_ty = self._compute_remove(ty)
         positions = [self._positions[e] for e in entities]
 
@@ -183,11 +186,16 @@ class Archetype:
         ]
         dst._extend(cb)
         self._discard(positions)
-        for k, comp in zip(comps_ty, removed_comps, strict=True):
-            hooks = self._world._components_cache[k][1]
-            hooks._remove(zip(removed_ents, comp, strict=True))
 
-    def remove(self, entities: list["Entity"]) -> None:
+        def res() -> None:
+            for k, comp in zip(comps_ty, removed_comps, strict=True):
+                hooks = self._world._components_cache[k][1]
+                hooks._remove(zip(removed_ents, comp, strict=True))
+
+        return res
+
+    def remove(self, entities: list["Entity"]) -> Callable[[], None]:
+        entities = [e for e in entities if e in self._positions]
         positions = [self._positions[e] for e in entities]
         comps = [
             (
@@ -199,8 +207,12 @@ class Archetype:
         self._discard(positions)
         for ent in entities:
             del self._world._entities[ent]
-        for hook, comp in comps:
-            hook._remove(zip(entities, comp, strict=True))
+
+        def res() -> None:
+            for hook, comp in comps:
+                hook._remove(zip(entities, comp, strict=True))
+
+        return res
 
     def _extend(
         self,
@@ -767,7 +779,7 @@ class World(FromWorld):
         self._base_archetype.add_components(
             cast(type[Components], ty),
             list(zip(ents, cast(list[Components], comps), strict=True)),
-        )
+        )()
         return ents
 
     def despawn(self, ent: Entity) -> None:
@@ -780,8 +792,9 @@ class World(FromWorld):
             if arch not in archs:
                 archs[arch] = []
             archs[arch].append(ent)
-        for arch, lst in archs.items():
-            arch.remove(lst)
+        cbs = [arch.remove(lst) for arch, lst in archs.items()]
+        for cb in cbs:
+            cb()
 
     def insert[T: Components](self, ty: type[T], ent: Entity, comp: T) -> None:
         self.insert_many(ty, [(ent, comp)])
@@ -796,8 +809,9 @@ class World(FromWorld):
                 archs[arch] = []
             archs[arch].append((ent, comp))
 
-        for arch, lst in archs.items():
-            arch.add_components(ty, lst)
+        cbs = [arch.add_components(ty, lst) for arch, lst in archs.items()]
+        for cb in cbs:
+            cb()
 
     def remove[T: Components](self, ty: type[T], ent: Entity) -> None:
         self.remove_many(ty, [ent])
@@ -807,13 +821,16 @@ class World(FromWorld):
     ) -> None:
         archs: dict[Archetype, list[Entity]] = {}
         for ent in comps:
+            if ent not in self._entities:
+                continue
             arch = self._entities[ent]
             if arch not in archs:
                 archs[arch] = []
             archs[arch].append(ent)
 
-        for arch, lst in archs.items():
-            arch.remove_components(ty, lst)
+        cbs = [arch.remove_components(ty, lst) for arch, lst in archs.items()]
+        for cb in cbs:
+            cb()
 
     def _sys_extractors[T](
         self, system: System[T]
index a3e96a071b9ef3d0d07a0c9fab593cc7c6979a5b..7362e19c422db79248fad945b10acff7053aaf2b 100644 (file)
@@ -20,10 +20,18 @@ from pacman.ecs.graphics import (
     WindowEvents,
     graphics_plugins,
 )
-from pacman.ecs.hierarchy import ChildOf
+from pacman.ecs.hierarchy import ChildOf, Hierarchy
 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
+from pacman.ecs.world import (
+    CommandQueue,
+    Component,
+    Entity,
+    Query,
+    Resource,
+    Systems,
+    World,
+)
 
 _DIR_OFFSETS = [(0, -1), (1, 0), (0, 1), (-1, 0)]
 
@@ -253,7 +261,7 @@ def spawn_pacgums(
 
 
 def setup_maze(world: World) -> None:
-    world.add_system(create_maze)(MazeGenerator((14, 14)))
+    world.add_system(create_maze)(MazeGenerator((5, 5)))
 
 
 def move_in_motion(
@@ -322,10 +330,11 @@ def move_to_target(
 
 
 def spawn_pacman(
-    world: World, assets: Assets[Sprite2D], maze_q: Query[Maze]
+    world: World, assets: Assets[Sprite2D], maze_q: Query[tuple[Maze, Entity]]
 ) -> None:
-    if (maze := maze_q.single()) is None:
+    if (maze_t := maze_q.single()) is None:
         return
+    maze, maze_ent = maze_t
     world.spawn(
         tuple[
             Coord2D,
@@ -336,6 +345,7 @@ def spawn_pacman(
             InMotion,
             MoveSpeed,
             TilePos,
+            ChildOf,
         ],
         (
             Coord2D(3 * 64, 3 * 64, -10),
@@ -357,15 +367,17 @@ def spawn_pacman(
             InMotion(maze._cells[(3, 3)], maze._cells[(3, 3)], 1),
             MoveSpeed(3.0),
             TilePos(maze._cells[(3, 3)]),
+            ChildOf(maze_ent),
         ),
     )
 
 
 def spawn_ghosts(
-    world: World, assets: Assets[Sprite2D], maze_q: Query[Maze]
+    world: World, assets: Assets[Sprite2D], maze_q: Query[tuple[Maze, Entity]]
 ) -> None:
-    if (maze := maze_q.single()) is None:
+    if (maze_t := maze_q.single()) is None:
         return
+    maze, maze_ent = maze_t
     world.spawn_many(
         tuple[
             Coord2D,
@@ -378,6 +390,7 @@ def spawn_ghosts(
             Ghost,
             TargetTile,
             GhostStatus,
+            ChildOf,
         ],
         [
             (
@@ -403,6 +416,7 @@ def spawn_ghosts(
                 Ghost(variant, pos, maze._cells[pos]),
                 TargetTile(pos),
                 GhostStatus(),
+                ChildOf(maze_ent),
             )
             for variant, pos in [
                 (GhostVariant.A, (0, 0)),
@@ -487,8 +501,10 @@ def collect_superpacgum(
 def collide_ghost(
     pacmans: Query[CoordAbs2D, Pacman],
     ghosts: Query[
-        tuple[CoordAbs2D, GhostStatus, Ghost, InMotion, Coord2D], Ghost
+        tuple[CoordAbs2D, GhostStatus, Ghost, InMotion, Coord2D],
+        Ghost,
     ],
+    score: Score,
 ) -> None:
     for pacman_pos in pacmans:
         for ghost_pos, status, ghost, motion, pos in ghosts:
@@ -501,7 +517,9 @@ def collide_ghost(
                 case GhostState.wander | GhostState.chase:
                     # TODO: kill pacman
                     print("Pacman hit")
+
                 case GhostState.flee:
+                    score.score += 100  # FIXME
                     status.state = GhostState.dead
                     status.tick = 0
                     motion.src = ghost.home_entity
@@ -618,8 +636,13 @@ def update_inputs(inputs: Inputs, events: WindowEvents) -> None:
             del inputs.inputs[key]
 
 
-def victory() -> None:
-    return
+def victory(
+    hierarchy: Hierarchy[tuple[()]],
+    mazes: Query[Entity, Maze],
+    queue: CommandQueue,
+) -> None:
+    for maze in mazes:
+        queue.queue(hierarchy.despawn_with_children(maze))
 
 
 def run_game() -> None: