Skip to content

Commit

Permalink
Merge branch 'Corderius-College-Amersfoort:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
MartenPostma authored Dec 12, 2024
2 parents 35a9995 + 08ea652 commit febce31
Show file tree
Hide file tree
Showing 12 changed files with 341 additions and 160 deletions.
37 changes: 36 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,43 @@ permissions:
contents: read

jobs:
deploy:
build-pages:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pdoc
- run: pdoc ./play -o docs

- uses: actions/upload-pages-artifact@v3
with:
path: docs/

# Deploy the artifact to GitHub pages.
# This is a separate job so that only actions/deploy-pages has the necessary permissions.
deploy-pages:
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build-pages
runs-on: ubuntu-latest
steps:
- id: deployment
uses: actions/deploy-pages@v4

deploy:
needs: deploy-pages
runs-on: ubuntu-latest

steps:
Expand Down
10 changes: 0 additions & 10 deletions play/api/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,6 @@ async def wrapper():
return func


def repeat(number_of_times):
"""
Repeat a set of commands a certain number of times.
Equivalent to `range(1, number_of_times+1)`.
:param number_of_times: The number of times to repeat the commands.
:return: A range object that can be iterated over.
"""
return range(1, number_of_times + 1)


# @decorator
def repeat_forever(func):
"""
Expand Down
9 changes: 6 additions & 3 deletions play/callback/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ class CallbackType(Enum):
WHEN_CLICK_RELEASED = 5
WHEN_CLICKED_SPRITE = 6
WHEN_TOUCHING = 7
WHEN_CONTROLLER_BUTTON_PRESSED = 8
WHEN_CONTROLLER_BUTTON_RELEASED = 9
WHEN_CONTROLLER_AXIS_MOVED = 10
WHEN_STOPPED_TOUCHING = 8
WHEN_TOUCHING_WALL = 9
WHEN_STOPPED_TOUCHING_WALL = 10
WHEN_CONTROLLER_BUTTON_PRESSED = 11
WHEN_CONTROLLER_BUTTON_RELEASED = 12
WHEN_CONTROLLER_AXIS_MOVED = 13


class CallbackManager:
Expand Down
134 changes: 12 additions & 122 deletions play/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""Core game loop and event handling functions."""

import math as _math

import pygame # pylint: disable=import-error

