Forráskód Böngészése

calculate_collisions() added to character. _globals was restructured and _events moved to utils.

Zero! Studio 1 éve
szülő
commit
758d1868c7
7 módosított fájl, 122 hozzáadás és 81 törlés
  1. 0 9
      jogai/_events.py
  2. 7 3
      jogai/_globals.py
  3. 68 30
      jogai/character.py
  4. 1 0
      jogai/group.py
  5. 8 0
      jogai/utils.py
  6. 37 36
      jogai/videogame.py
  7. 1 3
      scripts/basics.py

+ 0 - 9
jogai/_events.py

@@ -1,9 +0,0 @@
-import pygame
-
-
-def _get_events() -> list:
-    return pygame.event.get()
-
-
-def _get_keys() -> list:
-    return pygame.key.get_pressed()

+ 7 - 3
jogai/_globals.py

@@ -8,6 +8,10 @@ _default_data = utils._load_yaml_from_filename(
     settings.DEFAULT_DATA_FILENAME, directory=settings.BASE_DIR
 )
 
-_window = pygame.display.set_mode(
-    (settings.WINDOW_WIDTH, settings.WINDOW_HEIGHT)
-)
+_events = []
+_keys = []
+_window = None
+
+vg_scene = None
+vg_characters = []
+vg_groups = []

+ 68 - 30
jogai/character.py

@@ -5,9 +5,9 @@ from typing import Type
 import pygame
 
 import jogai
-from jogai import _events, logger, settings, utils
-from jogai._events import _get_keys
-from jogai._globals import _window
+import jogai._globals as _globals
+from jogai import logger, settings, utils
+from jogai.utils import _get_keys
 from jogai.translations import gettext as _
 
 
@@ -73,7 +73,7 @@ class Character(pygame.sprite.Sprite):
         self.jump_force = settings.DEFAULT_JUMP_FORCE
         self._jump_counter = self.jump_force
         self.angle = 0
-        self.collidable = False
+        self.collidable = True
         self.flipped = {'horizontal': False, 'vertical': False}
 
         super().__init__()
@@ -121,6 +121,46 @@ class Character(pygame.sprite.Sprite):
 
         eval(f'self.{function}({processed_arguments})')
 
