mini-arcade-core 1.1.1__py3-none-any.whl → 1.2.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 (57) hide show
  1. mini_arcade_core/__init__.py +14 -42
  2. mini_arcade_core/backend/__init__.py +1 -2
  3. mini_arcade_core/backend/backend.py +182 -184
  4. mini_arcade_core/backend/types.py +5 -1
  5. mini_arcade_core/engine/commands.py +8 -8
  6. mini_arcade_core/engine/game.py +54 -354
  7. mini_arcade_core/engine/game_config.py +40 -0
  8. mini_arcade_core/engine/gameplay_settings.py +24 -0
  9. mini_arcade_core/engine/loop/config.py +20 -0
  10. mini_arcade_core/engine/loop/hooks.py +77 -0
  11. mini_arcade_core/engine/loop/runner.py +272 -0
  12. mini_arcade_core/engine/loop/state.py +32 -0
  13. mini_arcade_core/engine/managers.py +24 -0
  14. mini_arcade_core/engine/render/context.py +0 -2
  15. mini_arcade_core/engine/render/effects/base.py +2 -2
  16. mini_arcade_core/engine/render/effects/crt.py +4 -4
  17. mini_arcade_core/engine/render/effects/registry.py +1 -1
  18. mini_arcade_core/engine/render/effects/vignette.py +8 -8
  19. mini_arcade_core/engine/render/passes/begin_frame.py +1 -1
  20. mini_arcade_core/engine/render/passes/end_frame.py +1 -1
  21. mini_arcade_core/engine/render/passes/postfx.py +1 -1
  22. mini_arcade_core/engine/render/passes/ui.py +1 -1
  23. mini_arcade_core/engine/render/passes/world.py +6 -6
  24. mini_arcade_core/engine/render/pipeline.py +7 -6
  25. mini_arcade_core/engine/render/viewport.py +10 -4
  26. mini_arcade_core/engine/scenes/models.py +54 -0
  27. mini_arcade_core/engine/scenes/scene_manager.py +213 -0
  28. mini_arcade_core/runtime/audio/audio_adapter.py +4 -3
  29. mini_arcade_core/runtime/audio/audio_port.py +0 -4
  30. mini_arcade_core/runtime/capture/capture_adapter.py +13 -6
  31. mini_arcade_core/runtime/capture/capture_port.py +0 -4
  32. mini_arcade_core/runtime/context.py +8 -6
  33. mini_arcade_core/runtime/scene/scene_query_adapter.py +31 -0
  34. mini_arcade_core/runtime/scene/scene_query_port.py +38 -0
  35. mini_arcade_core/runtime/services.py +3 -2
  36. mini_arcade_core/runtime/window/window_adapter.py +43 -41
  37. mini_arcade_core/runtime/window/window_port.py +3 -17
  38. mini_arcade_core/scenes/debug_overlay.py +5 -4
  39. mini_arcade_core/scenes/registry.py +11 -1
  40. mini_arcade_core/scenes/sim_scene.py +14 -14
  41. mini_arcade_core/ui/menu.py +54 -16
  42. mini_arcade_core/utils/__init__.py +2 -1
  43. mini_arcade_core/utils/logging.py +47 -18
  44. mini_arcade_core/utils/profiler.py +283 -0
  45. {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.0.dist-info}/METADATA +1 -1
  46. mini_arcade_core-1.2.0.dist-info/RECORD +92 -0
  47. {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.0.dist-info}/WHEEL +1 -1
  48. mini_arcade_core/managers/inputs.py +0 -284
  49. mini_arcade_core/runtime/scene/scene_adapter.py +0 -125
  50. mini_arcade_core/runtime/scene/scene_port.py +0 -170
  51. mini_arcade_core/sim/protocols.py +0 -41
  52. mini_arcade_core/sim/runner.py +0 -222
  53. mini_arcade_core-1.1.1.dist-info/RECORD +0 -85
  54. /mini_arcade_core/{managers → engine}/cheats.py +0 -0
  55. /mini_arcade_core/{managers → engine/loop}/__init__.py +0 -0
  56. /mini_arcade_core/{sim → engine/scenes}/__init__.py +0 -0
  57. {mini_arcade_core-1.1.1.dist-info → mini_arcade_core-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,92 @@
1
+ mini_arcade_core/__init__.py,sha256=AdhR8GzhJnPYBkwIdNag1cnB8e4JqCHNZxsZfrw5vCQ,2564
2
+ mini_arcade_core/backend/__init__.py,sha256=J1wZBHX-aqmBP3zh_ey9PK2b_gnWp72zxMfKcs3iwSw,274
3
+ mini_arcade_core/backend/backend.py,sha256=tJYjHqjWZst8P3uikStndEHIVWLvF0ypmibVtCErbag,8699
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=EW0bW4MvsEZKot0Z1h_5LuFSzoYGiJBphTquBz4oXf4,244
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/cheats.py,sha256=jMx2a8YnaNCkCG5MPmIzz4uHuS7-_aYf0J45cv2-3v0,5569
11
+ mini_arcade_core/engine/commands.py,sha256=Cw1tAwVRO5U2--hFX1Jq00LH_84oe1Oqw-Ngc0RnkGI,5383
12
+ mini_arcade_core/engine/game.py,sha256=DDdvlv0cN4sjBKtoIuP19W3YQCF1LwZbC_jYCz-1E4U,5767
13
+ mini_arcade_core/engine/game_config.py,sha256=4AP8n0Uk1HKEdPLOrV1xsySzBljAh8VhZASNrxPIMMc,1034
14
+ mini_arcade_core/engine/gameplay_settings.py,sha256=W8WBwfAvGZftkL4aMnOTx6SsGxwG-9Ou1Ey0AeWPCxs,549
15
+ mini_arcade_core/engine/loop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ mini_arcade_core/engine/loop/config.py,sha256=Sj1LrdnD_aACmHUuRQuKB7bDbDZy60WVe8sJHRmzmIU,461
17
+ mini_arcade_core/engine/loop/hooks.py,sha256=nmZi-35iMsZoPedTdZzsIKJP6O_iFUeKjB8xc4-XTHU,2417
18
+ mini_arcade_core/engine/loop/runner.py,sha256=uB4onDMO6lWFt40YNJsovkVHXiyaVdEuEFj8kYqWn6k,8858
19
+ mini_arcade_core/engine/loop/state.py,sha256=fzXQ9GP05PVNXEBTgIwA4qjMujxdUae3CXM6uRQz92Y,858
20
+ mini_arcade_core/engine/managers.py,sha256=eQJYe-xYtRha-FWxzJ3DcpwlcHwiT5sGt4oCD9ZPxEE,664
21
+ mini_arcade_core/engine/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ mini_arcade_core/engine/render/context.py,sha256=igoBmsasv3AJtrIIe2IGkjHXEm5ouEobvdN7Gmm0vxY,1327
23
+ mini_arcade_core/engine/render/effects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ mini_arcade_core/engine/render/effects/base.py,sha256=uix-kfzvN5j3dx655h_Yxe-a3rN1WTtN4tj5zeqFi3c,2357
25
+ mini_arcade_core/engine/render/effects/crt.py,sha256=75ejQVNhftN_GwZV9CYkHv2D672V84zDygmO9h_fo1A,2154
26
+ mini_arcade_core/engine/render/effects/registry.py,sha256=ZBbyyhF4K2gxb4f6hV35uh6RuSj7kOhQgcxwKocqJpY,1212
27
+ mini_arcade_core/engine/render/effects/vignette.py,sha256=K87CjIWpjlWN_tJVrnY3tW2njOvLvnuRezCwk05OgVw,2610
28
+ mini_arcade_core/engine/render/frame_packet.py,sha256=nYHvR7CHlIZa6ZazmPO2dU2P91vEkBjBzUVQGrOkaYc,624
29
+ mini_arcade_core/engine/render/packet.py,sha256=OiAPwGoVHo04OcUWMAoA_N1AFPUMyf8yxNgJthGj4-c,1440
30
+ mini_arcade_core/engine/render/passes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ mini_arcade_core/engine/render/passes/base.py,sha256=LWgWhfafbCvRKIFbt3koW-ibjYxMfKOIXyLNazCakcM,864
32
+ mini_arcade_core/engine/render/passes/begin_frame.py,sha256=RowMYnkSSQVlMCDVRHXEiy4Hb5Ru-VPBqxsmMzzkXuU,706
33
+ mini_arcade_core/engine/render/passes/end_frame.py,sha256=BBNyVhD8s7B9c_s373g9DDH_eVNGGVnMl5n0GhKIgiw,767
34
+ mini_arcade_core/engine/render/passes/lighting.py,sha256=ugmHHNNZWArL_Xs6-1SQIxLptUTQwe4M2sSPEk8X-7s,677
35
+ mini_arcade_core/engine/render/passes/postfx.py,sha256=YLLcJb1qwNwxg40f_6Nh1xXeGN1tsCnGd8_FdKsfRAM,1424
36
+ mini_arcade_core/engine/render/passes/ui.py,sha256=cct8v0Jjqv6w77IlKz2medPkQjPvG8cCifRv943qyFM,1129
37
+ mini_arcade_core/engine/render/passes/world.py,sha256=OhvDB8aPTkFGlHiROlL9A13pdjWAYGbo8NadkWCUK-c,1522
38
+ mini_arcade_core/engine/render/pipeline.py,sha256=A-Pxw0t0RbW_pswVtBEGNIgC-1dSNqwaK8p7JoXiw8k,3288
39
+ mini_arcade_core/engine/render/render_service.py,sha256=1ueir8MZ6Six5gAHt5StoICPAbyppX4DqzWb8HEuS9g,531
40
+ mini_arcade_core/engine/render/viewport.py,sha256=Fi7O04KRC6d3s01sP0cfchdwegt6s_vOdwomck9yryc,5972
41
+ mini_arcade_core/engine/scenes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ mini_arcade_core/engine/scenes/models.py,sha256=UKKGUWyFf_XFC09hICTFQ6_szDXytoI5KauCmr9ugoM,1217
43
+ mini_arcade_core/engine/scenes/scene_manager.py,sha256=xGzMH7tBsHExN9aYLZsx462aY9r5j-cDNqQvwnK3cI8,6455
44
+ mini_arcade_core/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ mini_arcade_core/runtime/audio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ mini_arcade_core/runtime/audio/audio_adapter.py,sha256=lnP35txPzSKX1_il0nXcK7RMF5Qp9Qhi9YMh_7LTdPM,588
47
+ mini_arcade_core/runtime/audio/audio_port.py,sha256=jBd9WabN41uK3MHjg_1n4AOw83NivJlGE2m430WZTnk,831
48
+ mini_arcade_core/runtime/capture/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ mini_arcade_core/runtime/capture/capture_adapter.py,sha256=XBtiKw3AS2dzB4QogPm9kjhiQAenS25guX87tg-zK58,4882
50
+ mini_arcade_core/runtime/capture/capture_port.py,sha256=niHi0pAo10mC9p73FxFkYBIGLOLRN0PiOvxE4Zgo5fM,1162
51
+ mini_arcade_core/runtime/context.py,sha256=ONKQryO3KEOOqHaByxCUola07kdjrnvr4WfXwgwTobk,1777
52
+ mini_arcade_core/runtime/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ mini_arcade_core/runtime/file/file_adapter.py,sha256=09q7G9Qijml9d4AAjo6HLC1yuoVTjE_7xaT8apT4mk0,523
54
+ mini_arcade_core/runtime/file/file_port.py,sha256=p1MouCSHXZw--rWNMw3aYBLU-of8mXaT_suopczPtM8,608
55
+ mini_arcade_core/runtime/input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
+ mini_arcade_core/runtime/input/input_adapter.py,sha256=vExQiwFIWTI3zYD8lmnD9TvoQPZvJfI6IINPJUqAdQ0,1467
57
+ mini_arcade_core/runtime/input/input_port.py,sha256=d4ptftwf92_LJdyaUMFxIsLHXBINzQyJACHn4laNyxQ,746
58
+ mini_arcade_core/runtime/input_frame.py,sha256=34-RAfOD-YScVLyRQrarpm7byFTHjsWM77lIH0JsmT8,2384
59
+ mini_arcade_core/runtime/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
+ mini_arcade_core/runtime/render/render_port.py,sha256=Sqp-JBh-iRzzGtgnO_nU1KiJEqyrTYPRDQbg04HdR0A,507
61
+ mini_arcade_core/runtime/scene/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ mini_arcade_core/runtime/scene/scene_query_adapter.py,sha256=FNkqXgwxfugX_xqqFlZl0ELXsrW_gco5Au0tJhMGLgQ,909
63
+ mini_arcade_core/runtime/scene/scene_query_port.py,sha256=qTikQVxOkJCdoMoH_lbe_ctJj7SWeJnnqDo6Ee0N_pQ,1019
64
+ mini_arcade_core/runtime/services.py,sha256=iYcXt2CTapgDzSb54DsPasYZ4jTN7tA_B0lV1Sl5b1g,1243
65
+ mini_arcade_core/runtime/window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ mini_arcade_core/runtime/window/window_adapter.py,sha256=VLZGYBVl7sGMmnk5mVowDleTyciAfE-Tc2woNFvRrgE,2890
67
+ mini_arcade_core/runtime/window/window_port.py,sha256=HBy2OjsZzlxbBDQiTqlKEbIaejpN1zDp5whgvKxZxaY,2322
68
+ mini_arcade_core/scenes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ mini_arcade_core/scenes/autoreg.py,sha256=wsuY7YUSZFmDyToKHFriAG78OU48-7J4BfL_X6T5GBg,1037
70
+ mini_arcade_core/scenes/debug_overlay.py,sha256=t7zWeTxosCUWj3gBDkYF2448EBM5zwLCOEmWVHXghMk,2495
71
+ mini_arcade_core/scenes/registry.py,sha256=DHliUGGiSLWugtRU9R6JeH5gK3GUDICmS-3iie6GtH8,3631
72
+ mini_arcade_core/scenes/sim_scene.py,sha256=32GVR9XHMak-afyyH9M6_UkCPAjRz8XiUKj2lcpAGAE,1061
73
+ mini_arcade_core/scenes/systems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ mini_arcade_core/scenes/systems/base_system.py,sha256=GfMrXsO8ynW3xOxWeav7Ug5XUbRnbF0vo8VzmG7gpec,1075
75
+ mini_arcade_core/scenes/systems/system_pipeline.py,sha256=Cy9y1DclbMLZZ-yx7OKYe34ORoGLNa6dReQfOdiO8SY,1642
76
+ mini_arcade_core/spaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ mini_arcade_core/spaces/d2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ mini_arcade_core/spaces/d2/boundaries2d.py,sha256=xeTnd0pW5DKfqaKsfSBXnufeb45aXNIspgHRyLXWejo,2804
79
+ mini_arcade_core/spaces/d2/collision2d.py,sha256=5IvgLnyVb8i0uzzZuum1noWsNhoxcvHOLaHkmrTMTxQ,1710
80
+ mini_arcade_core/spaces/d2/geometry2d.py,sha256=FuYzef-XdOyb1aeGLJbxINxr0WJHnqFFBgtbPi1WonY,1716
81
+ mini_arcade_core/spaces/d2/kinematics2d.py,sha256=AJ3DhPXNgm6wZYwCljMIE4_2BYx3E2rPcwhXTgQALkU,2030
82
+ mini_arcade_core/spaces/d2/physics2d.py,sha256=OQT7r-zMtmoKD2aWCSNmRAdI0OGIpxGX-pLR8LcAMbQ,1854
83
+ mini_arcade_core/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ mini_arcade_core/ui/menu.py,sha256=t2YpwiKqy4KZHfU9U7c6CUE3SNamBSyipWnd9Y4BVxA,25738
85
+ mini_arcade_core/utils/__init__.py,sha256=id1C0au8r1oIzGha42xXwnI9ojcU1hxPgto6QSh9H8c,236
86
+ mini_arcade_core/utils/deprecated_decorator.py,sha256=yrrW2ZqPskK-4MUTyIrMb465Wc54X2poV53ZQutZWqc,1140
87
+ mini_arcade_core/utils/logging.py,sha256=ygKpey6nikp30PrNDP_yRs8pxPPRbsQ0ivR6LUuEn3Q,6413
88
+ mini_arcade_core/utils/profiler.py,sha256=vLzrxDfAplgKGxpuzk4eFJx4t5DU5M3DQAn6sfS5D_4,8733
89
+ mini_arcade_core-1.2.0.dist-info/METADATA,sha256=PeD3ImQ4FG5bjuYKFdLThR59fOvr-lgOS3OaF1XVRis,8188
90
+ mini_arcade_core-1.2.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
91
+ mini_arcade_core-1.2.0.dist-info/licenses/LICENSE,sha256=3lHAuV0584cVS5vAqi2uC6GcsVgxUijvwvtZckyvaZ4,1096
92
+ mini_arcade_core-1.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.0
2
+ Generator: poetry-core 2.3.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,284 +0,0 @@
1
- """
2
- Input manager for handling input bindings and commands.
3
- """
4
-
5
- # TODO: Implement this manager into the new input system
6
- # Justification: These module will be used later.
7
- # pylint: disable=no-name-in-module,import-error,used-before-assignment
8
-
9
- from __future__ import annotations
10
-
11
- import logging
12
- from dataclasses import dataclass
13
- from typing import TYPE_CHECKING, Callable, Dict, Optional
14
-
15
- from mini_arcade_core.backend import Event, EventType
16
- from mini_arcade_core.keymaps import Key
17
-
18
- if TYPE_CHECKING:
19
- from mini_arcade_core.engine.commands import BaseCommand, BaseSceneCommand
20
- from mini_arcade_core.scenes.scene import Scene
21
-
22
- logger = logging.getLogger(__name__)
23
-
24
-
25
- Predicate = Callable[["Event"], bool]
26
-
27
-
28
- @dataclass(frozen=True)
29
- class InputBinding:
30
- """
31
- Defines an input binding.
32
-
33
- :ivar action (str): The action name.
34
- :ivar command (BaseCommand): The command to execute.
35
- :ivar predicate (Predicate): Predicate to match events.
36
- """
37
-
38
- action: str
39
- command: BaseCommand
40
- predicate: Predicate # decides whether this binding matches an event
41
-
42
-
43
- class InputManager:
44
- """
45
- Manager for handling input bindings and commands.
46
- """
47
-
48
- def __init__(self):
49
- # event_type -> key -> action -> command
50
- self._bindings: Dict[EventType, Dict[Key, Dict[str, BaseCommand]]] = {}
51
-
52
- # Justification: The method needs multiple optional parameters for flexibility.
53
- # pylint: disable=too-many-arguments
54
- def bind(
55
- self,
56
- event_type: EventType,
57
- action: str,
58
- command: BaseCommand,
59
- *,
60
- key: Optional[Key] = None,
61
- button: Optional[int] = None,
62
- predicate: Optional[Predicate] = None,
63
- ):
64
- """
65
- Generic binding.
66
-
67
- You can filter by:
68
- - key: for KEYDOWN/KEYUP
69
- - button: for MOUSEBUTTONDOWN/MOUSEBUTTONUP (if your Event exposes it)
70
- - predicate: custom matcher (for anything)
71
-
72
- :param event_type: The type of event to bind to.
73
- :type event_type: EventType
74
-
75
- :param action: The action name for the binding.
76
- :type action: str
77
-
78
- :param command: The command to execute when the binding is triggered.
79
- :type command: BaseCommand
80
-
81
- :param key: Optional key to filter KEYDOWN/KEYUP events.
82
- :type key: Key | None
83
-
84
- :param button: Optional button to filter MOUSEBUTTONDOWN/MOUSEBUTTONUP events.
85
- :type button: int | None
86
-
87
- :param predicate: Optional custom predicate to match events.
88
- :type predicate: Predicate | None
89
- """
90
- logger.debug(
91
- f"Binding {action} to {event_type} with key={key}, button={button}"
92
- )
93
-
94
- def default_predicate(ev: Event) -> bool:
95
- if key is not None and getattr(ev, "key", None) != key:
96
- return False
97
- if button is not None and getattr(ev, "button", None) != button:
98
- return False
99
- return True
100
-
101
- pred = predicate or default_predicate
102
- self._bindings.setdefault(event_type, []).append(
103
- InputBinding(action=action, command=command, predicate=pred)
104
- )
105
-
106
- # pylint: enable=too-many-arguments
107
-
108
- def unbind(self, event_type: EventType, action: str):
109
- """
110
- Remove bindings by action for an event type.
111
-
112
- :param event_type: The type of event to unbind from.
113
- :type event_type: EventType
114
-
115
- :param action: The action name of the binding to remove.
116
- :type action: str
117
- """
118
- lst = self._bindings.get(event_type, [])
119
- self._bindings[event_type] = [b for b in lst if b.action != action]
120
-
121
- def clear(self):
122
- """Clear all input bindings."""
123
- self._bindings.clear()
124
-
125
- def handle_event(self, event: Event, scene: Scene):
126
- """
127
- Handle an incoming event, executing any matching commands.
128
-
129
- :param event: The event to handle.
130
- :type event: Event
131
-
132
- :param scene: The current scene context.
133
- :type scene: Scene
134
- """
135
- et = event.type
136
-
137
- for binding in self._bindings.get(et, []):
138
- if binding.predicate(event):
139
- to_inject = (
140
- scene.model
141
- if isinstance(binding.command, BaseSceneCommand)
142
- else scene.game
143
- )
144
- binding.command.execute(to_inject)
145
-
146
- def on_quit(self, command: BaseCommand, action: str = "quit"):
147
- """
148
- Bind a command to the QUIT event.
149
-
150
- :param command: The command to execute on quit.
151
- :type command: BaseCommand
152
-
153
- :param action: The action name for the binding.
154
- :type action: str
155
- """
156
- self.bind(EventType.QUIT, action=action, command=command)
157
-
158
- def on_key_down(self, key: Key, command: BaseCommand, action: str):
159
- """
160
- Bind a command to a key down event.
161
-
162
- :param key: The key to bind to.
163
- :type key: Key
164
-
165
- :param command: The command to execute on key down.
166
- :type command: BaseCommand
167
-
168
- :param action: The action name for the binding.
169
- :type action: str
170
- """
171
- self.bind(EventType.KEYDOWN, key=key, action=action, command=command)
172
-
173
- def on_key_up(self, key: Key, command: BaseCommand, action: str):
174
- """
175
- Bind a command to a key up event.
176
-
177
- :param key: The key to bind to.
178
- :type key: Key
179
-
180
- :param command: The command to execute on key up.
181
- :type command: BaseCommand
182
-
183
- :param action: The action name for the binding.
184
- :type action: str
185
- """
186
- self.bind(EventType.KEYUP, key=key, action=action, command=command)
187
-
188
- def on_mouse_button_down(
189
- self, button: int, command: BaseCommand, action: str
190
- ):
191
- """
192
- Bind a command to a mouse button down event.
193
-
194
- :param button: The mouse button to bind to.
195
- :type button: int
196
-
197
- :param command: The command to execute on mouse button down.
198
- :type command: BaseCommand
199
-
200
- :param action: The action name for the binding.
201
- :type action: str
202
- """
203
- self.bind(
204
- EventType.MOUSEBUTTONDOWN,
205
- button=button,
206
- action=action,
207
- command=command,
208
- )
209
-
210
- def on_mouse_button_up(
211
- self, button: int, command: BaseCommand, action: str
212
- ):
213
- """
214
- Bind a command to a mouse button up event.
215
-
216
- :param button: The mouse button to bind to.
217
- :type button: int
218
-
219
- :param command: The command to execute on mouse button up.
220
- :type command: BaseCommand
221
-
222
- :param action: The action name for the binding.
223
- :type action: str
224
- """
225
- self.bind(
226
- EventType.MOUSEBUTTONUP,
227
- button=button,
228
- action=action,
229
- command=command,
230
- )
231
-
232
- def on_mouse_motion(
233
- self, command: BaseCommand, action: str = "mouse_motion"
234
- ):
235
- """
236
- Bind a command to mouse motion events.
237
-
238
- :param command: The command to execute on mouse motion.
239
- :type command: BaseCommand
240
-
241
- :param action: The action name for the binding.
242
- :type action: str
243
- """
244
- self.bind(EventType.MOUSEMOTION, action=action, command=command)
245
-
246
- def on_mouse_wheel(
247
- self, command: BaseCommand, action: str = "mouse_wheel"
248
- ):
249
- """
250
- Bind a command to mouse wheel events.
251
-
252
- :param command: The command to execute on mouse wheel.
253
- :type command: BaseCommand
254
-
255
- :param action: The action name for the binding.
256
- :type action: str
257
- """
258
- self.bind(EventType.MOUSEWHEEL, action=action, command=command)
259
-
260
- def on_window_resized(
261
- self, command: BaseCommand, action: str = "window_resized"
262
- ):
263
- """
264
- Bind a command to window resized events.
265
-
266
- :param command: The command to execute on window resize.
267
- :type command: BaseCommand
268
-
269
- :param action: The action name for the binding.
270
- :type action: str
271
- """
272
- self.bind(EventType.WINDOWRESIZED, action=action, command=command)
273
-
274
- def on_text_input(self, command: BaseCommand, action: str = "text_input"):
275
- """
276
- Bind a command to text input events.
277
-
278
- :param command: The command to execute on text input.
279
- :type command: BaseCommand
280
-
281
- :param action: The action name for the binding.
282
- :type action: str
283
- """
284
- self.bind(EventType.TEXTINPUT, action=action, command=command)
@@ -1,125 +0,0 @@
1
- """
2
- Module providing runtime adapters for window and scene management.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from mini_arcade_core.runtime.context import RuntimeContext
8
- from mini_arcade_core.runtime.scene.scene_port import (
9
- SceneEntry,
10
- ScenePolicy,
11
- ScenePort,
12
- StackItem,
13
- )
14
-
15
-
16
- class SceneAdapter(ScenePort):
17
- """
18
- Manages multiple scenes (not implemented).
19
- """
20
-
21
- def __init__(self, registry, game):
22
- self._registry = registry
23
- self._stack = []
24
- self._game = game
25
-
26
- @property
27
- def current_scene(self):
28
- return self._stack[-1].entry.scene if self._stack else None
29
-
30
- @property
31
- def visible_stack(self):
32
- return [e.scene for e in self.visible_entries()]
33
-
34
- def change(self, scene_id):
35
- self.clean()
36
- self.push(scene_id, as_overlay=False)
37
-
38
- def push(
39
- self,
40
- scene_id,
41
- *,
42
- as_overlay=False,
43
- policy=None,
44
- ):
45
- # default policy based on overlay vs base
46
- if policy is None:
47
- # base scenes: do not block anything by default
48
- policy = ScenePolicy()
49
- runtime_context = RuntimeContext.from_game(self._game)
50
- scene = self._registry.create(
51
- scene_id, runtime_context
52
- ) # or whatever your factory call is
53
- scene.on_enter()
54
-
55
- entry = SceneEntry(
56
- scene_id=scene_id,
57
- scene=scene,
58
- is_overlay=as_overlay,
59
- policy=policy,
60
- )
61
- self._stack.append(StackItem(entry=entry))
62
-
63
- def pop(self):
64
- if not self._stack:
65
- return
66
- item = self._stack.pop()
67
- item.entry.scene.on_exit()
68
-
69
- def clean(self):
70
- while self._stack:
71
- self.pop()
72
-
73
- def quit(self):
74
- self._game.quit()
75
-
76
- def visible_entries(self):
77
- entries = [i.entry for i in self._stack]
78
- # find highest opaque from top down; render starting there
79
- for idx in range(len(entries) - 1, -1, -1):
80
- if entries[idx].policy.is_opaque:
81
- return entries[idx:]
82
- return entries
83
-
84
- def update_entries(self):
85
- vis = self.visible_entries()
86
- if not vis:
87
- return []
88
- out = []
89
- for entry in reversed(vis): # top->down
90
- out.append(entry)
91
- if entry.policy.blocks_update:
92
- break
93
- return list(reversed(out)) # bottom->top order
94
-
95
- def input_entry(self):
96
- vis = self.visible_entries()
97
- if not vis:
98
- return None
99
-
100
- # If some scene blocks input, only scenes at/above it can receive.
101
- start_idx = 0
102
- for idx in range(len(vis) - 1, -1, -1):
103
- if vis[idx].policy.blocks_input:
104
- start_idx = idx
105
- break
106
-
107
- candidates = vis[start_idx:]
108
-
109
- # Pick the top-most candidate that actually receives input.
110
- for entry in reversed(candidates):
111
- if entry.policy.receives_input:
112
- return entry
113
-
114
- return None
115
-
116
- def has_scene(self, scene_id: str) -> bool:
117
- return any(item.entry.scene_id == scene_id for item in self._stack)
118
-
119
- def remove_scene(self, scene_id: str):
120
- # remove first match from top (overlay is usually near top)
121
- for i in range(len(self._stack) - 1, -1, -1):
122
- if self._stack[i].entry.scene_id == scene_id:
123
- item = self._stack.pop(i)
124
- item.entry.scene.on_exit()
125
- return
@@ -1,170 +0,0 @@
1
- """
2
- Service interfaces for runtime components.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from dataclasses import dataclass
8
- from typing import TYPE_CHECKING, List
9
-
10
- from mini_arcade_core.scenes.registry import SceneRegistry
11
-
12
- if TYPE_CHECKING:
13
- from mini_arcade_core.engine.game import Game
14
- from mini_arcade_core.scenes.scene import Scene
15
- from mini_arcade_core.sim.protocols import SimScene
16
-
17
-
18
- @dataclass(frozen=True)
19
- class ScenePolicy:
20
- """
21
- Controls how a scene behaves in the scene stack.
22
-
23
- blocks_update: if True, scenes below do not tick/update (pause modal)
24
- blocks_input: if True, scenes below do not receive input
25
- is_opaque: if True, scenes below are not rendered
26
- receives_input: if True, scene can receive input
27
- """
28
-
29
- blocks_update: bool = False
30
- blocks_input: bool = False
31
- is_opaque: bool = False
32
- receives_input: bool = True
33
-
34
-
35
- @dataclass(frozen=True)
36
- class SceneEntry:
37
- """
38
- An entry in the scene stack.
39
-
40
- :ivar scene_id (str): Identifier of the scene.
41
- :ivar scene (Scene): The scene instance.
42
- :ivar is_overlay (bool): Whether the scene is an overlay.
43
- :ivar policy (ScenePolicy): The scene's policy.
44
- """
45
-
46
- scene_id: str
47
- scene: SimScene
48
- is_overlay: bool
49
- policy: ScenePolicy
50
-
51
-
52
- @dataclass
53
- class StackItem:
54
- """
55
- An item in the scene stack.
56
-
57
- :ivar entry (SceneEntry): The scene entry.
58
- """
59
-
60
- entry: SceneEntry
61
-
62
-
63
- class ScenePort:
64
- """Interface for scene management operations."""
65
-
66
- _registry: SceneRegistry
67
- _stack: List[StackItem]
68
- _game: Game
69
-
70
- @property
71
- def current_scene(self) -> "SimScene | None":
72
- """
73
- Get the currently active scene.
74
-
75
- :return: The active Scene instance, or None if no scene is active.
76
- :rtype: SimScene | None
77
- """
78
-
79
- @property
80
- def visible_stack(self) -> List["SimScene"]:
81
- """
82
- Return the list of scenes that should be drawn (base + overlays).
83
- We draw from the top-most non-overlay scene upward.
84
-
85
- :return: List of visible Scene instances.
86
- :rtype: List[SimScene]
87
- """
88
-
89
- def change(self, scene_id: str):
90
- """
91
- Change the current scene to the specified scene.
92
-
93
- :param scene_id: Identifier of the scene to switch to.
94
- :type scene_id: str
95
- """
96
-
97
- def push(self, scene_id: str, *, as_overlay: bool = False):
98
- """
99
- Push a new scene onto the scene stack.
100
-
101
- :param scene_id: Identifier of the scene to push.
102
- :type scene_id: str
103
-
104
- :param as_overlay: Whether to push the scene as an overlay.
105
- :type as_overlay: bool
106
- """
107
-
108
- def pop(self) -> "Scene | None":
109
- """
110
- Pop the current scene from the scene stack.
111
-
112
- :return: The popped Scene instance, or None if the stack was empty.
113
- :rtype: Scene | None
114
- """
115
-
116
- def clean(self):
117
- """
118
- Clean up all scenes from the scene stack.
119
- """
120
-
121
- def quit(self):
122
- """
123
- Quit the game
124
- """
125
-
126
- def visible_entries(self) -> list[SceneEntry]:
127
- """
128
- Render from bottom->top unless an opaque entry exists; if so,
129
- render only from that entry up.
130
-
131
- :return: List of SceneEntry instances to render.
132
- :rtype: list[SceneEntry]
133
- """
134
-
135
- def update_entries(self) -> list[SceneEntry]:
136
- """
137
- Tick/update scenes considering blocks_update.
138
- Typical: pause overlay blocks update below it.
139
-
140
- :return: List of SceneEntry instances to update.
141
- :rtype: list[SceneEntry]
142
- """
143
-
144
- def input_entry(self) -> SceneEntry | None:
145
- """
146
- Who gets input this frame. If top blocks_input, only it receives input.
147
- If not, top still gets input (v1 simple). Later you can allow fall-through.
148
-
149
- :return: The SceneEntry that receives input, or None if no scenes are active.
150
- :rtype: SceneEntry | None
151
- """
152
-
153
- def has_scene(self, scene_id: str) -> bool:
154
- """
155
- Check if a scene with the given ID exists in the stack.
156
-
157
- :param scene_id: Identifier of the scene to check.
158
- :type scene_id: str
159
-
160
- :return: True if the scene exists in the stack, False otherwise.
161
- :rtype: bool
162
- """
163
-
164
- def remove_scene(self, scene_id: str):
165
- """
166
- Remove a scene with the given ID from the stack.
167
-
168
- :param scene_id: Identifier of the scene to remove.
169
- :type scene_id: str
170
- """