diff --git a/g.py b/g.py index f6cf6b4..623e95e 100644 --- a/g.py +++ b/g.py @@ -2,47 +2,20 @@ from __future__ import annotations -import tcod.console -import tcod.context -import tcod.ecs -import tcod.sdl.video -import tcod.tileset -import tcod.sdl.render -import tcod.render +from tcod.context import Context +from tcod.ecs import Registry from game.screens import Screen -from game.components import Position +from game.render import Renderer -context: tcod.context.Context +context: Context """The window managed by tcod.""" -world: tcod.ecs.Registry +world: Registry """The active ECS registry and current session.""" screens: list[Screen] = [] """A stack of states with the last item being the active state.""" -foreground: tcod.console.Console -"""The foreground console""" - -background: tcod.console.Console -"""The background console""" - -tileset: tcod.tileset.Tileset -"""The tileset to use""" - -sdl_window: tcod.sdl.video.Window -"""The SDL window""" - -sdl_renderer: tcod.sdl.render.Renderer -"""The SDL renderer""" - -target_texture: tcod.sdl.render.Texture -"""The final texture that is rendered""" - -atlas: tcod.render.SDLTilesetAtlas -"""The tileset atlas""" - -console_render1: tcod.render.SDLConsoleRender -console_render2: tcod.render.SDLConsoleRender -"""The console renderer""" +renderer: Renderer +"""Renderer for the game""" diff --git a/game/render.py b/game/render.py new file mode 100644 index 0000000..fc60abf --- /dev/null +++ b/game/render.py @@ -0,0 +1,84 @@ +"""All rendering stuff""" + + +from tcod.sdl.video import new_window +from tcod.console import Console +from tcod.sdl.video import WindowFlags, Window +from tcod.render import SDLConsoleRender, SDLTilesetAtlas +from tcod.sdl.render import new_renderer, TextureAccess, Renderer, Texture +from tcod.tileset import Tileset +from tcod.event import Event, MouseState + +import g + + +class Renderer(): + foreground: Console + """The foreground console""" + background: Console + """The background console""" + console_render1: SDLConsoleRender + console_render2: SDLConsoleRender + """The console renderer""" + + sdl_window: Window + """The SDL window""" + sdl_renderer: Renderer + """The SDL renderer""" + target_texture: Texture + """The final texture that is rendered""" + atlas: SDLTilesetAtlas + """The tileset atlas""" + tileset: Tileset + """The tileset to use""" + + def __init__(self, w, h, tileset): + self.background = Console(w, h) + self.foreground = Console(w, h) + self.tileset = tileset + w, h = self.background.width * tileset.tile_width, self.background.height * tileset.tile_height + self.sdl_window = new_window(w,h, flags=WindowFlags.RESIZABLE) + + self.sdl_renderer = new_renderer(self.sdl_window, target_textures=True) + self.target_texture = self.sdl_renderer.new_texture(w, h, access=TextureAccess.TARGET) + self.atlas = SDLTilesetAtlas(self.sdl_renderer, self.tileset) + + self.console_render1 = SDLConsoleRender(atlas=self.atlas) + self.console_render2 = SDLConsoleRender(atlas=self.atlas) + + def main_draw(self) -> None: + """Render and present the active screen.""" + if not g.screens: + return + self.background.clear() + self.foreground.clear() + self.foreground.rgba["bg"][:] = 0 + self.foreground.rgba["fg"][:] = 0 + g.screens[-1].on_draw() + + bg_tex = self.console_render1.render(self.background) + bg_tex.blend_mode = 1 + + fg_tex = self.console_render2.render(self.foreground) + fg_tex.blend_mode = 1 + + with self.sdl_renderer.set_render_target(self.target_texture): + self.sdl_renderer.clear() + self.sdl_renderer.copy(bg_tex) + self.sdl_renderer.copy(fg_tex) + + self.sdl_renderer.copy(self.target_texture) + self.sdl_renderer.present() + + def convert_event(self, event: Event): + match event: + case MouseState(position=pos): + width, height = self.tileset.tile_width, self.tileset.tile_height + event.position = (pos[0]//width, pos[1]//height) + return event + + def dim(self): + self.foreground.rgb["fg"] //= 4 + self.foreground.rgb["bg"] //= 4 + self.background.rgb["fg"] //= 4 + self.background.rgb["bg"] //= 4 diff --git a/game/screens/__init__.py b/game/screens/__init__.py index 80afee5..e799b7d 100644 --- a/game/screens/__init__.py +++ b/game/screens/__init__.py @@ -15,7 +15,7 @@ import tcod.sdl.render import tcod.sdl.video import g - +from game.render import Renderer class Screen(Protocol): """An abstract game screen.""" @@ -25,7 +25,7 @@ class Screen(Protocol): def on_event(self, event: Event) -> ScreenResult: """Called on events.""" - def on_draw(self, console: Console) -> None: + def on_draw(self) -> None: """Called when the screen is being drawn.""" @@ -52,31 +52,6 @@ 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.background.clear() - g.foreground.clear() - g.foreground.rgba["bg"][:] = 0 - g.foreground.rgba["fg"][:] = 0 - g.screens[-1].on_draw(g.background) - - bg_tex = g.console_render1.render(g.background) - bg_tex.blend_mode = 1 - - fg_tex = g.console_render2.render(g.foreground) - fg_tex.blend_mode = 1 - - with g.sdl_renderer.set_render_target(g.target_texture): - g.sdl_renderer.clear() - g.sdl_renderer.copy(bg_tex) - g.sdl_renderer.copy(fg_tex) - - g.sdl_renderer.copy(g.target_texture) - g.sdl_renderer.present() - - def _apply_screen_result(result: ScreenResult) -> None: """Apply a ScreenResult to `g.screens`.""" match result: @@ -93,22 +68,15 @@ def _apply_screen_result(result: ScreenResult) -> None: case _: raise TypeError(result) -def convert_event(event: tcod.event.Event): - match event: - case tcod.event.MouseState(position=pos): - width, height = g.tileset.tile_width, g.tileset.tile_height - event.position = (pos[0]//width, pos[1]//height) - return event - def main_loop() -> None: """Run the active screen forever.""" while g.screens: - main_draw() + g.renderer.main_draw() for event in wait_for_event(): match event: case Quit(): raise SystemExit() - tile_event = convert_event(event) + tile_event = g.renderer.convert_event(event) if g.screens: _apply_screen_result(g.screens[-1].on_event(tile_event)) @@ -119,14 +87,11 @@ def get_previous_screen(screen: Screen) -> Screen | None: 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: +def draw_previous_screens(screen: Screen, dim: bool = True) -> None: """Draw previous screens, optionally dimming all but the active screen.""" prev_screen = get_previous_screen(screen) if prev_screen is None: return - prev_screen.on_draw(console) + prev_screen.on_draw() if dim and screen is g.screens[-1]: - g.foreground.rgb["fg"] //= 4 - g.foreground.rgb["bg"] //= 4 - g.background.rgb["fg"] //= 4 - g.background.rgb["bg"] //= 4 + g.renderer.dim() diff --git a/game/screens/game_screens.py b/game/screens/game_screens.py index f5a46ce..6ea4621 100644 --- a/game/screens/game_screens.py +++ b/game/screens/game_screens.py @@ -64,7 +64,7 @@ def _draw_entity(entity: tcod.ecs.Entity, camera_pos, camera_radius_x, camera_ra graphic = entity.components[Graphic] r,gg,b,_ = graphic.fg fg = (r,gg,b,255) - g.foreground.rgba[["ch", "fg"]][screen_pos.y + camera_radius_y, screen_pos.x + camera_radius_x] = graphic.ch, fg + g.renderer.foreground.rgba[["ch", "fg"]][screen_pos.y + camera_radius_y, screen_pos.x + camera_radius_x] = graphic.ch, fg @attrs.define() @@ -86,9 +86,9 @@ class MainScreen(Screen): case _: return None - def on_draw(self, console: tcod.console.Console) -> None: + def on_draw(self) -> None: """Draw the standard screen.""" - console = g.background + console = g.renderer.background centers = [a.components[Position] for a in g.world.Q.all_of(tags=[IsPlayer])] camera_pos = sum(centers, start=Position(0,0)) camera_pos = camera_pos.mod(len(centers)) diff --git a/game/screens/menus.py b/game/screens/menus.py index 66343ff..262b97f 100644 --- a/game/screens/menus.py +++ b/game/screens/menus.py @@ -45,10 +45,9 @@ class SelectItem(MenuItem): case _: return None - def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: + def on_draw(self, x: int, y: int, highlight: bool) -> None: """Render this items label.""" - #g.foreground.print(x, y, self.label, fg=(255, 255, 255), bg=(64, 64, 64) if highlight else (0, 0, 0)) - g.background.print(x, y, self.label, fg=(255, 255, 255), bg=(64, 64, 64) if highlight else (0, 0, 0)) + g.renderer.background.print(x, y, self.label, fg=(255, 255, 255), bg=(64, 64, 64) if highlight else (0, 0, 0)) @attrs.define() @@ -94,8 +93,8 @@ class ListMenu(Screen): """Handle escape or right click being pressed on menus.""" return Pop() - def on_draw(self, console: tcod.console.Console) -> None: + def on_draw(self) -> None: """Render the menu.""" - draw_previous_screens(self, g.foreground) + draw_previous_screens(self) for i, item in enumerate(self.items): - item.on_draw(g.foreground, x=self.x, y=self.y + i, highlight=i == self.selected) + item.on_draw(x=self.x, y=self.y + i, highlight=i == self.selected) diff --git a/main.py b/main.py index a64bff0..ce9d0ef 100755 --- a/main.py +++ b/main.py @@ -15,27 +15,15 @@ import g import game.tilesetmanager import game.screens from game.screens.menu_screens import MainMenu +from game.render import Renderer def main() -> None: """Entry point function.""" - g.tileset = game.tilesetmanager.valid_tileset() + tileset = game.tilesetmanager.valid_tileset() #tcod.tileset.procedural_block_elements(tileset=tileset) g.screens = [MainMenu()] - g.background = tcod.console.Console(80, 35) - g.foreground = tcod.console.Console(80, 35) - w, h = g.background.width * g.tileset.tile_width, g.background.height * g.tileset.tile_height - win = tcod.sdl.video.new_window(w,h, flags=tcod.lib.SDL_WINDOW_RESIZABLE) - - g.sdl_window = win - - - g.sdl_renderer = tcod.sdl.render.new_renderer(g.sdl_window, target_textures=True) - g.target_texture = g.sdl_renderer.new_texture(w, h, access=tcod.sdl.render.TextureAccess.TARGET) - g.atlas = tcod.render.SDLTilesetAtlas(g.sdl_renderer, g.tileset) - - g.console_render1 = tcod.render.SDLConsoleRender(atlas=g.atlas) - g.console_render2 = tcod.render.SDLConsoleRender(atlas=g.atlas) + g.renderer = Renderer(80, 35, tileset) game.screens.main_loop()