Coverage for src/glomph/renderer.py: 0%
64 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-06 00:58 +0800
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-06 00:58 +0800
1"""Rendering engine for Glomph Maze."""
3from collections.abc import Sequence
4from typing import TYPE_CHECKING
6from glomph.entities import Entity
7from glomph.terminal import get_terminal
9if TYPE_CHECKING:
10 from glomph.game import Game
13class Renderer:
14 """Handles rendering of the game world."""
16 def __init__(self) -> None:
17 """Initialize the renderer."""
18 self.terminal = get_terminal()
19 self.viewport_width = 0
20 self.viewport_height = 0
21 self.camera_x = 0
22 self.camera_y = 0
24 def set_viewport(self, width: int, height: int) -> None:
25 """Set the viewport dimensions."""
26 self.viewport_width = width
27 self.viewport_height = height
29 def set_camera(self, x: int, y: int) -> None:
30 """Set camera position for scrolling."""
31 self.camera_x = x
32 self.camera_y = y
34 def world_to_screen(self, world_x: int, world_y: int) -> tuple[int, int]:
35 """Convert world coordinates to screen coordinates."""
36 screen_x = world_x - self.camera_x
37 screen_y = world_y - self.camera_y
38 return screen_x, screen_y
40 def is_visible(self, world_x: int, world_y: int) -> bool:
41 """Check if a world position is visible in the current viewport."""
42 screen_x, screen_y = self.world_to_screen(world_x, world_y)
43 return (0 <= screen_x < self.viewport_width and
44 0 <= screen_y < self.viewport_height)
46 def render_maze(self, game: 'Game') -> None:
47 """Render a maze to the screen."""
48 # Render walls from collision map and dots from dot map
49 for y in range(min(len(game.collision_map), self.viewport_height)):
50 for x in range(min(len(game.collision_map[y]), self.viewport_width)):
51 char = ' '
52 color = 0
54 # Check collision map for walls
55 if game.collision_map[y][x] == '#':
56 char = '#'
57 color = 4 # Blue walls
58 # Check dot map for collectibles
59 elif game.dot_map[y][x] == '.':
60 char = '.'
61 color = 3 # Yellow dots
62 elif game.dot_map[y][x] == 'o':
63 char = 'o'
64 color = 3 # Yellow power pellets
66 if char != ' ':
67 self.terminal.draw_char(y, x, char, color)
69 def render_entity(self, entity: Entity) -> None:
70 """Render a game entity."""
71 if not self.is_visible(entity.position.x, entity.position.y):
72 return
74 screen_x, screen_y = self.world_to_screen(entity.position.x, entity.position.y)
75 self.terminal.draw_char(screen_y, screen_x, entity.char, entity.color)
77 def render_entities(self, entities: Sequence[Entity]) -> None:
78 """Render multiple entities."""
79 for entity in entities:
80 self.render_entity(entity)
82 def render_ui(self, game_state: dict[str, int]) -> None:
83 """Render UI elements (score, lives, etc.)."""
84 # Render at the top of the screen
85 ui_y = 0
87 # Clear UI area
88 for x in range(self.viewport_width):
89 self.terminal.draw_char(ui_y, x, ' ')
91 # Render game info
92 level = game_state.get('level', 1)
93 score = game_state.get('score', 0)
94 lives = game_state.get('lives', 3)
96 ui_text = f"Level: {level} Score: {score} Lives: {lives}"
97 self.terminal.draw_text(ui_y, 0, ui_text, 7) # White text
99 def clear(self) -> None:
100 """Clear the screen."""
101 self.terminal.clear()
103 def present(self) -> None:
104 """Present the rendered frame."""
105 self.terminal.refresh()
108# Global renderer instance
109_renderer = Renderer()
112def get_renderer() -> Renderer:
113 """Get the global renderer instance."""
114 return _renderer