Skip to content

Level Module

Level

Represents a game level, managing all objects, events, input, and rendering for that level.

Handles the game loop, background, user input (keyboard, mouse, joystick), object management, collision detection, and timed events.

Attributes:

Name Type Description
screen Surface

The pygame surface to render the level on.

objects list

List of all RoomObject instances in the level.

keyboard_objects list

Objects that handle keyboard events.

mouse_objects list

Objects that handle mouse events.

_clock Clock

Clock for managing frame rate.

running bool

Indicates if the level is currently running.

quitting bool

Indicates if the level is quitting.

background_color tuple

RGB color for the background.

background_set bool

Whether a background image is set.

background_image

The background image surface or identifier.

background_y int

Y position for background scrolling.

background_scroll_speed int

Speed of background scrolling.

background_scrolling bool

Whether background scrolling is enabled.

user_events list

List of timed user events.

joysticks Joystick

List of connected joystick devices.

p1_btns list

State of player 1's joystick buttons/axes.

p2_btns list

State of player 2's joystick buttons/axes.

has_buttons_1 bool

Whether player 1's joystick has buttons.

has_buttons_2 bool

Whether player 2's joystick has buttons.

has_hat_1 bool

Whether player 1's joystick has axes.

has_hat_2 bool

Whether player 2's joystick has axes.

