Browse Source

docstrings added and settings reorganized

Zero! Studio 1 năm trước cách đây
mục cha
commit
2566e4797e

+ 1 - 0
data/02.yaml

@@ -4,6 +4,7 @@ scene: test_large.png
 characters:
   protagonist:
     gravitative: True
+    chronometer: 30
     score: 10
     lives: 5
     speed: 20

+ 1 - 0
docs/api/character.md

@@ -0,0 +1 @@
+::: character

+ 1 - 0
docs/api/group.md

@@ -0,0 +1 @@
+::: group

+ 1 - 0
docs/api/scene.md

@@ -0,0 +1 @@
+::: scene

+ 1 - 0
docs/api/utils.md

@@ -0,0 +1 @@
+::: utils

+ 1 - 0
docs/api/videogame.md

@@ -0,0 +1 @@
+::: videogame

+ 1 - 0
docs/api/widgets.md

@@ -0,0 +1 @@
+::: widgets

+ 2 - 1
jogai/_globals.py

@@ -23,4 +23,5 @@ vg_characters = []
 vg_groups = []
 vg_levelbars = []
 vg_scoreboards = []
-vg_widgets = []
+vg_texts = []
+vg_chronometers = []

+ 28 - 2
jogai/character.py

@@ -40,6 +40,7 @@ class Character(pygame.sprite.Sprite):
 
     _predefined_attributes_allowed = [
         _('collidable'),
+        _('chronometer'),
         _('gravitative'),
         _('height'),
         _('keys'),
@@ -79,6 +80,7 @@ class Character(pygame.sprite.Sprite):
         self.flipped = {'horizontal': False, 'vertical': False}
         self.score = None
         self.lives = None
+        self.chronometer = None
 
         super().__init__()
         self.load(image_filename)
@@ -495,6 +497,29 @@ class Character(pygame.sprite.Sprite):
             self._original_image, self.angle, self.scale_factor
         )
 
+    def set_chronometer(self, time: int):
+        """
+        Set a chronometer for this character.
+
+        Parameters:
+            time: Initial time to countdown. If zero, progressive count.
+
+        Examples:
+            >>> protagonist = Character('protagonist.png')
+            >>> protagonist.set_chronometer(30)
+        """
+        chronometer_pos = len(_globals.vg_chronometers)
+        self.chronometer = widgets.Chronometer(
+            time,
+            position=(
+                settings.CHRONOMETERS_DEFAULT_POSITION[0],
+                settings.CHRONOMETERS_DEFAULT_POSITION[1]
+                - chronometer_pos * settings.CHRONOMETERS_DEFAULT_HEIGHT,
+            ),
+        )
+        self.chronometer.start()
+        _globals.vg_chronometers.append(self.chronometer)
+
     def set_collidable(self, collidable: bool = True):
         """
         Set the character collidable.
@@ -619,8 +644,9 @@ class Character(pygame.sprite.Sprite):
                 f'{self.name} ',
                 score=score,
                 position=(
-                    settings.SCOREBOARD_POSITION[0],
-                    settings.SCOREBOARD_POSITION[1] + scoreboard_pos * 50,
+                    settings.SCOREBOARDS_DEFAULT_POSITION[0],
+                    settings.SCOREBOARDS_DEFAULT_POSITION[1]
+                    + scoreboard_pos * 50,
                 ),
             )
             _globals.vg_scoreboards.append(self.score)

+ 14 - 2
jogai/settings.py

@@ -28,14 +28,26 @@ DATA_DIR = _('data')
 # DEFAULT CHARACTER FEATURES
 DEFAULT_JUMP_FORCE = 10.0
 
+# ------- #
+# WIDGETS #
+# ------- #
+
+# CHRONOMETERS
+CHRONOMETERS_DEFAULT_HEIGHT = 60
+CHRONOMETERS_DEFAULT_WIDTH = 90
+CHRONOMETERS_DEFAULT_POSITION = (
+    WINDOW_WIDTH - CHRONOMETERS_DEFAULT_WIDTH - 20,
+    WINDOW_HEIGHT - CHRONOMETERS_DEFAULT_HEIGHT - 20,
+)
+
 # SCOREBOARDS
-SCOREBOARD_POSITION = (20, 20)
+SCOREBOARDS_DEFAULT_POSITION = (20, 20)
 
 # LEVELBARS
 LEVELBARS_MAX_PARTS = 25
 LEVELBARS_DEFAULT_WIDTH = 180
 LEVELBARS_DEFAULT_HEIGHT = 20
 LEVELBARS_DEFAULT_THICK = 3
-LEVELBARS_DEFAULT_POSITION = (WINDOW_WIDTH - LEVELBARS_DEFAULT_WIDTH - 20, 25)
 LEVELBARS_DEFAULT_BORDER_COLOR = (200, 0, 0)
 LEVELBARS_DEFAULT_FILL_COLOR = (241, 74, 6)
+LEVELBARS_DEFAULT_POSITION = (WINDOW_WIDTH - LEVELBARS_DEFAULT_WIDTH - 20, 25)

+ 42 - 8
jogai/videogame.py

@@ -26,9 +26,6 @@ _predefined_attributes_allowed = [
 #    FUNCTIONS       #
 # ------------------ #
 
-# TODO:
-# Nome do videojogo -> set_caption
-
 
 def _load_scene(data: dict) -> 'scene.Scene':
     """
