### ### utility for writing coroutines ### extends Object class_name GDBCoroutine class __WaitAll: var __SIG_SEPERATOR = Reference.new() var remaining_signals : Array var results := [] func __connect_signals(): for i in range(remaining_signals.size()): var ele = remaining_signals[i] assert(ele is Dictionary && ele.has("object") && ele.has("signal")) ele["object"].connect(ele["signal"], self, "_on_signal", [__SIG_SEPERATOR, ele["object"], ele["signal"], i]) results.resize(remaining_signals.size()) func _on_signal(p0 = null, p1 = null, p2 = null, p3 = null, p4 = null, p5 = null, p6 = null, p7 = null): var params := [p0, p1, p2, p3, p4, p5, p6, p7] assert(__SIG_SEPERATOR in params) # store the parameters var param = params.pop_front() var sig_params = [] while param != __SIG_SEPERATOR: sig_params.append(param) param = params.pop_front() # object, signal and index should be remaining (and some nulls) assert(params.size() >= 3) for i in range(remaining_signals.size()): if remaining_signals[i]["object"] == params[0] && remaining_signals[i]["signal"] == params[1]: remaining_signals.remove(i) results[params[2]] = sig_params break if remaining_signals.empty(): emit_signal("finished", results) signal finished(results) class __WaitAny: var __SIG_SEPERATOR = Reference.new() func __connect_signals(signals : Array): for ele in signals: assert(ele is Dictionary && ele.has("object") && ele.has("signal")) ele["object"].connect(ele["signal"], self, "_on_signal", [__SIG_SEPERATOR, ele["object"], ele["signal"]]) func _on_signal(p0 = null, p1 = null, p2 = null, p3 = null, p4 = null, p5 = null, p6 = null): var params := [p0, p1, p2, p3, p4, p5, p6] assert(__SIG_SEPERATOR in params) # store the parameters var param = params.pop_front() var sig_params := [] while param != __SIG_SEPERATOR: sig_params.append(param) param = params.pop_front() # object and signal should be remaining (and some nulls) assert(params.size() >= 2) emit_signal("finished", params[0], params[1], sig_params) for con in get_signal_connection_list("_on_signal"): con["source"].disconnect(con["signal"], self, "_on_signal") signal finished(obj, sig, result) ################ # public stuff # ################ func _init() -> void: assert(0, "This class should not be instantiated.") #! Wait for multiple signals. #! #! Utility function to wait for multiple signals to occur, in any order. #! The returned object will emit a "finished" signal after all signals #! fired. #! #! The parameters provided to the signal handlers are stored inside a #! "results" field inside the returned object and also provided to the #! finish signal. Their order corresponds to the order of the objects #! and signals provided in the parameters. #! #! If no signals have been provided, it will fire on the next frame. (This could #! easily be increased by editing the source of this function, if required.) #! #! For technical reasons the signals must provide at most four parameters. #! #! Example (waits until all three buttons have been pressed): #! yield(coroutine.wait_for_all([ #! {"object": $button0, "signal": "pressed"}, #! {"object": $button1, "signal": "pressed"}, #! {"object": $button2, "signal": "pressed"} #! ]), "finished") #! #! \param objects_and_signals An array of dictionaries, each containing #! "object" and "signal" elements. #! \returns An object with a "finished" signal. static func wait_for_all(objects_and_signals : Array) -> Object: var obj := __WaitAll.new() if objects_and_signals: obj.remaining_signals = objects_and_signals else: obj.remaining_signals = [{"object": GDBUtility.get_scene_tree(), "signal": "idle_frame"}] obj.__connect_signals() return obj #! Wait for multiple signals. #! #! Utility function to wait for one of multiple signals to occur. #! The returned object will emit a "finished" signal after any of the #! provided signals occured. It will only be emitted a single time. #! #! If no signals have been provided, the signal will never fire. #! #! The provided signal takes three parameters: 1. the object that #! emitted the initial signal, 2. the signal that had been emitted #! and 3. the parameters provided to the signal handler, as an array. #! #! For technical reasons the signals must provide at most four parameters. #! #! Example (waits until any of the buttons has been pressed): #! var res = yield(coroutine.wait_for_any([ #! {"object": $button0, "signal": "pressed"}, #! {"object": $button1, "signal": "pressed"}, #! {"object": $button2, "signal": "pressed"} #! ]), "finished") #! var button = res[0] #! button.disabled = true #! #! \param objects_and_signals An array of dictionaries, each containing #! "object" and "signal" elements. #! \returns An object with a "finished" signal. static func wait_for_any(objects_and_signals : Array): var obj := __WaitAny.new() obj.__connect_signals(objects_and_signals) return obj static func await(res): if res is GDScriptFunctionState: return yield(res, "completed") yield(GDBUtility.get_scene_tree(), "idle_frame") return res