Coverage for topdownengine/scenes.py: 45%

47 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-24 02:27 +0000

1import pygame as pg 

2from .math import scale_rect 

3 

4class BaseScene: 

5 """A scene that determines the behavior of the game loop. 

6  

7 Attributes: 

8 game (Game): The game object to use. 

9 ui_containers (list[UIContainer]): The list of UIContainers for this scene. 

10 """ 

11 def __init__(self, game): 

12 """Initialize the scene. 

13  

14 Args: 

15 game (Game): The game object to use. 

16 """ 

17 self.game = game 

18 self.ui_containers = [] 

19 

20 def handle_event(self, event: pg.Event): 

21 """Handle a single event. 

22  

23 Args: 

24 event (pygame.Event): The event to handle. 

25 """ 

26 for container in self.ui_containers: 

27 container.handle_event(event) 

28 

29 def update(self, dt: float): 

30 """Update the scene. 

31  

32 Args: 

33 dt (float): The deltatime. 

34 """ 

35 for container in self.ui_containers: 

36 container.update(dt) 

37 

38 def render(self): 

39 "Render the scene." 

40 for container in self.ui_containers: 

41 container.render(self.game.screen) 

42 

43class GameplayScene(BaseScene): 

44 """A scene that updates and renders all `GameObject` instances. 

45  

46 Attributes: 

47 global_alpha (int): The global alpha for lighting. 

48 """ 

49 

50 def __init__(self, game): 

51 """Initialize the GameplayScene. 

52  

53 Args: 

54 game (Game): The game object to use. 

55 """ 

56 super().__init__(game) 

57 self.global_alpha = 0 

58 self._light_filters = {} 

59 

60 def get_light(self, radius: int): 

61 if not radius in self._light_filters: self._light_filters[radius] = self.create_light(radius) 

62 return self._light_filters[(radius)] 

63 

64 def create_light(self, radius: int): 

65 surface = pg.Surface((max(radius * 2, 1), max(radius * 2, 1)), pg.SRCALPHA) 

66 for i in range(int(radius), 0, -1): 

67 alpha = 255 * (1 - i / radius) 

68 pg.draw.circle(surface, (0, 0, 0, alpha), (radius, radius), i) 

69 return surface 

70 

71 def update(self, dt): 

72 """Update the GameplayScene. 

73  

74 Args: 

75 dt (float): The deltatime. 

76 """ 

77 super().update(dt) 

78 self.game.game_object_group.update(dt, self.game) 

79 

80 def render(self): 

81 "Render the GameplayScene." 

82 overlay = pg.Surface(self.game.screen.size, pg.SRCALPHA) 

83 overlay.fill((0, 0, 0, self.global_alpha)) 

84 

85 for game_object in sorted(self.game.game_object_group.game_objects, key=lambda g: g.draw_index): 

86 cr = game_object.rect.move(-self.game.camera.position * game_object.SCALE) 

87 self.game.screen.blit(game_object.image, cr) 

88 if self.global_alpha > 0 and game_object.light_radius > 0: 

89 scaled_lr = game_object.light_radius * game_object.SCALE 

90 overlay.blit( 

91 self.get_light(scaled_lr), 

92 ( 

93 cr.centerx - scaled_lr, 

94 cr.bottom - scaled_lr - game_object.z * game_object.SCALE - game_object.current_frame.height / 2 + game_object.elevation * game_object.SCALE 

95 ), 

96 special_flags=pg.BLEND_RGBA_SUB 

97 ) 

98 

99 # Draw debug in a separate loop so that it is drawn over images. 

100 if self.game.debug: 

101 for game_object in self.game.game_object_group.game_objects: 

102 # Draw Colliders 

103 for collider in game_object.world_colliders: 

104 pg.draw.rect( 

105 self.game.screen, 

106 (0, 0, 255), 

107 scale_rect(collider.move(-self.game.camera.position), game_object.SCALE), 

108 1 

109 ) 

110 

111 self.game.screen.blit(overlay, (0, 0)) 

112 

113 super().render() # Draw UI