mini-arcade-core 0.9.9__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.
- mini_arcade_core/__init__.py +44 -80
- mini_arcade_core/backend/__init__.py +0 -5
- mini_arcade_core/backend/backend.py +9 -0
- mini_arcade_core/backend/events.py +1 -1
- mini_arcade_core/{keymaps/sdl.py → backend/sdl_map.py} +1 -1
- mini_arcade_core/bus.py +57 -0
- mini_arcade_core/engine/__init__.py +0 -0
- mini_arcade_core/engine/commands.py +169 -0
- mini_arcade_core/engine/game.py +354 -0
- mini_arcade_core/engine/render/__init__.py +0 -0
- mini_arcade_core/engine/render/packet.py +56 -0
- mini_arcade_core/engine/render/pipeline.py +39 -0
- mini_arcade_core/managers/__init__.py +0 -14
- mini_arcade_core/managers/cheats.py +186 -0
- mini_arcade_core/managers/inputs.py +286 -0
- mini_arcade_core/runtime/__init__.py +0 -0
- mini_arcade_core/runtime/audio/__init__.py +0 -0
- mini_arcade_core/runtime/audio/audio_adapter.py +13 -0
- mini_arcade_core/runtime/audio/audio_port.py +17 -0
- mini_arcade_core/runtime/capture/__init__.py +0 -0
- mini_arcade_core/runtime/capture/capture_adapter.py +143 -0
- mini_arcade_core/runtime/capture/capture_port.py +32 -0
- mini_arcade_core/runtime/context.py +53 -0
- mini_arcade_core/runtime/file/__init__.py +0 -0
- mini_arcade_core/runtime/file/file_adapter.py +20 -0
- mini_arcade_core/runtime/file/file_port.py +31 -0
- mini_arcade_core/runtime/input/__init__.py +0 -0
- mini_arcade_core/runtime/input/input_adapter.py +49 -0
- mini_arcade_core/runtime/input/input_port.py +31 -0
- mini_arcade_core/runtime/input_frame.py +71 -0
- mini_arcade_core/runtime/scene/__init__.py +0 -0
- mini_arcade_core/runtime/scene/scene_adapter.py +97 -0
- mini_arcade_core/runtime/scene/scene_port.py +149 -0
- mini_arcade_core/runtime/services.py +35 -0
- mini_arcade_core/runtime/window/__init__.py +0 -0
- mini_arcade_core/runtime/window/window_adapter.py +26 -0
- mini_arcade_core/runtime/window/window_port.py +47 -0
- mini_arcade_core/scenes/__init__.py +0 -12
- mini_arcade_core/scenes/autoreg.py +1 -1
- mini_arcade_core/scenes/registry.py +21 -19
- mini_arcade_core/scenes/sim_scene.py +41 -0
- mini_arcade_core/scenes/systems/__init__.py +0 -0
- mini_arcade_core/scenes/systems/base_system.py +40 -0
- mini_arcade_core/scenes/systems/system_pipeline.py +57 -0
- mini_arcade_core/sim/__init__.py +0 -0
- mini_arcade_core/sim/protocols.py +41 -0
- mini_arcade_core/sim/runner.py +222 -0
- mini_arcade_core/spaces/__init__.py +0 -0
- mini_arcade_core/spaces/d2/__init__.py +0 -0
- mini_arcade_core/{two_d → spaces/d2}/collision2d.py +25 -28
- mini_arcade_core/{two_d → spaces/d2}/geometry2d.py +18 -0
- mini_arcade_core/{two_d → spaces/d2}/kinematics2d.py +5 -8
- mini_arcade_core/{two_d → spaces/d2}/physics2d.py +9 -0
- mini_arcade_core/ui/__init__.py +0 -14
- mini_arcade_core/ui/menu.py +415 -56
- mini_arcade_core/utils/__init__.py +10 -0
- mini_arcade_core/utils/deprecated_decorator.py +45 -0
- mini_arcade_core/utils/logging.py +174 -0
- {mini_arcade_core-0.9.9.dist-info → mini_arcade_core-1.0.0.dist-info}/METADATA +1 -1
- mini_arcade_core-1.0.0.dist-info/RECORD +65 -0
- {mini_arcade_core-0.9.9.dist-info → mini_arcade_core-1.0.0.dist-info}/WHEEL +1 -1
- mini_arcade_core/cheats.py +0 -235
- mini_arcade_core/entity.py +0 -71
- mini_arcade_core/game.py +0 -287
- mini_arcade_core/keymaps/__init__.py +0 -15
- mini_arcade_core/managers/base.py +0 -91
- mini_arcade_core/managers/entity_manager.py +0 -38
- mini_arcade_core/managers/overlay_manager.py +0 -33
- mini_arcade_core/scenes/scene.py +0 -93
- mini_arcade_core/two_d/__init__.py +0 -30
- mini_arcade_core-0.9.9.dist-info/RECORD +0 -31
- /mini_arcade_core/{keymaps → backend}/keys.py +0 -0
- /mini_arcade_core/{two_d → spaces/d2}/boundaries2d.py +0 -0
- {mini_arcade_core-0.9.9.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")
|
|
@@ -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,,
|
mini_arcade_core/cheats.py
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Cheats module for Mini Arcade Core.
|
|
3
|
-
Provides cheat codes and related functionality.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
from collections import deque
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from enum import Enum
|
|
11
|
-
from typing import Callable, Deque, Dict, Optional, Sequence, TypeVar
|
|
12
|
-
|
|
13
|
-
# Justification: We want to keep the type variable name simple here.
|
|
14
|
-
# pylint: disable=invalid-name
|
|
15
|
-
TContext = TypeVar("TContext")
|
|
16
|
-
# pylint: enable=invalid-name
|
|
17
|
-
|
|
18
|
-
CheatCallback = Callable[[TContext], None]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass(frozen=True)
|
|
22
|
-
class CheatCode:
|
|
23
|
-
"""
|
|
24
|
-
Represents a registered cheat code.
|
|
25
|
-
|
|
26
|
-
:ivar name (str): Unique name of the cheat code.
|
|
27
|
-
:ivar sequence (tuple[str, ...]): Sequence of key strings that trigger the cheat.
|
|
28
|
-
:ivar callback (CheatCallback): Function to call when the cheat is activated.
|
|
29
|
-
:ivar clear_buffer_on_match (bool): Whether to clear the input buffer after a match.
|
|
30
|
-
:ivar enabled (bool): Whether the cheat code is enabled.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
name: str
|
|
34
|
-
sequence: tuple[str, ...]
|
|
35
|
-
callback: CheatCallback
|
|
36
|
-
clear_buffer_on_match: bool = False
|
|
37
|
-
enabled: bool = True
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class CheatManager:
|
|
41
|
-
"""
|
|
42
|
-
Reusable cheat code matcher.
|
|
43
|
-
Keeps a rolling buffer of recent keys and triggers callbacks on sequence match.
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
def __init__(
|
|
47
|
-
self,
|
|
48
|
-
buffer_size: int = 16,
|
|
49
|
-
*,
|
|
50
|
-
enabled: bool = True,
|
|
51
|
-
normalize: Optional[Callable[[str], str]] = None,
|
|
52
|
-
key_getter: Optional[Callable[[object], Optional[str]]] = None,
|
|
53
|
-
):
|
|
54
|
-
"""
|
|
55
|
-
:param buffer_size: Maximum size of the input buffer.
|
|
56
|
-
:type buffer_size: int
|
|
57
|
-
|
|
58
|
-
:param enabled: Whether the cheat manager is enabled.
|
|
59
|
-
:type enabled: bool
|
|
60
|
-
|
|
61
|
-
:param normalize: Optional function to normalize key strings.
|
|
62
|
-
:type normalize: Callable[[str], str] | None
|
|
63
|
-
|
|
64
|
-
:param key_getter: Optional function to extract key string from event object.
|
|
65
|
-
:type key_getter: Callable[[object], Optional[str]] | None
|
|
66
|
-
"""
|
|
67
|
-
self.enabled = enabled
|
|
68
|
-
self._buffer: Deque[str] = deque(maxlen=buffer_size)
|
|
69
|
-
self._codes: Dict[str, CheatCode] = {}
|
|
70
|
-
|
|
71
|
-
self._normalize = normalize or (lambda s: s.strip().upper())
|
|
72
|
-
# key_getter: how to extract a key from an engine/backend event object
|
|
73
|
-
self._key_getter = key_getter or self._default_key_getter
|
|
74
|
-
|
|
75
|
-
# Justification: We want to keep the number of arguments manageable here.
|
|
76
|
-
# pylint: disable=too-many-arguments
|
|
77
|
-
def register_code(
|
|
78
|
-
self,
|
|
79
|
-
name: str,
|
|
80
|
-
sequence: Sequence[str],
|
|
81
|
-
callback: CheatCallback,
|
|
82
|
-
*,
|
|
83
|
-
clear_buffer_on_match: bool = False,
|
|
84
|
-
enabled: bool = True,
|
|
85
|
-
):
|
|
86
|
-
"""
|
|
87
|
-
Register a new cheat code.
|
|
88
|
-
|
|
89
|
-
:param name: Unique name for the cheat code.
|
|
90
|
-
:type name: str
|
|
91
|
-
|
|
92
|
-
:param sequence: Sequence of key strings that trigger the cheat.
|
|
93
|
-
:type sequence: Sequence[str]
|
|
94
|
-
|
|
95
|
-
:param callback: Function to call when the cheat is activated.
|
|
96
|
-
:type callback: CheatCallback
|
|
97
|
-
|
|
98
|
-
:param clear_buffer_on_match: Whether to clear the input buffer after a match.
|
|
99
|
-
:type clear_buffer_on_match: bool
|
|
100
|
-
|
|
101
|
-
:param enabled: Whether the cheat code is enabled.
|
|
102
|
-
:type enabled: bool
|
|
103
|
-
"""
|
|
104
|
-
if not name:
|
|
105
|
-
raise ValueError("Cheat code name must be non-empty.")
|
|
106
|
-
if not sequence:
|
|
107
|
-
raise ValueError(
|
|
108
|
-
f"Cheat code '{name}' sequence must be non-empty."
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
norm_seq = tuple(self._normalize(k) for k in sequence)
|
|
112
|
-
self._codes[name] = CheatCode(
|
|
113
|
-
name=name,
|
|
114
|
-
sequence=norm_seq,
|
|
115
|
-
callback=callback,
|
|
116
|
-
clear_buffer_on_match=clear_buffer_on_match,
|
|
117
|
-
enabled=enabled,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# pylint: enable=too-many-arguments
|
|
121
|
-
|
|
122
|
-
def unregister_code(self, name: str):
|
|
123
|
-
"""
|
|
124
|
-
Unregister a cheat code by name.
|
|
125
|
-
|
|
126
|
-
:param name: Name of the cheat code to unregister.
|
|
127
|
-
:type name: str
|
|
128
|
-
"""
|
|
129
|
-
self._codes.pop(name, None)
|
|
130
|
-
|
|
131
|
-
def clear_buffer(self):
|
|
132
|
-
"""Clear the input buffer."""
|
|
133
|
-
self._buffer.clear()
|
|
134
|
-
|
|
135
|
-
def process_event(self, event: object, context: TContext) -> list[str]:
|
|
136
|
-
"""
|
|
137
|
-
Call from Scene when a key is pressed.
|
|
138
|
-
Returns list of cheat names matched (often 0 or 1).
|
|
139
|
-
|
|
140
|
-
:param event: The event object from the backend/engine.
|
|
141
|
-
:type event: object
|
|
142
|
-
|
|
143
|
-
:param context: Context object passed to cheat callbacks.
|
|
144
|
-
:type context: TContext
|
|
145
|
-
"""
|
|
146
|
-
if not self.enabled:
|
|
147
|
-
return []
|
|
148
|
-
key = self._key_getter(event)
|
|
149
|
-
if not key:
|
|
150
|
-
return []
|
|
151
|
-
return self.process_key(key, context)
|
|
152
|
-
|
|
153
|
-
def process_key(self, key: str, context: TContext) -> list[str]:
|
|
154
|
-
"""
|
|
155
|
-
Pure method for tests: feed a key string directly.
|
|
156
|
-
|
|
157
|
-
:param key: The key string to process.
|
|
158
|
-
:type key: str
|
|
159
|
-
|
|
160
|
-
:param context: Context object passed to cheat callbacks.
|
|
161
|
-
:type context: TContext
|
|
162
|
-
|
|
163
|
-
:return: List of cheat names matched.
|
|
164
|
-
:rtype: list[str]
|
|
165
|
-
"""
|
|
166
|
-
if not self.enabled:
|
|
167
|
-
return []
|
|
168
|
-
|
|
169
|
-
k = self._normalize(key)
|
|
170
|
-
if not k:
|
|
171
|
-
return []
|
|
172
|
-
|
|
173
|
-
self._buffer.append(k)
|
|
174
|
-
buf = tuple(self._buffer)
|
|
175
|
-
|
|
176
|
-
matched: list[str] = []
|
|
177
|
-
# Check if buffer ends with any cheat sequence
|
|
178
|
-
for code in self._codes.values():
|
|
179
|
-
if not code.enabled:
|
|
180
|
-
continue
|
|
181
|
-
seq = code.sequence
|
|
182
|
-
if len(seq) > len(buf):
|
|
183
|
-
continue
|
|
184
|
-
if buf[-len(seq) :] == seq:
|
|
185
|
-
code.callback(context)
|
|
186
|
-
matched.append(code.name)
|
|
187
|
-
if code.clear_buffer_on_match:
|
|
188
|
-
self.clear_buffer()
|
|
189
|
-
break # buffer changed; stop early
|
|
190
|
-
|
|
191
|
-
return matched
|
|
192
|
-
|
|
193
|
-
@staticmethod
|
|
194
|
-
def _default_key_getter(event: object) -> Optional[str]:
|
|
195
|
-
"""
|
|
196
|
-
Best-effort extraction:
|
|
197
|
-
- event.key
|
|
198
|
-
- event.key_code
|
|
199
|
-
- event.code
|
|
200
|
-
- event.scancode
|
|
201
|
-
- dict-like {"key": "..."}
|
|
202
|
-
Adjust/override with key_getter in your engine if needed.
|
|
203
|
-
|
|
204
|
-
:param event: The event object.
|
|
205
|
-
:type event: object
|
|
206
|
-
|
|
207
|
-
:return: Extracted key string, or None if not found.
|
|
208
|
-
:rtype: Optional[str]
|
|
209
|
-
"""
|
|
210
|
-
if event is None:
|
|
211
|
-
return None
|
|
212
|
-
|
|
213
|
-
# dict-like
|
|
214
|
-
if isinstance(event, dict):
|
|
215
|
-
v = (
|
|
216
|
-
event.get("key")
|
|
217
|
-
or event.get("key_code")
|
|
218
|
-
or event.get("code")
|
|
219
|
-
or event.get("scancode")
|
|
220
|
-
)
|
|
221
|
-
if isinstance(v, Enum):
|
|
222
|
-
return v.name
|
|
223
|
-
return str(v) if v is not None else None
|
|
224
|
-
|
|
225
|
-
# object-like
|
|
226
|
-
for attr in ("key", "key_code", "code", "scancode"):
|
|
227
|
-
if hasattr(event, attr):
|
|
228
|
-
v = getattr(event, attr)
|
|
229
|
-
if v is None:
|
|
230
|
-
continue
|
|
231
|
-
if isinstance(v, Enum):
|
|
232
|
-
return v.name # <-- THIS is the important bit
|
|
233
|
-
return str(v)
|
|
234
|
-
|
|
235
|
-
return None
|
mini_arcade_core/entity.py
DELETED
|
@@ -1,71 +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.two_d 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.velocity = kinematic_data.velocity
|
|
67
|
-
|
|
68
|
-
def update(self, dt: float):
|
|
69
|
-
self.position.x, self.position.y = self.velocity.advance(
|
|
70
|
-
self.position.x, self.position.y, dt
|
|
71
|
-
)
|