mini-arcade-core 0.7.2__py3-none-any.whl → 0.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,10 @@ def run_game(initial_scene_cls: type[Scene], config: GameConfig | None = None):
21
21
  Convenience helper to bootstrap and run a game with a single scene.
22
22
 
23
23
  :param initial_scene_cls: The Scene subclass to instantiate as the initial scene.
24
+ :type initial_scene_cls: type[Scene]
25
+
24
26
  :param config: Optional GameConfig to customize game settings.
27
+ :type config: GameConfig | None
25
28
  """
26
29
  game = Game(config or GameConfig())
27
30
  scene = initial_scene_cls(game)
@@ -11,7 +11,14 @@ from typing import Iterable, Protocol
11
11
 
12
12
 
13
13
  class EventType(Enum):
14
- """High-level event types understood by the core."""
14
+ """
15
+ High-level event types understood by the core.
16
+
17
+ :cvar UNKNOWN: Unknown/unhandled event.
18
+ :cvar QUIT: User requested to quit the game.
19
+ :cvar KEYDOWN: A key was pressed.
20
+ :cvar KEYUP: A key was released.
21
+ """
15
22
 
16
23
  UNKNOWN = auto()
17
24
  QUIT = auto()
@@ -46,20 +53,39 @@ class Backend(Protocol):
46
53
  def init(self, width: int, height: int, title: str) -> None:
47
54
  """
48
55
  Initialize the backend and open a window.
49
-
50
56
  Should be called once before the main loop.
57
+
58
+ :param width: Width of the window in pixels.
59
+ :type width: int
60
+
61
+ :param height: Height of the window in pixels.
62
+ :type height: int
63
+
64
+ :param title: Title of the window.
65
+ :type title: str
51
66
  """
52
67
 
53
68
  def poll_events(self) -> Iterable[Event]:
54
69
  """
55
70
  Return all pending events since last call.
56
-
57
71
  Concrete backends will translate their native events into core Event objects.
72
+
73
+ :return: An iterable of Event objects.
74
+ :rtype: Iterable[Event]
58
75
  """
59
76
 
60
77
  def set_clear_color(self, r: int, g: int, b: int) -> None:
61
78
  """
62
79
  Set the background/clear color used by begin_frame.
80
+
81
+ :param r: Red component (0-255).
82
+ :type r: int
83
+
84
+ :param g: Green component (0-255).
85
+ :type g: int
86
+
87
+ :param b: Blue component (0-255).
88
+ :type b: int
63
89
  """
64
90
 
65
91
  def begin_frame(self) -> None:
@@ -84,8 +110,22 @@ class Backend(Protocol):
84
110
  ) -> None:
85
111
  """
86
112
  Draw a filled rectangle in some default color.
87
-
88
113
  We'll keep this minimal for now; later we can extend with colors/sprites.
114
+
115
+ :param x: X position of the rectangle's top-left corner.
116
+ :type x: int
117
+
118
+ :param y: Y position of the rectangle's top-left corner.
119
+ :type y: int
120
+
121
+ :param w: Width of the rectangle.
122
+ :type w: int
123
+
124
+ :param h: Height of the rectangle.
125
+ :type h: int
126
+
127
+ :param color: RGB color tuple.
128
+ :type color: tuple[int, int, int]
89
129
  """
90
130
 
91
131
  # pylint: enable=too-many-arguments,too-many-positional-arguments
@@ -102,6 +142,18 @@ class Backend(Protocol):
102
142
 
103
143
  Backends may ignore advanced styling for now; this is just to render
104
144
  simple labels like menu items, scores, etc.
145
+
146
+ :param x: X position of the text's top-left corner.
147
+ :type x: int
148
+
149
+ :param y: Y position of the text's top-left corner.
150
+ :type y: int
151
+
152
+ :param text: The text string to draw.
153
+ :type text: str
154
+
155
+ :param color: RGB color tuple.
156
+ :type color: tuple[int, int, int]
105
157
  """
106
158
 
107
159
  def capture_frame(self, path: str | None = None) -> bytes | None:
@@ -109,5 +161,11 @@ class Backend(Protocol):
109
161
  Capture the current frame.
110
162
  If `path` is provided, save to that file (e.g. PNG).
111
163
  Returns raw bytes (PNG) or None if unsupported.