@@ -148,6 +145,22 @@ def add_character(new_character: 'character.Character'):
     _globals.vg_characters.append(new_character)
 
 
+def add_chronometer(new_chronometer: 'widgets.Chronometer'):
+    """
+    Add a new chronometer to the videogame.
+
+    Parameters:
+        new_chronometer: the chronometer to be added
+
+    Examples:
+        >>> chrono = widgets.Chronometer(30)
+        >>> add_chronometer(chrono) # doctest: +SKIP
+    """
+    if not isinstance(new_chronometer, widgets.Chronometer):
+        raise TypeError(_('Only Chronometer instances can be added.'))
+    _globals.vg_chronometers.append(new_chronometer)
+
+
 def add_group(new_group: 'group.SquareGroup'):
     """
     Add a new group to the videogame.
@@ -210,7 +223,7 @@ def add_text(new_text: 'widgets.Text'):
     """
     if not isinstance(new_text, widgets.Text):
         raise TypeError(_('Only Text instances can be added.'))
-    _globals.vg_widgets.append(new_text)
+    _globals.vg_texts.append(new_text)
 
 
 def init():
@@ -293,7 +306,7 @@ def paint(
     | Type['widgets.Text'],
 ):
     """
-    Paint both a scene and a character.
+    Paint multiple sprite types on the main window.
 
     Parameters:
         sprite: The element to be painted.
@@ -306,6 +319,12 @@ def paint(
 
 
 def paint_group(group: Type['group.SquareGroup']):
+    """
+    Paint a group of sprites on the main window.
+
+    Parameters:
+        group: The group of sprites to be painted.
+    """
     group.draw(_globals._window)
 
 
@@ -347,6 +366,17 @@ def set_marks(
     width: int = 1,
     debug: bool = False,
 ):
+    """
+    Set distance mark lines and position information along both axis.
+
+    Parameters:
+        x_gap: The gap distance on abscissa axis.
+        y_gap: The gap distance on ordinate axis.
+        x_color: The line color on abscissa axis.
+        y_color: The line color on ordinate axis.
+        width: The line width.
+        debug: If True, extra detailed information is shown.
+    """
     _globals.marks = utils.Marks(x_gap, y_gap, x_color, y_color, width, debug)
 
 
@@ -403,6 +433,10 @@ def update():
             vg_character._update_rect_position(tracking=tracking)
             paint(vg_character)
             vg_character.update()
+    if _globals.vg_chronometers:
+        for chronometers_item in _globals.vg_chronometers:
+            paint(chronometers_item)
+            chronometers_item.update()
     if _globals.vg_groups:
         for group_item in _globals.vg_groups:
             for sprite_item in group_item.sprites():
@@ -415,9 +449,9 @@ def update():
     if _globals.vg_scoreboards:
         for scoreboard_item in _globals.vg_scoreboards:
             paint(scoreboard_item)
-    if _globals.vg_widgets:
-        for widgets_item in _globals.vg_widgets:
-            paint(widgets_item)
+    if _globals.vg_texts:
+        for texts_item in _globals.vg_texts:
+            paint(texts_item)
     if _globals.marks:
         _globals.marks.draw()
     pygame.display.flip()

+ 64 - 2
jogai/widgets.py

@@ -119,7 +119,32 @@ class Chronometer(Text):
     def onpause(self) -> bool:
         return self._onpause
 
-    def format_clock(self, include_millis: bool = False) -> str:
+    def format_clock(
+        self,
+        include_millis: bool = False,
+        delimiter: str = ':',
+        delimiter_millis: str = '.',
+    ) -> str:
+        """
+        Create a string with the chronometer time information, formatted as a digital clock.
+
+        Parameters:
+            include_millis: True if milliseconds must be present. False, otherwise.
+            delimiter: The string delimiter between hours, minutes and seconds.
+            delimiter_millis: The string delimiter between seconds and milliseconds.
+
+        Returns:
+            A string with the formatted time information.
+        """
+        if not isinstance(include_millis, bool):
+            raise TypeError(_('include_millis must be a boolean.'))
+        if not isinstance(delimiter, str):
+            raise TypeError(_('The delimiter must be a string.'))
+        if not isinstance(delimiter_millis, str):
+            raise TypeError(
+                _('The delimiter for milliseconds must be a string.')
+            )
+
         formatted_time = tuple(
             map(lambda x: str(x).zfill(2), self.get_clock())
         )
@@ -129,6 +154,12 @@ class Chronometer(Text):
         return formatted_clock
 
     def get_clock(self) -> tuple:
+        """
+        Get the current chronometer time information.
+
+        Returns:
+            A tuple with the time information (hours, minutes, seconds, milliseconds)
+        """
         millis = self.get_milliseconds()
         if millis > 0:
             quotient = millis // 1000
@@ -141,6 +172,12 @@ class Chronometer(Text):
         return 0, 0, 0, 0
 
     def get_milliseconds(self) -> int:
+        """
+        Get the current chronometer time information in milliseconds.
+
+        Returns:
+            An integer with the current time in milliseconds.
+        """
         if self._onpause:
             current = (
                 self._initial - self._time if self._initial else self._time
@@ -159,25 +196,50 @@ class Chronometer(Text):
         return current
 
     def get_seconds(self) -> int:
+        """
+        Get the current chronometer time information in seconds.
+
+        Returns:
+            An integer with the current time in seconds.
+        """
         return self.get_milliseconds() // 1000
 
-    def init(self):
+    def start(self):
+        """
+        Start the chronometer count.
+        """
         self._time = 0
         self._timestamp = pygame.time.get_ticks()
         self._onpause = False
 
     def pause(self):
+        """
+        Pause the chronometer count.
+        """
         if not self._onpause:
             self._onpause = True
             self._time = self._time + pygame.time.get_ticks() - self._timestamp
 
+    def reset(self):
+        """
+        Reset the chronometer count.
+        """
+        self._time = 0
+
     def resume(self):
+        """
+        Resume the chronometer count.
+        """
         if self._onpause:
             self._onpause = False
             self._timestamp = pygame.time.get_ticks()
 
     def update(self):
+        """
+        Update the text message with the current time information.
+        """
         self._message = self.format_clock()
+        self.render()
 
 
 class LevelBar(pygame.Surface):

+ 0 - 4
scripts/basics.py

@@ -6,10 +6,6 @@ def test_example01():
     prepare('02.yaml')
     sq = StairsGroup(brick, 5)
     add_group(sq)
-    crono = Chronometer(5)
-    add_text(crono)
-    crono.init()
 
     while is_running():
         update()
-        print(crono)