Browse Source

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

Zero! Studio 1 year ago
parent
commit
758d1868c7
7 changed files with 122 additions and 81 deletions
  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
     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 pygame
 
 
 import jogai
 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 _
 from jogai.translations import gettext as _
 
 
 
 
@@ -73,7 +73,7 @@ class Character(pygame.sprite.Sprite):
         self.jump_force = settings.DEFAULT_JUMP_FORCE
         self.jump_force = settings.DEFAULT_JUMP_FORCE
         self._jump_counter = self.jump_force
         self._jump_counter = self.jump_force
         self.angle = 0
         self.angle = 0
-        self.collidable = False
+        self.collidable = True
         self.flipped = {'horizontal': False, 'vertical': False}
         self.flipped = {'horizontal': False, 'vertical': False}
 
 
         super().__init__()
         super().__init__()
@@ -121,6 +121,46 @@ class Character(pygame.sprite.Sprite):
 
 
         eval(f'self.{function}({processed_arguments})')
         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(
     def move(
         self,
         self,
         x: int | None = None,
         x: int | None = None,
@@ -633,17 +673,6 @@ class Character(pygame.sprite.Sprite):
         new_character.set_position((self.x, self.y))
         new_character.set_position((self.x, self.y))
         return new_character
         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):
     def _update_jump(self):
         """
         """
         Calculate and update the character position during the jump.
         Calculate and update the character position during the jump.
@@ -660,40 +689,49 @@ class Character(pygame.sprite.Sprite):
                 self.is_jumping = False
                 self.is_jumping = False
 
 
     def _update_rect_position(
     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.
         Calculate and update the position where the character must be painted.
         Only one character can be tracked.
         Only one character can be tracked.
 
 
         Parameters:
         Parameters:
-            vg_scene: The scene information.
             tracking: True if the character must be followed, False otherwise.
             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):
         if not isinstance(tracking, bool):
             raise TypeError(_('The tracking parameter must be a boolean.'))
             raise TypeError(_('The tracking parameter must be a boolean.'))
 
 
-        if not vg_scene:
+        if not _globals.vg_scene:
             return None
             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 tracking:
             if self.x < settings.WINDOW_WIDTH // 2:
             if self.x < settings.WINDOW_WIDTH // 2:
-                self.rect.x = self.x
+                offset_rect.x = x
             elif (
             elif (
                 settings.WINDOW_WIDTH // 2
                 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:
         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):
     def update(self):
         for action, key in self.predefined_keys.items():
         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 row in range(2, self.steps + 1):
             for col in range(row):
             for col in range(row):
                 new_character = model.copy()
                 new_character = model.copy()
+                new_character.name = model.name
                 new_character.set_position(
                 new_character.set_position(
                     (
                     (
                         model.x + model.width * col * horizontal_sign,
                         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
     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:
 def eval_key(key: str) -> int:
     """
     """
     Evaluate a string as a pygame key.
     Evaluate a string as a pygame key.

+ 37 - 36
jogai/videogame.py

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

+ 1 - 3
scripts/basics.py

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