from .game_loop_wrapper import listen_to_failure
from .mouse_loop import _handle_mouse_loop, mouse_state
from .sprites_loop import _update_sprites
from ..callback import callback_manager, CallbackType
from ..callback.callback_helpers import run_callback
from ..globals import backdrop, FRAME_RATE, sprites_group
from ..globals import backdrop, FRAME_RATE, _walls
from ..io import screen, PYGAME_DISPLAY, convert_pos
from ..io.keypress import (
key_num_to_name as _pygame_key_to_name,
Expand All @@ -16,9 +16,7 @@
_pressed_keys,
) # don't pollute user-facing namespace with library internals
from ..io.mouse import mouse
from ..objects.line import Line
from ..objects.sprite import point_touching_sprite
from ..physics import simulate_physics
from .physics_loop import simulate_physics
from ..utils import color_name_to_rgb as _color_name_to_rgb
from ..loop import loop as _loop
from .controller_loop import (
Expand All @@ -31,15 +29,9 @@

_clock = pygame.time.Clock()

click_happened_this_frame = False # pylint: disable=invalid-name
click_release_happened_this_frame = False # pylint: disable=invalid-name


def _handle_pygame_events():
"""Handle pygame events in the game loop."""
global click_happened_this_frame
global click_release_happened_this_frame

for event in pygame.event.get():
if event.type == pygame.QUIT or ( # pylint: disable=no-member
event.type == pygame.KEYDOWN # pylint: disable=no-member
Expand All @@ -53,10 +45,10 @@ def _handle_pygame_events():
_loop.stop()
return False
if event.type == pygame.MOUSEBUTTONDOWN: # pylint: disable=no-member
click_happened_this_frame = True
mouse_state.click_happened_this_frame = True
mouse._is_clicked = True
if event.type == pygame.MOUSEBUTTONUP: # pylint: disable=no-member
click_release_happened_this_frame = True
mouse_state.click_release_happened_this_frame = True
mouse._is_clicked = False
if event.type == pygame.MOUSEMOTION: # pylint: disable=no-member
mouse.x, mouse.y = (event.pos[0] - screen.width / 2.0), (
Expand Down Expand Up @@ -147,118 +139,13 @@ def _handle_keyboard():
)


def _handle_mouse_loop():
"""Handle mouse events in the game loop."""
####################################
# @mouse.when_clicked callbacks
####################################
if (
click_happened_this_frame
and callback_manager.get_callbacks(CallbackType.WHEN_CLICKED) is not None
):
for callback in callback_manager.get_callbacks(CallbackType.WHEN_CLICKED):
run_callback(
callback,
[],
[],
)

########################################
# @mouse.when_click_released callbacks
########################################
if (
click_release_happened_this_frame
and callback_manager.get_callbacks(CallbackType.WHEN_CLICK_RELEASED) is not None
):
for callback in callback_manager.get_callbacks(
CallbackType.WHEN_CLICK_RELEASED
):
run_callback(
callback,
[],
[],
)


def _update_sprites():
# pylint: disable=too-many-nested-blocks
sprites_group.update()
for sprite in sprites_group.sprites():
sprite._is_clicked = False
if sprite.is_hidden:
continue

######################################################
# update sprites with results of physics simulation
######################################################
if sprite.physics and sprite.physics.can_move:

body = sprite.physics._pymunk_body
angle = _math.degrees(body.angle)
if isinstance(sprite, Line):
sprite._x = body.position.x - (sprite.length / 2) * _math.cos(angle)
sprite._y = body.position.y - (sprite.length / 2) * _math.sin(angle)
sprite._x1 = body.position.x + (sprite.length / 2) * _math.cos(angle)
sprite._y1 = body.position.y + (sprite.length / 2) * _math.sin(angle)
# sprite._length, sprite._angle = sprite._calc_length_angle()
else:
if (
str(body.position.x) != "nan"
): # this condition can happen when changing sprite.physics.can_move
sprite._x = body.position.x
if str(body.position.y) != "nan":
sprite._y = body.position.y

sprite.angle = (
angle # needs to be .angle, not ._angle so surface gets recalculated
)
sprite.physics._x_speed, sprite.physics._y_speed = body.velocity

#################################
# @sprite.when_clicked events
#################################
if mouse.is_clicked:
if (
point_touching_sprite(convert_pos(mouse.x, mouse.y), sprite)
and click_happened_this_frame
):
# only run sprite clicks on the frame the mouse was clicked
sprite._is_clicked = True
if callback_manager.get_callback(
CallbackType.WHEN_CLICKED_SPRITE, id(sprite)
):
for callback in callback_manager.get_callback(
CallbackType.WHEN_CLICKED_SPRITE, id(sprite)
):
if not callback.is_running:
run_callback(
callback,
[],
[],
)

#################################
# @sprite.when_touching events
#################################
if sprite._active_callbacks:
for cb in sprite._active_callbacks:
run_callback(
cb,
[],
[],
)

sprites_group.draw(PYGAME_DISPLAY)


# pylint: disable=too-many-branches, too-many-statements
@listen_to_failure()
def game_loop():
"""The main game loop."""
_keys_released_this_frame.clear()
global click_happened_this_frame, click_release_happened_this_frame
click_happened_this_frame = False
click_release_happened_this_frame = False
mouse_state.click_happened_this_frame = False
mouse_state.click_release_happened_this_frame = False

_clock.tick(FRAME_RATE)

Expand All @@ -267,7 +154,10 @@ def game_loop():

_handle_keyboard()

if click_happened_this_frame or click_release_happened_this_frame:
if (
mouse_state.click_happened_this_frame
or mouse_state.click_release_happened_this_frame
):
_handle_mouse_loop()

_handle_controller()
Expand Down
45 changes: 45 additions & 0 deletions play/core/mouse_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""This module contains the mouse loop."""

from ..callback.callback_helpers import run_callback
from ..callback import callback_manager, CallbackType


class MouseState: # pylint: disable=too-few-public-methods
click_happened_this_frame = False # pylint: disable=invalid-name
click_release_happened_this_frame = False # pylint: disable=invalid-name


mouse_state = MouseState()


def _handle_mouse_loop():
"""Handle mouse events in the game loop."""
####################################
# @mouse.when_clicked callbacks
####################################
if (
mouse_state.click_happened_this_frame
and callback_manager.get_callbacks(CallbackType.WHEN_CLICKED) is not None
):
for callback in callback_manager.get_callbacks(CallbackType.WHEN_CLICKED):
run_callback(
callback,
[],
[],
)

########################################
# @mouse.when_click_released callbacks
########################################
if (
mouse_state.click_release_happened_this_frame
and callback_manager.get_callbacks(CallbackType.WHEN_CLICK_RELEASED) is not None
):
for callback in callback_manager.get_callbacks(
CallbackType.WHEN_CLICK_RELEASED
):
run_callback(
callback,
[],
[],
)
16 changes: 16 additions & 0 deletions play/core/physics_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""This module contains the function that simulates the physics of the game"""

from ..globals import FRAME_RATE
from ..physics import physics_space, _NUM_SIMULATION_STEPS
from .sprites_loop import _update_sprites


def simulate_physics():
"""
Simulate the physics of the game
"""
# more steps means more accurate simulation, but more processing time
for _ in range(_NUM_SIMULATION_STEPS):
# the smaller the simulation step, the more accurate the simulation
physics_space.step(1 / (FRAME_RATE * _NUM_SIMULATION_STEPS))
_update_sprites(True)
Loading

0 comments on commit febce31

Please sign in to comment.