/*-- 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(...); }