Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion amoginarium/logic/_run_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ def run_continuous( # noqa: PLR0917

# update entities
last_update_success = lp.update_entities(delta)
GameCollisions.collision_manager.calculate_all_collisions()
GameCollisions.calculate_all_collisions()

# don't update if paused
if lp.paused:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from amoginarium import pv
from amoginarium.shared import BaseLogicEntityLike, ENTITY_COUNTER

from .._groups import Dead, Updated

if tp.TYPE_CHECKING:
from ctypes import Array
from types import EllipsisType
Expand All @@ -36,6 +34,14 @@ class BaseLogicEntity(BaseLogicEntityLike):
- Groups
- Update
- Visibility

:ivar _parent: The parent entity of this entity, optional.
:ivar _children: List of all children that this entity is the parent to.
:ivar _lifetime: Time since entity spawn.
:ivar _runtime_buffer: Logic runtime buffer.
:ivar __id: Entity id (+ buffer location).
:ivar __groups: List of groups this entity is in.
:ivar _alive: Whether the entity is alive or already dead.
"""

__slots__ = [
Expand All @@ -57,7 +63,6 @@ class BaseLogicEntity(BaseLogicEntityLike):
__groups: list[LogicGroup]

_alive: bool

# endregion

def __init__(
Expand Down Expand Up @@ -88,39 +93,41 @@ def __init__(
# directly write to RAM to make sure the graphics entity has correct data
pv.E_BUFF[self.__id] = self._runtime_buffer[self.__id]

self.add(Updated)

# region Properties
@property
def alive(self) -> bool:
"""Whether the entity is alive."""
""":return: Whether the entity is alive."""
return self._alive

@property
def id(self) -> int:
""":return: entity id (+ buffer location)"""
""":return: Entity id (+ buffer location)"""
return self.__id

@property
def parent(self) -> BaseLogicEntity | None:
""":return: Entity parent if present"""
""":return: Entity parent if present."""
return self._parent

def parent_died(self) -> None:
"""Call when the parent dies."""
self._parent = None

@property
def root(self) -> BaseLogicEntity:
""":return: root entity; entity parent if present else self"""
""":return: Root entity; entity parent if present else self"""
if self._parent:
return self._parent.root
return self

@property
def children(self) -> list[EntityChildViable]:
""":return: list of all children of this entity"""
""":return: List of all children of this entity"""
return self._children

@property
def _buffer(self) -> base_entity_t:
""":return: runtime buffer data for this entity"""
""":return: Runtime buffer data for this entity"""
return self._runtime_buffer[self.__id]

@property
Expand Down Expand Up @@ -228,14 +235,18 @@ def _kill(
# commit suicide
for group in self.__groups:
group.remove(self)
self.__groups = []

self._set_bit("flags", 0, False) # set alive # noqa: FBT003
self._set_bit("flags", 0, False) # set alive # noqa: FBT003
ENTITY_COUNTER.pop_id(self.__id)

self.__groups.clear()

# add to dead
Dead.add(self)
# delete parent reference
self._parent = None
for child in self._children:
child.parent_died()
self._children = []

def _after_kill(
self,
Expand Down Expand Up @@ -266,7 +277,7 @@ def kill(
:param killed_by: Who killed this entity
:param kill_children: Whether to kill children as well as recursively
:param force_kill: Whether to kill even if before kill returns False
:return: Whether the entity wa0s killed or not. May be denied by _before_kill.
:return: Whether the entity was killed or not. May be denied by _before_kill.
None if the entity is already dead.
"""
if self._alive:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,28 @@

from amoginarium.shared import PositionedLogicEntityLike

from .._groups import Dead, Updated
from ._base_logic_entity import BaseLogicEntity

if tp.TYPE_CHECKING:
from ctypes import Array
from types import EllipsisType

from amoginarium.shared import base_entity_t, CIDType
from amoginarium.shared import base_entity_t, CIDType, MurderViable
from amoginarium.shared.utility import Vec2


class PositionedLogicEntity(BaseLogicEntity, PositionedLogicEntityLike):
"""A logic entity with position and size."""
"""
A logic entity with position and size.

:cvar _CID: The component ID of this entity type for serialization.
:ivar _parent: The parent entity of this entity, optional.
:ivar position: The 2D position of the entity. Public for faster access
Do not modify freely!
:ivar size: The 2D size of the entity. Public for faster access.
Do not modify freely!
"""

__slots__ = ("position", "size")

Expand Down Expand Up @@ -64,6 +74,8 @@ def __init__(
self.position = position
self.size = size

self.add(Updated)

# region Class-Methods
@classmethod
def has_cid(cls) -> bool:
Expand All @@ -86,6 +98,12 @@ def cid(cls) -> CIDType:
# endregion

# region Properties (and other getters)
@tp.override
@property
def parent(self) -> PositionedLogicEntity | None:
""":return: Entity parent if present."""
return self._parent

def _get_ids(self) -> list[int]:
""":return: list of all entity IDs including this one and parents"""
if self._parent is None:
Expand All @@ -94,7 +112,7 @@ def _get_ids(self) -> list[int]:

# endregion

# region Methods: Update
# region Methods: Update & Kill
@tp.override
def _update(self, delta: float) -> None:
"""
Expand All @@ -113,4 +131,19 @@ def _update(self, delta: float) -> None:

super()._update(delta)

def _kill(
self,
*,
killed_by: MurderViable | EllipsisType = ...,
kill_children: bool = True,
) -> None:
"""
Kill the entity and all its children.

:param killed_by: Who killed this entity
:param kill_children: Whether to kill children as well recursively
"""
super()._kill(killed_by=killed_by, kill_children=kill_children)
Dead.add(self)

# endregion
34 changes: 34 additions & 0 deletions amoginarium/logic/entities/_base/_collision/_game_collisions.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class _GameCollisions:
"hitboxes",
"_registered_relations",
"__exception_num",
"__extra_calculate_callbacks",
)

collision_manager: tp.Final[CollisionManager]
Expand All @@ -70,6 +71,8 @@ class _GameCollisions:
_registered_relations: set[tuple[int, int]]
__exception_num: int

__extra_calculate_callbacks: set[tp.Callable[[], tp.Any]]

def __init__(self) -> None:
self.collision_manager = CollisionManager(
base_cell_size=500,
Expand All @@ -89,6 +92,8 @@ def init(
"""
self.COLLISION_START = callback_start
self.COLLISION_END = callback_end

self.__extra_calculate_callbacks = set()
self._setup_groups()

def _setup_groups(self) -> None:
Expand Down Expand Up @@ -273,5 +278,34 @@ def add_exception(self) -> int:
self.__exception_num += 1
return self.__exception_num

def calculate_all_collisions(self) -> None:
"""
Calculates all collisions and calls all extra calculated callbacks.
"""
self.collision_manager.calculate_all_collisions()
for callback in self.__extra_calculate_callbacks:
callback()

def add_extra_calculate_callback(self, callback: tp.Callable[[], tp.Any]) -> None:
"""
Add a callback that gets called when all collisions are calculated.

Useful for manual collisions that would normally be done in update to be called
after all entities are updated and not mid-update!

:param callback: The callback that will be called
"""
self.__extra_calculate_callbacks.add(callback)

def remove_extra_calculate_callback(
self, callback: tp.Callable[[], tp.Any]
) -> None:
"""
Remove a previously added extra-calculate-callback.

:param callback: The callback to remove
"""
self.__extra_calculate_callbacks.discard(callback)


GameCollisions: tp.Final[_GameCollisions] = _GameCollisions()
Loading