Source code in GameFrame/Level.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
class Level:
    """
    Represents a game level, managing all objects, events, input, and rendering for that level.

    Handles the game loop, background, user input (keyboard, mouse, joystick), object management,
    collision detection, and timed events.

    Attributes:
        screen (Surface): The pygame surface to render the level on.
        objects (list): List of all RoomObject instances in the level.
        keyboard_objects (list): Objects that handle keyboard events.
        mouse_objects (list): Objects that handle mouse events.
        _clock (pygame.time.Clock): Clock for managing frame rate.
        running (bool): Indicates if the level is currently running.
        quitting (bool): Indicates if the level is quitting.
        background_color (tuple): RGB color for the background.
        background_set (bool): Whether a background image is set.
        background_image: The background image surface or identifier.
        background_y (int): Y position for background scrolling.
        background_scroll_speed (int): Speed of background scrolling.
        background_scrolling (bool): Whether background scrolling is enabled.
        user_events (list): List of timed user events.
        joysticks (Joystick): List of connected joystick devices.
        p1_btns (list): State of player 1's joystick buttons/axes.
        p2_btns (list): State of player 2's joystick buttons/axes.
        has_buttons_1 (bool): Whether player 1's joystick has buttons.
        has_buttons_2 (bool): Whether player 2's joystick has buttons.
        has_hat_1 (bool): Whether player 1's joystick has axes.
        has_hat_2 (bool): Whether player 2's joystick has axes.
    """

    def __init__(self, screen: Surface, joysticks: Joystick):
        """
        Initializes the Level with the given screen and joysticks.

        Args:
            screen (Surface): The pygame surface to render the level on.
            joysticks (Joystick): List of connected joystick devices.
        """
        self.screen = screen
        self.objects = []
        self.keyboard_objects = []
        self.mouse_objects = []
        self._clock = pygame.time.Clock()
        self.running = False
        self.quitting = False
        self.background_color = (0, 0, 0)
        self.background_set = False
        self.background_image = 0
        self.background_y = 0
        self.background_scroll_speed = 0
        self.background_scrolling = False
        self.user_events = []
        self.joysticks = joysticks
        self.p1_btns = []
        self.p2_btns = []
        self.has_buttons_1 = False
        self.has_buttons_2 = False
        self.has_hat_1 = False
        self.has_hat_2 = False
        if len(self.joysticks) > 0:
            buttons = self.joysticks[0].get_numbuttons()
            if buttons > 0:
                self.has_buttons_1 = True
            for i in range(buttons):
                self.p1_btns.append(self.joysticks[0].get_button(i))
            axes = self.joysticks[0].get_numaxes()
            if axes > 0:
                self.has_hat_1 = True
            for i in range(axes):
                self.p1_btns.append(self.joysticks[0].get_axis(i))
            if len(self.joysticks) > 1:
                buttons = self.joysticks[1].get_numbuttons()
                if buttons > 0:
                    self.has_buttons_2 = True
                for i in range(buttons):
                    self.p2_btns.append(self.joysticks[1].get_button(i))
                axes = self.joysticks[1].get_numaxes()
                if axes > 0:
                    self.has_hat_2 = True
                for i in range(axes):
                    self.p1_btns.append(self.joysticks[1].get_axis(i))

    def run(self) -> bool:
        """
        Runs the main game loop for the level.

        Returns:
            bool: True if the level is quitting, False otherwise.
        """
        self.running = True
        for obj in self.objects:
            self.init_collision_list(obj)

        while self.running:
            self._clock.tick(Globals.FRAMES_PER_SECOND)

            for obj in self.objects:
                obj.prev_x = obj.x
                obj.prev_y = obj.y

            # - Process user events - #
            self.process_user_events()

            # Call Pre step on all objects
            for item in self.objects:
                item.prestep()

            events = pygame.event.get()
            for event in events:
                if event.type == pygame.QUIT:
                    self.running = False
                    self.quitting = True
                    Globals.exiting = True
                    pass
                # - Check for mouse click and pass to objects registered for mouse events - #
                if event.type == pygame.MOUSEBUTTONUP:
                    mouse_pos = pygame.mouse.get_pos()
                    for obj in self.mouse_objects:
                        if obj.rect.collidepoint(mouse_pos):
                            obj.clicked(event.button)

            # -  Check for joystick events and pass  - #
            # - to objects registered for key events - #
            signals = False
            if self.has_buttons_1:
                for i in range(self.joysticks[0].get_numbuttons()):
                    self.p1_btns[i] = self.joysticks[0].get_button(i)
                    if self.p1_btns[i] == 1:
                        signals = True
            if self.has_hat_1:
                for i in range(self.joysticks[0].get_numaxes()):
                    self.p1_btns[-(i+1)] = self.joysticks[0].get_axis(i)
                    if self.p1_btns[-(i+1)] > 0 or self.p1_btns[-(i+1)] < 0:
                        signals = True
            if self.has_buttons_2:
                for i in range(self.joysticks[1].get_numbuttons()):
                    self.p2_btns[i] = self.joysticks[1].get_button(i)
                    if self.p2_btns[i] == 1:
                        signals = True
            if self.has_hat_2:
                for i in range(self.joysticks[1].get_numaxes()):
                    self.p1_btns[-i] = self.joysticks[1].get_axis(i)
                    if self.p2_btns[-i] > 0 or self.p2_btns[-i] < 0:
                        signals = True

            if signals:
                for obj in self.keyboard_objects:
                    obj.joy_pad_signal(self.p1_btns, self.p2_btns)

            # - Check for a keyboard event and pass - #
            # - to objects registered for key events - #
            keys = pygame.key.get_pressed()
            if len(keys):
                for obj in self.keyboard_objects:
                    obj.key_pressed(keys)

            # - Check for a mouse event and pass - #
            # - to objects registered for mouse events - #
            (mouse_x, mouse_y) = pygame.mouse.get_pos()
            (button_left, button_middle, button_right) = pygame.mouse.get_pressed()
            for obj in self.mouse_objects:
                    obj.mouse_event(mouse_x, mouse_y, button_left, button_middle, button_right)

            # - Handle all other events - #
            self.catch_events(events)

            # - Clear the screen - #
            self.screen.fill(self.background_color)
            # - Add Background if set - #
            if self.background_set:
                # - Scrolling if set - #
                if self.background_scrolling:
                    self.background_y += self.background_scroll_speed
                    if self.background_y >= Globals.SCREEN_HEIGHT:
                        self.background_y = 0
                    self.screen.blit(self.background_image, (0, self.background_y))
                    self.screen.blit(self.background_image, (0, self.background_y - Globals.SCREEN_HEIGHT))
                else:
                    self.screen.blit(self.background_image, (0, 0))
            # Call Update on all objects
            for item in self.objects:
                item.update()
                item.step()

            # Check collisions
            for item in self.objects:
                item.check_collisions()

            for item in self.objects:
                self.screen.blit(item.image, (item.x, item.y))

            pygame.display.update()

        return self.quitting

    def set_background_image(self, image_file: str):
        """
        Sets the background image for the level.

        Args:
            image_file (str): Filename of the background image.
        """
        self.background_set = True
        self.background_image = pygame.image.load(os.path.join('Images', image_file)).convert_alpha()

    def set_background_scroll(self, speed: int):
        """
        Enables background scrolling at the specified speed.

        Args:
            speed (int): The scroll speed in pixels per frame.
        """
        self.background_scrolling = True
        self.background_scroll_speed = speed

    def add_room_object(self, room_object: RoomObject):
        """
        Adds a RoomObject to the level and registers it for events as needed.

        Args:
            room_object (RoomObject): The object to add.
        """
        # - Add to room objects list - #
        if len(self.objects) == 0:
            self.objects.append(room_object)
        else:
            for index, item in enumerate(self.objects):
                if item.depth >= room_object.depth:
                    self.objects.insert(index, room_object)
                    break
                elif index == len(self.objects) - 1:
                    self.objects.append(room_object)
                    break

        # - Add objects that handle key events to array - #
        if room_object.handle_key_events:
            self.keyboard_objects.append(room_object)

        # - Add objects that handle mouse events to array - #
        if room_object.handle_mouse_events:
            self.mouse_objects.append(room_object)

        if self.running:
            self.dynamic_init_collision_list(room_object)

    def load_sound(self, sound_file: str) -> Sound:
        """
        Loads a sound file from the Sounds directory.

        Args:
            sound_file (str): Filename of the sound to load.

        Returns:
            Sound: The loaded pygame Sound object.
        """
        fq_filename = os.path.join('Sounds', sound_file)
        return pygame.mixer.Sound(fq_filename)

    def stop_sound(self):
        """
        Stops all currently playing sounds.
        """
        pygame.mixer.stop()

    def load_image(self, file_name: str) -> str:
        """
        Returns the full path to an image file in the Images directory.

        Args:
            file_name (str): Filename of the image.

        Returns:
            str: Full path to the image file.
        """
        return os.path.join('Images', file_name)

    def init_collision_list(self, room_object: RoomObject):
        """
        Initializes the collision list for a given object.

        Args:
            room_object (RoomObject): The object to initialize collisions for.
        """
        # - Initialise collision list for object - #
        for obj_name in room_object.collision_object_types:
            for obj_instance in self.objects:
                if type(obj_instance).__name__ == obj_name and obj_instance is not room_object:
                    room_object.collision_objects.append(obj_instance)

    def dynamic_init_collision_list(self, room_object: RoomObject):
        """
        Dynamically updates collision lists when a new object is added.

        Args:
            room_object (RoomObject): The new object to add to collision lists.
        """
        self.init_collision_list(room_object)
        obj_type = type(room_object).__name__
        for obj in self.objects:
            if obj is not room_object:
                if obj_type in obj.collision_object_types:
                    obj.collision_objects.append(room_object)

    def catch_events(self, events):
        """
        Handles additional pygame events not processed elsewhere.

        Args:
            events: List of pygame events.
        """
        pass

    def delete_object(self, obj: RoomObject):
        """
        Removes an object from the level and all event/collision lists.

        Args:
            obj (RoomObject): The object to remove.
        """
        for index, list_obj in self.enumerate_backwards(self.objects):
            if list_obj is obj:
                self.objects.pop(index)
            else:
                list_obj.remove_object(obj)
        for index, list_obj in self.enumerate_backwards(self.keyboard_objects):
            if list_obj is obj:
                self.keyboard_objects.pop(index)
        for index, list_obj in self.enumerate_backwards(self.mouse_objects):
            if list_obj is obj:
                self.mouse_objects.pop(index)
        # Remove any timed function calls for the deleted object
        for index, event_method in self.enumerate_backwards(self.user_events):
            obj_inst = event_method[1].__self__
            if obj_inst is obj:
                self.user_events.pop(index)

    def set_timer(self, ticks: int, function_call: Callable):
        """
        Sets a timed event to call a function after a number of ticks.

        Args:
            ticks (int): Number of frames to wait before calling the function.
            function_call (Callable): The function to call.
        """
        self.user_events.append([ticks, function_call])

    def process_user_events(self):
        """
        Processes and triggers any timed user events.
        """
        for index, user_event in self.enumerate_backwards(self.user_events):
            user_event[0] -= 1
            if user_event[0] <= 0:
                event = user_event[1]
                self.user_events.pop(index)
                event()

    def count_object(self, object_name):
        """
        Counts the number of objects of a given type in the level.

        Args:
            object_name (str): The class name of the object to count.

        Returns:
            int: The number of objects of the specified type.
        """
        total = 0
        for item in self.objects:
            item_type = type(item).__name__
            if object_name == item_type:
                total += 1            
        return total

    # Iterate backwards over a list, using an index and item iterator
    def enumerate_backwards(self, object_list: List):
        """
        Iterates backwards over a list, yielding index and item.

        Args:
            object_list (List): The list to iterate over.

        Yields:
            tuple: (index, item) for each element in reverse order.
        """
        index = len(object_list)
        for item in reversed(object_list):
            index -= 1
            yield index, item

