From 1a8740ae079678d4c890c7cad5b169315fa71a78 Mon Sep 17 00:00:00 2001 From: Daniel Haus Date: Sun, 18 Jan 2026 17:15:50 +0300 Subject: [PATCH] Functional Expansion: Add object oriented entity concept --- src/dnd_dm_toolkit/core/bus.py | 68 +++++++++++++++++++ src/dnd_dm_toolkit/core/event_bus.py | 0 src/dnd_dm_toolkit/core/hooks.py | 0 .../stuctures/{character.py => creature.py} | 11 ++- .../core/{ => stuctures}/sentinel.py | 0 .../{utils/languages.py => core/utils.py} | 2 +- src/dnd_dm_toolkit/utils/__init__.py | 0 7 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 src/dnd_dm_toolkit/core/bus.py delete mode 100644 src/dnd_dm_toolkit/core/event_bus.py delete mode 100644 src/dnd_dm_toolkit/core/hooks.py rename src/dnd_dm_toolkit/core/stuctures/{character.py => creature.py} (95%) rename src/dnd_dm_toolkit/core/{ => stuctures}/sentinel.py (100%) rename src/dnd_dm_toolkit/{utils/languages.py => core/utils.py} (90%) delete mode 100644 src/dnd_dm_toolkit/utils/__init__.py diff --git a/src/dnd_dm_toolkit/core/bus.py b/src/dnd_dm_toolkit/core/bus.py new file mode 100644 index 0000000..c884b6f --- /dev/null +++ b/src/dnd_dm_toolkit/core/bus.py @@ -0,0 +1,68 @@ +from typing import Protocol, List, Callable, Dict +from dataclasses import dataclass +from ..core.stuctures.event import ( + GameEvent +) +from ..core.enums.game import EventType + +@dataclass +class Hook: + """ + Represents a single event hook. + + Attributes: + event_type: Type of event to listen for. + handler: Function to call when event fires. + priority: Execution order (lower number = earlier execution). + source: Name of what created this hook (for debugging). + """ + event_type: EventType + handler: Callable[[GameEvent], None] + priority: int = 100 + source: object | None = None + +class Hookable(Protocol): + hooks: List[Hook] + + def _init_hooks(self, hooks: List[Hook]): ... + + def add_hook(self, hook: Hook): ... + + def del_hook(self, hook: Hook): ... + +class EventBus: + """ + Global event bus for managing game events (Singleton). + + This is the central communication hub where: + - Events are emitted (broadcasted) + - Hooks listen for specific event types + - Multiple hooks can react to the same event + """ + _instance = None + _initialized = False + + def __new__(cls): + """Ensure only one instance exists (Singleton pattern).""" + if cls._instance == None: + cls._instance = super().__new__(cls) + return cls._instance + + def __init__(self): + """Initialize the event bus (only runs once due to _initialized flag).""" + if self._initialized: + return + + # Storage for hooks: EventType -> List of Hooks + self._hooks: Dict[EventType, List[Hook]] = {} + + # Counter for generating unique hook IDs + self._next_hook_id = 1 + + # Mapping: hook_id -> (event_type, hook_index) + # This allows O(1) hook removal by ID + self._hook_id_map: Dict[int, tuple[EventType, int]] = dict() + + self._initialized = True + + \ No newline at end of file diff --git a/src/dnd_dm_toolkit/core/event_bus.py b/src/dnd_dm_toolkit/core/event_bus.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/dnd_dm_toolkit/core/hooks.py b/src/dnd_dm_toolkit/core/hooks.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/dnd_dm_toolkit/core/stuctures/character.py b/src/dnd_dm_toolkit/core/stuctures/creature.py similarity index 95% rename from src/dnd_dm_toolkit/core/stuctures/character.py rename to src/dnd_dm_toolkit/core/stuctures/creature.py index 9583569..219add7 100644 --- a/src/dnd_dm_toolkit/core/stuctures/character.py +++ b/src/dnd_dm_toolkit/core/stuctures/creature.py @@ -1,5 +1,5 @@ import warnings -from typing import List, Callable, Protocol, Any +from typing import List, Callable, Protocol from dataclasses import dataclass from ..enums.game import ( Ability, @@ -12,7 +12,7 @@ from ..enums.game import ( CreatureType ) from ..sentinel import _AllStatsSentinel -from ..constants import MAXIMUM_STAT_VALUE +from ..constants import MAXIMUM_STAT_VALUE, ALL_STATS @dataclass class Attributes: @@ -193,12 +193,11 @@ class Race: alignment_tendency: Optional typical alignment for the race. """ name: str - ability_score_increases: dict[Ability, int] + ability_score_increases: dict[Ability | ALL_STATS, int] size: CreatureSize speed: int type: CreatureType languages: list[Language] features: list[Feature] - age_description: str = "" - alignment_tendency: str = "" - extra_language_choices: int = 0 \ No newline at end of file + age_description: str = "they can live approximately 80-100 years" + alignment_tendency: str = "None" \ No newline at end of file diff --git a/src/dnd_dm_toolkit/core/sentinel.py b/src/dnd_dm_toolkit/core/stuctures/sentinel.py similarity index 100% rename from src/dnd_dm_toolkit/core/sentinel.py rename to src/dnd_dm_toolkit/core/stuctures/sentinel.py diff --git a/src/dnd_dm_toolkit/utils/languages.py b/src/dnd_dm_toolkit/core/utils.py similarity index 90% rename from src/dnd_dm_toolkit/utils/languages.py rename to src/dnd_dm_toolkit/core/utils.py index 2018db6..0419434 100644 --- a/src/dnd_dm_toolkit/utils/languages.py +++ b/src/dnd_dm_toolkit/core/utils.py @@ -1,5 +1,5 @@ from ..core.enums.game import LanguageRarity -from ..core.stuctures.character import Language, Race +from ..core.stuctures.creature import Language, Race def get_available_languages( race: Race, diff --git a/src/dnd_dm_toolkit/utils/__init__.py b/src/dnd_dm_toolkit/utils/__init__.py deleted file mode 100644 index e69de29..0000000