diff --git a/effects_plugins/__init__.py b/effects_plugins/__init__.py index fc3c8d5..64caea2 100644 --- a/effects_plugins/__init__.py +++ b/effects_plugins/__init__.py @@ -5,6 +5,7 @@ PLUGIN_DIR = Path(__file__).parent def discover_plugins(): from engine.effects.registry import get_registry + from engine.effects.types import EffectPlugin registry = get_registry() imported = {} @@ -22,8 +23,8 @@ def discover_plugins(): attr = getattr(module, attr_name) if ( isinstance(attr, type) - and hasattr(attr, "name") - and hasattr(attr, "process") + and issubclass(attr, EffectPlugin) + and attr is not EffectPlugin and attr_name.endswith("Effect") ): plugin = attr() diff --git a/effects_plugins/fade.py b/effects_plugins/fade.py index 98ede65..be106ee 100644 --- a/effects_plugins/fade.py +++ b/effects_plugins/fade.py @@ -3,7 +3,7 @@ import random from engine.effects.types import EffectConfig, EffectContext, EffectPlugin -class FadeEffect: +class FadeEffect(EffectPlugin): name = "fade" config = EffectConfig(enabled=True, intensity=1.0) diff --git a/effects_plugins/firehose.py b/effects_plugins/firehose.py index 4be520b..38157cb 100644 --- a/effects_plugins/firehose.py +++ b/effects_plugins/firehose.py @@ -7,7 +7,7 @@ from engine.sources import FEEDS, POETRY_SOURCES from engine.terminal import C_DIM, G_DIM, G_LO, RST, W_GHOST -class FirehoseEffect: +class FirehoseEffect(EffectPlugin): name = "firehose" config = EffectConfig(enabled=True, intensity=1.0) diff --git a/effects_plugins/glitch.py b/effects_plugins/glitch.py index d23244a..eb54cba 100644 --- a/effects_plugins/glitch.py +++ b/effects_plugins/glitch.py @@ -5,7 +5,7 @@ from engine.effects.types import EffectConfig, EffectContext, EffectPlugin from engine.terminal import C_DIM, DIM, G_DIM, G_LO, RST -class GlitchEffect: +class GlitchEffect(EffectPlugin): name = "glitch" config = EffectConfig(enabled=True, intensity=1.0) diff --git a/effects_plugins/noise.py b/effects_plugins/noise.py index d7bf316..71d6833 100644 --- a/effects_plugins/noise.py +++ b/effects_plugins/noise.py @@ -5,7 +5,7 @@ from engine.effects.types import EffectConfig, EffectContext, EffectPlugin from engine.terminal import C_DIM, G_DIM, G_LO, RST, W_GHOST -class NoiseEffect: +class NoiseEffect(EffectPlugin): name = "noise" config = EffectConfig(enabled=True, intensity=0.15) diff --git a/engine/effects/types.py b/engine/effects/types.py index 1d2c340..1291af5 100644 --- a/engine/effects/types.py +++ b/engine/effects/types.py @@ -1,3 +1,4 @@ +from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Any @@ -8,10 +9,11 @@ class EffectContext: terminal_height: int scroll_cam: int ticker_height: int - mic_excess: float - grad_offset: float - frame_number: int - has_message: bool + camera_x: int = 0 + mic_excess: float = 0.0 + grad_offset: float = 0.0 + frame_number: int = 0 + has_message: bool = False items: list = field(default_factory=list) @@ -22,15 +24,42 @@ class EffectConfig: params: dict[str, Any] = field(default_factory=dict) -class EffectPlugin: +class EffectPlugin(ABC): name: str config: EffectConfig - def process(self, buf: list[str], ctx: EffectContext) -> list[str]: - raise NotImplementedError + @abstractmethod + def process(self, buf: list[str], ctx: EffectContext) -> list[str]: ... - def configure(self, config: EffectConfig) -> None: - raise NotImplementedError + @abstractmethod + def configure(self, config: EffectConfig) -> None: ... + + +def create_effect_context( + terminal_width: int = 80, + terminal_height: int = 24, + scroll_cam: int = 0, + ticker_height: int = 0, + camera_x: int = 0, + mic_excess: float = 0.0, + grad_offset: float = 0.0, + frame_number: int = 0, + has_message: bool = False, + items: list | None = None, +) -> EffectContext: + """Factory function to create EffectContext with sensible defaults.""" + return EffectContext( + terminal_width=terminal_width, + terminal_height=terminal_height, + scroll_cam=scroll_cam, + ticker_height=ticker_height, + camera_x=camera_x, + mic_excess=mic_excess, + grad_offset=grad_offset, + frame_number=frame_number, + has_message=has_message, + items=items or [], + ) @dataclass