__init__(screen, joysticks)

Initializes the Level with the given screen and joysticks.

Parameters:

Name Type Description Default
screen Surface

The pygame surface to render the level on.

required
joysticks Joystick

List of connected joystick devices.

required
Source code in GameFrame/Level.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def __init__(self, screen: Surface, joysticks: Joystick):
    """
    Initializes the Level with the given screen and joysticks.

    Args:
        screen (Surface): The pygame surface to render the level on.
        joysticks (Joystick): List of connected joystick devices.
    """
    self.screen = screen
    self.objects = []
    self.keyboard_objects = []
    self.mouse_objects = []
    self._clock = pygame.time.Clock()
    self.running = False
    self.quitting = False
    self.background_color = (0, 0, 0)
    self.background_set = False
    self.background_image = 0
    self.background_y = 0
    self.background_scroll_speed = 0
    self.background_scrolling = False
    self.user_events = []
    self.joysticks = joysticks
    self.p1_btns = []
    self.p2_btns = []
    self.has_buttons_1 = False
    self.has_buttons_2 = False
    self.has_hat_1 = False
    self.has_hat_2 = False
    if len(self.joysticks) > 0:
        buttons = self.joysticks[0].get_numbuttons()
        if buttons > 0:
            self.has_buttons_1 = True
        for i in range(buttons):
            self.p1_btns.append(self.joysticks[0].get_button(i))
        axes = self.joysticks[0].get_numaxes()
        if axes > 0:
            self.has_hat_1 = True
        for i in range(axes):
            self.p1_btns.append(self.joysticks[0].get_axis(i))
        if len(self.joysticks) > 1:
            buttons = self.joysticks[1].get_numbuttons()
            if buttons > 0:
                self.has_buttons_2 = True
            for i in range(buttons):
                self.p2_btns.append(self.joysticks[1].get_button(i))
            axes = self.joysticks[1].get_numaxes()
            if axes > 0:
                self.has_hat_2 = True
            for i in range(axes):
                self.p1_btns.append(self.joysticks[1].get_axis(i))

