11#include " loreline_interpreter.h"
2+ #include " loreline_runtime.h"
23#include " loreline_options.h"
34
45#include < godot_cpp/variant/callable.hpp>
6+ #include < godot_cpp/variant/callable_custom.hpp>
57#include < godot_cpp/variant/utility_functions.hpp>
68
9+ // --- Custom Callables that hold a Ref<LorelineInterpreter> ---
10+ // Keeping a reference to the Callable keeps the interpreter alive.
11+
12+ class LorelineAdvanceCallable : public CallableCustom {
13+ Ref<LorelineInterpreter> _interp;
14+ public:
15+ LorelineAdvanceCallable (const Ref<LorelineInterpreter> &interp) : _interp(interp) {}
16+
17+ uint32_t hash () const override { return _interp->get_instance_id (); }
18+ String get_as_text () const override { return " LorelineInterpreter::advance()" ; }
19+ ObjectID get_object () const override { return ObjectID (_interp->get_instance_id ()); }
20+
21+ static bool compare_equal (const CallableCustom *a, const CallableCustom *b) {
22+ return a == b;
23+ }
24+ static bool compare_less (const CallableCustom *a, const CallableCustom *b) {
25+ return a < b;
26+ }
27+ CompareEqualFunc get_compare_equal_func () const override { return compare_equal; }
28+ CompareLessFunc get_compare_less_func () const override { return compare_less; }
29+
30+ void call (const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
31+ _interp->advance ();
32+ r_call_error.error = GDEXTENSION_CALL_OK;
33+ }
34+ };
35+
36+ class LorelineSelectCallable : public CallableCustom {
37+ Ref<LorelineInterpreter> _interp;
38+ public:
39+ LorelineSelectCallable (const Ref<LorelineInterpreter> &interp) : _interp(interp) {}
40+
41+ uint32_t hash () const override { return _interp->get_instance_id (); }
42+ String get_as_text () const override { return " LorelineInterpreter::select()" ; }
43+ ObjectID get_object () const override { return ObjectID (_interp->get_instance_id ()); }
44+
45+ static bool compare_equal (const CallableCustom *a, const CallableCustom *b) {
46+ return a == b;
47+ }
48+ static bool compare_less (const CallableCustom *a, const CallableCustom *b) {
49+ return a < b;
50+ }
51+ CompareEqualFunc get_compare_equal_func () const override { return compare_equal; }
52+ CompareLessFunc get_compare_less_func () const override { return compare_less; }
53+
54+ void call (const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
55+ int index = 0 ;
56+ if (p_argcount > 0 && p_arguments[0 ]->get_type () == Variant::INT) {
57+ index = *p_arguments[0 ];
58+ }
59+ _interp->select (index);
60+ r_call_error.error = GDEXTENSION_CALL_OK;
61+ }
62+ };
63+
764#ifdef LORELINE_USE_JS
865#include < godot_cpp/classes/java_script_bridge.hpp>
966#include < godot_cpp/classes/json.hpp>
@@ -19,9 +76,14 @@ LorelineInterpreter::LorelineInterpreter()
1976 : _interp(nullptr ), _pending_advance(nullptr ), _pending_select(nullptr )
2077#endif
2178{
79+ UtilityFunctions::print (" [Loreline] Interpreter created: " , Variant ((uint64_t )this ));
2280}
2381
2482LorelineInterpreter::~LorelineInterpreter () {
83+ UtilityFunctions::print (" [Loreline] Interpreter destroyed: " , Variant ((uint64_t )this ));
84+ // Remove from active list if still there (user dropped ref before finish)
85+ Loreline::_release_active_interpreter (this );
86+
2587#ifdef LORELINE_USE_JS
2688 if (_js_id != 0 ) {
2789 _js_registry.erase (_js_id);
@@ -182,7 +244,11 @@ void LorelineInterpreter::_on_dialogue(
182244 String godot_text = text.isNull () ? String () : String::utf8 (text.c_str ());
183245 Array godot_tags = _convert_tags (tags, tagCount);
184246
185- Callable advance_callable = Callable (self, " advance" );
247+ // Release from active list — the Callable now holds the Ref keeping the interpreter alive
248+ Loreline::_release_active_interpreter (self);
249+
250+ Ref<LorelineInterpreter> self_ref = self->_self_ref ;
251+ Callable advance_callable (memnew (LorelineAdvanceCallable (self_ref)));
186252 self->emit_signal (" dialogue" , self, godot_character, godot_text, godot_tags, advance_callable);
187253}
188254
@@ -198,7 +264,11 @@ void LorelineInterpreter::_on_choice(
198264
199265 Array godot_options = _convert_options (options, optionCount);
200266
201- Callable select_callable = Callable (self, " select" );
267+ // Release from active list — the Callable now holds the Ref keeping the interpreter alive
268+ Loreline::_release_active_interpreter (self);
269+
270+ Ref<LorelineInterpreter> self_ref = self->_self_ref ;
271+ Callable select_callable (memnew (LorelineSelectCallable (self_ref)));
202272 self->emit_signal (" choice" , self, godot_options, select_callable);
203273}
204274
@@ -209,6 +279,13 @@ void LorelineInterpreter::_on_finish(
209279 self->_pending_advance = nullptr ;
210280 self->_pending_select = nullptr ;
211281
282+ UtilityFunctions::print (" [Loreline] Script finished, cleaning up interpreter: " , Variant ((uint64_t )self));
283+
284+ // Keep alive through signal emission via _self_ref, then clean up
285+ Loreline::_release_active_interpreter (self);
286+ Variant guard = self->_self_ref ;
287+ self->_self_ref = Variant ();
288+
212289 self->emit_signal (" finished" , self);
213290}
214291
0 commit comments