class_name StateMachine extends Node export var debug_state_machine: bool = false signal state_changed(old_state_name, new_state) signal modifiers_updated(modifier_type, modifier_eventid, _merged_modifier) var current_state: State var state_modifiers: Array var merged_modifiers : Dictionary #onready var merged_animation_state_modifiers :StateModifierAnimatedActor = StateModifierAnimatedActor.new() export var starting_state_name = 'default' export(Array,Resource) var states var states_index :Dictionary func _ready(): if debug_state_machine: print (get_path(), " - ready state machine.") for s in states: var this_state = s if this_state is State: if debug_state_machine: print("Adding state ", this_state.name) #check for empty state name if this_state.name != '': #check for unique state name if !(states_index.has(this_state.name)): # call setup var dupe_state = this_state.duplicate() dupe_state.setup() if debug_state_machine: print("Initializing State Node: ", s) dupe_state.debug_state = true if dupe_state.state_timeout != null: add_child(dupe_state.state_timeout) # Add to state index states_index[dupe_state.name] = dupe_state else: push_error("Multiple states with same name in: ") else: push_error("State missing name in: " + str(get_path()) ) if states_index.has(starting_state_name): change_state(states_index[starting_state_name]) else: push_error("State machine cannot find starting state: " + starting_state_name) #func _physics_process(delta): # if(state_modifiers.size() > 0): # merged_animation_state_modifiers.reset() # merged_animation_state_modifiers.animation_name = 'floopy doo,' # #merge_modifiers() func merge_modifiers(): #merged_animation_state_modifiers.reset() for modgroup in merged_modifiers.keys(): merged_modifiers[modgroup].reset() for m in state_modifiers: if m is StateModifierAnimatedActor: if merged_modifiers.has("StateModifierAnimatedActor") == false: merged_modifiers["StateModifierAnimatedActor"] = StateModifierAnimatedActor.new() #print(m.name) var this_mod_type :StateModifierAnimatedActor = merged_modifiers["StateModifierAnimatedActor"] if m.is_active: #merged_animation_state_modifiers.merge(m) this_mod_type.merge(m) if m is StateModifierMovement: if merged_modifiers.has("StateModifierMovement") == false: merged_modifiers["StateModifierMovement"] = StateModifierMovement.new() #print(m.name) var this_mod_type :StateModifierMovement = merged_modifiers["StateModifierMovement"] if m.is_active: this_mod_type.merge(m) ## When trying to get multiple modifiers func get_modifiers_of_type(_modifier_type :String, _only_active: bool = false) -> Array: var mod_array :Array = [] for m in state_modifiers: if m.to_string() == _modifier_type: if (_only_active and m.is_active) or _only_active == false: mod_array.append(m) return mod_array # Change to the new state by first calling any exit logic on the current state. func change_state(new_state: State) -> void: var current_state_name :String if current_state: current_state.exit() current_state_name = current_state.name current_state = new_state current_state.enter() emit_signal("state_changed",current_state_name,current_state) puppet func puppet_change_to_known_state(new_state_name: String) -> void: if new_state_name.empty() == false: if states_index.has(new_state_name): change_state(states_index[new_state_name]) else: push_warning(get_parent().name + ": Attempt to switch state to unknown: " + new_state_name) change_state(states_index["default"]) func change_to_known_state(new_state_name: String) -> void: if is_network_master(): if new_state_name.empty() == false: if states_index.has(new_state_name): change_state(states_index[new_state_name]) else: push_warning(get_parent().name + ": Attempt to switch state to unknown: " + new_state_name) change_state(states_index["default"]) rpc("puppet_change_to_known_state", new_state_name) #func check_parent_before_state_change(new_state_name: String) -> bool: # ##CANCELLED: Make this a verifiable funcref or something # if get_parent().has_method("check_state_change"): # var _check_result = get_parent().check_state_change(new_state_name) # if _check_result: # change_to_known_state(new_state_name) # return true # return false func get_state_reference(state_name: String) -> State: if states_index.has(state_name): return states_index[state_name] return null func push_state_modifier(_state_modifier: StateModifier) -> void: if state_modifiers.has(_state_modifier) == false: state_modifiers.push_front(_state_modifier) _state_modifier.connect("modifier_event",self,"handle_modifier_events") if debug_state_machine: print("Add State Modifier: ", _state_modifier.name, " now size ", state_modifiers.size()) func handle_modifier_events(eventID :int, modifier_class_name :String): ##TODO: why did I care about the events? they both do the same thing if eventID == StateModifier.EVENT_ID.ACTIVATED: if debug_state_machine: print ("A mod activated! Which one though?", modifier_class_name) merge_modifiers() emit_signal("modifiers_updated",merged_modifiers[modifier_class_name].modifier_type, eventID, merged_modifiers[modifier_class_name] ) elif eventID == StateModifier.EVENT_ID.DEACTIVATED: merge_modifiers() emit_signal("modifiers_updated",merged_modifiers[modifier_class_name].modifier_type, eventID, merged_modifiers[modifier_class_name])