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

1"""Rendering engine for Glomph Maze.""" 

2 

3from collections.abc import Sequence 

4from typing import TYPE_CHECKING 

5 

6from glomph.entities import Entity 

7from glomph.terminal import get_terminal 

8 

9if TYPE_CHECKING: 

10 from glomph.game import Game 

11 

12 

13class Renderer: 

14 """Handles rendering of the game world.""" 

15 

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 

23 

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 

28 

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 

33 

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 

39 

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) 

45 

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 

53 

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 

65 

66 if char != ' ': 

67 self.terminal.draw_char(y, x, char, color) 

68 

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 

73 

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) 

76 

77 def render_entities(self, entities: Sequence[Entity]) -> None: 

78 """Render multiple entities.""" 

79 for entity in entities: 

80 self.render_entity(entity) 

81 

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 

86 

87 # Clear UI area 

88 for x in range(self.viewport_width): 

89 self.terminal.draw_char(ui_y, x, ' ') 

90 

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) 

95 

96 ui_text = f"Level: {level} Score: {score} Lives: {lives}" 

97 self.terminal.draw_text(ui_y, 0, ui_text, 7) # White text 

98 

99 def clear(self) -> None: 

100 """Clear the screen.""" 

101 self.terminal.clear() 

102 

103 def present(self) -> None: 

104 """Present the rendered frame.""" 

105 self.terminal.refresh() 

106 

107 

108# Global renderer instance 

109_renderer = Renderer() 

110 

111 

112def get_renderer() -> Renderer: 

113 """Get the global renderer instance.""" 

114 return _renderer