from mazegenerator import MazeGenerator # type: ignore[import-untyped]
from pydantic import BaseModel, Field
-from pygame import K_BACKSPACE, K_ESCAPE, K_RETURN, K_a, K_z, Vector2
+from pygame import K_BACKSPACE, K_ESCAPE, K_RETURN, K_a, K_c, K_n, K_z, Vector2
from pygame.math import clamp
from pacman.ecs import minimal_plugins
pacman_speed: float = Field(default=3.0, ge=0.1, le=100.0)
initial_pos_timer: int = Field(default=60, ge=0)
level_timer: int = Field(default=60 * 180, ge=60)
+ level_count: int = Field(default=10, ge=1)
initial_lives: int = Field(default=3, ge=1)
score_pacgum: int = Field(default=1, ge=0)
score_superpacgum: int = Field(default=5, ge=0)
score_ghost: int = Field(default=20, ge=0)
superpacgum_duration: int = Field(default=60 * 8, ge=60)
+ maze_width: int = Field(default=12, ge=5, le=15)
+ maze_heigth: int = Field(default=12, ge=5, le=15)
highscores_path: str = "highscores.json"
level: int = 0
timer: int = 0
lives: int = 3
+ state: str = ""
class CheatMode(Component):
)
-def setup_maze(world: World) -> None:
+def setup_maze(world: World, config: Config) -> None:
"""Create maze with a given MazeGenerator."""
- world.run_system(create_maze, MazeGenerator((14, 14)))
+ world.run_system(
+ create_maze, MazeGenerator((config.maze_width, config.maze_heigth))
+ )
def move_in_motion(
motion.direction = direction
+def chase(
+ ghosts: Query[tuple[InMotion, GhostStatus], Ghost],
+) -> None:
+ """Change ghost status to 'flee'."""
+ for ghost, status in ghosts:
+ if status.state in (GhostState.wander, GhostState.chase):
+ tmp = ghost.src
+ ghost.src = ghost.pos
+ ghost.pos = tmp
+ status.state = GhostState.flee
+ if status.state is GhostState.flee:
+ status.tick = 0
+
+
def collect_pacgum(
world: World,
pacgums: Query[tuple[CoordAbs2D, Entity], PacGum],
world: World,
pacgums: Query[tuple[CoordAbs2D, Entity], SuperPacGum],
pacmans: Query[CoordAbs2D, Pacman],
- ghosts: Query[tuple[InMotion, GhostStatus], Ghost],
score: GameState,
config: Config,
) -> None:
score.score += len(collided * config.score_superpacgum)
if len(collided) == 0:
return
- for ghost, status in ghosts:
- if status.state in (GhostState.wander, GhostState.chase):
- tmp = ghost.src
- ghost.src = ghost.pos
- ghost.pos = tmp
- status.state = GhostState.flee
- if status.state is GhostState.flee:
- status.tick = 0
+ world.run_simple_system(chase)
+ # for ghost, status in ghosts:
+ # if status.state in (GhostState.wander, GhostState.chase):
+ # tmp = ghost.src
+ # ghost.src = ghost.pos
+ # ghost.pos = tmp
+ # status.state = GhostState.flee
+ # if status.state is GhostState.flee:
+ # status.tick = 0
world.despawn_many(collided)
mazes: Query[Entity, Maze],
queue: CommandQueue,
state: GameState,
+ config: Config,
) -> None:
"""Despawn maze and spawn new maze or init game over."""
for maze in mazes:
queue.queue(hierarchy.despawn_with_children(maze))
- if state.level == 2:
+ if state.level == config.level_count - 1:
+ state.state = "Victory"
queue.queue(lambda world: world.run_simple_system(game_over))
+ queue.queue(lambda world: world.run_simple_system(spawn_game_over))
return
state.level += 1
state.lives += 1
return config.level_timer < state.timer or state.lives <= 0
-def spawn_game_over(world: World, fonts: Assets[Font]) -> None: # FIXME
+def spawn_game_over(
+ world: World,
+ fonts: Assets[Font],
+ state: GameState,
+) -> None:
"""Display game over menu."""
root = world.spawn(
tuple[WindowPositionned, GameOver],
(
- WindowPositionned(Vector2(0.5, 0), Vector2(0, 100)),
+ WindowPositionned(Vector2(0.5, 0), Vector2(0, 200)),
GameOver(),
),
)
- font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:100")
- text = "GAME OVER"
+ font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:200")
+ text = state.state
world.spawn(
tuple[ChildOf, ShouldRender, CoordBundle, TextDisplay],
(
(
ChildOf(root),
ShouldRender(),
- font.center_text_for("", Vector2(0, 300)),
- ComputedText(cast(System[str], print_name)),
+ font.center_text_for("Score = XXX", Vector2(0, 1200)),
+ ComputedText(cast(System[str], print_score)),
TextDisplay("", font),
),
)
- font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:30")
+ text = "Enter your name:"
+ world.spawn(
+ tuple[ChildOf, ShouldRender, CoordBundle, TextDisplay],
+ (
+ ChildOf(root),
+ ShouldRender(),
+ font.center_text_for(text, Vector2(0, 450)),
+ TextDisplay(text, font),
+ ),
+ )
+
+ font = fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:50")
world.spawn(
tuple[ChildOf, ShouldRender, CoordBundle, ComputedText, TextDisplay],
(
ChildOf(root),
ShouldRender(),
- font.center_text_for("Score = XXX", Vector2(0, 200)),
- ComputedText(cast(System[str], print_score)),
+ font.center_text_for(" ", Vector2(0, 700)),
+ ComputedText(cast(System[str], print_name)),
TextDisplay("", font),
),
)
gui: Query[Entity, GameGui],
hierarchy: Hierarchy[tuple[()]],
mazes: Query[Entity, Maze],
+ state: GameState,
queue: CommandQueue,
) -> None:
"""Despawn mane and run the game over."""
world.despawn_many(list(gui))
for maze in mazes:
queue.queue(hierarchy.despawn_with_children(maze))
+ state.state = "Game Over"
queue.queue(lambda world: world.add_simple_system(game_over)())
- world.run_simple_system(spawn_game_over)
+ queue.queue(lambda world: world.run_simple_system(spawn_game_over))
-def game_over(queue: CommandQueue) -> None:
+def game_over(world: World, queue: CommandQueue) -> None:
"""Spawn resources when game is over."""
+ if world.run_simple_system(cheat_mode_enabled):
+ world.run_simple_system(disable_cheat_mode)
queue.queue(lambda world: world.spawn_resource(GameIsOver()))
queue.queue(lambda world: world.spawn_resource(PlayerName()))
def print_name(name: PlayerName) -> str:
"""Return a string with the player score."""
- return f"Enter your name: {name.name}"
+ return name.name
def print_score(state: GameState) -> str:
return f"Time left: {time // 60}"
+def print_level(state: GameState, config: Config) -> str:
+ """Return a string with the level timer."""
+ return f"Level {state.level + 1} / {config.level_count}"
+
+
def spawn_hud(world: World, fonts: Assets[Font]) -> None:
"""Draw in game HUD with lives, levels and timer informations."""
world.spawn_many(
],
[
(
- WindowPositionned(Vector2(), Vector2(20, y * 100 + 20)),
+ WindowPositionned(Vector2(), Vector2(20, y * 60 + 20)),
CoordBundle(depth=-20),
ShouldRender(),
ComputedText(cast(System[str], fn)),
)
for y, fn in enumerate(
[
+ print_level,
print_score,
print_lives,
print_timer,
world: World,
hierarchy: Hierarchy[tuple[()]],
menus: Query[Entity, MainMenu],
+ config: Config,
) -> None:
"""Close main menu and starting the game and HUD."""
for menu in menus:
hierarchy.despawn_with_children(menu)(world)
world.despawn_resource(SelectedButton)
- world.spawn_resource(GameState())
+ world.spawn_resource(GameState(lives=config.initial_lives))
world.run_simple_system(setup_new_maze)
world.run_simple_system(spawn_hud)
CheatMode(),
),
)
+ text = """
+ Cheat commands:
+Press 'n to go to next level
+Press 'c' to start chase
+"""
+ world.spawn_many(
+ tuple[
+ WindowPositionned,
+ ShouldRender,
+ TextDisplay,
+ CoordBundle,
+ CheatMode,
+ ],
+ [
+ (
+ WindowPositionned(Vector2(1, 1), Vector2(-400, -220 + i * 50)),
+ ShouldRender(),
+ TextDisplay(
+ s,
+ fonts.load("./asset/fonts/JetBrainsMonoNL-Regular.ttf:22"),
+ ),
+ CoordBundle(depth=-20),
+ CheatMode(),
+ )
+ for i, s in enumerate(text.split("\n"))
+ ],
+ )
def cheat_mode_enabled(cheat: Query[CheatMode]) -> bool:
print(key)
+def cheat_input(world: World, events: WindowEvents) -> None:
+ """Get cheat inputs."""
+ for key, state in events.keys_itr():
+ if key == K_n and state:
+ world.run_simple_system(victory)
+ if key == K_c and state:
+ world.run_simple_system(chase)
+
+
def add_score(
world: World,
name: PlayerName,
# FIXME
scores.scores[name.name] = state.score
world.run_simple_system(save_highscores)
- # items = [item for item in menus]
- # # world.despawn_many(items)
for menu in menus:
queue.queue(hierarchy.despawn_with_children(menu))
world.despawn_resource(GameIsOver)
+ world.despawn_resource(GameState)
world.run_simple_system(setup_main_menu)
Systems(victory).run_if(level_is_finished, in_game),
Systems(lose).run_if(level_is_lost),
Systems(get_player_name).run_if(game_is_over),
+ Systems(cheat_input).run_if(
+ cheat_mode_enabled, initial_pause_done, in_game
+ ),
).run_main()