Coverage for topdownengine/scenes.py: 45%
47 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-24 02:27 +0000
« 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
4class BaseScene:
5 """A scene that determines the behavior of the game loop.
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.
14 Args:
15 game (Game): The game object to use.
16 """
17 self.game = game
18 self.ui_containers = []
20 def handle_event(self, event: pg.Event):
21 """Handle a single event.
23 Args:
24 event (pygame.Event): The event to handle.
25 """
26 for container in self.ui_containers:
27 container.handle_event(event)
29 def update(self, dt: float):
30 """Update the scene.
32 Args:
33 dt (float): The deltatime.
34 """
35 for container in self.ui_containers:
36 container.update(dt)
38 def render(self):
39 "Render the scene."
40 for container in self.ui_containers:
41 container.render(self.game.screen)
43class GameplayScene(BaseScene):
44 """A scene that updates and renders all `GameObject` instances.
46 Attributes:
47 global_alpha (int): The global alpha for lighting.
48 """
50 def __init__(self, game):
51 """Initialize the GameplayScene.
53 Args:
54 game (Game): The game object to use.
55 """
56 super().__init__(game)
57 self.global_alpha = 0
58 self._light_filters = {}
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)]
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
71 def update(self, dt):
72 """Update the GameplayScene.
74 Args:
75 dt (float): The deltatime.
76 """
77 super().update(dt)
78 self.game.game_object_group.update(dt, self.game)
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))
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 )
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 )
111 self.game.screen.blit(overlay, (0, 0))
113 super().render() # Draw UI