add_room_object(room_object)

Adds a RoomObject to the level and registers it for events as needed.

Parameters:

Name Type Description Default
room_object RoomObject

The object to add.

required
Source code in GameFrame/Level.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def add_room_object(self, room_object: RoomObject):
    """
    Adds a RoomObject to the level and registers it for events as needed.

    Args:
        room_object (RoomObject): The object to add.
    """
    # - Add to room objects list - #
    if len(self.objects) == 0:
        self.objects.append(room_object)
    else:
        for index, item in enumerate(self.objects):
            if item.depth >= room_object.depth:
                self.objects.insert(index, room_object)
                break
            elif index == len(self.objects) - 1:
                self.objects.append(room_object)
                break

    # - Add objects that handle key events to array - #
    if room_object.handle_key_events:
        self.keyboard_objects.append(room_object)

    # - Add objects that handle mouse events to array - #
    if room_object.handle_mouse_events:
        self.mouse_objects.append(room_object)

    if self.running:
        self.dynamic_init_collision_list(room_object)

catch_events(events)

Handles additional pygame events not processed elsewhere.

Parameters:

Name Type Description Default
events

List of pygame events.

required
Source code in GameFrame/Level.py
315
316
317
318
319
320
321
322
def catch_events(self, events):
    """
    Handles additional pygame events not processed elsewhere.

    Args:
        events: List of pygame events.
    """
    pass

