From 078858457ea30a1e41fd821806d987a066c1f2be Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 12 Oct 2026 15:35:14 +0200 Subject: [PATCH] Menus and fixed dumb issue with python range usage in archetypes --- src/pacman/ecs/gui.py | 16 +++++++-- src/pacman/ecs/world.py | 8 +++-- src/pacman/game/__init__.py | 71 ++++++++++++++++++++++++++++++------- 3 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/pacman/ecs/gui.py b/src/pacman/ecs/gui.py index 5bd5aeb..b5b3884 100644 --- a/src/pacman/ecs/gui.py +++ b/src/pacman/ecs/gui.py @@ -4,10 +4,14 @@ from enum import Enum, auto from typing import override import pygame -from pygame import Color +from pygame import Color, Vector2 from pacman.ecs.assets import Asset -from pacman.ecs.coord import CoordAbs2D, PropagateTransform +from pacman.ecs.coord import ( + CoordAbs2D, + CoordBundle, + PropagateTransform, +) from pacman.ecs.graphics import ( AnimationSet, GraphicsSystems, @@ -41,6 +45,14 @@ class Font(Asset, Component): path, size = path.split(":") return Font(pygame.font.Font(path, int(size))) + def center_text_for(self, text: str, dims: Vector2) -> CoordBundle: + width, _ = self.font.size(text) + height = self.font.get_ascent() + self.font.get_descent() + return CoordBundle( + (dims.x - width) / 2, + (dims.y - height) / 2 + self.font.get_descent(), + ) + @dataclass class TextDisplay(Component): diff --git a/src/pacman/ecs/world.py b/src/pacman/ecs/world.py index 2dc6757..ce049c7 100644 --- a/src/pacman/ecs/world.py +++ b/src/pacman/ecs/world.py @@ -151,7 +151,9 @@ class Archetype: return kv[ty] return map(self._components[ty].__getitem__, positions) - itr = range(len(dst._entities) - len(components), len(dst._entities)) + itr = lambda: range( + len(dst._entities) - len(components), len(dst._entities) + ) dst._extend(cb) self._discard(positions) @@ -159,12 +161,12 @@ class Archetype: 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) + 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) + hooks._insert((dst._entities[i], lst[i]) for i in itr()) return res diff --git a/src/pacman/game/__init__.py b/src/pacman/game/__init__.py index 662c46c..1e364d7 100644 --- a/src/pacman/game/__init__.py +++ b/src/pacman/game/__init__.py @@ -32,7 +32,7 @@ from pacman.ecs.gui import ( SelectedButton, TextDisplay, ) -from pacman.ecs.hierarchy import ChildOf, Hierarchy +from pacman.ecs.hierarchy import ChildOf, Children, Hierarchy from pacman.ecs.schedule import StartupSchedule, UpdateSchedule from pacman.ecs.update import PostUpdate, Update from pacman.ecs.world import ( @@ -42,8 +42,10 @@ from pacman.ecs.world import ( Not, Query, Resource, + System, Systems, World, + WorldShouldExit, ) _DIR_OFFSETS = [(0, -1), (1, 0), (0, 1), (-1, 0)] @@ -782,21 +784,52 @@ def print_timer(state: GameState, config: Config) -> str: return f"{time // 60}" -def print_a() -> None: - print("a") +def start_game() -> None: + print("Game started") -def print_b() -> None: - print("b") +def quit_game(world: World) -> None: + world.spawn_resource(WorldShouldExit()) -def print_c() -> None: - print("c") +def spawn_button( + world: World, fonts: Assets[Font], sprites: Assets[Sprite2D] +) -> Callable[[Vector2, str, System], Entity]: + def inner(pos: Vector2, s: str, cb: System) -> Entity: + parent = world.spawn( + tuple[CoordBundle, ShouldRender, AnimationSet, Button], + ( + CoordBundle(*pos.xy, depth=-100), + ShouldRender(), + AnimationSet( + [ + [sprites.load("./asset/gui/button_unselected.png")], + [sprites.load("./asset/gui/button_selected.png")], + [sprites.load("./asset/gui/button_selected.png")], + ], + 1, + ), + Button(cb), + ), + ) + font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30") + world.spawn( + tuple[CoordBundle, ShouldRender, TextDisplay, ChildOf], + ( + font.center_text_for(s, Vector2(300, 80)), + ShouldRender(), + TextDisplay(s, font), + ChildOf(parent), + ), + ) + return parent + + return inner def setup_text(world: World, fonts: Assets[Font]) -> None: - spawned = world.spawn_many( - tuple[CoordBundle, ShouldRender, ComputedText, TextDisplay, Button], + world.spawn_many( + tuple[CoordBundle, ShouldRender, ComputedText, TextDisplay], [ ( CoordBundle(depth=-20), @@ -806,7 +839,6 @@ def setup_text(world: World, fonts: Assets[Font]) -> None: "", fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30"), ), - Button(print_a), ), ( CoordBundle(y=100, depth=-20), @@ -816,7 +848,6 @@ def setup_text(world: World, fonts: Assets[Font]) -> None: "", fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30"), ), - Button(print_b), ), ( CoordBundle(y=200, depth=-20), @@ -826,11 +857,25 @@ def setup_text(world: World, fonts: Assets[Font]) -> None: "", fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30"), ), - Button(print_c), ), ], ) - world.spawn_resource(SelectedButton(spawned[0])) + + selected = world.run_system( + spawn_button, + Vector2((1600 - 300) / 2, (1200 - 200) / 2), + "Start game", + start_game, + ) + + world.run_system( + spawn_button, + Vector2((1600 - 300) / 2, (1200 - 200) / 2 + 120), + "Quit", + quit_game, + ) + + world.spawn_resource(SelectedButton(selected)) def run_game() -> None: -- 2.53.0