164
+
165
+ :param path: Optional file path to save the screenshot.
166
+ :type path: str | None
167
+
168
+ :return: Raw image bytes if no path given, else None.
169
+ :rtype: bytes | None
112
170
  """
113
171
  raise NotImplementedError
mini_arcade_core/game.py CHANGED
@@ -7,9 +7,12 @@ from __future__ import annotations
7
7
  import os
8
8
  from dataclasses import dataclass
9
9
  from datetime import datetime
10
+ from pathlib import Path
10
11
  from time import perf_counter, sleep
11
12
  from typing import TYPE_CHECKING
12
13
 
14
+ from PIL import Image # type: ignore[import]
15
+
13
16
  from .backend import Backend
14
17
 
15
18
  if TYPE_CHECKING: # avoid runtime circular import
@@ -119,18 +122,50 @@ class Game:
119
122
  if self._current_scene is not None:
120
123
  self._current_scene.on_exit()
121
124
 
125
+ @staticmethod
126
+ def _convert_bmp_to_image(bmp_path: str, out_path: str) -> bool:
127
+ """
128
+ Convert a BMP file to another image format using Pillow.
129
+
130
+ :param bmp_path: Path to the input BMP file.
131
+ :type bmp_path: str
132
+
133
+ :param out_path: Path to the output image file.
134
+ :type out_path: str
135
+
136
+ :return: True if conversion was successful, False otherwise.
137
+ :rtype: bool
138
+ """
139
+ try:
140
+ img = Image.open(bmp_path)
141
+ img.save(out_path) # Pillow chooses format from extension
142
+ return True
143
+ except Exception:
144
+ return False
145
+
122
146
  def screenshot(
123
147
  self, label: str | None = None, directory: str = "screenshots"
124
148
  ) -> str | None:
125
149
  """
126
150
  Ask backend to save a screenshot. Returns the file path or None.
151
+
152
+ :param label: Optional label to include in the filename.
153
+ :type label: str | None
154
+
155
+ :param directory: Directory to save screenshots in.
156
+ :type directory: str
157
+
158
+ :return: The file path of the saved screenshot, or None on failure.
159
+ :rtype: str | None
127
160
  """
128
161
  os.makedirs(directory, exist_ok=True)
129
162
  stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
130
163
  label = label or "shot"
131
- filename = f"{stamp}_{label}.png"
132
- path = os.path.join(directory, filename)
164
+ filename = f"{stamp}_{label}"
165
+ bmp_path = os.path.join(directory, f"{filename}.bmp")
133
166
 
134
- if self.backend.capture_frame(path):
135
- return path
167
+ if self.backend.capture_frame(bmp_path):
168
+ out_path = Path(directory) / f"{filename}.png"
169
+ self._convert_bmp_to_image(bmp_path, str(out_path))
170
+ return str(out_path)
136
171
  return None
mini_arcade_core/scene.py CHANGED
@@ -27,18 +27,35 @@ class Scene(ABC):
27
27
  self._overlays: List[OverlayFunc] = []
28
28
 
29
29
  def add_overlay(self, overlay: OverlayFunc) -> None:
30
- """Register an overlay (drawn every frame, after entities)."""
30
+ """
31
+ Register an overlay (drawn every frame, after entities).
32
+
33
+ :param overlay: A callable that takes a Backend and draws on it.
34
+ :type overlay: OverlayFunc
35
+ """
31
36
  self._overlays.append(overlay)
32
37
 
33
38
  def remove_overlay(self, overlay: OverlayFunc) -> None:
39
+ """
40
+ Unregister a previously added overlay.
41
+
42
+ :param overlay: The overlay to remove.
43
+ :type overlay: OverlayFunc
44
+ """
34
45
  if overlay in self._overlays:
35
46
  self._overlays.remove(overlay)
36
47
 
37
48
  def clear_overlays(self) -> None:
49
+ """Clear all registered overlays."""
38
50
  self._overlays.clear()
39
51
 
40
52
  def draw_overlays(self, surface: Backend) -> None:
41
- """Call all overlays. Scenes should call this at the end of draw()."""
53
+ """
54
+ Call all overlays. Scenes should call this at the end of draw().
55
+
56
+ :param surface: The backend surface to draw on.
57
+ :type surface: Backend
58
+ """
42
59
  for overlay in self._overlays:
43
60
  overlay(surface)
44
61
 
@@ -52,18 +69,27 @@ class Scene(ABC):
52
69
 
53
70
  @abstractmethod
54
71
  def handle_event(self, event: object):
55
- """Handle input / events (e.g. pygame.Event)."""
72
+ """
73
+ Handle input / events (e.g. pygame.Event).
74
+
75
+ :param event: The event to handle.
76
+ :type event: object
77
+ """
56
78
 
57
79
  @abstractmethod
58
80
  def update(self, dt: float):
59
- """Update game logic. ``dt`` is the delta time in seconds."""
81
+ """
82
+ Update game logic. ``dt`` is the delta time in seconds.
60
83
 