count_object(object_name)

Counts the number of objects of a given type in the level.

Parameters:

Name Type Description Default
object_name str

The class name of the object to count.

required

Returns:

Name Type Description
int

The number of objects of the specified type.

Source code in GameFrame/Level.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def count_object(self, object_name):
    """
    Counts the number of objects of a given type in the level.

    Args:
        object_name (str): The class name of the object to count.

    Returns:
        int: The number of objects of the specified type.
    """
    total = 0
    for item in self.objects:
        item_type = type(item).__name__
        if object_name == item_type:
            total += 1            
    return total

delete_object(obj)

Removes an object from the level and all event/collision lists.

Parameters:

Name Type Description Default
obj RoomObject

The object to remove.

required
Source code in GameFrame/Level.py
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def delete_object(self, obj: RoomObject):
    """
    Removes an object from the level and all event/collision lists.

    Args:
        obj (RoomObject): The object to remove.
    """
    for index, list_obj in self.enumerate_backwards(self.objects):
        if list_obj is obj:
            self.objects.pop(index)
        else:
            list_obj.remove_object(obj)
    for index, list_obj in self.enumerate_backwards(self.keyboard_objects):
        if list_obj is obj:
            self.keyboard_objects.pop(index)
    for index, list_obj in self.enumerate_backwards(self.mouse_objects):
        if list_obj is obj:
            self.mouse_objects.pop(index)
    # Remove any timed function calls for the deleted object
    for index, event_method in self.enumerate_backwards(self.user_events):
        obj_inst = event_method[1].__self__
        if obj_inst is obj:
            self.user_events.pop(index)

dynamic_init_collision_list(room_object)

Dynamically updates collision lists when a new object is added.

Parameters:

Name Type Description Default
room_object RoomObject

The new object to add to collision lists.

required
Source code in GameFrame/Level.py
301
302
303
304
305
306
307
308
309
310
311
312
313
def dynamic_init_collision_list(self, room_object: RoomObject):
    """
    Dynamically updates collision lists when a new object is added.

    Args:
        room_object (RoomObject): The new object to add to collision lists.
    """
    self.init_collision_list(room_object)
    obj_type = type(room_object).__name__
    for obj in self.objects:
        if obj is not room_object:
            if obj_type in obj.collision_object_types:
                obj.collision_objects.append(room_object)

enumerate_backwards(object_list)

Iterates backwards over a list, yielding index and item.

Parameters:

Name Type Description Default
object_list List

The list to iterate over.

required

Yields:

Name Type Description
tuple

(index, item) for each element in reverse order.

Source code in GameFrame/Level.py
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def enumerate_backwards(self, object_list: List):
    """
    Iterates backwards over a list, yielding index and item.

    Args:
        object_list (List): The list to iterate over.

    Yields:
        tuple: (index, item) for each element in reverse order.
    """
    index = len(object_list)
    for item in reversed(object_list):
        index -= 1
        yield index, item

