"""All the gameplay logic of pacman."""
+import random
import sys
from collections.abc import Callable
from dataclasses import dataclass
from enum import Enum, auto
-from random import random, shuffle
from typing import cast, override
from mazegenerator import ( # type: ignore[import-untyped, unused-ignore]
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)
+ seed: int = Field(default=42, ge=1)
highscores_path: str = "highscores.json"
level: int = 0
timer: int = 0
lives: int = 3
- state: str = ""
+ won: bool = False
class CheatMode(Component):
- """Component for the cheat mod."""
+ """Component for the cheat mode."""
class MainMenu(Component):
return [(1 & (self.n >> i)) == 1 for i in range(4)]
-def remove_comment(s: str) -> str:
+def remove_comments(s: str) -> str:
"""Return s with all comment lines removed."""
- return "\n".join(line for line in s.split("\n") if not s.startswith("#"))
+ return "\n".join(
+ line for line in s.split("\n") if not line.startswith("#")
+ )
def load_config(world: World) -> None:
)
with open(sys.argv[1]) as f:
- config = Config.model_validate_json(remove_comment(f.read()))
+ config = Config.model_validate_json(remove_comments(f.read()))
world.spawn_resource(config)
continue
if cell.n == 15:
continue
- if random() > 0.7:
+ if random.random() > 0.7:
continue
pacgums.append(
(
)
-def setup_maze(world: World, config: Config) -> None:
+def setup_maze(world: World, config: Config, state: GameState) -> None:
"""Create maze with a given MazeGenerator."""
+ seed = config.seed if state.level == 0 else 0
world.run_system(
- create_maze, MazeGenerator((config.maze_width, config.maze_heigth))
+ create_maze,
+ MazeGenerator((config.maze_width, config.maze_heigth), seed=seed),
)
)
if status.state is GhostState.flee:
lst = candidates[:-1]
- shuffle(lst)
+ random.shuffle(lst)
candidates = lst + [candidates[-1]]
nxt, nxt_dir = candidates[0]
motion.src = motion.pos
def victory(
hierarchy: Hierarchy[tuple[()]],
mazes: Query[Entity, Maze],
- queue: CommandQueue,
+ world: World,
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))
+ world.run_simple_system(hierarchy.despawn_with_children(maze))
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))
+ state.won = True
+ world.run_simple_system(game_over)
+ world.run_simple_system(spawn_game_over)
return
state.level += 1
state.lives += 1
state.timer = 0
- queue.queue(setup_new_maze)
+ world.run_simple_system(setup_new_maze)
def level_is_lost(
),
)
font = fonts.load("fonts/JetBrainsMonoNL-Regular.ttf:200")
- text = state.state
+ text = "Victory" if state.won else "Game Over"
world.spawn(
tuple[ChildOf, ShouldRender, CoordBundle, TextDisplay],
(
)
-def lose(
+def lose(world: World) -> None:
+ """Despawn maze and run the game over."""
+ world.run_simple_system(game_over)
+ world.run_simple_system(spawn_game_over)
+
+
+def game_over(
world: World,
+ queue: CommandQueue,
gui: Query[Entity, GameGui],
- hierarchy: Hierarchy[tuple[()]],
mazes: Query[Entity, Maze],
- state: GameState,
- queue: CommandQueue,
+ hierarchy: Hierarchy[tuple[()]],
) -> None:
- """Despawn mane and run the game over."""
- world.despawn_many(list(gui))
+ """Change state to game over."""
+ for e in gui:
+ queue.queue(hierarchy.despawn_with_children(e))
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)())
- queue.queue(lambda world: world.run_simple_system(spawn_game_over))
-
-
-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()))
+ world.spawn_resource(GameIsOver())
+ world.spawn_resource(PlayerName())
def update_game_timer(world: World, state: GameState) -> None:
state.timer += 1
+def update_pause_timer(timer: InitialPauseTimer) -> None:
+ """Update the pause timer."""
+ timer.tick += 1
+
+
def initial_pause_done(timer: InitialPauseTimer, config: Config) -> bool:
"""Return if the inital timer is done."""
- if timer.tick < config.initial_pos_timer:
- timer.tick += 1
- return False
- return True
+ return timer.tick >= config.initial_pos_timer
def print_name(name: PlayerName) -> str:
def enable_cheat_mode(world: World) -> None:
- """Enable cheat mod."""
+ """Enable cheat mode."""
world.run_simple_system(spawn_cheat_info)
world.spawn(CheatMode, CheatMode())
-def disable_cheat_mode(world: World, cheats: Query[Entity, CheatMode]) -> None:
- """Disable cheat mod."""
- items: list[Entity] = [cheat for cheat in cheats]
- world.despawn_many(items)
+def disable_cheat_mode(
+ queue: CommandQueue,
+ cheats: Query[Entity, CheatMode],
+ hierarchy: Hierarchy[tuple[()]],
+) -> None:
+ """Disable cheat mode."""
+ for e in cheats:
+ queue.queue(hierarchy.despawn_with_children(e))
def toggle_cheat_mode(world: World) -> None:
- """Enable or disable cheat mod."""
+ """Enable or disable cheat mode."""
if world.run_simple_system(cheat_mode_enabled):
world.run_simple_system(disable_cheat_mode)
else:
"""Remove maze with childrens and game GUI."""
world.run_simple_system(close_game_menu)
world.despawn_resource(GameState)
- world.despawn_many(list(gui))
+ for e in gui:
+ queue.queue(hierarchy.despawn_with_children(e))
for maze in mazes:
queue.queue(hierarchy.despawn_with_children(maze))
if world.run_simple_system(cheat_mode_enabled):
cheat_button = world.run_system(
spawn_button,
WindowPositionned(Vector2(0.5), Vector2(-150, -40)),
- "Cheat mod",
+ "Cheat mode",
toggle_cheat_mode,
)
)
]
+ instructions_font = fonts.load("fonts/JetBrainsMonoNL-Regular.ttf:20")
entities.extend(
world.spawn_many(
tuple[
[
(
WindowPositionned(
- Vector2(0, 0), Vector2(50, 200 + i * 50)
+ Vector2(0, 0),
+ Vector2(
+ 50, 200 + i * instructions_font.font.get_linesize()
+ ),
),
ShouldRender(),
- TextDisplay(
- s,
- fonts.load("fonts/JetBrainsMonoNL-Regular.ttf:20"),
- ),
+ TextDisplay(s, instructions_font),
)
for i, s in enumerate(text.split("\n"))
],
world.spawn_resource(SelectedButton(selected))
-def test(events: WindowEvents) -> None: # FIXME
- """Test."""
- for key, state in events.keys_itr():
- if state:
- print(key)
-
-
def cheat_input(world: World, events: WindowEvents) -> None:
"""Get cheat inputs."""
for key, state in events.keys_itr():
hierarchy: Hierarchy[tuple[()]],
) -> None:
"""Add players score to highscores, save it and go to main menu."""
- # FIXME
scores.scores[name.name] = state.score
world.run_simple_system(save_highscores)
for menu in menus:
.chain()
.run_if(initial_pause_done, in_game)
.in_set(Update),
+ Systems(update_pause_timer).in_set(PostUpdate).run_if(in_game),
Systems(toggle_game_menu)
.run_if(key_just_pressed(K_ESCAPE), maze_exists)
.in_set(PreUpdate)
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
- ),
+ Systems(cheat_input).run_if(cheat_mode_enabled, in_game),
).run_main()