class_name BossDirector extends RefCounted ## Boss encounter director. Reads a timed event timeline and fires mechanic events. ## Data-driven: the encounter definition (JSON) specifies when things happen. ## This class interprets that data and produces events for the server to act on. ## A single event in the boss timeline. class TimelineEvent: var time: float = 0.0 # Seconds into the fight var hp_threshold: float = -1.0 # Optional: trigger at this HP% instead of time (-1 = time-based) var event_type: String = "" # "aoe_spread", "stack_debuff", "phase_shift", "dialog", "enrage_soft", etc. var params: Dictionary = {} # Event-specific parameters var has_fired: bool = false ## The director manages the encounter timeline. var _events: Array[TimelineEvent] = [] var _current_time: float = 0.0 var _boss_hp_pct: float = 1.0 # Current boss HP as a fraction (1.0 = full) var _active: bool = false ## Signal-like: array of events that fired this tick (consumed by caller). var _pending_events: Array[TimelineEvent] = [] ## Loads the encounter timeline from a data structure. ## events: Array of dictionaries with keys matching TimelineEvent fields. func load_timeline(event_data: Array[Dictionary]) -> void: _events.clear() for ed in event_data: var ev := TimelineEvent.new() ev.time = ed.get("time", 0.0) ev.hp_threshold = ed.get("hp_threshold", -1.0) ev.event_type = ed.get("event_type", "") ev.params = ed.get("params", {}) ev.has_fired = false _events.append(ev) ## Starts the director (fight begins). func start() -> void: _current_time = 0.0 _active = true for ev in _events: ev.has_fired = false ## Stops the director (fight ended or wiped). func stop() -> void: _active = false _pending_events.clear() ## Updates boss HP fraction (called when boss takes damage). func set_boss_hp_pct(pct: float) -> void: _boss_hp_pct = clampf(pct, 0.0, 1.0) ## Ticks the director forward. Call once per server tick. ## Returns an array of TimelineEvents that fired this tick (may be empty). func tick(delta_time: float) -> Array[TimelineEvent]: _pending_events.clear() if not _active: return _pending_events _current_time += delta_time for ev in _events: if ev.has_fired: continue var should_fire := false if ev.hp_threshold >= 0.0: # HP-threshold triggered event should_fire = _boss_hp_pct <= ev.hp_threshold else: # Time-triggered event should_fire = _current_time >= ev.time if should_fire: ev.has_fired = true _pending_events.append(ev) return _pending_events ## Returns the current fight time. func get_current_time() -> float: return _current_time ## Returns whether the director is active. func is_active() -> bool: return _active