Source code for mini_arcade_core.scenes.systems.builtins.powerups
"""
Reusable pickup / power-up collection helpers.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Callable, Generic, Iterable, TypeVar
from mini_arcade_core.engine.entities import BaseEntity
from mini_arcade_core.scenes.systems.phases import SystemPhase
from mini_arcade_core.spaces.collision.intersections import intersects_entities
# pylint: disable=invalid-name
TCtx = TypeVar("TCtx")
# pylint: enable=invalid-name
def _default_enabled_when(_ctx: object) -> bool:
return True
def _default_predicate(_ctx: object, _entity: BaseEntity) -> bool:
return True
[docs]
@dataclass(frozen=True)
class PickupCollisionBinding(Generic[TCtx]):
"""
Declarative collector-vs-pickup intersection rule.
"""
collectors_getter: Callable[[TCtx], Iterable[BaseEntity]]
pickups_getter: Callable[[TCtx], Iterable[BaseEntity]]
predicate: Callable[[TCtx, BaseEntity], bool] = _default_predicate
on_collect: Callable[[TCtx, BaseEntity, BaseEntity], None] | None = None
remove_collected: bool = True
[docs]
@dataclass
class PickupCollisionSystem(Generic[TCtx]):
"""
Collect overlapping pickup-like entities and optionally remove them.
"""
name: str = "common_pickup_collision"
phase: int = SystemPhase.SIMULATION
order: int = 48
enabled_when: Callable[[TCtx], bool] = _default_enabled_when
bindings: tuple[PickupCollisionBinding[TCtx], ...] = ()
[docs]
def step(self, ctx: TCtx) -> None:
"""Resolve pickup collection for all configured bindings."""
if not self.enabled_when(ctx):
return
ids_to_remove: set[int] = set()
for binding in self.bindings:
collectors = tuple(binding.collectors_getter(ctx))
if not collectors:
continue
for pickup in tuple(binding.pickups_getter(ctx)):
pickup_id = int(pickup.id)
if pickup_id in ids_to_remove:
continue
if not binding.predicate(ctx, pickup):
continue
for collector in collectors:
if not intersects_entities(collector, pickup):
continue
if binding.on_collect is not None:
binding.on_collect(ctx, collector, pickup)
if binding.remove_collected:
ids_to_remove.add(pickup_id)
break
if ids_to_remove:
ctx.world.remove_entities_by_ids(ids_to_remove)
__all__ = [
"PickupCollisionBinding",
"PickupCollisionSystem",
]