From ea3b621fb4fb75bea0e70087ecdb1d121bbfa028 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 5 Oct 2026 13:37:16 +0200 Subject: [PATCH] Resource system cleanup --- src/pacman/ecs/graphics.py | 5 ++--- src/pacman/ecs/world.py | 32 +++++++++++++++++++++++++++++--- src/pacman/game/__init__.py | 4 ++-- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/pacman/ecs/graphics.py b/src/pacman/ecs/graphics.py index 1ea9c9e..caa3a70 100644 --- a/src/pacman/ecs/graphics.py +++ b/src/pacman/ecs/graphics.py @@ -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 diff --git a/src/pacman/ecs/world.py b/src/pacman/ecs/world.py index d663e30..8e4eed0 100644 --- a/src/pacman/ecs/world.py +++ b/src/pacman/ecs/world.py @@ -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]: diff --git a/src/pacman/game/__init__.py b/src/pacman/game/__init__.py index 58c6dbf..5d5c425 100644 --- a/src/pacman/game/__init__.py +++ b/src/pacman/game/__init__.py @@ -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: -- 2.53.0