class_name TimePhaseAdapter extends Node ## Swaps this actor's time-phase Effects when the world phase changes. ## Only ever talks to EffectContainer.add_effect / remove_effect; never ## touches stats, state axes or animations directly. Actors without a ## profile are simply unaffected by phase switches. @export var profile: Resource @export var effect_container_path := NodePath("../EffectContainer") @onready var effect_container: Node = get_node_or_null(effect_container_path) func _ready() -> void: for bus: Node in _event_buses(): if bus.has_signal("time_phase_changed") and not bus.is_connected("time_phase_changed", _on_time_phase_changed): bus.connect("time_phase_changed", _on_time_phase_changed) apply_time_phase(_current_time_phase()) func apply_time_phase(time_phase: StringName) -> void: if profile == null or effect_container == null: return for definition: Resource in _profile_effects(&"past") + _profile_effects(&"future"): if definition != null and effect_container.has_method("remove_effect"): effect_container.call("remove_effect", StringName(str(definition.get("id")))) for definition: Resource in _profile_effects(time_phase): if definition != null and effect_container.has_method("add_effect"): effect_container.call("add_effect", definition, &"time_phase") func visual_key() -> StringName: if profile != null and profile.has_method("visual_key_for_time_phase"): return profile.call("visual_key_for_time_phase", _current_time_phase()) return _current_time_phase() func _on_time_phase_changed(_previous: StringName, current: StringName, _reason: StringName) -> void: apply_time_phase(current) func _profile_effects(time_phase: StringName) -> Array[Resource]: var result: Array[Resource] = [] if profile == null: return result var effects = profile.call("effects_for_time_phase", time_phase) if profile.has_method("effects_for_time_phase") else null if effects is Array: for definition: Variant in effects: if definition is Resource: result.append(definition) return result func _current_time_phase() -> StringName: var manager := _time_phase_manager_or_null() if manager != null: return StringName(str(manager.get("current_time_phase"))) return &"past" func _time_phase_manager_or_null() -> Node: if not is_inside_tree(): return null return _last_root_child_matching(func(child: Node) -> bool: return child.has_method("set_time_phase") and child.get("current_time_phase") != null ) func _event_buses() -> Array[Node]: var buses: Array[Node] = [] if not is_inside_tree(): return buses for child: Node in get_tree().root.get_children(): if child.has_signal("time_phase_changed"): buses.append(child) return buses func _last_root_child_matching(predicate: Callable) -> Node: var children := get_tree().root.get_children() for index: int in range(children.size() - 1, -1, -1): var child: Node = children[index] if bool(predicate.call(child)): return child return null