forked from daeken/OpenEQ
-
Notifications
You must be signed in to change notification settings - Fork 0
/
GodotExtensions.cs
100 lines (88 loc) · 3.43 KB
/
GodotExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Godot;
using System.Reflection;
namespace OpenEQ {
public static class GodotExtensions {
// Shamelessly ripped from https://stackoverflow.com/questions/2119441/check-if-types-are-castable-subclasses
public static bool IsCastableTo(this Type from, Type to, bool implicitly = false) {
return to.IsAssignableFrom(from) || from.HasCastDefined(to, implicitly);
}
static bool HasCastDefined(this Type from, Type to, bool implicitly) {
if((from.IsPrimitive || from.IsEnum) && (to.IsPrimitive || to.IsEnum)) {
if(!implicitly)
return from == to || (from != typeof(Boolean) && to != typeof(Boolean));
Type[][] typeHierarchy = {
new Type[] { typeof(Byte), typeof(SByte), typeof(Char) },
new Type[] { typeof(Int16), typeof(UInt16) },
new Type[] { typeof(Int32), typeof(UInt32) },
new Type[] { typeof(Int64), typeof(UInt64) },
new Type[] { typeof(Single) },
new Type[] { typeof(Double) }
};
var lowerTypes = Enumerable.Empty<Type>();
foreach(var types in typeHierarchy) {
if(types.Any(t => t == to))
return lowerTypes.Any(t => t == from);
lowerTypes = lowerTypes.Concat(types);
}
return false; // IntPtr, UIntPtr, Enum, Boolean
}
return IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, implicitly, false)
|| IsCastDefined(from, _ => to, m => m.ReturnType, implicitly, true);
}
static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, Func<MethodInfo, Type> derivedType, bool implicitly, bool lookInBase) {
var bindingFlags = BindingFlags.Public | BindingFlags.Static
| (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly);
return type.GetMethods(bindingFlags).Any(
m => (m.Name == "op_Implicit" || (!implicitly && m.Name == "op_Explicit"))
&& baseType(m).IsAssignableFrom(derivedType(m)));
}
public static T GetNode<T>(this Node node, NodePath path, bool nullAllowed = false, bool blindCast = false) where T : Node {
var sub = node.GetNode(path);
if(sub != null) {
if(blindCast || sub.GetType().IsCastableTo(typeof(T)))
return (T) sub;
else
throw new Exception($"Node {sub.GetPath()} not castable to {typeof(T)}");
} else if(nullAllowed)
return null;
throw new Exception($"Could not get {path} from node '{node.GetPath()}'.");
}
public static T Get<T>(this Node node, string propname) {
return (T) node.Get(propname);
}
class SignalWrapper<T> : Godot.Object {
Action<T> CB;
T Target;
internal SignalWrapper(Action<T> cb, T target) {
CB = cb;
Target = target;
}
void Signal() {
CB(Target);
}
}
class SignalWrapper1<T> : Godot.Object {
Action<T> CB;
internal SignalWrapper1(Action<T> cb) {
CB = cb;
}
void Signal(T arg1) {
CB(arg1);
}
}
public static void Connect<T>(this T obj, string signal, Action<T> cb) where T : Godot.Object {
obj.Connect(signal, new SignalWrapper<T>(cb, obj), "Signal");
}
public static void Connect<T>(this T obj, string signal, Action cb) where T : Godot.Object {
obj.Connect(signal, _ => cb());
}
public static void Connect<T>(this Godot.Object obj, string signal, Action<T> cb) {
obj.Connect(signal, new SignalWrapper1<T>(cb), "Signal");
}
}
}