### ### a set of random utility functions ### extends Object class_name GDBUtility ################ # public stuff # ################ func _init() -> void: assert(0, "This class should not be instantiated.") static func find_node_filter(root : Node, object : Object, method : String, bfs := false) -> Node: if object.call(method, root): return root if bfs: for child in root.get_children(): if object.call(method, child): return child for child in root.get_children(): var res = find_node_filter(child, object, method) if res != null: return res return null static func find_node_by_type(root : Node, tp, bfs := false) -> Node: if root is tp: return root if bfs: for child in root.get_children(): if child is tp: return child for child in root.get_children(): var res = find_node_by_type(child, tp, bfs) if res != null: return res return null static func find_node_by_name(root : Node, name : String, bfs := false, use_cache := true) -> Node: if root.name == name: return root # create the cache regardless of use_cache if !root.has_meta("__find_by_name_node_cache"): root.set_meta("__find_by_name_node_cache", {}) var cache = root.get_meta("__find_by_name_node_cache") if use_cache && name in cache: var node = root.get_node(cache[name]) if node: return node else: cache.erase(name) if bfs: for child in root.get_children(): if child.name == name: cache[name] = child return child for child in root.get_children(): var res = find_node_by_name(child, name) if res != null: cache[name] = root.get_path_to(res) return res return null static func find_nodes_by_type(root : Node, tp, as_path = false) -> Array: var result = [] __find_nodes_by_type(root, tp, as_path, result) return result static func find_nodes_with_member(root : Node, member : String) -> Array: var result = [] __find_nodes_with_member(root, member, result) return result static func find_parent_with_method(root : Node, method : String) -> Node: if root.has_method(method): return root else: var parent = root.get_parent() if parent == null: return null return find_parent_with_method(parent, method) static func find_parent_with_member(root : Node, member : String) -> Node: if root.get(member) != null: return root else: var parent = root.get_parent() if parent == null: return null return find_parent_with_member(parent, member) static func find_parent_with_name(root : Node, name : String) -> Node: if root.name == name: return root else: var parent = root.get_parent() if parent == null: return null return find_parent_with_name(parent, name) static func find_parent_with_type(root : Node, tp) -> Node: if (tp is Script && tp.instance_has(root)) \ || root is tp: return root else: var parent = root.get_parent() if parent == null: return null return find_parent_with_type(parent, tp) static func find_treeitem_with_metadata(root : TreeItem, metadata, column := 0) -> TreeItem: while root: if root.get_metadata(column) == metadata: return root var in_children := find_treeitem_with_metadata(root.get_children(), metadata, column) if in_children: return in_children root = root.get_next() return null static func hide_properties(properties : Array, names_to_hide : Array) -> void: for i in range(properties.size() - 1, -1, -1): if properties[i]["name"] in names_to_hide: properties.remove(i) static func type_name(type : int) -> String: match type: TYPE_NIL: return "null" TYPE_BOOL: return "bool" TYPE_INT: return "int" TYPE_REAL: return "float" TYPE_STRING: return "String" TYPE_VECTOR2: return "Vector2" TYPE_RECT2: return "Rect2" TYPE_VECTOR3: return "Vector3" TYPE_TRANSFORM2D: return "Transform2D" TYPE_PLANE: return "Plane" TYPE_QUAT: return "Quat" TYPE_AABB: return "AABB" TYPE_BASIS: return "Basis" TYPE_TRANSFORM: return "Transform" TYPE_COLOR: return "Color" TYPE_NODE_PATH: return "NodePath" TYPE_RID: return "RID" TYPE_OBJECT: return "Object" TYPE_DICTIONARY: return "Dictionary" TYPE_ARRAY: return "Array" TYPE_RAW_ARRAY: return "PoolByteArray" TYPE_INT_ARRAY: return "PoolIntArray" TYPE_REAL_ARRAY: return "PoolRealArray" TYPE_STRING_ARRAY: return "PoolStringArray" TYPE_VECTOR2_ARRAY: return "Vector2Array" TYPE_VECTOR3_ARRAY: return "Vector3Array" TYPE_COLOR_ARRAY: return "ColorArray" _: return "Unknown" static func format_type(type : Dictionary, none_name := "void") -> String: match type["type"]: TYPE_NIL: return none_name TYPE_OBJECT: return type.get("class_name", "Object") var tp: return type_name(tp) static func format_method_signature(method : Dictionary, format := "{return} {name}({args})") -> String: var args := PoolStringArray() var rettype := "void" if method.has("return"): rettype = format_type(method["return"]) for i in range(method["args"].size()): var arg = method["args"][i] var def = "" if i < method["default_args"].size(): def = " = %s" % str(method["default_args"][i]) args.append("{name}: {type}{def}".format({ "name": arg["name"], "type": format_type(arg, "Variant"), "def": def })) return format.format({ "return": rettype, "name": method["name"], "args": args.join(", ") }) static func format_signal_signature(sig : Dictionary) -> String: return format_method_signature(sig, "{name}({args})") static func get_type_property_list(type : Dictionary) -> Array: match type["type"]: TYPE_VECTOR2: return [ {"name": "x", "type": TYPE_REAL}, {"name": "y", "type": TYPE_REAL} ] TYPE_RECT2: return [ {"name": "position", "type": TYPE_VECTOR2}, {"name": "size", "type": TYPE_VECTOR2}, {"name": "end", "type": TYPE_VECTOR2} ] TYPE_VECTOR3: return [ {"name": "x", "type": TYPE_REAL}, {"name": "y", "type": TYPE_REAL}, {"name": "z", "type": TYPE_REAL} ] TYPE_TRANSFORM2D: return [ {"name": "x", "type": TYPE_VECTOR2}, {"name": "y", "type": TYPE_VECTOR2}, {"name": "origin", "type": TYPE_VECTOR2} ] TYPE_PLANE: return [ {"name": "normal", "type": TYPE_VECTOR3}, {"name": "x", "type": TYPE_REAL}, {"name": "y", "type": TYPE_REAL}, {"name": "z", "type": TYPE_REAL}, {"name": "d", "type": TYPE_REAL} ] TYPE_QUAT: return [ {"name": "x", "type": TYPE_REAL}, {"name": "y", "type": TYPE_REAL}, {"name": "z", "type": TYPE_REAL}, {"name": "w", "type": TYPE_REAL} ] TYPE_AABB: return [ {"name": "position", "type": TYPE_VECTOR3}, {"name": "size", "type": TYPE_VECTOR3}, {"name": "end", "type": TYPE_VECTOR3} ] TYPE_BASIS: return [ {"name": "x", "type": TYPE_VECTOR3}, {"name": "y", "type": TYPE_VECTOR3}, {"name": "z", "type": TYPE_VECTOR3} ] TYPE_TRANSFORM: return [ {"name": "basis", "type": TYPE_BASIS}, {"name": "origin", "type": TYPE_VECTOR3} ] TYPE_COLOR: return [ {"name": "r", "type": TYPE_REAL}, {"name": "g", "type": TYPE_REAL}, {"name": "b", "type": TYPE_REAL}, {"name": "a", "type": TYPE_REAL}, {"name": "h", "type": TYPE_REAL}, {"name": "s", "type": TYPE_REAL}, {"name": "v", "type": TYPE_REAL}, {"name": "r8", "type": TYPE_INT}, {"name": "g8", "type": TYPE_INT}, {"name": "b8", "type": TYPE_INT}, {"name": "a8", "type": TYPE_INT} ] TYPE_OBJECT: if type.has("class_name"): return ClassDB.class_get_property_list(type["class_name"]) return [] static func get_property_type(obj : Object, property : String) -> int: return typeof(obj.get(property)) # TODO static func get_method_arg_types(obj : Object, method : String) -> Array: var methods := obj.get_method_list() var types := [] for ele in methods: if ele["name"] == method: for arg in ele["args"]: types.append(arg["type"]) break return types static func get_scene_tree() -> SceneTree: return Engine.get_main_loop() as SceneTree static func wait_frames(nframes : int) -> void: var tree := get_scene_tree() for i in range(max(1, nframes)): # yield at least once so this always returns a GDScriptFunctionState yield(tree, "idle_frame") static func disconnect_all(sender : Object, receiver : Object, signal_name := "") -> void: if signal_name == "": for sig in sender.get_signal_list(): disconnect_all(sender, receiver, sig["name"]) return for connection in sender.get_signal_connection_list(signal_name): if connection["target"] != receiver: continue sender.disconnect(signal_name, receiver, connection["method"]) static func try_connect(sender : Object, signal_name : String, receiver : Object, method : String, binds := []): if sender.has_signal(signal_name): sender.connect(signal_name, receiver, method, binds) static func copy_signal_handlers(target : Object, source : Object, sig_name : String) -> void: for connection in source.get_signal_connection_list(sig_name): target.connect(sig_name, connection["target"], connection["method"], connection["binds"]) static func copy_all_signal_handlers(target : Object, source : Object, sig_names : Array) -> void: for sig_name in sig_names: copy_signal_handlers(target, source, sig_name) static func control_is_in_front_of(front : Control, back : Control) -> bool: var front_popup : bool = find_parent_with_type(front, Popup) != null var back_popup : bool = find_parent_with_type(back, Popup) != null if front_popup && !back_popup: return true elif !front_popup && back_popup: return false elif front_popup && back_popup: return popup_is_in_front_of(front, back) else: return back.is_greater_than(front) static func popup_is_in_front_of(front : Popup, back : Popup) -> bool: return back.is_greater_than(front) # no idea?! static func get_state_object(meta_key : String, type): var tree := get_scene_tree() if !tree.has_meta(meta_key): tree.set_meta(meta_key, type.new()) return tree.get_meta(meta_key) static func translate(text : String) -> String: return get_scene_tree().root.tr(text) ################# # private stuff # ################# static func __find_nodes_by_type(root : Node, tp, as_path, result): if root is tp: if as_path: result.append(root.get_path()) else: result.append(root) for child in root.get_children(): __find_nodes_by_type(child, tp, as_path, result) return result static func __find_nodes_with_member(root : Node, member : String, result): if root.get(member) != null: result.append(root) for child in root.get_children(): __find_nodes_with_member(child, member, result)