gdbasics/scripts/libs/coroutine.gd

151 lines
5.2 KiB
GDScript

###
### utility for writing coroutines
###
extends Object
class_name GDBCoroutine
class __WaitAll:
var __SIG_SEPERATOR = RefCounted.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 : Array = [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] as int] = sig_params
break
if remaining_signals.is_empty():
finished.emit(results)
signal finished(results)
class __WaitAny:
var __SIG_SEPERATOR = RefCounted.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)
finished.emit(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():
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:
# TODO: this should just take an array of Signals now
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):
# TODO: this should just take an array of Signals now
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