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.
- mini_arcade_core/__init__.py +43 -60
- 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/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 -22
- mini_arcade_core/managers/cheats.py +71 -240
- mini_arcade_core/managers/inputs.py +5 -1
- 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 -22
- 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 -12
- mini_arcade_core/spaces/d2/__init__.py +0 -30
- mini_arcade_core/spaces/d2/collision2d.py +25 -28
- mini_arcade_core/spaces/d2/geometry2d.py +18 -0
- mini_arcade_core/spaces/d2/kinematics2d.py +2 -8
- mini_arcade_core/spaces/d2/physics2d.py +9 -0
- mini_arcade_core/ui/__init__.py +0 -26
- mini_arcade_core/ui/menu.py +265 -84
- 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.10.0.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.10.0.dist-info → mini_arcade_core-1.0.0.dist-info}/WHEEL +1 -1
- mini_arcade_core/commands.py +0 -84
- mini_arcade_core/entity.py +0 -72
- mini_arcade_core/game.py +0 -287
- mini_arcade_core/keymaps/__init__.py +0 -15
- mini_arcade_core/managers/base.py +0 -132
- mini_arcade_core/managers/entities.py +0 -38
- mini_arcade_core/managers/overlays.py +0 -53
- mini_arcade_core/managers/system.py +0 -26
- mini_arcade_core/scenes/model.py +0 -34
- mini_arcade_core/scenes/runtime.py +0 -29
- mini_arcade_core/scenes/scene.py +0 -109
- mini_arcade_core/scenes/system.py +0 -69
- mini_arcade_core/ui/overlays.py +0 -41
- mini_arcade_core-0.10.0.dist-info/RECORD +0 -40
- /mini_arcade_core/{keymaps → backend}/keys.py +0 -0
- {mini_arcade_core-0.10.0.dist-info → mini_arcade_core-1.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,8 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
|
-
from
|
|
11
|
-
|
|
10
|
+
from .collision2d import Collider2D
|
|
12
11
|
from .geometry2d import Position2D, Size2D
|
|
13
12
|
from .physics2d import Velocity2D
|
|
14
13
|
|
|
@@ -28,7 +27,7 @@ class KinematicData:
|
|
|
28
27
|
size: Size2D
|
|
29
28
|
velocity: Velocity2D
|
|
30
29
|
time_scale: float = 1.0
|
|
31
|
-
|
|
30
|
+
collider: Optional[Collider2D] = None
|
|
32
31
|
|
|
33
32
|
# Justification: Convenience factory with many params.
|
|
34
33
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
@@ -42,7 +41,6 @@ class KinematicData:
|
|
|
42
41
|
vx: float = 0.0,
|
|
43
42
|
vy: float = 0.0,
|
|
44
43
|
time_scale: float = 1.0,
|
|
45
|
-
color: Optional[Color] = None,
|
|
46
44
|
) -> "KinematicData":
|
|
47
45
|
"""
|
|
48
46
|
Convenience factory for rectangular kinematic data.
|
|
@@ -65,9 +63,6 @@ class KinematicData:
|
|
|
65
63
|
:param vy: Velocity in the Y direction.
|
|
66
64
|
:type vy: float
|
|
67
65
|
|
|
68
|
-
:param color: Optional color for rendering.
|
|
69
|
-
:type color: Optional[Color]
|
|
70
|
-
|
|
71
66
|
:return: KinematicData instance with the specified parameters.
|
|
72
67
|
:rtype: KinematicData
|
|
73
68
|
"""
|
|
@@ -76,7 +71,6 @@ class KinematicData:
|
|
|
76
71
|
size=Size2D(int(width), int(height)),
|
|
77
72
|
velocity=Velocity2D(float(vx), float(vy)),
|
|
78
73
|
time_scale=time_scale,
|
|
79
|
-
color=color,
|
|
80
74
|
)
|
|
81
75
|
|
|
82
76
|
# pylint: enable=too-many-arguments,too-many-positional-arguments
|
|
@@ -19,6 +19,15 @@ class Velocity2D:
|
|
|
19
19
|
vx: float = 0.0
|
|
20
20
|
vy: float = 0.0
|
|
21
21
|
|
|
22
|
+
def to_tuple(self) -> tuple[float, float]:
|
|
23
|
+
"""
|
|
24
|
+
Convert Velocity2D to a tuple.
|
|
25
|
+
|
|
26
|
+
:return: Tuple of (vx, vy).
|
|
27
|
+
:rtype: tuple[float, float]
|
|
28
|
+
"""
|
|
29
|
+
return (self.vx, self.vy)
|
|
30
|
+
|
|
22
31
|
def advance(self, x: float, y: float, dt: float) -> tuple[float, float]:
|
|
23
32
|
"""Return new (x, y) after dt seconds."""
|
|
24
33
|
return x + self.vx * dt, y + self.vy * dt
|
mini_arcade_core/ui/__init__.py
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
UI utilities and components for Mini Arcade Core.
|
|
3
|
-
Includes buttons, labels, and layout management.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
from .menu import (
|
|
9
|
-
BaseMenuScene,
|
|
10
|
-
Menu,
|
|
11
|
-
MenuItem,
|
|
12
|
-
MenuModel,
|
|
13
|
-
MenuStyle,
|
|
14
|
-
MenuSystem,
|
|
15
|
-
)
|
|
16
|
-
from .overlays import BaseOverlay
|
|
17
|
-
|
|
18
|
-
__all__ = [
|
|
19
|
-
"Menu",
|
|
20
|
-
"MenuItem",
|
|
21
|
-
"MenuStyle",
|
|
22
|
-
"MenuModel",
|
|
23
|
-
"MenuSystem",
|
|
24
|
-
"BaseMenuScene",
|
|
25
|
-
"BaseOverlay",
|
|
26
|
-
]
|
mini_arcade_core/ui/menu.py
CHANGED
|
@@ -7,12 +7,17 @@ from __future__ import annotations
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from typing import Callable, Optional, Sequence
|
|
9
9
|
|
|
10
|
-
from mini_arcade_core.backend import Backend
|
|
11
|
-
from mini_arcade_core.
|
|
12
|
-
from mini_arcade_core.
|
|
13
|
-
from mini_arcade_core.
|
|
14
|
-
from mini_arcade_core.
|
|
15
|
-
from mini_arcade_core.
|
|
10
|
+
from mini_arcade_core.backend import Backend
|
|
11
|
+
from mini_arcade_core.backend.events import Event, EventType
|
|
12
|
+
from mini_arcade_core.backend.keys import Key
|
|
13
|
+
from mini_arcade_core.backend.types import Color
|
|
14
|
+
from mini_arcade_core.engine.commands import Command, CommandQueue, QuitCommand
|
|
15
|
+
from mini_arcade_core.engine.render.packet import RenderPacket
|
|
16
|
+
from mini_arcade_core.runtime.context import RuntimeContext
|
|
17
|
+
from mini_arcade_core.runtime.input_frame import InputFrame
|
|
18
|
+
from mini_arcade_core.scenes.systems.system_pipeline import SystemPipeline
|
|
19
|
+
from mini_arcade_core.sim.protocols import SimScene
|
|
20
|
+
from mini_arcade_core.spaces.d2.geometry2d import Size2D
|
|
16
21
|
|
|
17
22
|
|
|
18
23
|
@dataclass(frozen=True)
|
|
@@ -26,20 +31,20 @@ class MenuItem:
|
|
|
26
31
|
|
|
27
32
|
id: str
|
|
28
33
|
label: str
|
|
29
|
-
|
|
30
|
-
label_fn: Optional[Callable[[
|
|
34
|
+
command_factory: Callable[[], Command]
|
|
35
|
+
label_fn: Optional[Callable[[object], str]] = None
|
|
31
36
|
|
|
32
|
-
def resolved_label(self,
|
|
37
|
+
def resolved_label(self, ctx: object) -> str:
|
|
33
38
|
"""
|
|
34
39
|
Get the resolved label for this menu item.
|
|
35
40
|
|
|
36
|
-
:param
|
|
37
|
-
:type
|
|
41
|
+
:param ctx: The current ctx instance.
|
|
42
|
+
:type ctx: object
|
|
38
43
|
|
|
39
44
|
:return: The resolved label string.
|
|
40
45
|
:rtype: str
|
|
41
46
|
"""
|
|
42
|
-
return self.label_fn(
|
|
47
|
+
return self.label_fn(ctx) if self.label_fn else self.label
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
# Justification: Data container for styling options needs
|
|
@@ -159,6 +164,21 @@ class Menu:
|
|
|
159
164
|
|
|
160
165
|
# pylint: enable=too-many-arguments
|
|
161
166
|
|
|
167
|
+
def set_items(self, items: Sequence[MenuItem]):
|
|
168
|
+
"""Set the menu items.
|
|
169
|
+
:param items: Sequence of new MenuItem instances.
|
|
170
|
+
:type items: Sequence[MenuItem]
|
|
171
|
+
"""
|
|
172
|
+
self.items = list(items)
|
|
173
|
+
|
|
174
|
+
def set_selected_index(self, index: int):
|
|
175
|
+
"""Set the selected index of the menu.
|
|
176
|
+
:param index: New selected index.
|
|
177
|
+
:type index: int
|
|
178
|
+
"""
|
|
179
|
+
if 0 <= index < len(self.items):
|
|
180
|
+
self.selected_index = index
|
|
181
|
+
|
|
162
182
|
def set_labels(self, labels: Sequence[str]):
|
|
163
183
|
"""Set the labels of the menu items.
|
|
164
184
|
:param labels: Sequence of new labels for the menu items.
|
|
@@ -170,7 +190,7 @@ class Menu:
|
|
|
170
190
|
self.items[index] = MenuItem(
|
|
171
191
|
id=item.id,
|
|
172
192
|
label=label,
|
|
173
|
-
|
|
193
|
+
command_factory=item.command_factory,
|
|
174
194
|
label_fn=item.label_fn,
|
|
175
195
|
)
|
|
176
196
|
|
|
@@ -242,7 +262,7 @@ class Menu:
|
|
|
242
262
|
"Menu requires viewport=Size2D for centering/layout"
|
|
243
263
|
)
|
|
244
264
|
|
|
245
|
-
vw, vh = self.viewport
|
|
265
|
+
vw, vh = self.viewport
|
|
246
266
|
|
|
247
267
|
# 0) Solid background (for main menus)
|
|
248
268
|
if self.style.background_color is not None:
|
|
@@ -465,130 +485,291 @@ class Menu:
|
|
|
465
485
|
|
|
466
486
|
|
|
467
487
|
@dataclass
|
|
468
|
-
class MenuModel
|
|
469
|
-
"""
|
|
488
|
+
class MenuModel:
|
|
489
|
+
"""
|
|
490
|
+
Data model for menu scenes.
|
|
491
|
+
|
|
492
|
+
:ivar selected (int): Currently selected menu item index.
|
|
493
|
+
:ivar move_cooldown (float): Cooldown time between menu moves.
|
|
494
|
+
:ivar _cooldown_timer (float): Internal timer for move cooldown.
|
|
495
|
+
"""
|
|
496
|
+
|
|
497
|
+
selected: int = 0
|
|
498
|
+
move_cooldown: float = 0.12
|
|
499
|
+
_cooldown_timer: float = 0.0
|
|
470
500
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
501
|
+
def step_timer(self, dt: float):
|
|
502
|
+
"""
|
|
503
|
+
Step the internal cooldown timer.
|
|
504
|
+
|
|
505
|
+
:param dt: Delta time since last update.
|
|
506
|
+
:type dt: float
|
|
507
|
+
"""
|
|
508
|
+
if self._cooldown_timer > 0:
|
|
509
|
+
self._cooldown_timer = max(0.0, self._cooldown_timer - dt)
|
|
510
|
+
|
|
511
|
+
def can_move(self) -> bool:
|
|
512
|
+
"""
|
|
513
|
+
Check if the menu can move selection (cooldown elapsed).
|
|
514
|
+
|
|
515
|
+
:return: True if movement is allowed, False otherwise.
|
|
516
|
+
:rtype: bool
|
|
517
|
+
"""
|
|
518
|
+
return self._cooldown_timer <= 0.0
|
|
519
|
+
|
|
520
|
+
def consume_move(self):
|
|
521
|
+
"""Consume a move action and reset the cooldown timer."""
|
|
522
|
+
self._cooldown_timer = self.move_cooldown
|
|
474
523
|
|
|
475
524
|
|
|
476
|
-
|
|
525
|
+
# TODO: Solve too-many-instance-attributes warning later
|
|
526
|
+
# Justification: Context for menu tick needs multiple attributes.
|
|
527
|
+
# pylint: disable=too-many-instance-attributes
|
|
528
|
+
@dataclass
|
|
529
|
+
class MenuTickContext:
|
|
477
530
|
"""
|
|
478
|
-
|
|
531
|
+
Context for a single tick of the menu scene.
|
|
532
|
+
|
|
533
|
+
:ivar input_frame (InputFrame): The current input frame.
|
|
534
|
+
:ivar dt (float): Delta time since last tick.
|
|
535
|
+
:ivar menu (Menu): The Menu instance.
|
|
536
|
+
:ivar model (MenuModel): The MenuModel instance.
|
|
537
|
+
:ivar commands (CommandQueue): The command queue for pushing commands.
|
|
538
|
+
:ivar intent (MenuIntent | None): The current menu intent.
|
|
539
|
+
:ivar quit_cmd_factory (callable | None): Factory for quit command.
|
|
540
|
+
:ivar packet (RenderPacket | None): The resulting render packet.
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
input_frame: InputFrame
|
|
544
|
+
dt: float
|
|
545
|
+
|
|
546
|
+
menu: "Menu"
|
|
547
|
+
model: "MenuModel"
|
|
548
|
+
commands: CommandQueue
|
|
549
|
+
|
|
550
|
+
# computed data that systems can write into
|
|
551
|
+
intent: "MenuIntent | None" = None
|
|
552
|
+
|
|
553
|
+
# scene hook: a callable to produce quit cmd (per scene override)
|
|
554
|
+
quit_cmd_factory: callable | None = None
|
|
555
|
+
|
|
556
|
+
# final output
|
|
557
|
+
packet: RenderPacket | None = None
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
# pylint: enable=too-many-instance-attributes
|
|
561
|
+
|
|
479
562
|
|
|
480
|
-
|
|
563
|
+
@dataclass(frozen=True)
|
|
564
|
+
class MenuIntent:
|
|
481
565
|
"""
|
|
566
|
+
Represents the user's intent in the menu for the current tick.
|
|
482
567
|
|
|
483
|
-
|
|
484
|
-
|
|
568
|
+
:ivar move_up (bool): Whether the user intends to move up.
|
|
569
|
+
:ivar move_down (bool): Whether the user intends to move down.
|
|
570
|
+
:ivar select (bool): Whether the user intends to select the current item.
|
|
571
|
+
:ivar quit (bool): Whether the user intends to quit the menu.
|
|
572
|
+
"""
|
|
485
573
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
574
|
+
move_up: bool = False
|
|
575
|
+
move_down: bool = False
|
|
576
|
+
select: bool = False
|
|
577
|
+
quit: bool = False
|
|
489
578
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
579
|
+
|
|
580
|
+
@dataclass
|
|
581
|
+
class MenuInputSystem:
|
|
582
|
+
"""Converts InputFrame -> MenuIntent."""
|
|
583
|
+
|
|
584
|
+
name: str = "menu_input"
|
|
585
|
+
order: int = 10
|
|
586
|
+
|
|
587
|
+
def step(self, ctx: MenuTickContext):
|
|
588
|
+
"""Step the input system to extract menu intent."""
|
|
589
|
+
pressed = ctx.input_frame.keys_pressed
|
|
590
|
+
ctx.intent = MenuIntent(
|
|
591
|
+
move_up=Key.UP in pressed,
|
|
592
|
+
move_down=Key.DOWN in pressed,
|
|
593
|
+
select=(Key.ENTER in pressed) or (Key.SPACE in pressed),
|
|
594
|
+
quit=Key.ESCAPE in pressed,
|
|
497
595
|
)
|
|
498
596
|
|
|
499
|
-
def handle_event(self, event: Event) -> bool:
|
|
500
|
-
if self.menu is None:
|
|
501
|
-
return False
|
|
502
597
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
up_key=self.scene.model.up_key,
|
|
507
|
-
down_key=self.scene.model.down_key,
|
|
508
|
-
select_key=self.scene.model.select_key,
|
|
509
|
-
)
|
|
510
|
-
return selected_action
|
|
598
|
+
@dataclass
|
|
599
|
+
class MenuNavigationSystem:
|
|
600
|
+
"""Menu navigation system."""
|
|
511
601
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
602
|
+
name: str = "menu_nav"
|
|
603
|
+
order: int = 20
|
|
604
|
+
|
|
605
|
+
def step(self, ctx: MenuTickContext):
|
|
606
|
+
"""Update menu selection based on intent."""
|
|
607
|
+
intent = ctx.intent
|
|
608
|
+
if intent is None:
|
|
609
|
+
return
|
|
610
|
+
|
|
611
|
+
ctx.model.step_timer(ctx.dt)
|
|
612
|
+
|
|
613
|
+
if not ctx.model.can_move():
|
|
614
|
+
return
|
|
615
|
+
|
|
616
|
+
if intent.move_up:
|
|
617
|
+
ctx.menu.move_up()
|
|
618
|
+
ctx.model.selected = ctx.menu.selected_index
|
|
619
|
+
ctx.model.consume_move()
|
|
620
|
+
return
|
|
621
|
+
|
|
622
|
+
if intent.move_down:
|
|
623
|
+
ctx.menu.move_down()
|
|
624
|
+
ctx.model.selected = ctx.menu.selected_index
|
|
625
|
+
ctx.model.consume_move()
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
@dataclass
|
|
629
|
+
class MenuActionSystem:
|
|
630
|
+
"""Menu action execution system."""
|
|
631
|
+
|
|
632
|
+
name: str = "menu_actions"
|
|
633
|
+
order: int = 30
|
|
634
|
+
|
|
635
|
+
def step(self, ctx: MenuTickContext):
|
|
636
|
+
"""Execute actions based on menu intent."""
|
|
637
|
+
intent = ctx.intent
|
|
638
|
+
if intent is None:
|
|
639
|
+
return
|
|
640
|
+
|
|
641
|
+
if intent.select and ctx.menu.items:
|
|
642
|
+
item = ctx.menu.items[ctx.menu.selected_index]
|
|
643
|
+
ctx.commands.push(item.command_factory())
|
|
644
|
+
|
|
645
|
+
if intent.quit and ctx.quit_cmd_factory is not None:
|
|
646
|
+
cmd = ctx.quit_cmd_factory()
|
|
647
|
+
if cmd is not None:
|
|
648
|
+
ctx.commands.push(cmd)
|
|
517
649
|
|
|
518
650
|
|
|
519
|
-
|
|
651
|
+
@dataclass
|
|
652
|
+
class MenuRenderSystem:
|
|
653
|
+
"""Menu rendering system."""
|
|
654
|
+
|
|
655
|
+
name: str = "menu_render"
|
|
656
|
+
order: int = 100
|
|
657
|
+
|
|
658
|
+
def step(self, ctx: MenuTickContext):
|
|
659
|
+
"""Set the render packet to draw the menu."""
|
|
660
|
+
ctx.packet = RenderPacket.from_ops([ctx.menu.draw])
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
class BaseMenuScene(SimScene):
|
|
520
664
|
"""
|
|
521
665
|
Base scene class for menu-based scenes.
|
|
522
666
|
|
|
523
667
|
:ivar model (MenuModel): The data model for the menu scene.
|
|
524
668
|
"""
|
|
525
669
|
|
|
526
|
-
|
|
670
|
+
menu: Menu
|
|
671
|
+
systems: SystemPipeline[MenuTickContext]
|
|
527
672
|
|
|
528
|
-
def __init__(self,
|
|
529
|
-
super().__init__(
|
|
673
|
+
def __init__(self, ctx: RuntimeContext):
|
|
674
|
+
super().__init__(ctx)
|
|
530
675
|
self.model = MenuModel()
|
|
531
676
|
|
|
532
|
-
# hooks
|
|
677
|
+
# ---- hooks (same spirit as before) ----
|
|
678
|
+
|
|
533
679
|
@property
|
|
534
680
|
def menu_title(self) -> str | None:
|
|
535
681
|
"""
|
|
536
|
-
|
|
682
|
+
Get the title of the menu.
|
|
537
683
|
|
|
538
|
-
:return:
|
|
684
|
+
:return: The menu title string, or None for no title.
|
|
539
685
|
:rtype: str | None
|
|
540
686
|
"""
|
|
541
687
|
return None
|
|
542
688
|
|
|
543
689
|
def menu_style(self) -> MenuStyle:
|
|
544
690
|
"""
|
|
545
|
-
|
|
691
|
+
Get the style configuration for the menu.
|
|
546
692
|
|
|
547
|
-
:return: MenuStyle instance.
|
|
693
|
+
:return: The MenuStyle instance for styling the menu.
|
|
548
694
|
:rtype: MenuStyle
|
|
549
695
|
"""
|
|
550
696
|
return MenuStyle()
|
|
551
697
|
|
|
552
698
|
def menu_items(self) -> list[MenuItem]:
|
|
553
699
|
"""
|
|
554
|
-
|
|
700
|
+
Get the list of menu items for the menu.
|
|
555
701
|
|
|
556
|
-
:return: List of MenuItem instances.
|
|
702
|
+
:return: List of MenuItem instances for the menu.
|
|
557
703
|
:rtype: list[MenuItem]
|
|
558
|
-
|
|
559
|
-
:raises NotImplementedError: If not overridden in subclass.
|
|
560
704
|
"""
|
|
561
705
|
raise NotImplementedError
|
|
562
706
|
|
|
563
|
-
def quit_command(self)
|
|
707
|
+
def quit_command(self):
|
|
564
708
|
"""
|
|
565
|
-
|
|
709
|
+
Get the command to execute when quitting the menu.
|
|
566
710
|
|
|
567
|
-
:return:
|
|
568
|
-
:rtype:
|
|
711
|
+
:return: The command to execute on quit.
|
|
712
|
+
:rtype: Command
|
|
569
713
|
"""
|
|
570
|
-
|
|
714
|
+
# default behavior: quit game
|
|
715
|
+
return QuitCommand()
|
|
571
716
|
|
|
572
717
|
def on_enter(self):
|
|
573
|
-
|
|
574
|
-
|
|
718
|
+
self.menu = Menu(
|
|
719
|
+
self._build_display_items(),
|
|
720
|
+
viewport=self.context.services.window.size,
|
|
721
|
+
title=self.menu_title,
|
|
722
|
+
style=self.menu_style(),
|
|
723
|
+
)
|
|
724
|
+
self.menu.selected_index = self.model.selected
|
|
725
|
+
self.systems = SystemPipeline()
|
|
726
|
+
self.systems.extend(
|
|
727
|
+
[
|
|
728
|
+
MenuInputSystem(),
|
|
729
|
+
MenuNavigationSystem(),
|
|
730
|
+
MenuActionSystem(),
|
|
731
|
+
MenuRenderSystem(),
|
|
732
|
+
]
|
|
733
|
+
)
|
|
575
734
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if
|
|
579
|
-
|
|
580
|
-
self.services.input.on_quit(cmd, "quit")
|
|
735
|
+
def tick(self, input_frame: InputFrame, dt: float) -> RenderPacket:
|
|
736
|
+
items = self.menu_items()
|
|
737
|
+
if not items:
|
|
738
|
+
return RenderPacket()
|
|
581
739
|
|
|
582
|
-
self.
|
|
740
|
+
self.model.step_timer(dt)
|
|
583
741
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
return
|
|
587
|
-
self.services.input.handle_event(event, self)
|
|
742
|
+
self.menu.set_items(self._build_display_items())
|
|
743
|
+
self.menu.set_selected_index(self.model.selected)
|
|
588
744
|
|
|
589
|
-
|
|
590
|
-
|
|
745
|
+
ctx = MenuTickContext(
|
|
746
|
+
input_frame=input_frame,
|
|
747
|
+
dt=dt,
|
|
748
|
+
menu=self.menu,
|
|
749
|
+
model=self.model,
|
|
750
|
+
commands=self.context.command_queue,
|
|
751
|
+
quit_cmd_factory=self.quit_command,
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
self.systems.step(ctx)
|
|
591
755
|
|
|
592
|
-
|
|
756
|
+
# always return packet from pipeline
|
|
757
|
+
return ctx.packet or RenderPacket()
|
|
593
758
|
|
|
594
|
-
def
|
|
759
|
+
def _build_display_items(self) -> list[MenuItem]:
|
|
760
|
+
"""
|
|
761
|
+
Resolve dynamic labels (label_fn(ctx)) into the label field the Menu draws.
|
|
762
|
+
Keeps command_factory intact.
|
|
763
|
+
"""
|
|
764
|
+
src = self.menu_items()
|
|
765
|
+
out: list[MenuItem] = []
|
|
766
|
+
for it in src:
|
|
767
|
+
out.append(
|
|
768
|
+
MenuItem(
|
|
769
|
+
id=it.id,
|
|
770
|
+
label=it.resolved_label(self.context),
|
|
771
|
+
command_factory=it.command_factory,
|
|
772
|
+
label_fn=it.label_fn,
|
|
773
|
+
)
|
|
774
|
+
)
|
|
775
|
+
return out
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deprecated utilities for the mini-arcade-core package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import functools
|
|
8
|
+
|
|
9
|
+
from mini_arcade_core.utils.logging import logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def deprecated(
|
|
13
|
+
reason: str | None = None,
|
|
14
|
+
version: str | None = None,
|
|
15
|
+
alternative: str | None = None,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Mark a function as deprecated.
|
|
19
|
+
|
|
20
|
+
:param reason: Optional reason for deprecation
|
|
21
|
+
:type reason: str | None
|
|
22
|
+
|
|
23
|
+
:param version: Optional version when it will be removed
|
|
24
|
+
:type version: str | None
|
|
25
|
+
|
|
26
|
+
:param alternative: Optional alternative function to use
|
|
27
|
+
:type alternative: str | None
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def decorator(func):
|
|
31
|
+
@functools.wraps(func)
|
|
32
|
+
def wrapper(*args, **kwargs):
|
|
33
|
+
message = f"The function {func.__name__} is deprecated"
|
|
34
|
+
if version:
|
|
35
|
+
message += f" and will be removed in version {version}"
|
|
36
|
+
if reason:
|
|
37
|
+
message += f". {reason}"
|
|
38
|
+
if alternative:
|
|
39
|
+
message += f" Use {alternative} instead."
|
|
40
|
+
logger.warning(message)
|
|
41
|
+
return func(*args, **kwargs)
|
|
42
|
+
|
|
43
|
+
return wrapper
|
|
44
|
+
|
|
45
|
+
return decorator
|