#include "state_machine.hpp" #include "state.hpp" #include "utils/godot_macros.h" #include #include #include namespace godot { void StateMachine::_bind_methods() { #define CLASSNAME StateMachine GDPROPERTY(initial_state, Variant::STRING_NAME); } void StateMachine::_enter_tree() { GDGAMEONLY(); this->override_state(this->initial_state); } void StateMachine::_exit_tree() { GDGAMEONLY(); for(KeyValue kvp : available) if(!kvp.value->as_node()->is_inside_tree()) kvp.value->as_node()->queue_free(); } void StateMachine::_process(double delta_time) { GDGAMEONLY(); if(this->state) this->override_state(this->state->get_next()); } void StateMachine::override_state(StringName next) { // don't change state if target is current if(this->state != nullptr && next == this->state->as_node()->get_class()) return; if(this->state) this->remove_child(this->state->as_node()); if(next.is_empty()) { this->state = nullptr; return; } // instantiate new state if this type is not yet available if(!this->available.has(next)) { IState *instance = dynamic_cast(Object::cast_to(ClassDB::instantiate(next))); if(instance == nullptr) { UtilityFunctions::push_error("Failure to instantiate state with class '", next, "'"); return; } this->available.insert(next, instance); instance->init(this->get_parent(), this); instance->as_node()->set_process_mode(ProcessMode::PROCESS_MODE_INHERIT); } // set the new state and add it to the tree this->state = this->available.get(next); this->add_child(this->state->as_node()); } void StateMachine::set_initial_state(StringName name) { this->initial_state = name; } StringName StateMachine::get_initial_state() const { return this->initial_state; } }