init_collision_list(room_object)

Initializes the collision list for a given object.

Parameters:

Name Type Description Default
room_object RoomObject

The object to initialize collisions for.

required
Source code in GameFrame/Level.py
288
289
290
291
292
293
294
295
296
297
298
299
def init_collision_list(self, room_object: RoomObject):
    """
    Initializes the collision list for a given object.

    Args:
        room_object (RoomObject): The object to initialize collisions for.
    """
    # - Initialise collision list for object - #
    for obj_name in room_object.collision_object_types:
        for obj_instance in self.objects:
            if type(obj_instance).__name__ == obj_name and obj_instance is not room_object:
                room_object.collision_objects.append(obj_instance)

load_image(file_name)

Returns the full path to an image file in the Images directory.

Parameters:

Name Type Description Default
file_name str

Filename of the image.

required

Returns:

Name Type Description
str str

Full path to the image file.

Source code in GameFrame/Level.py
276
277
278
279
280
281
282
283
284
285
286
def load_image(self, file_name: str) -> str:
    """
    Returns the full path to an image file in the Images directory.

    Args:
        file_name (str): Filename of the image.

    Returns:
        str: Full path to the image file.
    """
    return os.path.join('Images', file_name)

load_sound(sound_file)

Loads a sound file from the Sounds directory.

Parameters:

Name Type Description Default
sound_file str

Filename of the sound to load.

required

Returns:

Name Type Description
Sound Sound

The loaded pygame Sound object.

Source code in GameFrame/Level.py
257
258
259
260
261
262
263
264
265
266
267
268
def load_sound(self, sound_file: str) -> Sound:
    """
    Loads a sound file from the Sounds directory.

    Args:
        sound_file (str): Filename of the sound to load.

    Returns:
        Sound: The loaded pygame Sound object.
    """
    fq_filename = os.path.join('Sounds', sound_file)
    return pygame.mixer.Sound(fq_filename)

process_user_events()

Processes and triggers any timed user events.

Source code in GameFrame/Level.py
358
359
360
361
362
363
364
365
366
367
def process_user_events(self):
    """
    Processes and triggers any timed user events.
    """
    for index, user_event in self.enumerate_backwards(self.user_events):
        user_event[0] -= 1
        if user_event[0] <= 0:
            event = user_event[1]
            self.user_events.pop(index)
            event()

run()

Runs the main game loop for the level.

Returns:

Name Type Description
bool bool

True if the level is quitting, False otherwise.

