]> Untitled Git - axy/ft/pacman.git/commitdiff
Resource system cleanup
author= <=>
Mon, 5 Oct 2026 11:37:16 +0000 (13:37 +0200)
committer= <=>
Mon, 5 Oct 2026 11:37:16 +0000 (13:37 +0200)
src/pacman/ecs/graphics.py
src/pacman/ecs/world.py
src/pacman/game/__init__.py

index 1ea9c9e439838df38c35d4e67c522e79483811a3..caa3a70468588a6d2583d7220e7590f44d3eb8bb 100644 (file)
@@ -51,11 +51,10 @@ class WindowEvents(Resource):
 
 def init_window(world: World) -> None:
     pygame.init()
-    world.spawn(
-        Window,
+    world.spawn_resource(
         Window(pygame.display.set_mode((1600, 1200)), pygame.time.Clock()),
     )
-    world.spawn(WindowEvents, WindowEvents([]))
+    world.spawn_resource(WindowEvents([]))
 
 
 @dataclass
index d663e308522255a8939b922a42c77a193a02ea00..8e4eed08fd0db1c9bafa1e9fda33650dc7638944 100644 (file)
@@ -58,8 +58,8 @@ def components_type_iter(
             for sub in get_args(components)
             for e in components_type_iter(sub)
         )
-    elif issubclass(components, Component):
-        yield components
+    elif issubclass(origin_or_cls(components), Component):
+        yield cast(type[Component], components)
     else:
         yield from cast(type[Bundle], components).components_type_iter(
             components
@@ -632,6 +632,11 @@ class Query[Items, Filters = tuple[()]](
             raise Exception("More than one entity matches single query")
         return res
 
+    def single_e(self) -> Items:
+        if (res := self.single()) is None:
+            raise Exception("single_e must contain exactly one matching item")
+        return res
+
 
 class Resource(FromWorld, Component):
     @override
@@ -643,7 +648,14 @@ class Resource(FromWorld, Component):
         query: Callable[[], Query[Self]] = cast(
             Callable[[], Query[Self]], _query.extract_from_world(_query, world)
         )
-        default: Callable[[], Self | None] = cls.extract_default(ty, world)
+        def_cb: Callable[[], Self | None] = cls.extract_default(ty, world)
+
+        def default() -> Self | None:
+            if (val := def_cb()) is None:
+                return
+            world.spawn(cast(type[Self], ty), val)
+            return val
+
         return lambda: (
             res if (res := query().single()) is not None else default()
         )
@@ -734,6 +746,20 @@ class World(FromWorld):
     def spawn[T: Components](self, ty: type[T], comp: T) -> Entity:
         return self.spawn_many(ty, [comp])[0]
 
+    def spawn_resource[T: Component](
+        self, comp: T, ty: type[T] | None = None
+    ) -> Entity:
+        if ty is None:
+            ty = type(comp)
+
+        type Ty = ty  # type: ignore
+
+        def remove(query: Query[Entity, Ty], world: World) -> None:
+            world.remove_many(ty, list(query))
+
+        self.add_raw_system(remove)()
+        return self.spawn(ty, comp)
+
     def spawn_many[T: Components](
         self, ty: type[T], comps: list[T]
     ) -> list[Entity]:
index 58c6dbf3333f734715c72662313dc89a35e6f5ca..5d5c425f35b476d4458aa1697ba4ba382f2acfc0 100644 (file)
@@ -474,8 +474,8 @@ def update_ghost_anim(
 
 
 def setup_resources(world: World) -> None:
-    world.spawn(Inputs, Inputs(set()))
-    world.spawn(ChaseStatus, ChaseStatus())
+    world.spawn_resource(Inputs(set()))
+    world.spawn_resource(ChaseStatus())
 
 
 def update_inputs(inputs: Inputs, events: WindowEvents) -> None: