From a3f8c09766004e9d0a0add555704b28efcad8a2d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 18 Jan 2024 11:32:32 +0100 Subject: [PATCH] Use the new virtual workaround and rework LimboState and HSM --- hsm/limbo_hsm.cpp | 32 ++++++++++++++++---------------- hsm/limbo_hsm.h | 10 +++++----- hsm/limbo_state.cpp | 40 ++++++++++++++++++---------------------- hsm/limbo_state.h | 7 ------- tests/test_hsm.h | 2 +- util/limbo_compat.h | 28 ++++++++++++++++++++++++---- 6 files changed, 64 insertions(+), 55 deletions(-) diff --git a/hsm/limbo_hsm.cpp b/hsm/limbo_hsm.cpp index dac970d..1eb8616 100644 --- a/hsm/limbo_hsm.cpp +++ b/hsm/limbo_hsm.cpp @@ -49,9 +49,9 @@ void LimboHSM::set_active(bool p_active) { set_process_input(p_active); if (active) { - _do_enter(); + _enter(); } else { - _do_exit(); + _exit(); } } @@ -60,41 +60,41 @@ void LimboHSM::_change_state(LimboState *p_state) { ERR_FAIL_COND(p_state->get_parent() != this); if (active_state) { - active_state->_do_exit(); + active_state->_exit(); } active_state = p_state; - active_state->_do_enter(); + active_state->_enter(); emit_signal(LimboStringNames::get_singleton()->state_changed, active_state); } -void LimboHSM::_do_enter() { +void LimboHSM::_enter() { ERR_FAIL_COND_MSG(get_child_count() == 0, "LimboHSM has no candidate for initial substate."); ERR_FAIL_COND(active_state != nullptr); ERR_FAIL_COND_MSG(initial_state == nullptr, "LimboHSM: Initial state is not set."); - LimboState::_do_enter(); + LimboState::_enter(); _change_state(initial_state); } -void LimboHSM::_do_exit() { +void LimboHSM::_exit() { ERR_FAIL_COND(active_state == nullptr); - active_state->_do_exit(); + active_state->_exit(); active_state = nullptr; - LimboState::_do_exit(); + LimboState::_exit(); } -void LimboHSM::_do_update(double p_delta) { +void LimboHSM::_update(double p_delta) { if (active) { ERR_FAIL_COND(active_state == nullptr); - LimboState::_do_update(p_delta); - active_state->_do_update(p_delta); + LimboState::_update(p_delta); + active_state->_update(p_delta); } } void LimboHSM::update(double p_delta) { - _do_update(p_delta); + _update(p_delta); } void LimboHSM::add_transition(Node *p_from_state, Node *p_to_state, const String &p_event) { @@ -181,7 +181,7 @@ bool LimboHSM::dispatch(const String &p_event, const Variant &p_cargo) { } if (!event_consumed && p_event == LW_NAME(EVENT_FINISHED) && !(get_parent() && get_parent()->is_class("LimboState"))) { - _do_exit(); + _exit(); } return event_consumed; @@ -226,10 +226,10 @@ void LimboHSM::_notification(int p_what) { case NOTIFICATION_POST_ENTER_TREE: { } break; case NOTIFICATION_PROCESS: { - _do_update(get_process_delta_time()); + _update(get_process_delta_time()); } break; case NOTIFICATION_PHYSICS_PROCESS: { - _do_update(get_physics_process_delta_time()); + _update(get_physics_process_delta_time()); } break; } } diff --git a/hsm/limbo_hsm.h b/hsm/limbo_hsm.h index 1c163d9..6c59083 100644 --- a/hsm/limbo_hsm.h +++ b/hsm/limbo_hsm.h @@ -44,9 +44,9 @@ protected: virtual void _initialize(Node *p_agent, const Ref &p_blackboard) override; - virtual void _do_enter() override; - virtual void _do_exit() override; - virtual void _do_update(double p_delta) override; + virtual void _enter() override; + virtual void _exit() override; + virtual void _update(double p_delta) override; void _change_state(LimboState *p_state); @@ -54,7 +54,7 @@ public: void set_update_mode(UpdateMode p_mode) { update_mode = p_mode; } UpdateMode get_update_mode() const { return update_mode; } - LimboState *get_active_state() const { return active_state; }; + LimboState *get_active_state() const { return active_state; } LimboState *get_leaf_state() const; void set_active(bool p_active); @@ -67,7 +67,7 @@ public: void update(double p_delta); void add_transition(Node *p_from_state, Node *p_to_state, const String &p_event); // void add_transition_from_any_state(Node *p_to_state, const String &p_event); - LimboState *anystate() const { return nullptr; }; + LimboState *anystate() const { return nullptr; } LimboHSM(); }; diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index e24e5d6..4950e21 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -37,27 +37,32 @@ LimboState *LimboState::get_root() const { LimboState *LimboState::named(String p_name) { set_name(p_name); return this; -}; +} -void LimboState::_do_enter() { +void LimboState::_enter() { active = true; - VCALL_OR_NATIVE(_enter); + VCALL(_enter); emit_signal(LimboStringNames::get_singleton()->entered); -}; +} -void LimboState::_do_exit() { +void LimboState::_exit() { if (!active) { return; } - VCALL_OR_NATIVE(_exit); + VCALL(_exit); emit_signal(LimboStringNames::get_singleton()->exited); active = false; -}; +} -void LimboState::_do_update(double p_delta) { - VCALL_OR_NATIVE_ARGS(_update, p_delta); +void LimboState::_update(double p_delta) { + VCALL_ARGS(_update, p_delta); emit_signal(LimboStringNames::get_singleton()->updated, p_delta); -}; +} + +void LimboState::_setup() { + VCALL(_setup); + emit_signal(LimboStringNames::get_singleton()->setup); +} void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) { ERR_FAIL_COND(p_agent == nullptr); @@ -71,15 +76,9 @@ void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) } } - VCALL_OR_NATIVE(_setup); - emit_signal(LimboStringNames::get_singleton()->setup); + _setup(); } -void LimboState::_setup() {} -void LimboState::_enter() {} -void LimboState::_exit() {} -void LimboState::_update(double p_delta) {} - bool LimboState::dispatch(const String &p_event, const Variant &p_cargo) { ERR_FAIL_COND_V(p_event.is_empty(), false); if (handlers.size() > 0 && handlers.has(p_event)) { @@ -189,10 +188,7 @@ void LimboState::_bind_methods() { GDVIRTUAL_BIND(_exit); GDVIRTUAL_BIND(_update, "p_delta"); #elif LIMBOAI_GDEXTENSION - ClassDB::bind_method(D_METHOD("_setup"), &LimboState::_setup); - ClassDB::bind_method(D_METHOD("_enter"), &LimboState::_enter); - ClassDB::bind_method(D_METHOD("_exit"), &LimboState::_exit); - ClassDB::bind_method(D_METHOD("_update", "p_delta"), &LimboState::_update); + // TODO: Registering virtual functions is not available in godot-cpp... #endif ADD_PROPERTY(PropertyInfo(Variant::STRING, "EVENT_FINISHED", PROPERTY_HINT_NONE, "", 0), "", "event_finished"); @@ -204,7 +200,7 @@ void LimboState::_bind_methods() { ADD_SIGNAL(MethodInfo("entered")); ADD_SIGNAL(MethodInfo("exited")); ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::FLOAT, "p_delta"))); -}; +} LimboState::LimboState() { agent = nullptr; diff --git a/hsm/limbo_state.h b/hsm/limbo_state.h index 2c2f46a..7a908d6 100644 --- a/hsm/limbo_state.h +++ b/hsm/limbo_state.h @@ -56,7 +56,6 @@ protected: virtual void _initialize(Node *p_agent, const Ref &p_blackboard); - // Implemented in GDScript: virtual void _setup(); virtual void _enter(); virtual void _exit(); @@ -69,15 +68,9 @@ protected: GDVIRTUAL1(_update, double); #endif // LIMBOAI_MODULE - virtual void _do_enter(); - virtual void _do_exit(); - virtual void _do_update(double p_delta); - void add_event_handler(const String &p_event, const Callable &p_handler); public: - static String EVENT_FINISHED() { return LW_NAME(EVENT_FINISHED); } - Ref get_blackboard() const { return blackboard; } Node *get_agent() const { return agent; } diff --git a/tests/test_hsm.h b/tests/test_hsm.h index 733b9e9..43309b4 100644 --- a/tests/test_hsm.h +++ b/tests/test_hsm.h @@ -136,7 +136,7 @@ TEST_CASE("[Modules][LimboAI] HSM") { CHECK(beta_updates->num_callbacks == 2); CHECK(beta_exits->num_callbacks == 1); - hsm->dispatch(LimboState::EVENT_FINISHED()); + hsm->dispatch(hsm->event_finished()); CHECK(alpha_entries->num_callbacks == 2); CHECK(alpha_updates->num_callbacks == 3); CHECK(alpha_exits->num_callbacks == 2); // * exited diff --git a/util/limbo_compat.h b/util/limbo_compat.h index c6ae3ef..a382f01 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -58,8 +58,9 @@ // * Virtual calls #define VCALL(m_name, ...) (GDVIRTUAL_CALL(m_name, __VA_ARGS__)) -#define VCALL_ARGS(method, ...) (call(LW_NAME(method), __VA_ARGS__)) +#define VCALL_ARGS(m_name, ...) (GDVIRTUAL_CALL(m_name, __VA_ARGS__)) #define VCALL_V(m_name, r_ret) (GDVIRTUAL_CALL(m_name, r_ret)) +#define VCALL_ARGS_V(m_name, r_ret, ...) (GDVIRTUAL_CALL(m_name, __VA_ARGS__, r_ret)) #define VCALL_OR_NATIVE(m_name) \ if (!GDVIRTUAL_CALL(m_name)) { \ @@ -139,10 +140,29 @@ using namespace godot; // * Virtual calls: // * This is a workaround for missing ClassDB::add_virtual_method(). // ! When using these macros, DON'T BIND the native virtual methods! +// ----------------------------- +// VCALL*: only calls a script version if present. +// VCALL_OR_NATIVE*: calls a script version if present; otherwise, calls the native version. -#define VCALL(m_name) (call(LW_NAME(m_name))) -#define VCALL_ARGS(m_name, ...) (call(LW_NAME(m_name), __VA_ARGS__)) -#define VCALL_V(m_name, r_ret) (r_ret = call(LW_NAME(m_name))) +#define VCALL(m_name) \ + if (has_method(LW_NAME(m_name))) { \ + call(LW_NAME(m_name)); \ + } + +#define VCALL_ARGS(m_name, ...) \ + if (has_method(LW_NAME(m_name))) { \ + call(LW_NAME(m_name), __VA_ARGS__); \ + } + +#define VCALL_V(m_name, r_ret) \ + if (has_method(LW_NAME(m_name))) { \ + r_ret = call(LW_NAME(m_name)); \ + } + +#define VCALL_ARGS_V(m_name, r_ret, ...) \ + if (has_method(LW_NAME(m_name))) { \ + r_ret = call(LW_NAME(m_name, __VA_ARGS__)); \ + } #define VCALL_OR_NATIVE(m_name) \ if (has_method(LW_NAME(m_name))) { \