Source code in GameFrame/Level.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def run(self) -> bool:
    """
    Runs the main game loop for the level.

    Returns:
        bool: True if the level is quitting, False otherwise.
    """
    self.running = True
    for obj in self.objects:
        self.init_collision_list(obj)

    while self.running:
        self._clock.tick(Globals.FRAMES_PER_SECOND)

        for obj in self.objects:
            obj.prev_x = obj.x
            obj.prev_y = obj.y

        # - Process user events - #
        self.process_user_events()

        # Call Pre step on all objects
        for item in self.objects:
            item.prestep()

        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                self.running = False
                self.quitting = True
                Globals.exiting = True
                pass
            # - Check for mouse click and pass to objects registered for mouse events - #
            if event.type == pygame.MOUSEBUTTONUP:
                mouse_pos = pygame.mouse.get_pos()
                for obj in self.mouse_objects:
                    if obj.rect.collidepoint(mouse_pos):
                        obj.clicked(event.button)

        # -  Check for joystick events and pass  - #
        # - to objects registered for key events - #
        signals = False
        if self.has_buttons_1:
            for i in range(self.joysticks[0].get_numbuttons()):
                self.p1_btns[i] = self.joysticks[0].get_button(i)
                if self.p1_btns[i] == 1:
                    signals = True
        if self.has_hat_1:
            for i in range(self.joysticks[0].get_numaxes()):
                self.p1_btns[-(i+1)] = self.joysticks[0].get_axis(i)
                if self.p1_btns[-(i+1)] > 0 or self.p1_btns[-(i+1)] < 0:
                    signals = True
        if self.has_buttons_2:
            for i in range(self.joysticks[1].get_numbuttons()):
                self.p2_btns[i] = self.joysticks[1].get_button(i)
                if self.p2_btns[i] == 1:
                    signals = True
        if self.has_hat_2:
            for i in range(self.joysticks[1].get_numaxes()):
                self.p1_btns[-i] = self.joysticks[1].get_axis(i)
                if self.p2_btns[-i] > 0 or self.p2_btns[-i] < 0:
                    signals = True

        if signals:
            for obj in self.keyboard_objects:
                obj.joy_pad_signal(self.p1_btns, self.p2_btns)

        # - Check for a keyboard event and pass - #
        # - to objects registered for key events - #
        keys = pygame.key.get_pressed()
        if len(keys):
            for obj in self.keyboard_objects:
                obj.key_pressed(keys)

        # - Check for a mouse event and pass - #
        # - to objects registered for mouse events - #
        (mouse_x, mouse_y) = pygame.mouse.get_pos()
        (button_left, button_middle, button_right) = pygame.mouse.get_pressed()
        for obj in self.mouse_objects:
                obj.mouse_event(mouse_x, mouse_y, button_left, button_middle, button_right)

        # - Handle all other events - #
        self.catch_events(events)

        # - Clear the screen - #
        self.screen.fill(self.background_color)
        # - Add Background if set - #
        if self.background_set:
            # - Scrolling if set - #
            if self.background_scrolling:
                self.background_y += self.background_scroll_speed
                if self.background_y >= Globals.SCREEN_HEIGHT:
                    self.background_y = 0
                self.screen.blit(self.background_image, (0, self.background_y))
                self.screen.blit(self.background_image, (0, self.background_y - Globals.SCREEN_HEIGHT))
            else:
                self.screen.blit(self.background_image, (0, 0))
        # Call Update on all objects
        for item in self.objects:
            item.update()
            item.step()

        # Check collisions
        for item in self.objects:
            item.check_collisions()

        for item in self.objects:
            self.screen.blit(item.image, (item.x, item.y))

        pygame.display.update()

    return self.quitting

set_background_image(image_file)

Sets the background image for the level.

Parameters:

Name Type Description Default
image_file str

Filename of the background image.

required
Source code in GameFrame/Level.py
207
208
209
210
211
212
213
214
215
def set_background_image(self, image_file: str):
    """
    Sets the background image for the level.

    Args:
        image_file (str): Filename of the background image.
    """
    self.background_set = True
    self.background_image = pygame.image.load(os.path.join('Images', image_file)).convert_alpha()

set_background_scroll(speed)

Enables background scrolling at the specified speed.

Parameters:

Name Type Description Default
speed int

The scroll speed in pixels per frame.

required
Source code in GameFrame/Level.py
217
218
219
220
221
222
223
224
225
def set_background_scroll(self, speed: int):
    """
    Enables background scrolling at the specified speed.

    Args:
        speed (int): The scroll speed in pixels per frame.
    """
    self.background_scrolling = True
    self.background_scroll_speed = speed

set_timer(ticks, function_call)

Sets a timed event to call a function after a number of ticks.

Parameters:

Name Type Description Default
ticks int

Number of frames to wait before calling the function.

required
function_call Callable

The function to call.

required
Source code in GameFrame/Level.py
348
349
350
351
352
353
354
355
356
def set_timer(self, ticks: int, function_call: Callable):
    """
    Sets a timed event to call a function after a number of ticks.

    Args:
        ticks (int): Number of frames to wait before calling the function.
        function_call (Callable): The function to call.
    """
    self.user_events.append([ticks, function_call])

stop_sound()

Stops all currently playing sounds.

Source code in GameFrame/Level.py
270
271
272
273
274
def stop_sound(self):
    """
    Stops all currently playing sounds.
    """
    pygame.mixer.stop()