@tool class_name ActorModel extends Node ################################################################################ # SIGNALS # ################################################################################ signal state_changed(new_state: ActorState) ################################################################################ # PUBLIC PROPERTIES # ################################################################################ @export var default_state: ActorState = null: set = set_default_state @onready var actor: Actor = owner as Actor ################################################################################ # PRIVATE PROPERTIES # ################################################################################ var _active_state: ActorState = null var _states: Array[ActorState] = [] ################################################################################ # VIRTUAL METHOD OVERRIDES # ################################################################################ func _ready() -> void: for child_node: Node in get_children(): if child_node is ActorState: _states.append(child_node as ActorState) _set_active_state(default_state) func _get_configuration_warnings() -> PackedStringArray: var warnings: PackedStringArray = PackedStringArray() if owner is not Actor: warnings.append('"%s" must be owned by an Actor.' % name) if get_child_count() == 0: warnings.append("Model has no states.") else: for child_node: Node in get_children(): if child_node is not ActorState: warnings.append('"%s" is not an ActorState.') if default_state is not ActorState: warnings.append("Assigning a default state is required.") return warnings ################################################################################ # GETTERS & SETTERS # ################################################################################ func set_default_state(value: ActorState) -> void: default_state = value update_configuration_warnings() ################################################################################ # VIRTUAL METHOD DEFINITIONS # ################################################################################ func _update(actions: ActorActions, delta: float) -> void: var most_relevant_state: ActorState = _find_most_relevant_state(actions, delta) if most_relevant_state == null: most_relevant_state = default_state if _active_state != most_relevant_state: _set_active_state(most_relevant_state) _active_state._update(actions, delta) ################################################################################ # PRIVATE METHODS # ################################################################################ func _find_most_relevant_state(actions: ActorActions, delta: float) -> ActorState: var most_relevant_state: ActorState = null var highest_relevance: float = -INF for candidate: ActorState in _states: var relevance: float = clampf( snappedf(candidate._compute_relevance(actions, delta), ActorState.RELEVANCE_STEP), ActorState.RELEVANCE_MIN, ActorState.RELEVANCE_MAX ) var has_highest_relevance: bool = relevance > highest_relevance var is_of_equal_relevance: bool = ( !has_highest_relevance and is_equal_approx(relevance, highest_relevance) ) var has_higher_priority: bool = ( most_relevant_state != null and (candidate.priority > most_relevant_state.priority) ) if has_highest_relevance or (is_of_equal_relevance and has_higher_priority): most_relevant_state = candidate highest_relevance = relevance return most_relevant_state func _set_active_state(new_state: ActorState, actions: ActorActions = ActorActions.empty()) -> void: if _active_state: _active_state._exit_state(actions) new_state._enter_state(actions) _active_state = new_state state_changed.emit(_active_state)