mini-arcade-core 0.10.0__py3-none-any.whl → 1.0.0__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.
Files changed (76) hide show
  1. mini_arcade_core/__init__.py +43 -60
  2. mini_arcade_core/backend/__init__.py +0 -5
  3. mini_arcade_core/backend/backend.py +9 -0
  4. mini_arcade_core/backend/events.py +1 -1
  5. mini_arcade_core/{keymaps/sdl.py → backend/sdl_map.py} +1 -1
  6. mini_arcade_core/engine/__init__.py +0 -0
  7. mini_arcade_core/engine/commands.py +169 -0
  8. mini_arcade_core/engine/game.py +354 -0
  9. mini_arcade_core/engine/render/__init__.py +0 -0
  10. mini_arcade_core/engine/render/packet.py +56 -0
  11. mini_arcade_core/engine/render/pipeline.py +39 -0
  12. mini_arcade_core/managers/__init__.py +0 -22
  13. mini_arcade_core/managers/cheats.py +71 -240
  14. mini_arcade_core/managers/inputs.py +5 -1
  15. mini_arcade_core/runtime/__init__.py +0 -0
  16. mini_arcade_core/runtime/audio/__init__.py +0 -0
  17. mini_arcade_core/runtime/audio/audio_adapter.py +13 -0
  18. mini_arcade_core/runtime/audio/audio_port.py +17 -0
  19. mini_arcade_core/runtime/capture/__init__.py +0 -0
  20. mini_arcade_core/runtime/capture/capture_adapter.py +143 -0
  21. mini_arcade_core/runtime/capture/capture_port.py +32 -0
  22. mini_arcade_core/runtime/context.py +53 -0
  23. mini_arcade_core/runtime/file/__init__.py +0 -0
  24. mini_arcade_core/runtime/file/file_adapter.py +20 -0
  25. mini_arcade_core/runtime/file/file_port.py +31 -0
  26. mini_arcade_core/runtime/input/__init__.py +0 -0
  27. mini_arcade_core/runtime/input/input_adapter.py +49 -0
  28. mini_arcade_core/runtime/input/input_port.py +31 -0
  29. mini_arcade_core/runtime/input_frame.py +71 -0
  30. mini_arcade_core/runtime/scene/__init__.py +0 -0
  31. mini_arcade_core/runtime/scene/scene_adapter.py +97 -0
  32. mini_arcade_core/runtime/scene/scene_port.py +149 -0
  33. mini_arcade_core/runtime/services.py +35 -0
  34. mini_arcade_core/runtime/window/__init__.py +0 -0
  35. mini_arcade_core/runtime/window/window_adapter.py +26 -0
  36. mini_arcade_core/runtime/window/window_port.py +47 -0
  37. mini_arcade_core/scenes/__init__.py +0 -22
  38. mini_arcade_core/scenes/autoreg.py +1 -1
  39. mini_arcade_core/scenes/registry.py +21 -19
  40. mini_arcade_core/scenes/sim_scene.py +41 -0
  41. mini_arcade_core/scenes/systems/__init__.py +0 -0
  42. mini_arcade_core/scenes/systems/base_system.py +40 -0
  43. mini_arcade_core/scenes/systems/system_pipeline.py +57 -0
  44. mini_arcade_core/sim/__init__.py +0 -0
  45. mini_arcade_core/sim/protocols.py +41 -0
  46. mini_arcade_core/sim/runner.py +222 -0
  47. mini_arcade_core/spaces/__init__.py +0 -12
  48. mini_arcade_core/spaces/d2/__init__.py +0 -30
  49. mini_arcade_core/spaces/d2/collision2d.py +25 -28
  50. mini_arcade_core/spaces/d2/geometry2d.py +18 -0
  51. mini_arcade_core/spaces/d2/kinematics2d.py +2 -8
  52. mini_arcade_core/spaces/d2/physics2d.py +9 -0
  53. mini_arcade_core/ui/__init__.py +0 -26
  54. mini_arcade_core/ui/menu.py +265 -84
  55. mini_arcade_core/utils/__init__.py +10 -0
  56. mini_arcade_core/utils/deprecated_decorator.py +45 -0
  57. mini_arcade_core/utils/logging.py +174 -0
  58. {mini_arcade_core-0.10.0.dist-info → mini_arcade_core-1.0.0.dist-info}/METADATA +1 -1
  59. mini_arcade_core-1.0.0.dist-info/RECORD +65 -0
  60. {mini_arcade_core-0.10.0.dist-info → mini_arcade_core-1.0.0.dist-info}/WHEEL +1 -1
  61. mini_arcade_core/commands.py +0 -84
  62. mini_arcade_core/entity.py +0 -72
  63. mini_arcade_core/game.py +0 -287
  64. mini_arcade_core/keymaps/__init__.py +0 -15
  65. mini_arcade_core/managers/base.py +0 -132
  66. mini_arcade_core/managers/entities.py +0 -38
  67. mini_arcade_core/managers/overlays.py +0 -53
  68. mini_arcade_core/managers/system.py +0 -26
  69. mini_arcade_core/scenes/model.py +0 -34
  70. mini_arcade_core/scenes/runtime.py +0 -29
  71. mini_arcade_core/scenes/scene.py +0 -109
  72. mini_arcade_core/scenes/system.py +0 -69
  73. mini_arcade_core/ui/overlays.py +0 -41
  74. mini_arcade_core-0.10.0.dist-info/RECORD +0 -40
  75. /mini_arcade_core/{keymaps → backend}/keys.py +0 -0
  76. {mini_arcade_core-0.10.0.dist-info → mini_arcade_core-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,174 @@
1
+ """
2
+ Logging utilities for Mini Arcade Core.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ import os
9
+ import sys
10
+ from typing import Optional
11
+
12
+
13
+ def _classname_from_locals(locals_: dict) -> Optional[str]:
14
+ self_obj = locals_.get("self")
15
+ if self_obj is not None:
16
+ return type(self_obj).__name__
17
+ cls_obj = locals_.get("cls")
18
+ if isinstance(cls_obj, type):
19
+ return cls_obj.__name__
20
+ return None
21
+
22
+
23
+ class EnsureClassName(logging.Filter):
24
+ """
25
+ Populate record.classname by finding the *emitting* frame:
26
+ match by (pathname, funcName) and read self/cls from its locals.
27
+ Falls back to "-" when not in a class context.
28
+ """
29
+
30
+ def filter(self, record: logging.LogRecord) -> bool:
31
+ # record_factory ensures classname exists, but allow explicit override
32
+ if getattr(record, "classname", None) not in (None, "-"):
33
+ return True
34
+
35
+ target_path = record.pathname
36
+ target_func = record.funcName
37
+
38
+ # Justification: Seems pretty obvious here.
39
+ # pylint: disable=protected-access
40
+ f = sys._getframe()
41
+ # pylint: enable=protected-access
42
+
43
+ for _ in range(200):
44
+ if f is None:
45
+ break
46
+ code = f.f_code
47
+ if code.co_filename == target_path and code.co_name == target_func:
48
+ record.classname = _classname_from_locals(f.f_locals) or "-"
49
+ return True
50
+ f = f.f_back
51
+
52
+ record.classname = "-"
53
+ return True
54
+
55
+
56
+ class ConsoleColorFormatter(logging.Formatter):
57
+ """
58
+ Console formatter with ANSI colors by log level.
59
+ """
60
+
61
+ COLORS = {
62
+ logging.DEBUG: "\033[96m", # Cyan
63
+ logging.INFO: "\033[92m", # Green
64
+ logging.WARNING: "\033[93m", # Yellow
65
+ logging.ERROR: "\033[91m", # Red
66
+ logging.CRITICAL: "\033[95m", # Magenta
67
+ "RESET": "\033[0m",
68
+ }
69
+
70
+ def format(self, record: logging.LogRecord) -> str:
71
+ color = self.COLORS.get(record.levelno, self.COLORS["RESET"])
72
+ msg = super().format(record)
73
+ return f"{color}{msg}{self.COLORS['RESET']}"
74
+
75
+
76
+ # --------------------------------------------------------------------------------------
77
+ # Global logging setup (single source of truth)
78
+ # --------------------------------------------------------------------------------------
79
+
80
+ LOGGER_FORMAT = (
81
+ "%(asctime)s [%(levelname)-8.8s] [%(name)s] "
82
+ "%(module)s.%(classname)s.%(funcName)s: "
83
+ "%(message)s (%(filename)s:%(lineno)d)"
84
+ )
85
+
86
+
87
+ def _enable_windows_ansi():
88
+ """
89
+ Best-effort enable ANSI escape sequences on Windows terminals.
90
+ Newer Windows 10/11 terminals usually support this already.
91
+ """
92
+ if os.name != "nt":
93
+ return
94
+ try:
95
+ # Enables VT100 sequences in some consoles; harmless if unsupported
96
+ # Justification: Importing ctypes only on Windows is acceptable.
97
+ # pylint: disable=import-outside-toplevel
98
+ import ctypes
99
+
100
+ # pylint: enable=import-outside-toplevel
101
+
102
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
103
+ handle = kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE = -11
104
+ mode = ctypes.c_uint32()
105
+ if kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
106
+ # ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
107
+ kernel32.SetConsoleMode(handle, mode.value | 0x0004)
108
+ # Justification: We want to catch all exceptions here.
109
+ # pylint: disable=broad-exception-caught
110
+ except Exception:
111
+ # If it fails, we just keep going without breaking logging.
112
+ pass
113
+ # pylint: enable=broad-exception-caught
114
+
115
+
116
+ def _install_record_factory_defaults():
117
+ """
118
+ Ensure every LogRecord has `classname` so formatters never crash.
119
+ Safe to call multiple times; we keep the current factory chain.
120
+ """
121
+ old_factory = logging.getLogRecordFactory()
122
+
123
+ def record_factory(*args, **kwargs):
124
+ record = old_factory(*args, **kwargs)
125
+ if not hasattr(record, "classname"):
126
+ record.classname = "-"
127
+ return record
128
+
129
+ logging.setLogRecordFactory(record_factory)
130
+
131
+
132
+ def configure_logging(level: int = logging.DEBUG):
133
+ """
134
+ Configure logging once for the whole app (root logger).
135
+ Call this early (app entrypoint). Safe to call multiple times.
136
+ """
137
+ _enable_windows_ansi()
138
+ _install_record_factory_defaults()
139
+
140
+ root = logging.getLogger()
141
+ root.setLevel(level)
142
+
143
+ # Avoid duplicate handlers if reloaded/imported multiple times
144
+ # We tag our handler so we can find it reliably.
145
+ handler_tag = "_mini_arcade_core_console_handler"
146
+
147
+ for h in list(root.handlers):
148
+ if getattr(h, handler_tag, False):
149
+ # Already configured
150
+ return
151
+
152
+ console = logging.StreamHandler(stream=sys.stdout)
153
+ setattr(console, handler_tag, True)
154
+
155
+ console.setFormatter(ConsoleColorFormatter(LOGGER_FORMAT))
156
+ console.addFilter(EnsureClassName())
157
+
158
+ # Important: don’t leave any basicConfig handlers around if someone called it earlier
159
+ # We remove only the plain StreamHandlers that don't have our tag.
160
+ for h in list(root.handlers):
161
+ if isinstance(h, logging.StreamHandler) and not getattr(
162
+ h, handler_tag, False
163
+ ):
164
+ root.removeHandler(h)
165
+
166
+ root.addHandler(console)
167
+
168
+
169
+ # --------------------------------------------------------------------------------------
170
+ # Public logger for DejaBounce
171
+ # --------------------------------------------------------------------------------------
172
+
173
+ configure_logging()
174
+ logger = logging.getLogger("mini-arcade-core")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mini-arcade-core
3
- Version: 0.10.0
3
+ Version: 1.0.0
4
4
  Summary: Tiny scene-based game loop core for small arcade games.
5
5
  License: Copyright (c) 2025 Santiago Rincón
6
6
 
@@ -0,0 +1,65 @@
1
+ mini_arcade_core/__init__.py,sha256=Cyp7bCkNW018_AlhUGXRH29iPytyW03dE5cvetezxLg,3441
2
+ mini_arcade_core/backend/__init__.py,sha256=J1wZBHX-aqmBP3zh_ey9PK2b_gnWp72zxMfKcs3iwSw,274
3
+ mini_arcade_core/backend/backend.py,sha256=9CYcI7llbUhyO1p3Rjl-jmsw2c3KsQUtPAx0ys-WnW4,4237
4
+ mini_arcade_core/backend/events.py,sha256=5Ohve3CQ6n2CztiOhbCoz6yFDY4z0j4v4R9FBKRDRjc,2929
5
+ mini_arcade_core/backend/keys.py,sha256=LTg20SwLBI3kpPIiTNpq2yBft_QUGj-iNFSNm9M-Fus,3010
6
+ mini_arcade_core/backend/sdl_map.py,sha256=_yBRtvaFUcQKy1kcoIf-SPhbbKEW7dzvzBcI6TLmKjc,2060
7
+ mini_arcade_core/backend/types.py,sha256=SuiwXGNmXCZxfPsww6zj3V_NK7k4jpoCuzMn19afS-g,175
8
+ mini_arcade_core/bus.py,sha256=2Etpoa-UWhk33xJjqDlY5YslPDJEjxNoIEVtF3C73vs,1558
9
+ mini_arcade_core/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ mini_arcade_core/engine/commands.py,sha256=1g7XBWmiVYxe2IUggPpUs5uaZP9PHZKfr6cQuhez8eg,4044
11
+ mini_arcade_core/engine/game.py,sha256=ovfSCNczMN8Tu-rj83reHKl32OKEa649PwMbshlLpac,11479
12
+ mini_arcade_core/engine/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ mini_arcade_core/engine/render/packet.py,sha256=OiAPwGoVHo04OcUWMAoA_N1AFPUMyf8yxNgJthGj4-c,1440
14
+ mini_arcade_core/engine/render/pipeline.py,sha256=4Pdwwt-fo4dK3uqPb3M_IiRIDxymilAK8fOaiojsl10,891
15
+ mini_arcade_core/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ mini_arcade_core/managers/cheats.py,sha256=jMx2a8YnaNCkCG5MPmIzz4uHuS7-_aYf0J45cv2-3v0,5569
17
+ mini_arcade_core/managers/inputs.py,sha256=LL0Q_QYUF3K1orpLIrmYXOadBUascyJVpd2KYvpTNDU,8605
18
+ mini_arcade_core/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ mini_arcade_core/runtime/audio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ mini_arcade_core/runtime/audio/audio_adapter.py,sha256=CEMYbAhCT1MoIsH6PTqu2kE6iOCcB7SwnFjBqk3hLlc,286
21
+ mini_arcade_core/runtime/audio/audio_port.py,sha256=idUxRIr_qga46gL31_GMwQ3j-zfNyuyYC-Ygkl_tIO4,338
22
+ mini_arcade_core/runtime/capture/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ mini_arcade_core/runtime/capture/capture_adapter.py,sha256=qJ2JiOLJHbP00IesbAyyPGPBxSaxwPJRTMaMjMU4bXs,4660
24
+ mini_arcade_core/runtime/capture/capture_port.py,sha256=lZme02Cs9aeb_qpZnAz3sClZieG-lFc05wbFPCyB4_I,704
25
+ mini_arcade_core/runtime/context.py,sha256=IQpAhtbN0ZqJVXG6KXFq3FPk0sIGyPmyNgBYviqZl7A,1632
26
+ mini_arcade_core/runtime/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ mini_arcade_core/runtime/file/file_adapter.py,sha256=09q7G9Qijml9d4AAjo6HLC1yuoVTjE_7xaT8apT4mk0,523
28
+ mini_arcade_core/runtime/file/file_port.py,sha256=p1MouCSHXZw--rWNMw3aYBLU-of8mXaT_suopczPtM8,608
29
+ mini_arcade_core/runtime/input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ mini_arcade_core/runtime/input/input_adapter.py,sha256=vExQiwFIWTI3zYD8lmnD9TvoQPZvJfI6IINPJUqAdQ0,1467
31
+ mini_arcade_core/runtime/input/input_port.py,sha256=d4ptftwf92_LJdyaUMFxIsLHXBINzQyJACHn4laNyxQ,746
32
+ mini_arcade_core/runtime/input_frame.py,sha256=34-RAfOD-YScVLyRQrarpm7byFTHjsWM77lIH0JsmT8,2384
33
+ mini_arcade_core/runtime/scene/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ mini_arcade_core/runtime/scene/scene_adapter.py,sha256=dYkqFWN3FcRmf9tsZ5wz1QZX67CUntndiNMOWrO578U,2523
35
+ mini_arcade_core/runtime/scene/scene_port.py,sha256=uDFrN7fudIldNkivEkagQd9-b1HVI71BpTpSv5Oh6JI,3854
36
+ mini_arcade_core/runtime/services.py,sha256=9LX7O-AYFTxKgBDewzu0B_D01EJ41WVAdl8U1ZcvEYg,1061
37
+ mini_arcade_core/runtime/window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ mini_arcade_core/runtime/window/window_adapter.py,sha256=EMSWE72aWWzCN0uRuVJvry3g2PQTBmEa_zEfgK6weKY,637
39
+ mini_arcade_core/runtime/window/window_port.py,sha256=HwY8ilhZLLK5cZbEA93R5crjOA4i8Gn2MB8y4BbI4o4,988
40
+ mini_arcade_core/scenes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ mini_arcade_core/scenes/autoreg.py,sha256=wsuY7YUSZFmDyToKHFriAG78OU48-7J4BfL_X6T5GBg,1037
42
+ mini_arcade_core/scenes/registry.py,sha256=EF5qaAgoLwJa3IV_3wclvjHpE3jZt-KHB3GOJS9lQy8,3390
43
+ mini_arcade_core/scenes/sim_scene.py,sha256=b2JsOvPFkHCdCf8pMLJZ90qB0JJ6B8Ka3o5QK4cVshI,1055
44
+ mini_arcade_core/scenes/systems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ mini_arcade_core/scenes/systems/base_system.py,sha256=GfMrXsO8ynW3xOxWeav7Ug5XUbRnbF0vo8VzmG7gpec,1075
46
+ mini_arcade_core/scenes/systems/system_pipeline.py,sha256=Cy9y1DclbMLZZ-yx7OKYe34ORoGLNa6dReQfOdiO8SY,1642
47
+ mini_arcade_core/sim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ mini_arcade_core/sim/protocols.py,sha256=b7d2WAKRokTNbteNhUaWdGx9vc9Fnccxb-5rPwozDaA,1099
49
+ mini_arcade_core/sim/runner.py,sha256=ZF-BZJw-NcaFrg4zsUu1zOUUBZwZbRYflqcdF1jDcmM,7446
50
+ mini_arcade_core/spaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ mini_arcade_core/spaces/d2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ mini_arcade_core/spaces/d2/boundaries2d.py,sha256=H1HkCR1422MkQIEve2DFKvnav4RpvtLx-qTMxzmdDMQ,2610
53
+ mini_arcade_core/spaces/d2/collision2d.py,sha256=5IvgLnyVb8i0uzzZuum1noWsNhoxcvHOLaHkmrTMTxQ,1710
54
+ mini_arcade_core/spaces/d2/geometry2d.py,sha256=FuYzef-XdOyb1aeGLJbxINxr0WJHnqFFBgtbPi1WonY,1716
55
+ mini_arcade_core/spaces/d2/kinematics2d.py,sha256=AJ3DhPXNgm6wZYwCljMIE4_2BYx3E2rPcwhXTgQALkU,2030
56
+ mini_arcade_core/spaces/d2/physics2d.py,sha256=OQT7r-zMtmoKD2aWCSNmRAdI0OGIpxGX-pLR8LcAMbQ,1854
57
+ mini_arcade_core/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ mini_arcade_core/ui/menu.py,sha256=94W9lFI0qVB-v-f0b5U05p-MTmzaa5H1RGxy-1S01go,24345
59
+ mini_arcade_core/utils/__init__.py,sha256=3Q9r6bTyqImYix8BnOGwWjAz25nbTQezGcRq3m5KEYE,189
60
+ mini_arcade_core/utils/deprecated_decorator.py,sha256=yrrW2ZqPskK-4MUTyIrMb465Wc54X2poV53ZQutZWqc,1140
61
+ mini_arcade_core/utils/logging.py,sha256=IqkM1C5yezb3qnmqlftROlHO6mBCW0iWQmyIR9mwd-4,5576
62
+ mini_arcade_core-1.0.0.dist-info/METADATA,sha256=hMAIBh7Kk29GpAzrP4LziqDT3fzHc0SoiNmhz8A70YY,8188
63
+ mini_arcade_core-1.0.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
64
+ mini_arcade_core-1.0.0.dist-info/licenses/LICENSE,sha256=3lHAuV0584cVS5vAqi2uC6GcsVgxUijvwvtZckyvaZ4,1096
65
+ mini_arcade_core-1.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,84 +0,0 @@
1
- """
2
- Command protocol for executing commands with a given context.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from typing import TYPE_CHECKING, Generic, Protocol, TypeVar
8
-
9
- from mini_arcade_core.game import Game
10
- from mini_arcade_core.scenes.model import SceneModel
11
-
12
- if TYPE_CHECKING:
13
- from mini_arcade_core.scenes.scene import Scene
14
-
15
- # Justification: Generic type for context
16
- # pylint: disable=invalid-name
17
- TContext = TypeVar("TContext")
18
- # pylint: enable=invalid-name
19
-
20
-
21
- class BaseCommand(Protocol, Generic[TContext]):
22
- """
23
- Protocol for a command that can be executed with a given context.
24
- """
25
-
26
- def __call__(self, context: TContext) -> None:
27
- """
28
- Execute the cheat code with the given context.
29
-
30
- :param context: Context object for cheat execution.
31
- :type context: TContext
32
- """
33
- self.execute(context)
34
-
35
- def execute(self, context: TContext):
36
- """
37
- Execute the command with the given context.
38
-
39
- :param context: Context object for command execution.
40
- :type context: TContext
41
- """
42
-
43
-
44
- class BaseGameCommand(BaseCommand[Game]):
45
- """
46
- Base class for commands that operate on the Game context.
47
- """
48
-
49
- def execute(self, context: Game) -> None:
50
- """
51
- Execute the command with the given Game context.
52
-
53
- :param context: Game context for command execution.
54
- :type context: Game
55
- """
56
- raise NotImplementedError(
57
- "Execute method must be implemented by subclasses."
58
- )
59
-
60
-
61
- class BaseSceneCommand(BaseCommand[SceneModel]):
62
- """
63
- Base class for commands that operate on the Scene SceneModel context within a scene.
64
- """
65
-
66
- def execute(self, context: SceneModel) -> None:
67
- """
68
- Execute the command with the given Scene Model context.
69
-
70
- :param context: Scene Model context for command execution.
71
- :type context: SceneModel
72
- """
73
- raise NotImplementedError(
74
- "Execute method must be implemented by subclasses."
75
- )
76
-
77
-
78
- class QuitGameCommand(BaseGameCommand):
79
- """
80
- Command to quit the game.
81
- """
82
-
83
- def execute(self, context: Game) -> None:
84
- context.quit()
@@ -1,72 +0,0 @@
1
- """
2
- Entity base classes for mini_arcade_core.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from typing import Any
8
-
9
- from mini_arcade_core.spaces.d2 import (
10
- KinematicData,
11
- Position2D,
12
- RectCollider,
13
- Size2D,
14
- )
15
-
16
-
17
- class Entity:
18
- """Entity base class for game objects."""
19
-
20
- def update(self, dt: float):
21
- """
22
- Advance the entity state by ``dt`` seconds.
23
-
24
- :param dt: Time delta in seconds.
25
- :type dt: float
26
- """
27
-
28
- def draw(self, surface: Any):
29
- """
30
- Render the entity to the given surface.
31
-
32
- :param surface: The surface to draw on.
33
- :type surface: Any
34
- """
35
-
36
-
37
- class SpriteEntity(Entity):
38
- """Entity with position and size."""
39
-
40
- def __init__(self, position: Position2D, size: Size2D):
41
- """
42
- :param position: Top-left position of the entity.
43
- :type position: Position2D
44
-
45
- :param size: Size of the entity.
46
- :type size: Size2D
47
- """
48
- self.position = Position2D(float(position.x), float(position.y))
49
- self.size = Size2D(int(size.width), int(size.height))
50
- self.collider = RectCollider(self.position, self.size)
51
-
52
-
53
- class KinematicEntity(SpriteEntity):
54
- """SpriteEntity with velocity-based movement."""
55
-
56
- def __init__(self, kinematic_data: KinematicData):
57
- """
58
- :param kinematic_data: Kinematic data for the entity.
59
- :type kinematic_data: KinematicData
60
- """
61
- super().__init__(
62
- position=kinematic_data.position,
63
- size=kinematic_data.size,
64
- )
65
-
66
- self.time_scale = kinematic_data.time_scale
67
- self.velocity = kinematic_data.velocity
68
-
69
- def update(self, dt: float):
70
- self.position.x, self.position.y = self.velocity.advance(
71
- self.position.x, self.position.y, dt * self.time_scale
72
- )