From: Thomas Rimelen Date: Wed, 14 Oct 2026 13:32:45 +0000 (+0200) Subject: add game menu X-Git-Url: https://git.uwuaxy.net/Project%20Status.png?a=commitdiff_plain;h=8365330f3e3d161b097aa9de7880f0d98fcb3828;p=axy%2Fft%2Fpacman.git add game menu --- diff --git a/src/pacman/ecs/graphics.py b/src/pacman/ecs/graphics.py index 8cfb6e3..8bdad79 100644 --- a/src/pacman/ecs/graphics.py +++ b/src/pacman/ecs/graphics.py @@ -22,6 +22,7 @@ from pacman.ecs.world import ( Systems, World, WorldShouldExit, + System, ) @@ -29,6 +30,10 @@ class ShouldRender(Component): pass +class StopAnimation(Component): + pass + + @dataclass class Window(Resource): surface: pygame.Surface @@ -95,6 +100,13 @@ class Sprite2D(Component, Asset): return Sprite2D(self.surface) +def key_just_pressed(key: int) -> System[bool]: + def inner(events: WindowEvents) -> bool: + return any(key == k and pressed for k, pressed in events.keys_itr()) + + return inner + + def window_positionned_insert_hook( elems: Query[tuple[Coord2D | None, CoordAbs2D | None]], window: Maybe[Window], @@ -199,7 +211,9 @@ def insert_animation_sprites( def update_animations( - query: Query[tuple[Sprite2D, AnimationSet], ShouldRender], + query: Query[ + tuple[Sprite2D, AnimationSet], tuple[ShouldRender, Not[StopAnimation]] + ], ) -> None: for sprite, anim in query: anim.tick() diff --git a/src/pacman/game/__init__.py b/src/pacman/game/__init__.py index 7a604f7..5b4cedf 100644 --- a/src/pacman/game/__init__.py +++ b/src/pacman/game/__init__.py @@ -1,3 +1,4 @@ +import pacman from collections.abc import Callable from dataclasses import dataclass from enum import Enum, auto @@ -6,7 +7,7 @@ from typing import override from mazegenerator import MazeGenerator # type: ignore[import-untyped] from pydantic import BaseModel -from pygame import Vector2 +from pygame import Vector2, K_ESCAPE from pygame.math import clamp from pacman.ecs import minimal_plugins @@ -25,6 +26,10 @@ from pacman.ecs.graphics import ( Sprite2D, WindowPositionned, graphics_plugins, + WindowEvents, + key_just_pressed, + InputsSystems, + StopAnimation, ) from pacman.ecs.gui import ( Button, @@ -36,7 +41,7 @@ from pacman.ecs.gui import ( ) 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.update import PostUpdate, Update, PreUpdate from pacman.ecs.world import ( CommandQueue, Component, @@ -736,8 +741,14 @@ def setup_new_maze(world: World) -> None: world.run_raw_system(sys) -def in_game(itr: Query[Maze]) -> bool: - return len(list(itr)) != 0 +def maze_exists(itr: Query[Maze]) -> bool: + return next(itr.__iter__(), None) is not None + + +def in_game(world: World) -> bool: + return world.run_raw_system(maze_exists) and not world.run_raw_system( + in_game_menu + ) def level_is_finished( @@ -905,6 +916,81 @@ def spawn_button( return inner +def back_to_main_menu( + world: World, + hierarchy: Hierarchy[tuple[()]], + mazes: Query[Entity, Maze], + gui: Query[Entity, GameGui], + queue: CommandQueue, +) -> None: + world.run_raw_system(close_game_menu) + world.despawn_resource(GameState) + world.despawn_many(list(gui)) + for maze in mazes: + queue.queue(hierarchy.despawn_with_children(maze)) + world.run_raw_system(setup_main_menu) + + +def in_game_menu(menus: Query[GameMenu]) -> bool: + return next(menus.__iter__(), None) is not None + + +def toggle_game_menu(world: World) -> None: + if world.run_raw_system(in_game_menu): + world.run_raw_system(close_game_menu) + else: + world.run_raw_system(setup_game_menu) + + +def close_game_menu( + world: World, + hierarchy: Hierarchy[tuple[()]], + menus: Query[Entity, GameMenu], + animated: Query[Entity, Pacman | Ghost], +) -> None: + world.remove_many(StopAnimation, list(animated)) + for menu in menus: + hierarchy.despawn_with_children(menu)(world) + world.despawn_resource(SelectedButton) + + +def setup_game_menu( + world: World, animated: Query[Entity, Pacman | Ghost] +) -> None: + world.insert_many(StopAnimation, [(e, StopAnimation()) for e in animated]) + selected = world.run_system( + spawn_button, + WindowPositionned(Vector2(0.5), Vector2(-150, -150)), + "Resume", + close_game_menu, + ) + + cheat_button = world.run_system( + spawn_button, + WindowPositionned(Vector2(0.5), Vector2(-150, -40)), + "Cheat mod", + quit_game, + ) + + quit_button = world.run_system( + spawn_button, + WindowPositionned(Vector2(0.5), Vector2(-150, 70)), + "Back to menu", + back_to_main_menu, + ) + + world.spawn( + tuple[GameMenu, Children, CoordBundle], + ( + GameMenu(), + Children((selected, cheat_button, quit_button)), + CoordBundle(), + ), + ) + + world.spawn_resource(SelectedButton(selected)) + + def setup_main_menu(world: World) -> None: selected = world.run_system( spawn_button, @@ -949,6 +1035,10 @@ def run_game() -> None: .chain() .run_if(initial_pause_done, in_game) .in_set(Update), + Systems(toggle_game_menu) + .run_if(key_just_pressed(K_ESCAPE), maze_exists) + .in_set(PreUpdate) + .after(InputsSystems), Systems(update_pacman_anim, update_ghost_anim) .before(GraphicsSystems) .in_set(PostUpdate),