From 72ec02dbaef730da116e47c0f8e5182f5700f562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20N=C3=B6llemeyer?= Date: Sat, 17 Aug 2024 10:05:44 +0200 Subject: [PATCH 1/4] extracted states to own files --- game/{states.py => states/InGame.py} | 46 ++-------------------------- game/states/MainMenu.py | 45 +++++++++++++++++++++++++++ main.py | 4 ++- 3 files changed, 51 insertions(+), 44 deletions(-) rename game/{states.py => states/InGame.py} (76%) create mode 100644 game/states/MainMenu.py diff --git a/game/states.py b/game/states/InGame.py similarity index 76% rename from game/states.py rename to game/states/InGame.py index 181a13c..106f45c 100644 --- a/game/states.py +++ b/game/states/InGame.py @@ -1,7 +1,4 @@ -"""A collection of game states.""" - from __future__ import annotations -from functools import reduce import attrs import tcod.console @@ -10,13 +7,14 @@ import tcod.event from tcod.event import KeySym import g -import game.menus import game.world_tools from game.components import Gold, Graphic, Position from game.constants import DIRECTION_KEYS, ACTION_KEYS -from game.state import Push, Reset, State, StateResult +from game.state import Push, State, StateResult from game.tags import IsItem, IsPlayer, IsDoor, IsActor from game.constants import WALL_CHAR +from game.states import MainMenu + @attrs.define() class InGame(State): @@ -74,41 +72,3 @@ class InGame(State): draw(player.components[Position], player.components[Graphic]) if text := g.world[None].components.get(("Text", str)): console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0)) - - -class MainMenu(game.menus.ListMenu): - """Main/escape menu.""" - - __slots__ = () - - def __init__(self) -> None: - """Initialize the main menu.""" - items = [ - game.menus.SelectItem("New game", self.new_game), - game.menus.SelectItem("Quit", self.quit), - ] - if hasattr(g, "world"): - items.insert(0, game.menus.SelectItem("Continue", self.continue_)) - - super().__init__( - items=tuple(items), - selected=0, - x=5, - y=5, - ) - - @staticmethod - def continue_() -> StateResult: - """Return to the game.""" - return Reset(InGame()) - - @staticmethod - def new_game() -> StateResult: - """Begin a new game.""" - g.world = game.world_tools.new_world() - return Reset(InGame()) - - @staticmethod - def quit() -> StateResult: - """Close the program.""" - raise SystemExit() diff --git a/game/states/MainMenu.py b/game/states/MainMenu.py new file mode 100644 index 0000000..6854593 --- /dev/null +++ b/game/states/MainMenu.py @@ -0,0 +1,45 @@ +"""The main menu state""" + +from __future__ import annotations + +import g +import game.menus +import game.world_tools +from game.state import Reset, StateResult +from game.states.InGame import InGame + +class MainMenu(game.menus.ListMenu): + """Main/escape menu.""" + __slots__ = () + + def __init__(self) -> None: + """Initialize the main menu.""" + items = [ + game.menus.SelectItem("New game", self.new_game), + game.menus.SelectItem("Quit", self.quit), + ] + if hasattr(g, "world"): + items.insert(0, game.menus.SelectItem("Continue", self.continue_)) + + super().__init__( + items=tuple(items), + selected=0, + x=5, + y=5, + ) + + @staticmethod + def continue_() -> StateResult: + """Return to the game.""" + return Reset(InGame()) + + @staticmethod + def new_game() -> StateResult: + """Begin a new game.""" + g.world = game.world_tools.new_world() + return Reset(InGame()) + + @staticmethod + def quit() -> StateResult: + """Close the program.""" + raise SystemExit() diff --git a/main.py b/main.py index 176aefd..6703a24 100755 --- a/main.py +++ b/main.py @@ -11,6 +11,8 @@ import g import game.state_tools import game.states +from game.states.MainMenu import MainMenu + def main() -> None: """Entry point function.""" tileset = tcod.tileset.load_tilesheet( @@ -18,7 +20,7 @@ def main() -> None: ) tcod.tileset.procedural_block_elements(tileset=tileset) - g.states = [game.states.MainMenu()] + g.states = [MainMenu()] g.console = tcod.console.Console(80, 50) with tcod.context.new(console=g.console, tileset=tileset) as g.context: game.state_tools.main_loop() From 2560a4dcd933b31a673e9297e7cbfa01e574996f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20N=C3=B6llemeyer?= Date: Sat, 17 Aug 2024 10:40:57 +0200 Subject: [PATCH 2/4] module refactoring --- game/state.py | 55 ++++++++++++++++++ game/state_tools.py | 61 -------------------- game/states/{InGame.py => game_screens.py} | 6 +- game/states/{MainMenu.py => menu_screens.py} | 16 ++--- game/{ => states}/menus.py | 5 +- main.py | 7 +-- 6 files changed, 71 insertions(+), 79 deletions(-) delete mode 100644 game/state_tools.py rename game/states/{InGame.py => game_screens.py} (96%) rename game/states/{MainMenu.py => menu_screens.py} (65%) rename game/{ => states}/menus.py (96%) diff --git a/game/state.py b/game/state.py index 4b935c4..4497f0d 100644 --- a/game/state.py +++ b/game/state.py @@ -8,6 +8,8 @@ import attrs import tcod.console import tcod.event +import g + class State(Protocol): """An abstract game state.""" @@ -42,3 +44,56 @@ class Reset: StateResult: TypeAlias = "Push | Pop | Reset | None" """Union of state results.""" + + +def main_draw() -> None: + """Render and present the active state.""" + if not g.states: + return + g.console.clear() + g.states[-1].on_draw(g.console) + g.context.present(g.console) + + +def apply_state_result(result: StateResult) -> None: + """Apply a StateResult to `g.states`.""" + match result: + case Push(state=state): + g.states.append(state) + case Pop(): + g.states.pop() + case Reset(state=state): + while g.states: + apply_state_result(Pop()) + apply_state_result(Push(state)) + case None: + pass + case _: + raise TypeError(result) + + +def main_loop() -> None: + """Run the active state forever.""" + while g.states: + main_draw() + for event in tcod.event.wait(): + tile_event = g.context.convert_event(event) + if g.states: + apply_state_result(g.states[-1].on_event(tile_event)) + + +def get_previous_state(state: State) -> State | None: + """Return the state before `state` in the stack if it exists.""" + current_index = next(index for index, value in enumerate(g.states) if value is state) + return g.states[current_index - 1] if current_index > 0 else None + + +def draw_previous_states(state: State, console: tcod.console.Console, dim: bool = True) -> None: + """Draw previous states, optionally dimming all but the active state.""" + prev_state = get_previous_state(state) + if prev_state is None: + return + prev_state.on_draw(console) + if dim and state is g.states[-1]: + console.rgb["fg"] //= 4 + console.rgb["bg"] //= 4 diff --git a/game/state_tools.py b/game/state_tools.py deleted file mode 100644 index 77d2e22..0000000 --- a/game/state_tools.py +++ /dev/null @@ -1,61 +0,0 @@ -"""State handling functions.""" - -from __future__ import annotations - -import tcod.console - -import g -from game.state import Pop, Push, Reset, State, StateResult - - -def main_draw() -> None: - """Render and present the active state.""" - if not g.states: - return - g.console.clear() - g.states[-1].on_draw(g.console) - g.context.present(g.console) - - -def apply_state_result(result: StateResult) -> None: - """Apply a StateResult to `g.states`.""" - match result: - case Push(state=state): - g.states.append(state) - case Pop(): - g.states.pop() - case Reset(state=state): - while g.states: - apply_state_result(Pop()) - apply_state_result(Push(state)) - case None: - pass - case _: - raise TypeError(result) - - -def main_loop() -> None: - """Run the active state forever.""" - while g.states: - main_draw() - for event in tcod.event.wait(): - tile_event = g.context.convert_event(event) - if g.states: - apply_state_result(g.states[-1].on_event(tile_event)) - - -def get_previous_state(state: State) -> State | None: - """Return the state before `state` in the stack if it exists.""" - current_index = next(index for index, value in enumerate(g.states) if value is state) - return g.states[current_index - 1] if current_index > 0 else None - - -def draw_previous_state(state: State, console: tcod.console.Console, dim: bool = True) -> None: - """Draw previous states, optionally dimming all but the active state.""" - prev_state = get_previous_state(state) - if prev_state is None: - return - prev_state.on_draw(console) - if dim and state is g.states[-1]: - console.rgb["fg"] //= 4 - console.rgb["bg"] //= 4 diff --git a/game/states/InGame.py b/game/states/game_screens.py similarity index 96% rename from game/states/InGame.py rename to game/states/game_screens.py index 106f45c..583e431 100644 --- a/game/states/InGame.py +++ b/game/states/game_screens.py @@ -13,11 +13,11 @@ from game.constants import DIRECTION_KEYS, ACTION_KEYS from game.state import Push, State, StateResult from game.tags import IsItem, IsPlayer, IsDoor, IsActor from game.constants import WALL_CHAR -from game.states import MainMenu +from game.states import menu_screens @attrs.define() -class InGame(State): +class MainScreen(State): """Primary in-game state.""" def on_event(self, event: tcod.event.Event) -> StateResult: @@ -43,7 +43,7 @@ class InGame(State): gold.clear() return None case tcod.event.KeyDown(sym=KeySym.ESCAPE): - return Push(MainMenu()) + return Push(menu_screens()) case _: return None diff --git a/game/states/MainMenu.py b/game/states/menu_screens.py similarity index 65% rename from game/states/MainMenu.py rename to game/states/menu_screens.py index 6854593..320323f 100644 --- a/game/states/MainMenu.py +++ b/game/states/menu_screens.py @@ -3,23 +3,23 @@ from __future__ import annotations import g -import game.menus +import game.states.menus import game.world_tools from game.state import Reset, StateResult -from game.states.InGame import InGame +from game.states.game_screens import MainScreen -class MainMenu(game.menus.ListMenu): +class MainMenu(game.states.menus.ListMenu): """Main/escape menu.""" __slots__ = () def __init__(self) -> None: """Initialize the main menu.""" items = [ - game.menus.SelectItem("New game", self.new_game), - game.menus.SelectItem("Quit", self.quit), + game.states.menus.SelectItem("New game", self.new_game), + game.states.menus.SelectItem("Quit", self.quit), ] if hasattr(g, "world"): - items.insert(0, game.menus.SelectItem("Continue", self.continue_)) + items.insert(0, game.states.menus.SelectItem("Continue", self.continue_)) super().__init__( items=tuple(items), @@ -31,13 +31,13 @@ class MainMenu(game.menus.ListMenu): @staticmethod def continue_() -> StateResult: """Return to the game.""" - return Reset(InGame()) + return Reset(MainScreen()) @staticmethod def new_game() -> StateResult: """Begin a new game.""" g.world = game.world_tools.new_world() - return Reset(InGame()) + return Reset(MainScreen()) @staticmethod def quit() -> StateResult: diff --git a/game/menus.py b/game/states/menus.py similarity index 96% rename from game/menus.py rename to game/states/menus.py index 7c1bb37..c9e6219 100644 --- a/game/menus.py +++ b/game/states/menus.py @@ -10,9 +10,8 @@ import tcod.console import tcod.event from tcod.event import KeySym -import game.state_tools from game.constants import DIRECTION_KEYS -from game.state import Pop, State, StateResult +from game.state import Pop, State, StateResult, draw_previous_states class MenuItem(Protocol): @@ -96,6 +95,6 @@ class ListMenu(State): def on_draw(self, console: tcod.console.Console) -> None: """Render the menu.""" - game.state_tools.draw_previous_state(self, console) + draw_previous_states(self, console) for i, item in enumerate(self.items): item.on_draw(console, x=self.x, y=self.y + i, highlight=i == self.selected) diff --git a/main.py b/main.py index 6703a24..582f885 100755 --- a/main.py +++ b/main.py @@ -8,10 +8,9 @@ import tcod.context import tcod.tileset import g -import game.state_tools -import game.states -from game.states.MainMenu import MainMenu +import game.state +from game.states.menu_screens import MainMenu def main() -> None: """Entry point function.""" @@ -23,7 +22,7 @@ def main() -> None: g.states = [MainMenu()] g.console = tcod.console.Console(80, 50) with tcod.context.new(console=g.console, tileset=tileset) as g.context: - game.state_tools.main_loop() + game.state.main_loop() if __name__ == "__main__": From bfabba3d821372874cf4cb6a54ef90a9c17b885c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20N=C3=B6llemeyer?= Date: Sat, 17 Aug 2024 10:56:55 +0200 Subject: [PATCH 3/4] refactored state -> screen --- g.py | 4 +- game/screens/__init__.py | 99 ++++++++++++++++++++++++ game/{states => screens}/game_screens.py | 11 +-- game/{states => screens}/menu_screens.py | 20 ++--- game/{states => screens}/menus.py | 18 ++--- game/state.py | 99 ------------------------ main.py | 8 +- 7 files changed, 130 insertions(+), 129 deletions(-) create mode 100644 game/screens/__init__.py rename game/{states => screens}/game_screens.py (93%) rename game/{states => screens}/menu_screens.py (59%) rename game/{states => screens}/menus.py (85%) delete mode 100644 game/state.py diff --git a/g.py b/g.py index 52bf2ab..2d12b10 100644 --- a/g.py +++ b/g.py @@ -6,7 +6,7 @@ import tcod.console import tcod.context import tcod.ecs -import game.state +from game.screens import Screen from game.components import Position context: tcod.context.Context @@ -20,7 +20,7 @@ world_map: tcod.map.Map world_center: tuple[int,int] = Position(50, 50) -states: list[game.state.State] = [] +screens: list[Screen] = [] """A stack of states with the last item being the active state.""" console: tcod.console.Console diff --git a/game/screens/__init__.py b/game/screens/__init__.py new file mode 100644 index 0000000..ee16cc0 --- /dev/null +++ b/game/screens/__init__.py @@ -0,0 +1,99 @@ +"""Package for game state stuff.""" + +from __future__ import annotations + +from typing import Protocol, TypeAlias + +import attrs +import tcod.console +import tcod.event + +import g + + +class Screen(Protocol): + """An abstract game screen.""" + + __slots__ = () + + def on_event(self, event: tcod.event.Event) -> ScreenResult: + """Called on events.""" + + def on_draw(self, console: tcod.console.Console) -> None: + """Called when the screen is being drawn.""" + + +@attrs.define() +class Push: + """Push a new screen on top of the stack.""" + + screen: Screen + + +@attrs.define() +class Pop: + """Remove the current screen from the stack.""" + + +@attrs.define() +class Reset: + """Replace the entire stack with a new screen.""" + + screen: Screen + + +ScreenResult: TypeAlias = "Push | Pop | Reset | None" +"""Union of screen results.""" + + +def main_draw() -> None: + """Render and present the active screen.""" + if not g.screens: + return + g.console.clear() + g.screens[-1].on_draw(g.console) + g.context.present(g.console) + + +def apply_state_result(result: ScreenResult) -> None: + """Apply a StateResult to `g.states`.""" + match result: + case Push(screen=screen): + g.screens.append(screen) + case Pop(): + g.screens.pop() + case Reset(screen=screen): + while g.screens: + apply_state_result(Pop()) + apply_state_result(Push(screen)) + case None: + pass + case _: + raise TypeError(result) + + +def main_loop() -> None: + """Run the active state forever.""" + while g.screens: + main_draw() + for event in tcod.event.wait(): + tile_event = g.context.convert_event(event) + if g.screens: + apply_state_result(g.screens[-1].on_event(tile_event)) + + +def get_previous_screen(screen: Screen) -> Screen | None: + """Return the state before `state` in the stack if it exists.""" + current_index = next(index for index, value in enumerate(g.screens) if value is screen) + return g.screens[current_index - 1] if current_index > 0 else None + + +def draw_previous_screens(screen: Screen, console: tcod.console.Console, dim: bool = True) -> None: + """Draw previous states, optionally dimming all but the active state.""" + prev_screen = get_previous_screen(screen) + if prev_screen is None: + return + prev_screen.on_draw(console) + if dim and screen is g.screens[-1]: + console.rgb["fg"] //= 4 + console.rgb["bg"] //= 4 diff --git a/game/states/game_screens.py b/game/screens/game_screens.py similarity index 93% rename from game/states/game_screens.py rename to game/screens/game_screens.py index 583e431..504ccec 100644 --- a/game/states/game_screens.py +++ b/game/screens/game_screens.py @@ -1,3 +1,5 @@ +"""All states the are used in-game""" + from __future__ import annotations import attrs @@ -7,20 +9,19 @@ import tcod.event from tcod.event import KeySym import g -import game.world_tools from game.components import Gold, Graphic, Position from game.constants import DIRECTION_KEYS, ACTION_KEYS -from game.state import Push, State, StateResult +from game.screens import Push, Screen, ScreenResult from game.tags import IsItem, IsPlayer, IsDoor, IsActor from game.constants import WALL_CHAR -from game.states import menu_screens +from game.screens import menu_screens @attrs.define() -class MainScreen(State): +class MainScreen(Screen): """Primary in-game state.""" - def on_event(self, event: tcod.event.Event) -> StateResult: + def on_event(self, event: tcod.event.Event) -> ScreenResult: """Handle events for the in-game state.""" (player,) = g.world.Q.all_of(tags=[IsPlayer]) match event: diff --git a/game/states/menu_screens.py b/game/screens/menu_screens.py similarity index 59% rename from game/states/menu_screens.py rename to game/screens/menu_screens.py index 320323f..bfa4346 100644 --- a/game/states/menu_screens.py +++ b/game/screens/menu_screens.py @@ -3,23 +3,23 @@ from __future__ import annotations import g -import game.states.menus +import game.screens.menus import game.world_tools -from game.state import Reset, StateResult -from game.states.game_screens import MainScreen +from game.screens import Reset, ScreenResult +from game.screens.game_screens import MainScreen -class MainMenu(game.states.menus.ListMenu): +class MainMenu(game.screens.menus.ListMenu): """Main/escape menu.""" __slots__ = () def __init__(self) -> None: """Initialize the main menu.""" items = [ - game.states.menus.SelectItem("New game", self.new_game), - game.states.menus.SelectItem("Quit", self.quit), + game.screens.menus.SelectItem("New game", self.new_game), + game.screens.menus.SelectItem("Quit", self.quit), ] if hasattr(g, "world"): - items.insert(0, game.states.menus.SelectItem("Continue", self.continue_)) + items.insert(0, game.screens.menus.SelectItem("Continue", self.continue_)) super().__init__( items=tuple(items), @@ -29,17 +29,17 @@ class MainMenu(game.states.menus.ListMenu): ) @staticmethod - def continue_() -> StateResult: + def continue_() -> ScreenResult: """Return to the game.""" return Reset(MainScreen()) @staticmethod - def new_game() -> StateResult: + def new_game() -> ScreenResult: """Begin a new game.""" g.world = game.world_tools.new_world() return Reset(MainScreen()) @staticmethod - def quit() -> StateResult: + def quit() -> ScreenResult: """Close the program.""" raise SystemExit() diff --git a/game/states/menus.py b/game/screens/menus.py similarity index 85% rename from game/states/menus.py rename to game/screens/menus.py index c9e6219..7927cc5 100644 --- a/game/states/menus.py +++ b/game/screens/menus.py @@ -11,7 +11,7 @@ import tcod.event from tcod.event import KeySym from game.constants import DIRECTION_KEYS -from game.state import Pop, State, StateResult, draw_previous_states +from game.screens import Pop, Screen, ScreenResult, draw_previous_screens class MenuItem(Protocol): @@ -19,7 +19,7 @@ class MenuItem(Protocol): __slots__ = () - def on_event(self, event: tcod.event.Event) -> StateResult: + def on_event(self, event: tcod.event.Event) -> ScreenResult: """Handle events passed to the menu item.""" def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: @@ -31,9 +31,9 @@ class SelectItem(MenuItem): """Clickable menu item.""" label: str - callback: Callable[[], StateResult] + callback: Callable[[], ScreenResult] - def on_event(self, event: tcod.event.Event) -> StateResult: + def on_event(self, event: tcod.event.Event) -> ScreenResult: """Handle events selecting this item.""" match event: case tcod.event.KeyDown(sym=sym) if sym in {KeySym.RETURN, KeySym.RETURN2, KeySym.KP_ENTER}: @@ -49,7 +49,7 @@ class SelectItem(MenuItem): @attrs.define() -class ListMenu(State): +class ListMenu(Screen): """Simple list menu state.""" items: tuple[MenuItem, ...] @@ -57,7 +57,7 @@ class ListMenu(State): x: int = 0 y: int = 0 - def on_event(self, event: tcod.event.Event) -> StateResult: + def on_event(self, event: tcod.event.Event) -> ScreenResult: """Handle events for menus.""" match event: case tcod.event.Quit(): @@ -83,18 +83,18 @@ class ListMenu(State): case _: return self.activate_selected(event) - def activate_selected(self, event: tcod.event.Event) -> StateResult: + def activate_selected(self, event: tcod.event.Event) -> ScreenResult: """Call the selected menu items callback.""" if self.selected is not None: return self.items[self.selected].on_event(event) return None - def on_cancel(self) -> StateResult: + def on_cancel(self) -> ScreenResult: """Handle escape or right click being pressed on menus.""" return Pop() def on_draw(self, console: tcod.console.Console) -> None: """Render the menu.""" - draw_previous_states(self, console) + draw_previous_screens(self, console) for i, item in enumerate(self.items): item.on_draw(console, x=self.x, y=self.y + i, highlight=i == self.selected) diff --git a/game/state.py b/game/state.py deleted file mode 100644 index 4497f0d..0000000 --- a/game/state.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Base classes for states.""" - -from __future__ import annotations - -from typing import Protocol, TypeAlias - -import attrs -import tcod.console -import tcod.event - -import g - - -class State(Protocol): - """An abstract game state.""" - - __slots__ = () - - def on_event(self, event: tcod.event.Event) -> StateResult: - """Called on events.""" - - def on_draw(self, console: tcod.console.Console) -> None: - """Called when the state is being drawn.""" - - -@attrs.define() -class Push: - """Push a new state on top of the stack.""" - - state: State - - -@attrs.define() -class Pop: - """Remove the current state from the stack.""" - - -@attrs.define() -class Reset: - """Replace the entire stack with a new state.""" - - state: State - - -StateResult: TypeAlias = "Push | Pop | Reset | None" -"""Union of state results.""" - - -def main_draw() -> None: - """Render and present the active state.""" - if not g.states: - return - g.console.clear() - g.states[-1].on_draw(g.console) - g.context.present(g.console) - - -def apply_state_result(result: StateResult) -> None: - """Apply a StateResult to `g.states`.""" - match result: - case Push(state=state): - g.states.append(state) - case Pop(): - g.states.pop() - case Reset(state=state): - while g.states: - apply_state_result(Pop()) - apply_state_result(Push(state)) - case None: - pass - case _: - raise TypeError(result) - - -def main_loop() -> None: - """Run the active state forever.""" - while g.states: - main_draw() - for event in tcod.event.wait(): - tile_event = g.context.convert_event(event) - if g.states: - apply_state_result(g.states[-1].on_event(tile_event)) - - -def get_previous_state(state: State) -> State | None: - """Return the state before `state` in the stack if it exists.""" - current_index = next(index for index, value in enumerate(g.states) if value is state) - return g.states[current_index - 1] if current_index > 0 else None - - -def draw_previous_states(state: State, console: tcod.console.Console, dim: bool = True) -> None: - """Draw previous states, optionally dimming all but the active state.""" - prev_state = get_previous_state(state) - if prev_state is None: - return - prev_state.on_draw(console) - if dim and state is g.states[-1]: - console.rgb["fg"] //= 4 - console.rgb["bg"] //= 4 diff --git a/main.py b/main.py index 582f885..226a327 100755 --- a/main.py +++ b/main.py @@ -9,8 +9,8 @@ import tcod.tileset import g -import game.state -from game.states.menu_screens import MainMenu +import game.screens +from game.screens.menu_screens import MainMenu def main() -> None: """Entry point function.""" @@ -19,10 +19,10 @@ def main() -> None: ) tcod.tileset.procedural_block_elements(tileset=tileset) - g.states = [MainMenu()] + g.screens = [MainMenu()] g.console = tcod.console.Console(80, 50) with tcod.context.new(console=g.console, tileset=tileset) as g.context: - game.state.main_loop() + game.screens.main_loop() if __name__ == "__main__": From 45e7a8927a73db6c12a719ccb9df3a6c53cfe1d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20N=C3=B6llemeyer?= Date: Sat, 17 Aug 2024 11:29:39 +0200 Subject: [PATCH 4/4] fixed pause menu and made `world_map` a component of `world` --- g.py | 3 -- game/screens/game_screens.py | 7 +++-- game/world_tools.py | 56 ++++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/g.py b/g.py index 2d12b10..e30e095 100644 --- a/g.py +++ b/g.py @@ -15,9 +15,6 @@ context: tcod.context.Context world: tcod.ecs.Registry """The active ECS registry and current session.""" -world_map: tcod.map.Map -"""Wall Map of current World""" - world_center: tuple[int,int] = Position(50, 50) screens: list[Screen] = [] diff --git a/game/screens/game_screens.py b/game/screens/game_screens.py index 504ccec..e9bc948 100644 --- a/game/screens/game_screens.py +++ b/game/screens/game_screens.py @@ -7,6 +7,7 @@ import tcod.console import tcod.constants import tcod.event from tcod.event import KeySym +from tcod.map import Map import g from game.components import Gold, Graphic, Position @@ -33,7 +34,7 @@ class MainScreen(Screen): raise SystemExit() case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: new_pos = player.components[Position] + DIRECTION_KEYS[sym] - if not g.world_map.walkable[g.world_center.y+new_pos.y][g.world_center.x+new_pos.x]: + if not g.world[None].components[Map].walkable[g.world_center.y+new_pos.y][g.world_center.x+new_pos.x]: return None player.components[Position] = new_pos # Auto pickup gold @@ -44,7 +45,7 @@ class MainScreen(Screen): gold.clear() return None case tcod.event.KeyDown(sym=KeySym.ESCAPE): - return Push(menu_screens()) + return Push(menu_screens.MainMenu()) case _: return None @@ -61,7 +62,7 @@ class MainScreen(Screen): and -h <= screen_pos.y < h): graphic = e_graph console.rgb[["ch", "fg"]][screen_pos.y + h, screen_pos.x + w] = graphic.ch, graphic.fg - for (y, row) in enumerate(g.world_map.walkable): + for (y, row) in enumerate(g.world[None].components[Map].walkable): for (x, val) in enumerate(row): if not val: draw(Position(x,y)-g.world_center, Graphic(WALL_CHAR)) diff --git a/game/world_tools.py b/game/world_tools.py index 65b93c7..fc7022e 100644 --- a/game/world_tools.py +++ b/game/world_tools.py @@ -14,13 +14,18 @@ from game.tags import IsActor, IsItem, IsPlayer, IsDoor def add_wall(pos, remove=False): r_pos = g.world_center + pos - g.world_map.walkable[r_pos.y][r_pos.x] = remove + map = g.world[None].components[Map] + map.walkable[r_pos.y][r_pos.x] = remove + map.transparent[r_pos.y][r_pos.x] = remove def add_door(pos): - door = g.world[object()] - door.tags.add(IsDoor) - door.components[Position] = pos - door.components[Graphic] = Graphic(ord("\\")) + g.world.new_entity( + components={ + Position: pos, + Graphic: Graphic(ord('\\')) + }, + tags=[IsDoor] + ) add_wall(pos) @@ -28,22 +33,36 @@ def new_world() -> Registry: """Return a freshly generated world.""" world = Registry() g.world = world - g.world_map = Map(100, 100) - g.world_map.walkable[:] = True + + map = world[None].components[Map] = Map(100, 100) + map.walkable[:] = True + map.transparent[:] = True + rng = world[None].components[Random] = Random() - player = world[object()] - player.components[Position] = Position(5, 5) - player.components[Graphic] = Graphic(ord("@")) - player.components[Gold] = 0 - player.tags |= {IsPlayer, IsActor} + world.new_entity( + components={ + Position: Position(5,5), + Graphic: Graphic(ord('@')), + Gold: 0 + }, + tags=[IsActor, IsPlayer] + ) + # player = world[object()] # <- das hier ist das gleiche wie das world.new_entity darĂ¼ber + # player.components[Position] = Position(5, 5) + # player.components[Graphic] = Graphic(ord("@")) + # player.components[Gold] = 0 + # player.tags |= {IsPlayer, IsActor} for _ in range(10): - gold = world[object()] - gold.components[Position] = Position(rng.randint(0, 20), rng.randint(0, 20)) - gold.components[Graphic] = Graphic(ord("$"), fg=(255, 255, 0)) - gold.components[Gold] = rng.randint(1, 10) - gold.tags |= {IsItem} + world.new_entity( + components={ + Position: Position(rng.randint(0, 20), rng.randint(0, 20)), + Graphic: Graphic(ord("$"), fg=(255, 255, 0)), + Gold: rng.randint(1, 10) + }, + tags=[IsItem] + ) for i in range(20): if i == 5 or i == 9: @@ -57,5 +76,4 @@ def new_world() -> Registry: def unlock_door(door: Entity): door.components[Graphic] = Graphic(ord("_")) door.tags.clear() - pos = door.components[Position] - add_wall(pos, remove=True) + add_wall(door.components[Position], remove=True)