61
- @abstractmethod
62
- def draw(self, surface: object):
63
- """Render to the main surface."""
84
+ :param dt: Time delta in seconds.
85
+ :type dt: float
86
+ """
64
87
 
88
+ @abstractmethod
65
89
  def draw(self, surface: object):
66
- """Render to the main surface."""
90
+ """
91
+ Render to the main surface.
67
92
 
68
- def draw(self, surface: object):
69
- """Render to the main surface."""
93
+ :param surface: The backend surface to draw on.
94
+ :type surface: object
95
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mini-arcade-core
3
- Version: 0.7.2
3
+ Version: 0.7.4
4
4
  Summary: Tiny scene-based game loop core for small arcade games.
5
5
  License: Copyright (c) 2025 Santiago Rincón
6
6
 
@@ -34,6 +34,7 @@ Provides-Extra: dev
34
34
  Requires-Dist: black (>=24.10,<25.0) ; extra == "dev"
35
35
  Requires-Dist: isort (>=5.13,<6.0) ; extra == "dev"
36
36
  Requires-Dist: mypy (>=1.5,<2.0) ; extra == "dev"
37
+ Requires-Dist: pillow (<12)
37
38
  Requires-Dist: pylint (>=3.3,<4.0) ; extra == "dev"
38
39
  Requires-Dist: pytest (>=8.3,<9.0) ; extra == "dev"
39
40
  Requires-Dist: pytest-cov (>=6.0,<7.0) ; extra == "dev"
@@ -0,0 +1,9 @@
1
+ mini_arcade_core/__init__.py,sha256=0gkloHYTV8FmEZbBLxNNy-eJge2y7pkQ_PCcrfeJ3uo,1831
2
+ mini_arcade_core/backend.py,sha256=SvsHDO0Fq86buTqMkOCSAidx2E4QAJ6mLsSa9ZQXEqY,4514
3
+ mini_arcade_core/entity.py,sha256=mAkedH0j14giBQFRpQjaym46uczbQDln6nbxy0WFAqU,1077
4
+ mini_arcade_core/game.py,sha256=qfJ4UiAA2Mu-iAkj5SvxWxteGO9-s79IjlJk9ORHliw,5077
5
+ mini_arcade_core/scene.py,sha256=QENcn2hWUF3Ja9pU8NK67NYmC8ptnjdpUkg1qsUEl60,2435
6
+ mini_arcade_core-0.7.4.dist-info/METADATA,sha256=3-lnmTd16f89QKre5WegNgd8xQ2fR4MoY5ZrCB1A2n4,8324
7
+ mini_arcade_core-0.7.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
8
+ mini_arcade_core-0.7.4.dist-info/licenses/LICENSE,sha256=3lHAuV0584cVS5vAqi2uC6GcsVgxUijvwvtZckyvaZ4,1096
9
+ mini_arcade_core-0.7.4.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- mini_arcade_core/__init__.py,sha256=0PFa04KRSY3kT8YoD24zfFt92HYfrk6QrH8IlHbmeo4,1753
2
- mini_arcade_core/backend.py,sha256=DZUEO4zMlXIk3ctD9ZNqvgOo7n_ZKpC6x--hIede_YY,2946
3
- mini_arcade_core/entity.py,sha256=mAkedH0j14giBQFRpQjaym46uczbQDln6nbxy0WFAqU,1077
4
- mini_arcade_core/game.py,sha256=yRMy0IJVB_yuF9P_SKjcxJG0YVMs52khVFLomSUeVw8,3935
5
- mini_arcade_core/scene.py,sha256=i8lmcTZ1IPcM-NDwVvKrvjCMcFVq1aD5nacOvU0ZONU,1883
6
- mini_arcade_core-0.7.2.dist-info/METADATA,sha256=UNpZCJ0APedKmetmYqtENHGlSIgW2uDjsvr2eYxlidI,8296
7
- mini_arcade_core-0.7.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
8
- mini_arcade_core-0.7.2.dist-info/licenses/LICENSE,sha256=3lHAuV0584cVS5vAqi2uC6GcsVgxUijvwvtZckyvaZ4,1096
9
- mini_arcade_core-0.7.2.dist-info/RECORD,,