summaryrefslogtreecommitdiffstats
path: root/DTCallback.c
diff options
context:
space:
mode:
Diffstat (limited to 'DTCallback.c')
-rw-r--r--DTCallback.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/DTCallback.c b/DTCallback.c
new file mode 100644
index 0000000..6e026b7
--- /dev/null
+++ b/DTCallback.c
@@ -0,0 +1,324 @@
+/*-- Extensible callback system --*/
+#strict 2
+
+static const CallbackTarget_Bind = -1;
+static const CallbackTarget_Global = -2;
+static const CallbackTarget_Scenario = -3;
+
+global func GlobalCallback(string name) { return [CallbackTarget_Global, name]; }
+global func ScenarioCallback(string name) { return [CallbackTarget_Scenario, name]; }
+global func ObjectCallback(string name, object target) { return [target || this || FatalError("ObjectCallback without target object"), name]; }
+global func DefinitionCallback(string name, id target) { return [target || GetID() || FatalError("DefinitionCallback without target definition"), name]; }
+global func Callback(string name, target) { return [target || this || GetID() || CallbackTarget_Global, name]; }
+global func BindCallback(callback, array binding) { return [CallbackTarget_Bind, callback, binding]; }
+
+static const BindCallback_Bind_Value = -1;
+static const BindCallback_Bind_Context = -2;
+static const BindCallback_Bind_ContextDef = -3;
+static const BindCallback_Bind_CallbackResult = -4;
+static const BindCallback_Bind_ArgumentArrayPart = -5;
+static const BindCallback_Bind_Reference = -6;
+global func Bind(value) { return [BindCallback_Bind_Value, value]; }
+global func Bind_Context() { return [BindCallback_Bind_Context]; }
+global func Bind_ContextDef() { return [BindCallback_Bind_ContextDef]; }
+global func Bind_CallbackResult(callback) { return [BindCallback_Bind_CallbackResult, callback]; }
+global func Bind_ArgumentArrayPart(int argument, part)
+{
+ if(GetType(part) != C4V_Array)
+ {
+ part = [part];
+ }
+ return [BindCallback_Bind_ArgumentArrayPart, argument, part];
+}
+global func Bind_Reference(array scopedVar) { return [BindCallback_Bind_Reference, scopedVar]; }
+
+global func Call(callback)
+{
+ if(GetType(callback) == C4V_String)
+ {
+ return _inherited(callback, ...);
+ }
+ else
+ {
+ return CallA(callback, CreateFilledArray(...));
+ }
+}
+
+global func CallA(callback, args, bool safe, array refs)
+{
+ if(GetType(callback) == C4V_String)
+ {
+ callback = [this, callback];
+ }
+ if(GetType(callback) != C4V_Array)
+ {
+ return _inherited(callback, args, ...);
+ }
+ else
+ {
+ refs = refs || [];
+ var safeModifier = "";
+ if(safe)
+ {
+ safeModifier = "~";
+ }
+ var target = callback[0], function = callback[1];
+ var argsStr = "", pos = 0;
+ for(var arg in args)
+ {
+ if(pos > 0)
+ {
+ argsStr = Format("%s, ", argsStr);
+ }
+ if(refs[pos])
+ {
+ argsStr = Format("%sScopedVar(%s)", argsStr, Serialize(arg));
+ }
+ else
+ {
+ argsStr = Format("%s%s", argsStr, Serialize(arg));
+ }
+ ++pos;
+ }
+ if(target == CallbackTarget_Global)
+ {
+ return GlobalEval(Format("%s(%s)", function, argsStr));
+ }
+ else if(target == CallbackTarget_Scenario)
+ {
+ return GameCall(function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
+ }
+ else if(target == CallbackTarget_Bind)
+ {
+ return CallA(callback[1], BindArgs(callback[2], args, safe, refs), safe, refs);
+ }
+ else
+ {
+ if(GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID)
+ {
+ return eval(Format("%s->%s%s(%s)", Serialize(target), safeModifier, function, argsStr));
+ }
+ else
+ {
+ return (this && this->~CallCustom(callback, args, ...)) || ROCK->~CallCustom(callback, args, ...);
+ }
+ }
+ }
+}
+
+global func BindArgs_ArgumentArrayPart(array binding, array args, bool safe)
+{
+ var ret = args[binding[0]];
+ for(var i = 1; i < GetLength(binding); ++i)
+ {
+ if(!safe || GetType(ret) == C4V_Array)
+ {
+ ret = ret[binding[i]];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ return ret;
+}
+
+global func BindCustomArgs() { return _inherited(...); } // this allows "overloading" this function even if the "overloading" function is loaded before
+
+global func BindArgs(array binding, array args, bool safe, array& refs)
+{
+ refs = [];
+ var ret = CreateArray(GetLength(binding)), pos = 0;
+ for(var bind in binding)
+ {
+ if(GetType(bind) == C4V_Int || GetType(bind) == C4V_Any && bind >= 0)
+ {
+ ret[pos] = args[bind];
+ }
+ else if(GetType(bind) == C4V_Array && GetLength(bind))
+ {
+ if(bind[0] == BindCallback_Bind_Value)
+ {
+ ret[pos] = bind[1];
+ }
+ else if(bind[0] == BindCallback_Bind_Context)
+ {
+ ret[pos] = this;
+ }
+ else if(bind[0] == BindCallback_Bind_ContextDef)
+ {
+ ret[pos] = GetID();
+ }
+ else if(bind[0] == BindCallback_Bind_CallbackResult)
+ {
+ ret[pos] = CallA(bind[1], args, safe);
+ }
+ else if(bind[0] == BindCallback_Bind_ArgumentArrayPart)
+ {
+ ret[pos] = BindArgs_ArgumentArrayPart(bind[2], args[bind[1]], safe);
+ }
+ else if(bind[0] == BindCallback_Bind_Reference)
+ {
+ refs[pos] = true;
+ ret[pos] = bind[1];
+ }
+ else if(GetType(bind[0]) == C4V_Int && bind[0] < 0)
+ {
+ ret[pos] = BindCustomArgs(bind, args, safe);
+ }
+ else
+ {
+ ret[pos] = BindArgs(bind, args, safe);
+ }
+ }
+ else
+ {
+ FatalError(Format("BindArgs: incorrect binding %v", bind));
+ }
+ ++pos;
+ }
+ return ret;
+}
+
+global func GlobalEval(string code)
+{
+ var effect = AddEffect("IntGlobalEval", 0, 1);
+ var ret = EffectCall(0, effect, "Eval", code);
+ RemoveEffect(0, 0, effect);
+ return ret;
+}
+
+global func FxIntGlobalEvalEval(object target, int effectNumber, string code)
+{
+ return eval(code);
+}
+
+global func foreach(array arr, callback)
+{
+ var i = 0;
+ for(var val in arr)
+ {
+ CallA(callback, [val, i++]);
+ }
+}
+
+global func CheckCallback(callback)
+{
+ if(GetType(callback) == C4V_String)
+ {
+ return true;
+ }
+
+ if(GetType(callback) == C4V_Array)
+ {
+ if(GetType(callback[0]) == C4V_Int)
+ {
+ if(callback[0] == CallbackTarget_Bind && GetLength(callback) == 3)
+ {
+ if(CheckCallback(callback[1]) && CheckBindCallbackBinding(callback[2]))
+ {
+ return true;
+ }
+ }
+ else if(callback[0] == CallbackTarget_Global || callback[0] == CallbackTarget_Scenario)
+ {
+ if(GetLength(callback) == 2 && GetType(callback[1]) == C4V_String && callback[1] != "")
+ {
+ return true;
+ }
+ }
+ }
+ else if(GetType(callback[0]) == C4V_C4ID || GetType(callback[0]) == C4V_C4Object)
+ {
+ if(GetLength(callback) == 2 && GetType(callback[1]) == C4V_String && callback[1] != "")
+ {
+ return true;
+ }
+ }
+ }
+
+ return (this && this->~CheckCustomCallback(callback, ...)) || ROCK->~CheckCustomCallback(callback, ...);
+}
+
+global func CheckBindCallbackBinding(array binding)
+{
+ for(var b in binding)
+ {
+ if(b && GetType(b) != C4V_Int)
+ {
+ if(GetType(b) == C4V_Array)
+ {
+ if(b[0] == BindCallback_Bind_Value)
+ {
+ if(GetLength(b) != 2)
+ {
+ return false;
+ }
+ }
+ else if(b[0] == BindCallback_Bind_Context || b[0] == BindCallback_Bind_ContextDef)
+ {
+ continue;
+ }
+ else if(b[0] == BindCallback_Bind_CallbackResult)
+ {
+ if(GetLength(b) != 2 || !CheckCallback(b[1]))
+ {
+ return false;
+ }
+ }
+ else if(b[0] == BindCallback_Bind_ArgumentArrayPart)
+ {
+ if(GetLength(b) != 3)
+ {
+ return false;
+ }
+
+ if(b[1] && (GetType(b[1]) != C4V_Int || b[1] < 0))
+ {
+ return false;
+ }
+
+ for(var part in b[2])
+ {
+ if(part && (GetType(part) != C4V_Int || part < 0))
+ {
+ return false;
+ }
+ }
+ }
+ else if(b[0] == BindCallback_Bind_Reference)
+ {
+ if(GetLength(b) != 2 || !CheckScopedVar(b[1]))
+ {
+ return false;
+ }
+ }
+ else if(GetType(b[0]) == C4V_Int && b[0] < 0)
+ {
+ if(!CheckBindCallbackCustomBinding(b))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if(!CheckBindCallbackBinding(b))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if(b < 0)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+global func CheckBindCallbackCustomBinding() { return _inherited(...); }