+    def _get_all_collidables(self):
+        other_characters = _globals.vg_characters.copy()
+        other_groups = _globals.vg_groups.copy()
+        for other_group in other_groups:
+            other_characters.extend(other_group.sprites())
+        if self in other_characters:
+            other_characters.remove(self)
+        return [other for other in other_characters if other.collidable]
+
+    def calculate_collisions(self, position: tuple | list) -> list:
+        """
+        Calculate all collisions if the character is moved to a position.
+
+        Parameters:
+            position: A tuple with the x and y coordinates.
+
+        Returns:
+            A list of all other collidable characters that collide with the character.
+        """
+        if not isinstance(position, tuple) and not isinstance(position, list):
+            raise TypeError(
+                _(
+                    'The position must be a tuple or a list with the coordinates (x, y).'
+                )
+            )
+        if len(position) != 2 or not all(
+            map(lambda x: isinstance(x, int), position)
+        ):
+            raise ValueError(
+                _(
+                    'The position must be a tuple or a list of integers with the coordinates (x, y).'
+                )
+            )
+        offset_rect = self._update_rect_position(simulate=position)
+        collisions = []
+        for collidable in self._get_all_collidables():
+            if offset_rect.colliderect(collidable):
+                collisions.append(collidable)
+        return collisions
+
     def move(
         self,
         x: int | None = None,
@@ -633,17 +673,6 @@ class Character(pygame.sprite.Sprite):
         new_character.set_position((self.x, self.y))
         return new_character
 
-    def paint(self):
-        """
-        Paint the character in the main surface (window).
-
-        Examples:
-            >>> protagonist = Character('protagonist.png')
-            >>> protagonist.paint()
-        """
-        if self.image:
-            _window.blit(self.image, self.rect)
-
     def _update_jump(self):
         """
         Calculate and update the character position during the jump.
@@ -660,40 +689,49 @@ class Character(pygame.sprite.Sprite):
                 self.is_jumping = False
 
     def _update_rect_position(
-        self, vg_scene: Type['scene.Scene'], tracking: bool = False
-    ):
+        self,
+        tracking: bool = False,
+        simulate: tuple = (),
+    ) -> Type['pygame.Rect']:
         """
         Calculate and update the position where the character must be painted.
         Only one character can be tracked.
 
         Parameters:
-            vg_scene: The scene information.
             tracking: True if the character must be followed, False otherwise.
+
+        Returns:
+            A rect placed in the videogame window.
         """
-        # TODO: check vg_scene type
         if not isinstance(tracking, bool):
             raise TypeError(_('The tracking parameter must be a boolean.'))
 
-        if not vg_scene:
+        if not _globals.vg_scene:
             return None
 
+        if simulate:
+            offset_rect = self.rect.copy()
+            x, y = simulate
+        else:
+            offset_rect = self.rect
+            x, y = self.x, self.y
         if tracking:
             if self.x < settings.WINDOW_WIDTH // 2:
-                self.rect.x = self.x
+                offset_rect.x = x
             elif (
                 settings.WINDOW_WIDTH // 2
-                < self.x
-                < vg_scene.width - settings.WINDOW_WIDTH
+                < x
+                < _globals.vg_scene.width - settings.WINDOW_WIDTH
             ):
-                self.rect.x = settings.WINDOW_WIDTH // 2
-            elif self.x > vg_scene.width - settings.WINDOW_WIDTH:
-                self.rect.x = settings.WINDOW_WIDTH * 3 / 2 - (
-                    -self.x % vg_scene.width
+                offset_rect.x = settings.WINDOW_WIDTH // 2
+            elif x > _globals.vg_scene.width - settings.WINDOW_WIDTH:
+                offset_rect.x = settings.WINDOW_WIDTH * 3 / 2 - (
+                    -x % _globals.vg_scene.width
                 )
-            self.rect.y = self.y
         else:
-            self.rect.x = self.x + vg_scene.x
-            self.rect.y = self.y
+            offset_rect.x = x + _globals.vg_scene.x
+        offset_rect.y = y
+        return offset_rect
 
     def update(self):
         for action, key in self.predefined_keys.items():

+ 1 - 0
jogai/group.py

@@ -184,6 +184,7 @@ class StairsGroup(Group):
         for row in range(2, self.steps + 1):
             for col in range(row):
                 new_character = model.copy()
+                new_character.name = model.name
                 new_character.set_position(
                     (
                         model.x + model.width * col * horizontal_sign,

+ 8 - 0
jogai/utils.py

@@ -93,6 +93,14 @@ def _convert_function_arguments_to_string(arguments: Any) -> str:
     return processed_arguments
 
 
+def _get_events() -> list:
+    return pygame.event.get()
+
+
+def _get_keys() -> list:
+    return pygame.key.get_pressed()
+
+
 def eval_key(key: str) -> int:
     """
     Evaluate a string as a pygame key.

+ 37 - 36
jogai/videogame.py

@@ -5,19 +5,17 @@ from typing import Tuple, Type
 
 import pygame
 
+import jogai
+import jogai._globals as _globals
 import jogai.settings as settings
 from jogai import character, group, logger, scene, utils
-from jogai._events import _get_events, _get_keys
-from jogai._globals import _clock, _default_data, _window
+from jogai.utils import _get_events, _get_keys
 from jogai.translations import gettext as _
 
 # --------------- #
 #    GLOBALS      #
 # --------------- #
 
-vg_scene = None
-vg_characters = []
-vg_groups = []
 running = True
 
 # ------------------ #
@@ -125,7 +123,7 @@ def add_character(new_character: 'character.Character'):
     """
     if not isinstance(new_character, character.Character):
         raise TypeError(_('Only Character instances can be added.'))
-    vg_characters.append(new_character)
+    _globals.vg_characters.append(new_character)
 
 
 def add_group(new_group: 'group.SquareGroup'):
@@ -142,7 +140,7 @@ def add_group(new_group: 'group.SquareGroup'):
     """
     if not isinstance(new_group, group.Group):
         raise TypeError(_('Only Group instances can be added.'))
-    vg_groups.append(new_group)
+    _globals.vg_groups.append(new_group)
 
 
 def init():
@@ -152,10 +150,9 @@ def init():
     Examples:
         >>> init()
     """
-    global _window
     if not pygame.get_init():
         pygame.init()
-    _window = pygame.display.set_mode(
+    _globals._window = pygame.display.set_mode(
         (settings.WINDOW_WIDTH, settings.WINDOW_HEIGHT)
     )
 
@@ -181,10 +178,9 @@ def listen_events():
         >>> simulate_key_press('ESCAPE')
         >>> listen_events()
     """
-    global _events, _keys
-    _events = _get_events()
-    _keys = _get_keys()
-    for event in _events:
+    _globals._events = _get_events()
+    _globals._keys = _get_keys()
+    for event in _globals._events:
         match event.type:
             case pygame.KEYDOWN:
                 if event.key == pygame.K_ESCAPE:
@@ -229,13 +225,13 @@ def paint(sprite: Type['scene.Scene'] | Type['character.Character']):
     """
     # TODO: check sprite type
     if hasattr(sprite, 'background'):
-        _window.fill(sprite.background)
+        _globals._window.fill(sprite.background)
     if sprite.image and sprite.rect:
-        _window.blit(sprite.image, sprite.rect)
+        _globals._window.blit(sprite.image, sprite.rect)
 
 
 def paint_group(group: Type['group.SquareGroup']):
-    group.draw(_window)
+    group.draw(_globals._window)
 
 
 def prepare(data_filename: str = ''):
@@ -246,19 +242,20 @@ def prepare(data_filename: str = ''):
         >>> init()
         >>> prepare()
     """
-    global vg_scene, vg_characters
+    if not _globals._window:
+        init()
     pygame.key.set_repeat(settings.KEY_DELAY, settings.KEY_INTERVAL)
     if data_filename:
         vg_data = utils._load_yaml_from_filename(data_filename)
     else:
         vg_data = _default_data
 
-    vg_scene = _load_scene(vg_data)
-    vg_characters = _load_characters(vg_data)
+    _globals.vg_scene = _load_scene(vg_data)
+    _globals.vg_characters = _load_characters(vg_data)
 
     # TODO: Check and filter variable names added to builtins
-    setattr(builtins, 'vgscene', vg_scene)
-    for vg_character in vg_characters:
+    setattr(builtins, 'vgscene', _globals.vg_scene)
+    for vg_character in _globals.vg_characters:
         setattr(builtins, vg_character.name, vg_character)
 
 
@@ -274,26 +271,30 @@ def update():
     """
     Refresh the display surface and listen events.
     """
-    global _clock, _window, vg_scene, vg_characters, vg_groups
-    if vg_scene:
-        if vg_characters:
-            vg_scene.set_position(vg_characters[0].x)
-        paint(vg_scene)
-    if vg_characters:
-        for pos, vg_character in enumerate(vg_characters):
-            vg_character._update_rect_position(
-                vg_scene, False if pos else True
-            )
+    if _globals.vg_scene:
+        if _globals.vg_characters:
+            _globals.vg_scene.set_position(_globals.vg_characters[0].x)
+        paint(_globals.vg_scene)
+    if _globals.vg_characters:
+        for pos, vg_character in enumerate(_globals.vg_characters):
+            if vg_character.collidable:
+                rest_of_characters = _globals.vg_characters.copy()
+                rest_of_characters.remove(vg_character)
+                for other_character in rest_of_characters:
+                    if other_character.collidable:
+                        vg_character.touch(other_character)
+
+            vg_character._update_rect_position(False if pos else True)
             paint(vg_character)
             vg_character.update()
-    if vg_groups:
-        for g in vg_groups:
+    if _globals.vg_groups:
+        for g in _globals.vg_groups:
             for e in g.sprites():
-                e._update_rect_position(vg_scene)
-                e.paint()
+                e._update_rect_position()
+                paint(e)
 
     pygame.display.flip()
-    _clock.tick(settings.FPS)
+    _globals._clock.tick(settings.FPS)
     listen_events()
 
 

+ 1 - 3
scripts/basics.py

@@ -2,12 +2,10 @@ from jogai import *
 
 
 def test_example01():
-    def move():
-        ...
-
     prepare('02.yaml')
     sq = StairsGroup(brick, 5)
     add_group(sq)
 
     while is_running():
         update()
+        print(protagonist._calculate_collisions(0, 0))