149 lines
5.1 KiB
GDScript
149 lines
5.1 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():
|
|
emit_signal("finished", 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)
|
|
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():
|
|
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
|