summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2017-01-05 16:19:56 +0100
committerMarkus Mittendrein <git@maxmitti.tk>2017-01-05 16:19:56 +0100
commit9b5d0a3ddf41e686439dcda1edfe11eee51c3b07 (patch)
treeeda63d0f51f49b36e0269b6efdf4c612d9ab7d68
downloadSystem.c4g-9b5d0a3ddf41e686439dcda1edfe11eee51c3b07.tar.gz
System.c4g-9b5d0a3ddf41e686439dcda1edfe11eee51c3b07.zip
Initial
-rw-r--r--DTArrays.c52
-rw-r--r--DTCallback.c324
-rw-r--r--DTCallbackScopedVars.c55
-rw-r--r--DTColorStack.c86
-rw-r--r--DTCompatibility.c105
-rw-r--r--DTContents.c16
-rw-r--r--DTFindObject.c338
-rw-r--r--DTFormatN.c87
-rw-r--r--DTMenuCompatibility.c20
-rw-r--r--DTObjectSerializing.c406
-rw-r--r--DTParticles.c15
-rw-r--r--DTPhysicalStack.c72
-rw-r--r--DTPlayers.c82
-rw-r--r--DTQuickSort.c66
-rw-r--r--DTRemoveNotification.c43
-rw-r--r--DTScenUtils.c91
-rw-r--r--DTScopedVars.c87
-rw-r--r--DTTemplateFunctions.c307
-rw-r--r--DTTimer.c26
-rw-r--r--DTTransform.c240
-rw-r--r--DTUtility.c744
21 files changed, 3262 insertions, 0 deletions
diff --git a/DTArrays.c b/DTArrays.c
new file mode 100644
index 0000000..0b78ef4
--- /dev/null
+++ b/DTArrays.c
@@ -0,0 +1,52 @@
+#strict 2
+
+global func ArrayErase(array& arr, int index)
+{
+ if(index < GetLength(arr))
+ {
+ for(var i = index; i < GetLength(arr) - 1; ++i)
+ {
+ arr[i] = arr[i + 1];
+ }
+ SetLength(arr, GetLength(arr) - 1);
+ }
+}
+
+global func ArrayEraseItem(array& arr, item, bool multiple)
+{
+ var index, count = 0;
+ while((index = GetIndexOf2(item, arr)) != -1)
+ {
+ ArrayErase(arr, index);
+ if(!multiple)
+ {
+ return index;
+ }
+ ++count;
+ }
+ return (!multiple && -1) || count;
+}
+
+global func ArrayInsert(array& arr, int index, value)
+{
+ for(var i = GetLength(arr); i > index; --i)
+ {
+ arr[i] = arr[i - 1];
+ }
+ arr[index] = value;
+}
+
+global func ArrayAppend(array& arr, value)
+{
+ arr[GetLength(arr)] = value;
+}
+
+global func ArrayAppendArray(array& arr, array append)
+{
+ var i = GetLength(arr);
+ SetLength(arr, GetLength(arr) + GetLength(append));
+ for(var val in append)
+ {
+ arr[i++] = val;
+ }
+}
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(...); }
diff --git a/DTCallbackScopedVars.c b/DTCallbackScopedVars.c
new file mode 100644
index 0000000..75eb5e2
--- /dev/null
+++ b/DTCallbackScopedVars.c
@@ -0,0 +1,55 @@
+#strict 2
+
+static const ScopedVar_Callback = 4;
+
+global func CallbackVar(callback) { return [ScopedVar_Callback, callback]; }
+
+global func &CustomScopedVar(array variable)
+{
+ if(variable[0] == ScopedVar_Callback)
+ {
+ return CallA(variable[1], []);
+ }
+ else
+ {
+ return _inherited(variable, ...);
+ }
+}
+
+static const BindCallback_Bind_ScopedVar = -6;
+
+global func Bind_Var(array scopedVar) { return [BindCallback_Bind_ScopedVar, scopedVar]; }
+
+global func BindCustomArgs(array bind, array args, bool safe)
+{
+ if(bind[0] == BindCallback_Bind_ScopedVar)
+ {
+ return ScopedVar(bind[1]);
+ }
+ else
+ {
+ return _inherited(bind, args, safe, ...);
+ }
+}
+
+global func CheckCustomScopedVar(array variable)
+{
+ if(GetType(variable) == C4V_Array && variable[0] == ScopedVar_Callback && GetLength(variable) == 2)
+ {
+ if(CheckCallback(variable[1]))
+ {
+ return true;
+ }
+ }
+
+ return _inherited(variable, ...);
+}
+
+global func CheckBindCallbackCustomBinding(binding)
+{
+ if(GetType(binding) == C4V_Array && GetLength(binding) == 2 && binding[0] == BindCallback_Bind_ScopedVar && CheckScopedVar(binding[1]))
+ {
+ return true;
+ }
+ return _inherited(binding, ...);
+}
diff --git a/DTColorStack.c b/DTColorStack.c
new file mode 100644
index 0000000..9f9535f
--- /dev/null
+++ b/DTColorStack.c
@@ -0,0 +1,86 @@
+#strict 2
+
+global func GetClrModulationStack(object target)
+{
+ target || FatalError("GetClrModulationStack: no target object given");
+ return GetEffect("ClrModulationStack", target) || AddEffect("ClrModulationStack", target, 200);
+}
+
+global func AddClrModulation(int color, int baseColor, int factor, int precision, object target)
+{
+ baseColor = baseColor || RGB(127, 127, 127);
+ target = target || this || FatalError("AddClrModulation: no target object given");
+ factor = factor || 1;
+ precision = precision || 1;
+
+ return EffectCall(target, GetClrModulationStack(target), "AddModulation", color, baseColor, factor, precision);
+}
+
+global func RemoveClrModulation(int id, object target)
+{
+ target = target || this || FatalError("RemoveClrModulation: no target object given");
+
+ return EffectCall(target, GetClrModulationStack(target), "RemoveModulation", id);
+}
+
+global func FxClrModulationStackStart(object target, int effectNumber, int temp)
+{
+ if(!temp)
+ {
+ EffectVar(1, target, effectNumber) = [];
+ }
+}
+
+global func FxClrModulationStackAddModulation(object target, int effectNumber, int color, int baseColor, int factor, int precision)
+{
+ ArrayAppend(EffectVar(1, target, effectNumber), [++EffectVar(0, target, effectNumber), color, baseColor, factor, precision]);
+ EffectCall(target, effectNumber, "ApplyModulation");
+ return EffectVar(0, target, effectNumber);
+}
+
+global func FxClrModulationStackRemoveModulation(object target, int effectNumber, int id)
+{
+ for(var i = 0; i < GetLength(EffectVar(1, target, effectNumber)); ++i)
+ {
+ if(EffectVar(1, target, effectNumber)[i][0] == id)
+ {
+ ArrayErase(EffectVar(1, target, effectNumber), i);
+ EffectCall(target, effectNumber, "ApplyModulation");
+ return true;
+ }
+ }
+ return 0;
+}
+
+global func FxClrModulationStackApplyModulation(object target, int effectNumber)
+{
+ if(GetLength(EffectVar(1, target, effectNumber)) == 0)
+ {
+ SetClrModulation(0, target);
+ return 0;
+ }
+ var r, g, b, a;
+ SplitRGBaValue(EffectVar(1, target, effectNumber)[0][1], r, g, b, a);
+ a = 255 - a;
+ for(var i = 1; i < GetLength(EffectVar(1, target, effectNumber)); ++i)
+ {
+ var clrPart = EffectVar(1, target, effectNumber)[i];
+ var mr, mg, mb, ma, br, bg, bb, ba;
+ SplitRGBaValue(clrPart[1], mr, mg, mb, ma);
+ SplitRGBaValue(clrPart[2], br, bg, bb, ba);
+ ma = 255 - ma;
+ a *= ma;
+ a /= 0xFF;
+ r += mr - br;
+ r *= clrPart[3];
+ r /= clrPart[4];
+ g += mg - bg;
+ g *= clrPart[3];
+ g /= clrPart[4];
+ b += mb - bb;
+ b *= clrPart[3];
+ b /= clrPart[4];
+ }
+ SetClrModulation(RGBa(BoundBy(r, 0, 255), BoundBy(g, 0, 255), BoundBy(b, 0, 255), 255 - a), target);
+ return true;
+}
diff --git a/DTCompatibility.c b/DTCompatibility.c
new file mode 100644
index 0000000..d3ae922
--- /dev/null
+++ b/DTCompatibility.c
@@ -0,0 +1,105 @@
+/*-- Avoid errors from non-existing Engine-Functions --*/
+
+#strict 2
+
+static hostFeatures;
+static hostTime;
+
+global func SetGameComment() { return _inherited(...); }
+global func IsHost() { return _inherited(...); }
+global func RequestHostFeature(string feature) { if(!HaveHostFeature(feature) || feature == "HostTime") return _inherited(feature, ...); }
+
+global func AnnounceHostFeature(string feature)
+{
+ hostFeatures || (hostFeatures = []);
+ hostFeatures[GetLength(hostFeatures)] = feature;
+}
+
+global func HaveHostFeature(string feature)
+{
+ hostFeatures || (hostFeatures = []);
+ return GetIndexOf(feature, hostFeatures) != -1;
+}
+
+global func ReRequestHostFeatures()
+{
+ if(IsNetwork())
+ {
+ var features = hostFeatures;
+ hostFeatures = [];
+
+ if(features)
+ {
+ for(var feature in features)
+ {
+ RequestHostFeature(feature);
+ }
+ }
+ }
+}
+
+global func AnnounceHostTime(int time)
+{
+ hostTime = time;
+}
+
+global func GetSystemTime(int what)
+{
+ if(HaveHostFeature("HostTime"))
+ {
+ if(what == 10) // is leap year
+ {
+ var year = GetSystemTime(0);
+ return !!((!(year % 4) && year % 100) || !(year % 400));
+ }
+ if(what == 9) return hostTime; // timestamp
+ if(what == 8) // day of year
+ {
+ return (hostTime - (GetSystemTime(0) - 1970) * 31556926)/(3600*24) + 1;
+ }
+ if(what == 7) return 0;
+ if(what == 6) return hostTime % 60;
+ if(what == 5) return (hostTime/60) % 60;
+ if(what == 4) return (hostTime/3600) % 24;
+ if(what == 3)
+ {
+ var dayofyear = GetSystemTime(8);
+ var leapyear = GetSystemTime(10);
+ if(dayofyear < 32) return dayofyear;
+ else if(dayofyear < 60 + leapyear) return dayofyear - 31;
+ else if(dayofyear < 91 + leapyear) return dayofyear - 59;
+ else if(dayofyear < 121 + leapyear) return dayofyear - (90 + leapyear);
+ else if(dayofyear < 152 + leapyear) return dayofyear - (120 + leapyear);
+ else if(dayofyear < 182 + leapyear) return dayofyear - (151 + leapyear);
+ else if(dayofyear < 213 + leapyear) return dayofyear - (181 + leapyear);
+ else if(dayofyear < 244 + leapyear) return dayofyear - (212 + leapyear);
+ else if(dayofyear < 274 + leapyear) return dayofyear - (243 + leapyear);
+ else if(dayofyear < 305 + leapyear) return dayofyear - (273 + leapyear);
+ else if(dayofyear < 335 + leapyear) return dayofyear - (204 + leapyear);
+ else return dayofyear - (334 + leapyear);
+ }
+ if(what == 2)
+ {
+ return ((hostTime/86400) + 4) % 7;
+ }
+ if(what == 1)
+ {
+ var dayofyear = GetSystemTime(8);
+ var leapyear = GetSystemTime(10);
+ if(dayofyear < 32) return 1;
+ else if(dayofyear < 60 + leapyear) return 2;
+ else if(dayofyear < 91 + leapyear) return 3;
+ else if(dayofyear < 121 + leapyear) return 4;
+ else if(dayofyear < 152 + leapyear) return 5;
+ else if(dayofyear < 182 + leapyear) return 6;
+ else if(dayofyear < 213 + leapyear) return 7;
+ else if(dayofyear < 244 + leapyear) return 8;
+ else if(dayofyear < 274 + leapyear) return 9;
+ else if(dayofyear < 305 + leapyear) return 10;
+ else if(dayofyear < 335 + leapyear) return 11;
+ else return 12;
+ }
+ if(what == 0) return 1970 + ((hostTime + 31556926 * 2)/(31556926)) - 2;
+ }
+ else return _inherited(what, ...);
+}
diff --git a/DTContents.c b/DTContents.c
new file mode 100644
index 0000000..cb49691
--- /dev/null
+++ b/DTContents.c
@@ -0,0 +1,16 @@
+#strict 2
+
+global func GetContents(bool recursive, object obj)
+{
+ obj = obj || this;
+ var ret = [];
+ for(var i = 0; i < obj->ContentsCount(); ++i)
+ {
+ ret[GetLength(ret)] = obj->Contents(i);
+ if(recursive)
+ {
+ ArrayAppendArray(ret, obj->Contents(i)->GetContents(true));
+ }
+ }
+ return ret;
+}
diff --git a/DTFindObject.c b/DTFindObject.c
new file mode 100644
index 0000000..b7205e6
--- /dev/null
+++ b/DTFindObject.c
@@ -0,0 +1,338 @@
+/*--
+ * Overloads Find_-functions available in System.c4g so Find_Or and Find_And can be used inside other criterions like so:
+ * Find_ID(Find_Or(ROCK, FLNT))
+ * Find_Func("IsBlock", ["CompareLayer", LAYER_FG])
+ * --*/
+#strict 2
+
+global func ProxySimpleFind_AndOr(int type, criteria)
+{
+ if(GetType(criteria) != C4V_Array)
+ {
+ return [type, criteria];
+ }
+ else
+ {
+ var ret = [criteria[0]];
+ for(var i = 1; i < GetLength(criteria); ++i)
+ {
+ ret[i] = [type, criteria[i]];
+ }
+ return ret;
+ }
+}
+
+global func ProxyFind_AndOr(criteria, &type, &parts)
+{
+ type = criteria[0];
+ parts = CreateArray(GetLength(criteria) - 1);
+ for(var i = 1; i < GetLength(criteria); ++i)
+ {
+ if(GetType(criteria[i]) == C4V_Array)
+ {
+ parts[i - 1] = criteria[i];
+ }
+ else
+ {
+ parts[i - 1] = [criteria[i]];
+ }
+ }
+}
+
+global func Find_Action(what) { return ProxySimpleFind_AndOr(C4FO_Action, what); }
+global func Find_Category(what) { return ProxySimpleFind_AndOr(C4FO_Category, what); }
+global func Find_Container(what) { return ProxySimpleFind_AndOr(C4FO_Container, what); }
+global func Find_Controller(what) { return ProxySimpleFind_AndOr(C4FO_Controller, what); }
+global func Find_ID(what) { return ProxySimpleFind_AndOr(C4FO_ID, what); }
+global func Find_Layer(what) { return ProxySimpleFind_AndOr(C4FO_Layer, what); }
+global func Find_OCF(what) { return ProxySimpleFind_AndOr(C4FO_OCF, what); }
+global func Find_Owner(what) { return ProxySimpleFind_AndOr(C4FO_Owner, what); }
+
+global func Find_Exclude(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_InRect(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_AtRect(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_OnLine(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Distance(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_ActionTarget(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Hostile(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Allied(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Func(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_PathFree(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Command(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_ColorDw(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Effect(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Local(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_Team(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_FuncEqual(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_FuncInside(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
+global func Find_ID(what)
+{
+ if(GetType(what) != C4V_Array)
+ {
+ return _inherited(what);
+ }
+
+ var ret = [], parts, i = 0;
+ ProxyFind_AndOr(what, ret[0], parts);
+ for(var part in parts)
+ {
+ ret[++i] = _inherited(part[0], part[1], part[2], part[3], part[4], part[5], part[6], part[7], part[8], part[9]);
+ }
+ return ret;
+}
+
diff --git a/DTFormatN.c b/DTFormatN.c
new file mode 100644
index 0000000..0f9481b
--- /dev/null
+++ b/DTFormatN.c
@@ -0,0 +1,87 @@
+#strict 2
+
+global func FormatN(string format, array placeholders, array items)
+{
+ if(!items && GetType(placeholders[0]) == C4V_Array)
+ {
+ items = CreateArray(GetLength(placeholders));
+ for(var i = 0; i < GetLength(placeholders); ++i)
+ {
+ items[i] = placeholders[i][1];
+ placeholders[i] = placeholders[i][0];
+ }
+ }
+
+ var ret = "";
+
+ var inPlaceholder = 0;
+ var placeholderType = "";
+ var placeholderPart = "";
+
+ for(var i = 0; i < GetLength(format); ++i)
+ {
+ var c = GetChar(format, i);
+ if(c == 37) // %
+ {
+ if(inPlaceholder == 0)
+ {
+ inPlaceholder = 1;
+ }
+ else if(inPlaceholder == 1)
+ {
+ if(c == 37 && GetLength(placeholderType) == 0) // %
+ {
+ ret = Format("%s%%", ret);
+ inPlaceholder = 0;
+ }
+ else
+ {
+ inPlaceholder = 2;
+ }
+ }
+ else if(inPlaceholder == 2)
+ {
+ var index = GetIndexOf2(placeholderPart, placeholders);
+ if(index == -1)
+ {
+ FatalError(Format("FormatN: Unkown placeholder \"%s\"", placeholderPart));
+ return 0;
+ }
+
+ ret = Format(Format("%%s%%%s", placeholderType), ret, items[index]);
+
+ inPlaceholder = 0;
+ placeholderType = "";
+ placeholderPart = "";
+ }
+ }
+ else
+ {
+ if(inPlaceholder == 0)
+ {
+ ret = Format("%s%c", ret, c);
+ }
+ else if(inPlaceholder == 1)
+ {
+ placeholderType = Format("%s%c", placeholderType, c);
+ }
+ else if(inPlaceholder == 2)
+ {
+ placeholderPart = Format("%s%c", placeholderPart, c);
+ }
+ }
+ }
+
+ if(inPlaceholder == 1)
+ {
+ FatalError(Format("FormatN: Placeholder not finished at end of format-string: \"%%%s\"", placeholderType));
+ return 0;
+ }
+ else if(inPlaceholder == 2)
+ {
+ FatalError(Format("FormatN: Placeholder not finished at end of format-string: \"%%%s%%%s\"", placeholderType, placeholderPart));
+ return 0;
+ }
+
+ return ret;
+}
diff --git a/DTMenuCompatibility.c b/DTMenuCompatibility.c
new file mode 100644
index 0000000..0401b95
--- /dev/null
+++ b/DTMenuCompatibility.c
@@ -0,0 +1,20 @@
+#strict 2
+#appendto MN7I
+
+static const Menu_Enum_Symbol = Menu_Layout_Symbol;
+static const Menu_Enum_InfoCaption = Menu_Layout_InfoCaption;
+
+static const Menu_Enum_Caption = Menu_Layout_Caption;
+static const Menu_Enum_Value = Menu_Layout_Value;
+
+global func Menu_SubMenu(caption, symbol, string infoCaption, array menuEntries_Settings, int count, int extra, XPar1, XPar2)
+{
+ if(GetType(caption) == C4V_Array) // caption contains all entry settings for the current menu and symbol contains all submenu entries and settings
+ {
+ return inherited(caption, symbol, infoCaption, menuEntries_Settings, count, extra, XPar1, XPar2, ...);
+ }
+ else
+ {
+ return inherited([Menu_Entry_Caption(caption), Menu_Entry_Symbol(symbol), Menu_Entry_Count(count), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Extra(extra), Menu_Entry_XPar1(XPar1), Menu_Entry_XPar2(XPar2)], menuEntries_Settings);
+ }
+}
diff --git a/DTObjectSerializing.c b/DTObjectSerializing.c
new file mode 100644
index 0000000..bd68ddc
--- /dev/null
+++ b/DTObjectSerializing.c
@@ -0,0 +1,406 @@
+#strict 2
+
+static const Object_Status = 0;
+static const Object_Name = 1;
+static const Object_Owner = 2;
+static const Object_Controller = 3;
+static const Object_Killer = 4;
+static const Object_Category = 5;
+static const Object_Position = 6;
+static const Object_Speed = 7;
+static const Object_Con = 8;
+static const Object_OwnMass = 9;
+static const Object_Damage = 10;
+static const Object_Energy = 11;
+static const Object_MagicEnergy = 12;
+static const Object_Alive = 13;
+static const Object_Breath = 14;
+static const Object_ColorDw = 15;
+static const Object_Shape = 16;
+static const Object_Vertices = 17;
+static const Object_SolidMask = 18;
+static const Object_Picture = 19;
+static const Object_Entrance = 20;
+static const Object_Physicals = 21;
+static const Object_Action = 22;
+static const Object_Components = 23;
+static const Object_Contents = 24;
+static const Object_PlrViewRange = 25;
+static const Object_Visibility = 26;
+static const Object_BlitMode = 27;
+static const Object_CrewEnabled = 28;
+static const Object_ObjectLayer = 29;
+static const Object_Locals = 30;
+static const Object_Graphics = 31;
+static const Object_Effects = 32;
+static const Object_ID = 33;
+static const Object_ContactDensity = 34;
+static const Object_Dir = 35;
+static const Object_ComDir = 36;
+static const Object_Origin = 37;
+static const Object_Custom = 38;
+static const Object_Count = 39;
+//_enum(Object_, Status, Name, Owner, Controller, Killer, Category, Position, Speed, Con, OwnMass, Damage, Energy, MagicEnergy, Alive, Breath, ColorDw, Shape, Vertices, SolidMask, Picture, Entrance, Physicals, Action, Components, Contents, PlrViewRange, Visibility, BlitMode, CrewEnabled, ObjectLayer, Locals, Graphics, Effects, ID, ContactDensity, Dir, ComDir, Origin, Custom, Count)
+// DONT KNOW: OnFire, Commands
+
+global func& LocalX(name, object obj)
+{
+ obj = obj || this;
+ if(GetType(name) == C4V_String)
+ {
+ return LocalN(name, obj);
+ }
+ else
+ {
+ return Local(name, obj);
+ }
+}
+
+global func GetEffectVarCount(object target, int effectNumber) // WARNING: this could eventually be wrong (with a very very small probability) when some EffectVars are set right coincidentally
+{ // based on and inspired by C4Effect::CompileFunc
+ var name = GetEffect(0, target, effectNumber, 1);
+ var prio = GetEffect(0, target, effectNumber, 2);
+ var timer = GetEffect(0, target, effectNumber, 3);
+ var lifeTime = GetEffect(0, target, effectNumber, 6);
+
+ if(!name)
+ {
+ return -1;
+ }
+
+ // determine offset for this effect within GetObjectVal
+ for(var i = 0; ; ++i)
+ {
+ if(GetObjectVal("Effects", 0, target, i) == name && GetObjectVal("Effects", 0, target, i + 1) == effectNumber && GetObjectVal("Effects", 0, target, i + 2) == prio && GetObjectVal("Effects", 0, target, i + 3) == lifeTime && GetObjectVal("Effects", 0, target, i + 4) == timer)
+ {
+ var count = GetObjectVal("Effects", 0, target, i + 7);
+ if(count == 1)
+ {
+ var nextEffectName = GetObjectVal("Effects", 0, target, i + 8);
+ if(GetType(nextEffectName) == C4V_String)
+ {
+ var nextNumber = GetObjectVal("Effects", 0, target, i + 9);
+ if(GetType(nextNumber) == C4V_Int && GetEffect(0, target, nextNumber, 1) == nextEffectName)
+ {
+ return 0;
+ }
+ }
+
+ }
+ return GetType(count) == C4V_Int && count;
+ }
+ }
+}
+
+global func GetObjectSaveData(object obj)
+{
+ obj = obj || this;
+ var id = GetID(obj);
+ var ret = CreateArray(Object_Count);
+ ret[Object_ID] = id;
+ ret[Object_Origin] = obj;
+ ret[Object_Status] = GetObjectStatus(obj);
+ if(GetName(obj) != GetName(0, id))
+ {
+ ret[Object_Name] = GetName(obj);
+ }
+ ret[Object_Owner] = GetOwner(obj);
+ ret[Object_Controller] = GetController(obj);
+ ret[Object_Killer] = GetKiller(obj);
+ ret[Object_Category] = GetCategory(obj);
+ ret[Object_Position] = [GetX(obj), GetY(obj), GetR(obj)];
+ ret[Object_Speed] = [GetXDir(obj, 65536), GetYDir(obj, 65536), GetRDir(obj, 65536)];
+ ret[Object_Con] = GetCon(obj);
+ ret[Object_OwnMass] = GetMass(obj) - GetMass(0, id); // maybe save complete Mass instead?
+ ret[Object_Damage] = GetDamage(obj);
+ ret[Object_Energy] = GetObjectVal("Energy", 0, obj);
+ ret[Object_MagicEnergy] = GetMagicEnergy(obj);
+ ret[Object_Alive] = GetAlive(obj);
+ ret[Object_Breath] = GetBreath(obj);
+ ret[Object_ColorDw] = GetColorDw(obj);
+ ret[Object_Shape] = [GetObjectVal("Offset", 0, obj, 0), GetObjectVal("Offset", 0, obj, 1), GetObjectVal("Width", 0, obj), GetObjectVal("Height", 0, obj)];
+
+ if(GetObjectVal("OwnVertices", 0, obj))
+ {
+ var vertexNum = GetVertexNum(obj);
+ var vertices = CreateArray(vertexNum);
+ for(var i = 0; i < vertexNum; ++i)
+ {
+ vertices[i] = [GetVertex(i, VTX_X, obj), GetVertex(i, VTX_Y, obj), GetVertex(i, VTX_CNAT, obj), GetVertex(i, VTX_Friction, obj)];
+ }
+ ret[Object_Vertices] = vertices;
+ }
+
+ ret[Object_SolidMask] = GetSolidMask(obj);
+ ret[Object_Picture] = [GetObjectVal("Picture", 0, obj, 0), GetObjectVal("Picture", 0, obj, 1), GetObjectVal("Picture", 0, obj, 2), GetObjectVal("Picture", 0, obj, 3)];
+ ret[Object_Entrance] = GetEntrance(obj);
+ ret[Object_Action] = [GetAction(obj), GetPhase(obj), GetActionTarget(0, obj), GetActionTarget(1, obj), GetObjectVal("ActionData", 0, obj)];
+
+ var defComponents = [], objComponents = [];
+ for(var i = 0, componentID; componentID = GetComponent(0, i, 0, id); ++i)
+ {
+ var cnt = GetComponent(componentID, 0, 0, id);
+ if(cnt != 0)
+ {
+ defComponents[GetLength(defComponents)] = [componentID, cnt];
+ }
+ }
+ for(var i = 0, componentID; componentID = GetComponent(0, i, obj); ++i)
+ {
+ var cnt = GetComponent(componentID, 0, obj);
+ if(cnt != 0)
+ {
+ objComponents[GetLength(objComponents)] = [componentID, cnt];
+ }
+ }
+
+ var diffComponents = [];
+ for(var i = 0; i < GetLength(defComponents); ++i)
+ {
+ var defID = defComponents[i][0], defCnt = defComponents[i][1];
+ var cnt = 0;
+ for(var j = 0; j < GetLength(objComponents); ++j)
+ {
+ if(objComponents[j][0] == defID)
+ {
+ cnt = objComponents[j][1];
+ ArrayErase(objComponents, j);
+ break;
+ }
+ }
+
+ if(cnt != defCnt)
+ {
+ diffComponents[GetLength(diffComponents)] = [defID, cnt];
+ }
+ }
+ ArrayAppendArray(diffComponents, objComponents);
+
+ if(GetLength(diffComponents) != 0)
+ {
+ ret[Object_Components] = diffComponents;
+ }
+
+ var contents = [];
+ for(var content in obj->GetContents())
+ {
+ contents[GetLength(contents)] = GetObjectSaveData(content);
+ }
+ ret[Object_Contents] = contents;
+ ret[Object_PlrViewRange] = GetObjectVal("PlrViewRange", 0, obj);
+ ret[Object_Visibility] = GetVisibility(obj);
+ ret[Object_BlitMode] = GetObjectBlitMode(obj);
+ ret[Object_CrewEnabled] = GetCrewEnabled(obj);
+ ret[Object_ObjectLayer] = GetObjectLayer(obj);
+ ret[Object_Dir] = GetDir(obj);
+ ret[Object_ComDir] = GetComDir(obj);
+ ret[Object_ContactDensity] = GetObjectVal("ContactDensity", 0, obj);
+
+ var locals = [];
+ for(var f in [0, 1])
+ {
+ var cnt = GetLocalCount(obj, f);
+ for(var i = 0; i < cnt; ++i)
+ {
+ var name = GetLocalByIndex(i, obj, f);
+ locals[GetLength(locals)] = [name, LocalX(name, obj)];
+ }
+ }
+
+ ret[Object_Locals] = locals;
+
+ var overlays = [[GetGraphics(1, obj, 0), GetGraphics(0, obj, 0), 0, GetGraphics(2, obj, 0), GetGraphics(3, obj, 0), GetGraphics(4, obj, 0), 0, obj->GetObjDrawTransform(0), GetClrModulation(obj, 0)]];
+ var offset = 0, diffIndex = 0;
+ while((offset = obj->GetOverlayValueByIndex(diffIndex, offset)) >= 0)
+ {
+ var overlayID = obj->GetOverlayValueByIndex(0, offset, 1);
+ overlays[GetLength(overlays)] = [obj->GetOverlayValueByIndex(0, offset, 4), obj->GetOverlayValueByIndex(0, offset, 3), overlayID, obj->GetOverlayValueByIndex(0, offset, 2), obj->GetOverlayValueByIndex(0, offset, 5), obj->GetOverlayValueByIndex(0, offset, 6), Object(obj->GetOverlayValueByIndex(0, offset, 8)), obj->GetObjDrawTransform(-1, offset), GetClrModulation(obj, overlayID)];
+ diffIndex = 1;
+ }
+ ret[Object_Graphics] = overlays;
+
+ var physicals = []; // TODO: Think about StackTemporary? What about Effects? Maybe pop the whole stack while saving and restore afterwards
+ for(var physical in ["Energy", "Breath", "Walk", "Jump", "Scale", "Hangle", "Dig", "Swim", "Throw", "Push", "Fight", "Magic", "Float", "CanScale", "CanHangle", "CanDig", "CanConstruct", "CanChop", "CanSwimDig", "CanFly", "CorrosionResist", "BreatheWater"])
+ {
+ var value = GetPhysical(physical, PHYS_Current, obj);
+ if(value != GetPhysical(physical, 0, 0, id))
+ {
+ physicals[GetLength(physicals)] = [physical, value];
+ }
+ }
+ if(GetLength(physicals) != 0)
+ {
+ ret[Object_Physicals] = physicals;
+ }
+
+ var effects = [];
+ for(var i = 0, index; index = GetEffect("*", obj, i, 0); ++i)
+ {
+ var varCnt = GetEffectVarCount(obj, index);
+ var effectVars = CreateArray(varCnt);
+ for(var j = 0; j < varCnt; ++j)
+ {
+ effectVars[j] = EffectVar(j, obj, index);
+ }
+ effects[i] = [GetEffect(0, obj, index, 1), GetEffect(0, obj, index, 2), GetEffect(0, obj, index, 3), GetEffect(0, obj, index, 4), GetEffect(0, obj, index, 5), GetEffect(0, obj, index, 6), effectVars, index];
+ }
+ if(GetLength(effects) != 0)
+ {
+ ret[Object_Effects] = effects;
+ }
+
+ // TODO: Custom
+
+ return ret;
+}
+
+
+global func ApplyObjectSaveData(array data, object obj)
+{
+ obj = obj || this;
+ // set values which can trigger callbacks before changing def
+ obj->SetObjectStatus(data[Object_Status]);
+ obj->SetCon(data[Object_Con]);
+ obj->SetOwner(data[Object_Owner]);
+ obj->DoDamage(data[Object_Damage] - obj->GetDamage());
+ if(data[Object_Energy])
+ {
+ if(GetPhysical("Energy", PHYS_Current, obj) < data[Object_Energy])
+ {
+ SetPhysical("Energy", data[Object_Energy], PHYS_Temporary, obj);
+ }
+ obj->DoEnergy(data[Object_Energy] - GetObjectVal("Energy", 0, obj), obj, true);
+ }
+
+ for(var content in data[Object_Contents])
+ {
+ var temp = obj->CreateContents(ROCK);
+ temp->ApplyObjectSaveData(content);
+ }
+
+ obj->ChangeDef(data[Object_ID]);
+
+ // the following changes should not call Callbacks anymore
+ obj->SetName(data[Object_Name]);
+ obj->SetController(data[Object_Controller]);
+ obj->SetKiller(data[Object_Killer]);
+ obj->SetCategory(data[Object_Category]);
+ obj->SetPosition(data[Object_Position][0], data[Object_Position][1]);
+ obj->SetR(data[Object_Position][2]);
+ SetXDir(data[Object_Speed][0], obj, 65536);
+ SetYDir(data[Object_Speed][1], obj, 65536);
+ SetRDir(data[Object_Speed][2], obj, 65536);
+ obj->SetMass(data[Object_OwnMass] - obj->GetMass());
+ obj->SetColorDw(data[Object_ColorDw]);
+ obj->SetShape(data[Object_Shape][0], data[Object_Shape][1], data[Object_Shape][2], data[Object_Shape][3]);
+ obj->SetSolidMask(data[Object_SolidMask][0], data[Object_SolidMask][1], data[Object_SolidMask][2], data[Object_SolidMask][3], data[Object_SolidMask][4], data[Object_SolidMask][5]);
+ obj->SetPicture(data[Object_Picture][0], data[Object_Picture][1], data[Object_Picture][2], data[Object_Picture][3]);
+ obj->SetAlive(data[Object_Alive]);
+ obj->DoMagicEnergy(data[Object_MagicEnergy] - obj->GetMagicEnergy());
+ obj->DoBreath(data[Object_Breath] - obj->GetBreath());
+ obj->SetEntrance(data[Object_Entrance]);
+
+ if(data[Object_Vertices])
+ {
+ var cnt = GetLength(data[Object_Vertices]);
+ while(obj->GetVertexNum() < cnt)
+ {
+ obj->AddVertex();
+ }
+
+ var i = 0;
+ for(var vertex in data[Object_Vertices])
+ {
+ obj->SetVertex(i, VTX_X, vertex[0], VTX_SetPermanent);
+ obj->SetVertex(i, VTX_Y, vertex[1], VTX_SetPermanent);
+ obj->SetVertex(i, VTX_CNAT, vertex[2], VTX_SetPermanent);
+ obj->SetVertex(i, VTX_Friction, vertex[3], (i == cnt - 1 && VTX_SetPermanentUpd) || VTX_SetPermanent);
+ ++i;
+ }
+ }
+
+ var id = data[Object_ID];
+ for(var physical in ["Energy", "Breath", "Walk", "Jump", "Scale", "Hangle", "Dig", "Swim", "Throw", "Push", "Fight", "Magic", "Float", "CanScale", "CanHangle", "CanDig", "CanConstruct", "CanChop", "CanSwimDig", "CanFly", "CorrosionResist", "BreatheWater"])
+ {
+ obj->SetPhysical(physical, GetPhysical(physical, 0, 0, id), PHYS_Temporary);
+ }
+
+ if(data[Object_Physicals]) // TODO: Update for StackTemporary and try to represent right mode
+ {
+ for(var physical in data[Object_Physicals])
+ {
+ obj->SetPhysical(physical[0], physical[1], PHYS_Temporary);
+ }
+ }
+
+ for(var localVar in data[Object_Locals])
+ {
+ LocalX(localVar[0]) = localVar[1];
+ }
+
+ if(data[Object_Components])
+ {
+ for(var component in data[Object_Components])
+ {
+ obj->SetComponent(component[0], component[1]);
+ }
+ }
+ SetPlrViewRange(data[Object_PlrViewRange], obj, true);
+ obj->SetVisibility(data[Object_Visibility]);
+ obj->SetObjectBlitMode(data[Object_BlitMode]);
+ obj->SetCrewEnabled(data[Object_CrewEnabled]);
+ obj->SetObjectLayer(data[Object_ObjectLayer]);
+ obj->SetContactDensity(data[Object_ContactDensity]);
+ obj->SetComDir(data[Object_ComDir]);
+
+ // unfortunately there is no SetAction without triggering CallCallbacks
+ // SetAction at last, so the important stuff is setup already
+ obj->SetAction(data[Object_Action][0], data[Object_Action][2], data[Object_Action][3], true);
+ obj->SetPhase(data[Object_Action][1]);
+ obj->SetActionData(data[Object_Action][4]);
+
+ obj->SetDir(data[Object_Dir]); // Set Dir after Action because Dirs are enabled per action
+
+ for(var graphic in data[Object_Graphics])
+ {
+ SetGraphics(graphic[0], obj, graphic[1], graphic[2], graphic[3], graphic[4], graphic[5], graphic[6]);
+
+ var transform = graphic[7];
+ if(GetLength(transform) == 6)
+ {
+ SetObjDrawTransform(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], obj, graphic[2]);
+ }
+ else
+ {
+ SetObjDrawTransform(1000, 0, 0, 0, 1000, 0, obj, graphic[2]);
+ obj->SetObjDrawTransform2(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], transform[6], transform[7], transform[8], graphic[2]);
+ }
+
+ SetClrModulation(graphic[8], obj, graphic[2]);
+ }
+
+ if(data[Object_Effects])
+ {
+ // WARNING: Can't restore lifetime
+ for(var effect in data[Object_Effects])
+ {
+ var temp = AddEffect("ApplyEffectDummy", obj, effect[1], effect[2], effect[3], effect[4]);
+
+ for(var i = 0; i < GetLength(effect[6]); ++i)
+ {
+ EffectVar(i, obj, temp) = effect[6][i];
+ }
+
+ ChangeEffect(0, obj, temp, effect[0], -1);
+ }
+ }
+
+ // TODO: Custom
+}
+
+global func InstantiateObjectSaveData(array data)
+{
+ var ret = CreateObject(ROCK);
+ ret->ApplyObjectSaveData(data);
+ return ret;
+}
diff --git a/DTParticles.c b/DTParticles.c
new file mode 100644
index 0000000..5373134
--- /dev/null
+++ b/DTParticles.c
@@ -0,0 +1,15 @@
+#strict 2
+
+global func DrawParticleRect(string name, array rect, int dist, int a, b, int yDir)
+{
+ var ret = 0;
+ if(GetType(b) != C4V_Array)
+ {
+ b = [b, b, b, b];
+ }
+ ret = DrawParticleLine(name, rect[0], rect[1], rect[0] + rect[2], rect[1], dist, a, b[0], b[1], yDir);
+ ret += DrawParticleLine(name, rect[0] + rect[2], rect[1], rect[0] + rect[2], rect[1] + rect[3], dist, a, b[1], b[2], yDir);
+ ret += DrawParticleLine(name, rect[0] + rect[2], rect[1] + rect[3], rect[0], rect[1] + rect[3], dist, a, b[2], b[3], yDir);
+ ret += DrawParticleLine(name, rect[0], rect[1] + rect[3], rect[0], rect[1], dist, a, b[3], b[0], yDir);
+ return ret;
+}
diff --git a/DTPhysicalStack.c b/DTPhysicalStack.c
new file mode 100644
index 0000000..c0aa7ad
--- /dev/null
+++ b/DTPhysicalStack.c
@@ -0,0 +1,72 @@
+#strict 2
+
+global func GetPhysicalFactorStack(object target)
+{
+ target || FatalError("GetPhysicalFactorStack: no target object given");
+ return GetEffect("PhysicalFactorStack", target) || AddEffect("PhysicalFactorStack", target, 200);
+}
+
+global func AddPhysicalFactor(string physical, int factor, int precision, object target)
+{
+ target = target || this || FatalError("AddPhysicalFactor: no target object given");
+ precision = precision || 100;
+
+ return EffectCall(target, GetPhysicalFactorStack(target), "AddFactor", physical, factor, precision);
+}
+
+global func RemovePhysicalFactor(int id, object target)
+{
+ target = target || this || FatalError("AddPhysicalFactor: no target object given");
+
+ return EffectCall(target, GetPhysicalFactorStack(target), "RemoveFactor", id);
+}
+
+global func FxPhysicalFactorStackStart(object target, int effectNumber, int temp)
+{
+ if(!temp)
+ {
+ EffectVar(1, target, effectNumber) = [];
+ }
+}
+
+global func FxPhysicalFactorStackAddFactor(object target, int effectNumber, string physical, int factor, int precision)
+{
+ ArrayAppend(EffectVar(1, target, effectNumber), [++EffectVar(0, target, effectNumber), physical, factor, precision]);
+ EffectCall(target, effectNumber, "ApplyPhysical", physical);
+ return EffectVar(0, target, effectNumber);
+}
+
+global func FxPhysicalFactorStackRemoveFactor(object target, int effectNumber, int id)
+{
+ for(var i = 0; i < GetLength(EffectVar(1, target, effectNumber)); ++i)
+ {
+ if(EffectVar(1, target, effectNumber)[i][0] == id)
+ {
+ var physical = EffectVar(1, target, effectNumber)[i][1];
+ ArrayErase(EffectVar(1, target, effectNumber), i);
+ EffectCall(target, effectNumber, "ApplyPhysical", physical);
+ return true;
+ }
+ }
+ return 0;
+}
+
+global func FxPhysicalFactorStackApplyPhysical(object target, int effectNumber, string physical)
+{
+ var phys = GetPhysical(physical, PHYS_Temporary, target);
+ if(phys)
+ {
+ ResetPhysical(target, physical);
+ }
+ phys = GetPhysical(physical, PHYS_Current, target);
+ for(var factor in EffectVar(1, target, effectNumber))
+ {
+ if(factor[1] == physical)
+ {
+ phys *= factor[2];
+ phys /= factor[3];
+ }
+ }
+ SetPhysical(physical, phys, PHYS_StackTemporary, target);
+ return phys;
+}
diff --git a/DTPlayers.c b/DTPlayers.c
new file mode 100644
index 0000000..ab75aef
--- /dev/null
+++ b/DTPlayers.c
@@ -0,0 +1,82 @@
+/*-- Helper functions for iterating through all players and teams --*/
+#strict 2
+
+static const Player_Number = 0;
+static const Player_ID = 0 + 1;
+static const Player_Name = 0 + 1 + 1;
+static const Player_TaggedName = 0 + 1 + 1 + 1;
+static const Player_Color = 0 + 1 + 1 + 1 + 1;
+static const Player_Team = 0 + 1 + 1 + 1 + 1 + 1;
+static const Player_Type = 0 + 1 + 1 + 1 + 1 + 1 + 1;
+
+global func GetPlayers(int team, bool exclude)
+{
+ var ret = [];
+ for(var plr, plrTeam, i = 0; (i < GetPlayerCount()) && ((plrTeam = GetPlayerTeam(plr = GetPlayerByIndex(i))) || true); ++i)
+ {
+ if(!team || (team == plrTeam) != exclude)
+ {
+ var plrInfo = [];
+ plrInfo[Player_Number] = plr;
+ plrInfo[Player_ID] = GetPlayerID(plr);
+ plrInfo[Player_Name] = GetPlayerName(plr);
+ plrInfo[Player_TaggedName] = GetTaggedPlayerName(plr);
+ plrInfo[Player_Color] = GetPlrColorDw(plr);
+ plrInfo[Player_Team] = GetPlayerTeam(plr);
+ plrInfo[Player_Type] = GetPlayerType(plr);
+ ret[GetLength(ret)] = plrInfo;
+ }
+ }
+
+ return ret;
+}
+
+global func GetPlayerNumbers(int team, bool exclude)
+{
+ var ret = [];
+ for(var plr, i = 0; (i < GetPlayerCount()) && ((plr = GetPlayerByIndex(i)) || true); ++i)
+ {
+ if(!team || (team == GetPlayerTeam(plr)) != exclude)
+ {
+ ret[GetLength(ret)] = plr;
+ }
+ }
+ return ret;
+}
+
+global func GetTaggedTeamName(int team)
+{
+ return Format("<c %x>%s</c>", GetTeamColor(team), GetTeamName(team));
+}
+
+static const Team_ID = 0;
+static const Team_Name = 0 + 1;
+static const Team_TaggedName = 0 + 1 + 1;
+static const Team_Color = 0 + 1 + 1 + 1;
+static const Team_Players = 0 + 1 + 1 + 1 + 1;
+
+global func GetTeams()
+{
+ var ret = [];
+ for(var team, i = 0; (i < GetTeamCount()) && ((team = GetTeamByIndex(i)) || true); ++i)
+ {
+ var teamInfo = [];
+ teamInfo[Team_ID] = team;
+ teamInfo[Team_Name] = GetTeamName(team);
+ teamInfo[Team_TaggedName] = GetTaggedTeamName();
+ teamInfo[Team_Color] = GetTeamColor(team);
+ teamInfo[Team_Players] = GetPlayers(team);
+ ret[GetLength(ret)] = teamInfo;
+ }
+ return ret;
+}
+
+global func GetTeamIDs()
+{
+ var ret = [];
+ for(var team, i = 0; (i < GetTeamCount()) && ((team = GetTeamByIndex(i)) || true); ++i)
+ {
+ ret[GetLength(ret)] = team;;
+ }
+ return ret;
+}
diff --git a/DTQuickSort.c b/DTQuickSort.c
new file mode 100644
index 0000000..7434263
--- /dev/null
+++ b/DTQuickSort.c
@@ -0,0 +1,66 @@
+#strict 2
+
+global func SortNumeric(int a, int b)
+{
+ if(a < b)
+ {
+ return -1;
+ }
+ else if(a > b)
+ {
+ return 1;
+ }
+}
+
+global func SortNumericInverse(int a, int b)
+{
+ return -SortNumeric(a, b);
+}
+
+global func QSort(array& data, callback) // WARNING: Can cause stack-overflow with too much data
+{
+ if(callback == true)
+ {
+ callback = GlobalCallback("SortNumericInverse");
+ }
+ QSort_Part(data, callback || GlobalCallback("SortNumeric"), 0, GetLength(data) - 1);
+ return data;
+}
+
+global func QSort_Part(array& data, callback, int left, int right)
+{
+ if(left < right)
+ {
+ // Partitioning
+ var pPos = RandomX(left, right);
+ var p = data[pPos];
+ var tmp;
+
+ tmp = data[pPos];
+ data[pPos] = data[right];
+ data[right] = tmp;
+ pPos = right;
+
+ var i = left - 1;
+ for(var j = left; j <= right; ++j)
+ {
+ if(Call(callback, data[j], p) < 0)
+ {
+ ++i;
+ tmp = data[i];
+ data[i] = data[j];
+ data[j] = tmp;
+ }
+ }
+
+ ++i;
+
+ tmp = data[i];
+ data[i] = data[pPos];
+ data[pPos] = tmp;
+ // -----
+
+ QSort_Part(data, callback, left, i - 1);
+ QSort_Part(data, callback, i + 1, right);
+ }
+}
diff --git a/DTRemoveNotification.c b/DTRemoveNotification.c
new file mode 100644
index 0000000..eb4a47c
--- /dev/null
+++ b/DTRemoveNotification.c
@@ -0,0 +1,43 @@
+#strict 2
+
+global func AddRemoveNotification(callback, object target, object callbackTarget, int prio)
+{
+ if(!target || !callback)
+ {
+ return;
+ }
+ callbackTarget = callbackTarget || this;
+ return AddEffect("IntRemoveNotifier", target, prio || 1, 0, callbackTarget, 0, callback);
+}
+
+global func RemoveRemoveNotification(int id, object target)
+{
+ if(!target)
+ {
+ return;
+ }
+ return RemoveEffect(0, target, id);
+}
+
+global func FxIntRemoveNotifierStart(object target, int effectNumber, int temp, callback)
+{
+ if(!temp)
+ {
+ EffectVar(0, target, effectNumber) = callback;
+ }
+}
+
+global func FxIntRemoveNotifierStop(object target, int effectNumber, int reason, bool temp)
+{
+ if(!temp)
+ {
+ if(reason == FX_Call_RemoveClear)
+ {
+ Call(EffectVar(0, target, effectNumber), target);
+ }
+ else if(reason == FX_Call_RemoveDeath)
+ {
+ return FX_Stop_Deny;
+ }
+ }
+}
diff --git a/DTScenUtils.c b/DTScenUtils.c
new file mode 100644
index 0000000..be55cfe
--- /dev/null
+++ b/DTScenUtils.c
@@ -0,0 +1,91 @@
+#strict 2
+
+static const ObjStr_Skip = -1;
+static const ObjStr_Defaults = 0;
+static const ObjStr_NoDefault = 1;
+static const ObjStr_NoLocals = 2;
+static const ObjStr_NoContents = 4;
+static const ObjStr_ModeParts = 1; // NoDefault
+
+global func LogObjects(array criteria, bool inline, bool& first)
+{
+ for(var obj in FindObjects(criteria))
+ {
+ var str = GetObjectString(obj);
+ if(str) Log((inline && ((first && " %s") || ", %s")) || " %s;", str);
+ first = false;
+ }
+}
+
+global func GetObjects(bool inline)
+{
+ var first = true, exp = Find_And(Find_Not(Find_Func("IsHUD")), Find_Not(Find_Func("IsLight")), Find_NoContainer());
+ if(inline) Log("[");
+ LogObjects(Find_And(Find_Category(C4D_Rule | C4D_Goal | C4D_Environment), exp), inline, first); // goals, rules and environment first
+ LogObjects(Find_And(Find_Not(Find_Category(C4D_Rule | C4D_Goal | C4D_Environment)), exp), inline, first); // and the rest
+ if(inline) Log("]");
+}
+
+global func GetObjectString(obj)
+{
+ var extra, extray, custom, customStr = "";
+ extra = "";
+ extray = 0;
+ custom = obj->~CustomObjectString(customStr);
+ if(custom == ObjStr_Skip)
+ {
+ return;
+ }
+ else if((custom & ObjStr_ModeParts) != ObjStr_NoDefault)
+ {
+ if(obj->GetR() != 0) extra = Format("%s->SetRX(%d)", extra, obj->GetR());
+ if(obj->GetCon() != 100)
+ {
+ extra = Format("%s->SetConX(%d)", extra, obj->GetCon());
+ extray = -GetDefCoreVal("Offset", "DefCore", GetID(obj), 1) * (100-obj->GetCon()) / 100;
+ }
+ if(obj->GetDir())
+ {
+ extra = Format("%s->SetDirX(%d)", extra, obj->GetDir());
+ }
+ if(!(custom & ObjStr_NoLocals)) for(var iFunc = 0; iFunc < 2; ++iFunc)
+ {
+ for(var i = 0; i < GetLocalCount(obj, iFunc); ++i)
+ {
+ var key = GetLocalByIndex(i, obj, iFunc);
+ var val;
+ if(GetType(key) == C4V_Int || GetType(key) == C4V_Any) val = Local(key, obj);
+ else if(GetType(key) == C4V_String) val = LocalN(key, obj);
+ if(val == 0) continue;
+ extra = Format("%s->SetLocalX(%s, %s)", extra, Serialize(key), Serialize(val));
+ }
+ }
+ if(!(custom & ObjStr_NoContents)) for(var content in FindObjects(Find_Container(obj)))
+ {
+ extra = Format("%s->EnterX(%s)", extra, GetObjectString(content));
+ }
+ }
+ if(GetCategory(obj) & C4D_StaticBack)
+ {
+ extray -= GetDefOffset(GetID(obj), 1) * 2 + GetDefHeight(GetID(obj));
+ }
+ return Format("CreateObjectX(%i, %d, %d, %d)%s%s",
+ GetID(obj),
+ GetX(obj),
+ GetY(obj)/* - GetDefCoreVal("Offset", "DefCore", GetID(obj), 1) - extray*/,
+ GetOwner(obj),
+ extra, customStr);
+}
+
+global func CreateObjectX(id id, int x, int y, int owner) { return CreateObject(id, x, y, owner)->SetPositionX(x, y); }
+global func SetPositionX(int x, int y) { this->SetPosition(x, y); return this; }
+global func SetDirX(int dir) { this->SetDir(dir); return this; }
+global func SetRX(int r) { this->SetR(r); return this; }
+global func SetConX(int con) { this->SetCon(con); return this; }
+global func EnterX(object obj) { Enter(this, obj); return this; }
+global func SetLocalX(key, val)
+{
+ if(GetType(key) == C4V_Int || GetType(key) == C4V_Any) Local(key, this) = val;
+ else LocalN(key) = val;
+ return this;
+}
diff --git a/DTScopedVars.c b/DTScopedVars.c
new file mode 100644
index 0000000..f64ccdc
--- /dev/null
+++ b/DTScopedVars.c
@@ -0,0 +1,87 @@
+#strict 2
+
+// TODO: ScopedVars with temporary target (like a reference to a function local var)
+
+static const ScopedVar_Global = 1;
+static const ScopedVar_Local = 2;
+static const ScopedVar_ArrayIndex = 3;
+
+global func GlobalVar(name) { return [ScopedVar_Global, name]; }
+global func LocalVar(name, object target) { return [ScopedVar_Local, name, target || this]; }
+global func ArrayVar(index, array scopedVar) { return [ScopedVar_ArrayIndex, scopedVar, index]; }
+
+global func &ScopedVar(array variable)
+{
+ if(!variable)
+ {
+ return 0;
+ }
+ if(variable[0] == ScopedVar_Global)
+ {
+ if(GetType(variable[1]) == C4V_String)
+ {
+ return GlobalN(variable[1]);
+ }
+ else
+ {
+ return Global(variable[1]);
+ }
+ }
+ else if(variable[0] == ScopedVar_Local)
+ {
+ if(GetType(variable[1]) == C4V_String)
+ {
+ return LocalN(variable[1], variable[2]);
+ }
+ else
+ {
+ return Local(variable[1], variable[2]);
+ }
+ }
+ else if(variable[0] == ScopedVar_ArrayIndex)
+ {
+ var index = variable[2];
+ if(GetType(index) != C4V_Int && GetType(index) != C4V_Any)
+ {
+ index = ScopedVar(index);
+ }
+ return ScopedVar(variable[1])[index];
+ }
+ else
+ {
+ return ScopedVar(CustomScopedVar(variable));
+ }
+}
+
+global func &CustomScopedVar() { return _inherited(...); } // this allows "overloading" this function even if the "overloading" function is loaded before
+
+global func CheckScopedVar(array variable)
+{
+ if(!variable)
+ {
+ return false;
+ }
+
+ if(GetType(variable[0]) == C4V_Int)
+ {
+ if(variable[0] == ScopedVar_Global || variable[0] == ScopedVar_Local)
+ {
+ if(!variable[1] || GetType(variable[1]) == C4V_Int || (GetType(variable[1]) == C4V_String && variable[1] != ""))
+ {
+ return true;
+ }
+ }
+
+ if(variable[0] == ScopedVar_ArrayIndex)
+ {
+ if(CheckScopedVar(variable[1]) && !variable[2] || (GetType(variable[2]) == C4V_Int && variable[2] >= 0))
+ {
+ return true;
+ }
+ }
+ }
+
+ return CheckCustomScopedVar(variable);
+}
+
+global func CheckCustomScopedVar() { return _inherited(...); }
diff --git a/DTTemplateFunctions.c b/DTTemplateFunctions.c
new file mode 100644
index 0000000..860c9e1
--- /dev/null
+++ b/DTTemplateFunctions.c
@@ -0,0 +1,307 @@
+/*-- Template Functions --*/
+
+#strict 2
+
+static v;
+static TMPL_Funcs;
+static const TMPL_FuncArguments = 0;
+static const TMPL_FuncContents = 1;
+static const TMPL_FuncName = 2;
+static const TMPL_FuncCommands = 3;
+
+global func InitializeTemplateFunctions()
+{
+ if(GetType(v) != C4V_Array) v = [];
+ if(GetType(TMPL_Funcs) != C4V_Array) TMPL_Funcs = [];
+}
+
+global func DefineFunction(string name, arguments, commands)
+{
+ InitializeTemplateFunctions();
+
+ var anonymousFunction = Function(arguments, commands);
+
+ var function = [anonymousFunction[0], anonymousFunction[1], name, commands];
+
+ SetFunction(name, function);
+}
+
+global func SetFunction(string name, array function)
+{
+ var index = FindTemplateFunction(name);
+ if(index != -1)
+ {
+ TMPL_Funcs[index] = function;
+ }
+ else
+ {
+ TMPL_Funcs[GetLength(TMPL_Funcs)] = function;
+ }
+}
+
+global func RemoveFunction(string name)
+{
+ var index = FindTemplateFunction(name);
+ if(index != -1)
+ {
+ var i;
+ for(i = index; i < GetLength(TMPL_Funcs) - 1; ++i)
+ {
+ TMPL_Funcs[i] = TMPL_Funcs[i + 1];
+ }
+ TMPL_Funcs[GetLength(TMPL_Funcs) - 1] = 0;
+ }
+}
+
+global func Function(arguments, commands)
+{
+ if(!commands)
+ {
+ commands = arguments;
+ arguments = [];
+ }
+
+ if(GetType(arguments) == C4V_String) arguments = [arguments];
+ else if(arguments == 0) arguments = [];
+
+ if(GetType(commands) == C4V_String) commands = [commands];
+
+ var funcContents = [];
+ for(var command in commands)
+ {
+ var isArg = false;
+ var argument = "";
+ var funcContent = [];
+ var contentPart = "";
+ var escapeCount = 0;
+ for(var i = 0; i < GetLength(command); ++i)
+ {
+ var c = GetChar(command, i);
+ if(isArg)
+ {
+ if(c == 37) // %
+ {
+ if(argument == "")
+ {
+ contentPart = Format("%s%%", contentPart);
+ }
+ else
+ {
+ var index = GetIndexOf2(argument, arguments);
+ if(index == -1)
+ {
+ FatalError(Format("Function: Unknown argument-name %s", argument));
+ return;
+ }
+ else
+ {
+ funcContent[GetLength(funcContent)] = [index, escapeCount];
+ argument = "";
+ escapeCount = 0;
+ }
+ }
+ isArg = false;
+ }
+ else if(argument == "" && c == 47) // /
+ {
+ ++escapeCount;
+ }
+ else
+ {
+ if(contentPart != "")
+ {
+ funcContent[GetLength(funcContent)] = contentPart;
+ contentPart = "";
+ }
+
+ argument = Format("%s%c", argument, c);
+ }
+ }
+ else
+ {
+ if(c == 37) // %
+ {
+ isArg = true;
+ }
+ else if(c == 39) // '
+ {
+ contentPart = Format("%s\"", contentPart);
+ }
+ else
+ {
+ contentPart = Format("%s%c", contentPart, c);
+ }
+ }
+ }
+ if(isArg) FatalError(Format("Function: argument-reference \"%s\" not closed.", argument));
+ else if(contentPart) funcContent[GetLength(funcContent)] = contentPart;
+ funcContents[GetLength(funcContents)] = funcContent;
+ }
+
+ return [arguments, funcContents];
+}
+
+global func FindTemplateFunction(string name)
+{
+ var i = 0;
+ for(var function in TMPL_Funcs)
+ {
+ if(GetType(function) == C4V_Array && function[TMPL_FuncName] == name) return i;
+ ++i;
+ }
+ return -1;
+}
+
+global func F(string name, array args)
+{
+ return f(Func(name), args);
+}
+
+global func f(array function, array args)
+{
+ var ret;
+ for(var content in function[TMPL_FuncContents])
+ {
+ var command = "";
+ for(var contentPart in content)
+ {
+ if(GetType(contentPart) == C4V_Array)
+ {
+ var part = StringReplace("'", Serialize(args[contentPart[0]]), "\\\"");
+ for(var i = 0; i < contentPart[1]; ++i)
+ {
+ part = EscapeString(part);
+ }
+ command = Format("%s%s", command, part);
+ }
+ else command = Format("%s%s", command, contentPart);
+ }
+ ret = eval(command);
+ }
+ return ret;
+}
+
+global func DebugF(string function, array args)
+{
+ return DebugFunc(Func(function), args);
+}
+
+global func DebugFunc(array function, array args)
+{
+ var ret;
+ for(var content in function[TMPL_FuncContents])
+ {
+ var command = "";
+ for(var contentPart in content)
+ {
+ if(GetType(contentPart) == C4V_Array)
+ {
+ var part = StringReplace("'", Serialize(args[contentPart[0]]), "\\\"");
+ for(var i = 0; i < contentPart[1]; ++i)
+ {
+ part = EscapeString(part);
+ }
+ command = Format("%s%s", command, part);
+ }
+ else command = Format("%s%s", command, contentPart);
+ }
+ Log(command);
+ }
+ return ret;
+}
+
+global func Func(string name)
+{
+ var index = FindTemplateFunction(name);
+ if(index == -1)
+ {
+ FatalError(Format("Func: Unknown function %s.", name));
+ return;
+ }
+ return TMPL_Funcs[index];
+}
+
+global func SaveFunction(int plr, string name, array function)
+{
+ return SetPlrExtraDataArray(plr, Format("TemplateFunction_%s", name), function);
+}
+
+global func LoadFunction(int plr, string name, string loadAs)
+{
+ var function = GetPlrExtraDataArray(plr, Format("TemplateFunction_%s", name));
+ if(function == 0)
+ {
+ FatalError(Format("LoadFunction: Unknown function: \"%s\"", name));
+ return;
+ }
+ if(loadAs != 0)
+ {
+ SetFunction(loadAs, function);
+ }
+ return function;
+}
+
+global func DeleteFunction(int plr, string name)
+{
+ return SetPlrExtraDataString(plr, Format("TemplateFunction_%s", name), "");
+}
+
+global func CallCustom(callback, args)
+{
+ if(GetType(callback) == C4V_Array && GetLength(callback) >= 2 && GetType(callback[0]) == C4V_Array && GetType(callback[1]) == C4V_Array)
+ {
+ return f(callback, args);
+ }
+ else
+ {
+ return _inherited(callback, args, ...);
+ }
+}
+
+global func CheckCustomCallback(callback)
+{
+ if(GetType(callback) == C4V_Array && (GetLength(callback) == 2 || (GetLength(callback) == 4 && GetType(callback[2]) == C4V_String && GetType(callback[3]) == C4V_Array)) && GetType(callback[0]) == C4V_Array && GetType(callback[1]) == C4V_Array)
+ {
+ var stringOnlyChecks = [callback[0]];
+
+ if(GetLength(callback) == 4)
+ {
+ stringOnlyChecks[1] = callback[3];
+ }
+
+ for(var check in stringOnlyChecks)
+ {
+ for(var val in check)
+ {
+ if(GetType(val) != C4V_String)
+ {
+ return _inherited(callback, ...);
+ }
+ }
+ }
+
+ for(var contentParts in callback[1])
+ {
+ if(GetType(contentParts) != C4V_Array)
+ {
+ return _inherited(callback, ...);
+ }
+
+ for(var content in contentParts)
+ {
+ if(GetType(content) == C4V_String)
+ {
+ continue;
+ }
+
+ if(GetType(content) != C4V_Array || GetLength(content) != 2 || (content[0] && GetType(content[0]) == C4V_Int && content[0] < 0) || (content[1] && GetType(content[1]) == C4V_Int && content[1] < 0))
+ {
+ return _inherited(callback, ...);
+ }
+ }
+ }
+
+ return true;
+ }
+ return _inherited(callback, ...);
+}
diff --git a/DTTimer.c b/DTTimer.c
new file mode 100644
index 0000000..ecc5885
--- /dev/null
+++ b/DTTimer.c
@@ -0,0 +1,26 @@
+#strict 2
+
+global func AddTimer(callback, int timer, object target, int prio)
+{
+ target = target || this;
+ return AddEffect("IntTimer", target, prio || 1, timer, target, 0, callback);
+}
+
+global func RemoveTimer(int id, object target)
+{
+ target = target || this;
+ return RemoveEffect(0, target, id);
+}
+
+global func FxIntTimerStart(object target, int effectNumber, int temp, callback)
+{
+ if(!temp)
+ {
+ EffectVar(0, target, effectNumber) = callback;
+ }
+}
+
+global func FxIntTimerTimer(object target, int effectNumber, int effectTime)
+{
+ return Call(EffectVar(0, target, effectNumber));
+}
diff --git a/DTTransform.c b/DTTransform.c
new file mode 100644
index 0000000..07206c2
--- /dev/null
+++ b/DTTransform.c
@@ -0,0 +1,240 @@
+#strict 2
+
+global func ResetTransform(overlays)
+{
+ for(var overlay in TransformOverlays(overlays))
+ {
+ if(!overlay) SetObjDrawTransform(0, 0, 0, 0, 0, 0, this, overlay);
+ else SetObjDrawTransform(1000, 0, 0, 0, 1000, 0, this, overlay);
+ }
+}
+
+global func SetTransform(array transforms, overlays)
+{
+ for(var overlay in TransformOverlays(overlays))
+ {
+ ResetTransform(overlay);
+ AddTransform(transforms, overlay);
+ }
+}
+
+global func AddTransform(array transforms, overlays)
+{
+ for(var overlay in TransformOverlays(overlays))
+ {
+ for(var transform in transforms)
+ {
+ SetObjDrawTransform2(transform[0][0], transform[0][1], transform[0][2], transform[1][0], transform[1][1], transform[1][2], transform[2][0], transform[2][1], transform[2][2], overlay);
+ }
+ }
+}
+
+global func TransformOverlays(overlays)
+{
+ if(!overlays || GetType(overlays) == C4V_Int)
+ {
+ if(overlays >= 0) return [overlays];
+ else
+ {
+ var ret = [];
+ overlays = Abs(overlays);
+ for(var i = 0; i <= overlays; ++i)
+ {
+ ret[GetLength(ret)] = i;
+ }
+ return ret;
+ }
+ }
+ else if(GetType(overlays) == C4V_Array)
+ {
+ var ret = [], last;
+ last = overlays[0];
+ var i = 1;
+ if(last < 0)
+ {
+ i = 0;
+ last = 0;
+ }
+ else
+ {
+ ret[0] = last;
+ }
+ for(; i < GetLength(overlays); ++i)
+ {
+ var overlay = overlays[i];
+ if(overlay < 0)
+ {
+ overlay = Abs(overlay);
+ for(var j = last + 1; j <= overlay; ++j)
+ {
+ ret[GetLength(ret)] = j;
+ }
+ }
+ else
+ {
+ ret[GetLength(ret)] = overlay;
+ }
+ last = overlay;
+ }
+
+ return ret;
+ }
+ else
+ {
+ FatalError("TransformOverlays: overlays must be a single integer or an array.");
+ }
+}
+
+global func Rotate() { return RotateZ(...); }
+
+global func RotateX(int phi, int prec)
+{
+ prec = prec || 1;
+
+ var cos = Cos(phi, 1000, prec);
+ var sin = Sin(phi, 1000, prec);
+
+ return
+ [
+ [1000, 0, 0],
+ [ 0, cos, -sin],
+ [ 0, sin, cos]
+ ];
+}
+
+global func RotateY(int phi, int prec)
+{
+ prec = prec || 1;
+
+ var cos = Cos(phi, 1000, prec);
+ var sin = Sin(phi, 1000, prec);
+
+ return
+ [
+ [ cos, 0, sin],
+ [ 0, 1000, 0],
+ [-sin, 0, cos]
+ ];
+}
+
+global func RotateZ(int phi, int prec)
+{
+ prec = prec || 1;
+
+ var cos = Cos(phi, 1000, prec);
+ var sin = Sin(phi, 1000, prec);
+
+ return
+ [
+ [cos, -sin, 0],
+ [sin, cos, 0],
+ [ 0, 0, 1000]
+ ];
+}
+
+global func Scale3D(int x, int y, int z)
+{
+ return
+ [
+ [x, 0, 0],
+ [0, y, 0],
+ [0, 0, z]
+ ];
+}
+
+global func Scale2D(int x, int y) { return Scale3D(x, y, 1000); }
+
+global func ScaleX(int f) { return Scale3D(f, 1000, 1000); }
+global func ScaleY(int f) { return Scale3D(1000, f, 1000); }
+global func ScaleZ(int f) { return Scale3D(1000, 1000, f); }
+
+global func Translate2D(int x, int y, int prec)
+{
+ prec = prec || 1;
+ return
+ [
+ [1000, 0, x * 1000 / prec],
+ [ 0, 1000, y * 1000 / prec],
+ [ 0, 0, 1000]
+ ];
+}
+
+global func TranslateX(int x, int prec) { return Translate2D(x, 0, prec); }
+global func TranslateY(int y, int prec) { return Translate2D(0, y, prec); }
+
+global func MirrorX()
+{
+ return
+ [
+ [-1000, 0, 0],
+ [ 0, 1000, 0],
+ [ 0, 0, 1000]
+ ];
+}
+
+global func MirrorY()
+{
+ return
+ [
+ [1000, 0, 0],
+ [ 0, -1000, 0],
+ [ 0, 0, 1000]
+ ];
+}
+
+global func MirrorZ() // useless?
+{
+ return
+ [
+ [1000, 0, 0],
+ [ 0, 1000, 0],
+ [ 0, 0, -1000]
+ ];
+}
+
+global func Skew2D(int x, int y)
+{
+ return
+ [
+ [1000, x, 0],
+ [ y, 1000, 0],
+ [ 0, 0, 1000]
+ ];
+}
+
+global func SkewX(int x) { return Skew2D(x, 0); }
+global func SkewY(int y) { return Skew2D(0, y); }
+
+global func Matrix3x3Multiply(array a, array b, int precision)
+{
+ return
+ [
+ [(a[0][2] * b[2][0] + a[0][1] * b[1][0] + a[0][0] * b[0][0]) / precision, (a[0][2] * b[2][1] + a[0][1] * b[1][1] + a[0][0] * b[0][1]) / precision, (a[0][2] * b[2][2] + a[0][1] * b[1][2] + a[0][0] * b[0][2]) / precision],
+ [(a[1][2] * b[2][0] + a[1][1] * b[1][0] + a[1][0] * b[0][0]) / precision, (a[1][2] * b[2][1] + a[1][1] * b[1][1] + a[1][0] * b[0][1]) / precision, (a[1][2] * b[2][2] + a[1][1] * b[1][2] + a[1][0] * b[0][2]) / precision],
+ [(a[2][2] * b[2][0] + a[2][1] * b[1][0] + a[2][0] * b[0][0]) / precision, (a[2][2] * b[2][1] + a[2][1] * b[1][1] + a[2][0] * b[0][1]) / precision, (a[2][2] * b[2][2] + a[2][1] * b[1][2] + a[2][0] * b[0][2]) / precision]
+ ];
+}
+
+global func PreTransform(array transforms) // funzt nit
+{
+ if(GetLength(transforms) == 0)
+ {
+ FatalError("PreTransform called without transforms.");
+ }
+ else
+ {
+ var ret = transforms[0];
+ for(var i = 1; i < GetLength(transforms); ++i)
+ {
+ ret = Matrix3x3Multiply(ret, transforms[i], 1000);
+ }
+ return ret;
+ }
+}
+
+// Riesenschädl
+// GetCursor(0)->SetTransform([TranslateY(8), RotateX(4), ScaleZ(400), TranslateY(-10)])
+
+// Ähnlich Chipakyus Demo
+// Schedule("FindObject(HUT2)->SetTransform([TranslateY(-20), RotateZ(++Global(0)), TranslateY(0), RotateX(-1), TranslateY(20)])", 1, 10000)
+// Schedule("GetCursor(0)->SetTransform([TranslateY(-8), RotateZ(++Global(0)), TranslateY(0), RotateX(-2), TranslateY(8)])", 1, 10000)
diff --git a/DTUtility.c b/DTUtility.c
new file mode 100644
index 0000000..a747d33
--- /dev/null
+++ b/DTUtility.c
@@ -0,0 +1,744 @@
+#strict 2
+
+global func Isolate(object obj) // Puts the object in its own layer to prevent interaction
+{
+ obj || obj = this;
+ return obj->SetObjectLayer(obj);
+}
+
+global func GetIndexOf2(val, array arr) // Because GetIndexOf doesn't find Format("%c", GetChar("a", 0)) in ["a"]
+{
+ var i = 0;
+ for(var value in arr)
+ {
+ if(val == value) return i;
+ ++i;
+ }
+ return -1;
+}
+
+global func Serialize(value)
+{
+ if(GetType(value) == C4V_Array)
+ {
+ var first = true;
+ var ret = "[";
+ for(var val in value)
+ {
+ if(first)
+ {
+ first = false;
+ }
+ else
+ {
+ ret = Format("%s,", ret);
+ }
+ ret = Format("%s%s", ret, Serialize(val));
+ }
+ return Format("%s]", ret);
+ }
+ else if(GetType(value) == C4V_Bool) if(value) return "true"; else return "false";
+ else if(GetType(value) == C4V_C4ID) return Format("%i", value);
+ else if(GetType(value) == C4V_C4Object) return Format("Object(%d)", ObjectNumber(value));
+ else if(GetType(value) == C4V_Int || GetType(value) == C4V_Any) return Format("%d", value);
+ else if(GetType(value) == C4V_String) return Format("\"%s\"", EscapeString(value));
+}
+
+global func EscapeString(string value)
+{
+ var ret = "";
+ for(var i = 0; i < GetLength(value); ++i)
+ {
+ var c = GetChar(value, i);
+ if(c == 34) // "
+ {
+ ret = Format("%s\\\"", ret);
+ }
+ else if(c == 92) // \
+ {
+ ret = Format("%s\\\\", ret);
+ }
+ else
+ {
+ ret = Format("%s%c", ret, c);
+ }
+}
+return ret;
+}
+
+global func CreateEnum(string prefix, array enum)
+{
+ var log = Format("%c// This enum was created with CreateEnum(%s, %s);%c", 10, Serialize(prefix), Serialize(enum), 10);
+ var switch = "";
+
+ for(var i = 0; i < GetLength(enum); ++i)
+ {
+ log = Format("%s%cstatic const %s%s = %d;", log, 10, prefix, enum[i], i);
+
+ if(switch == "")
+ {
+ switch = Format("if(switchval == %s%s)%c{%c%c}%c", prefix, enum[i], 10, 10, 10, 10);
+ }
+ else
+ {
+ switch = Format("%selse if(switchval == %s%s)%c{%c%c}%c", switch, prefix, enum[i], 10, 10, 10, 10);
+ }
+ }
+
+ Log("%s%c%c// Template if-structure - replace switchval with the value to compare.%c%c%s", log, 10, 10, 10, 10, switch);
+}
+
+global func CreateBitField(string prefix, array flags)
+{
+ if(GetLength(flags) > 32)
+ {
+ FatalError("CreateBitField: A bitfield can only have a maximum of up to 32 flags.");
+ return;
+ }
+
+ var log = Format("%c// This bitfield was created with CreateBitField(%s, %s);%c", 10, Serialize(prefix), Serialize(flags), 10);
+
+ for(var i = 0, j = 1; i < GetLength(flags); ++i)
+ {
+ log = Format("%s%cstatic const %s%s = %d;", log, 10, prefix, flags[i], j);
+ j = j << 1;
+ }
+
+ Log("%s%c%c", log, 10, 10);
+}
+
+// pos >= 0 => begin at pos | pos < 0 => begin at GetLength(string) - pos
+// count = 0 => replace all | count > 0 replace a maximum of count occurences from starting from pos
+
+global func Lowercase(int c)
+{
+ if((c >= 65 && c <= 90) || (c >= 192 && c <= 214) || (c >= 216 && c <= 221)) return c + 32;
+ else return c;
+}
+
+global func StringLowercase(string string)
+{
+ var ret = "";
+ for(var i = 0; i < GetLength(string); ++i)
+ {
+ ret = Format("%s%c", ret, Lowercase(GetChar(string, i)));
+ }
+ return ret;
+}
+
+global func MultiStringLowercase(array strings)
+{
+ var ret = [];
+ for(var string in strings)
+ {
+ ret[GetLength(ret)] = StringLowercase(string);
+ }
+ return ret;
+}
+
+global func CaseInsensitive(int c, bool caseSensitive) // TODO
+{
+ if(caseSensitive) return c;
+ else return Lowercase(c);
+}
+
+global func StringConcatenate(array strings)
+{
+ var ret = strings[0];
+
+ for(var i = 1; i < GetLength(strings); ++i)
+ {
+ ret = Format("%s%s", ret, strings[i]);
+ }
+
+ return ret;
+}
+
+global func MultiStringReplace(find, strings, replace, bool caseInsensitive, int count, int pos, int maxpos)
+{
+ var returnString = false;
+ if(GetType(strings) == C4V_String)
+ {
+ strings = [strings];
+ returnString = true;
+ }
+ else if(GetType(strings) != C4V_Array) return strings;
+ if(GetType(find) == C4V_String) find = [find];
+ else if(GetType(find) != C4V_Array) return strings;
+ if(GetType(replace) == C4V_String) replace = [find];
+ else if(GetType(replace) != C4V_Array) return strings;
+
+ var ret = [];
+
+ for(var i = 0; i < GetLength(strings); ++i)
+ {
+ ret[i] = strings[i];
+ for(var j = 0; j < GetLength(find); ++j)
+ {
+ ret[i] = StringReplace(find[j], ret[i], replace[j % GetLength(replace)], caseInsensitive, count, pos, maxpos);
+ }
+ }
+
+ if(returnString) return ret[0];
+ else return ret;
+}
+
+global func StringReplace(string find, string string, string replace, bool caseInsensitive, int count, int pos, int maxpos)
+{
+ if(GetLength(find) == 0 || GetLength(string) == 0) return string;
+ var ret = "";
+ if(pos < 0) pos = GetLength() - pos;
+ if(maxpos < 0) maxpos = GetLength(string) - pos;
+ if(maxpos == 0) maxpos = GetLength(string);
+ if(pos < 0 || pos > GetLength(string) || maxpos < 0 || maxpos > GetLength(string) || count < 0) return string;
+ if(count == 0) count = -1;
+
+ for(var i = 0; i < pos; ++i)
+ {
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+
+ var match = 0;
+
+ var i;
+
+ for(i = pos; i + GetLength(find) - match - 1 < maxpos && count != 0; ++i)
+ {
+ var c = GetChar(string, i);
+ if(CaseInsensitive(c, !caseInsensitive) == CaseInsensitive(GetChar(find, match), !caseInsensitive))
+ {
+ if(++match == GetLength(find))
+ {
+ ret = StringConcatenate([ret, replace]);
+ match = 0;
+ --count;
+ }
+ }
+ else
+ {
+ if(match != 0)
+ {
+ for(var j = i - match; j < i; ++j)
+ {
+ ret = Format("%s%c", ret, GetChar(string, j));
+ }
+ }
+ match = 0;
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+ }
+
+ for(i -= match; i < GetLength(string); ++i)
+ {
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+
+ return ret;
+}
+
+global func StringReplaceMulti(string find, string string, array replace)
+{
+ var ret = [];
+ for(var rep in replace)
+ {
+ ret[GetLength(ret)] = StringReplace(find, string, rep);
+ }
+ return ret;
+}
+
+global func MultiStringReplaceMulti(string string, array findReplace)
+{
+ var ret = [];
+
+ for(var j = 0; j < GetLength(findReplace[0][1]); ++j)
+ {
+ var part = string;
+ for(var i = 0; i < GetLength(findReplace); ++i)
+ {
+ part = StringReplace(findReplace[i][0], part, findReplace[i][1][j]);
+ }
+ ret[j] = part;
+ }
+
+ return ret;
+}
+
+global func GetMapValue(key, array map, default)
+{
+ for(var val in map)
+ {
+ if(val[0] == key) return val;
+ }
+ return default;
+}
+
+global func GetIDByName(string name, int category) // WARNING: desyncs between clients with different languages
+{
+ var i, id;
+ while(id = GetDefinition(i++, category))
+ {
+ if(GetName(0, id) == name) return id;
+ }
+ return 0;
+}
+
+global func GetIDsByName(string name, int category) // WARNING: desyncs between clients with different languages
+{
+ var i, id, ret = [];
+ while(id = GetDefinition(i++, category))
+ {
+ if(GetName(0, id) == name)
+ {
+ ret[GetLength(ret)] = id;
+ }
+ }
+ return ret;
+}
+
+global func ArrayConcatenate(array arrays)
+{
+ var ret = [];
+ for(var arr in arrays)
+ {
+ for(var val in arr)
+ {
+ ret[GetLength(ret)] = val;
+ }
+ }
+ return ret;
+}
+
+global func SetPlrExtraDataString(int plr, string name, string data)
+{
+ var part = 0;
+ var i;
+ for(i = 0; i < GetLength(data); ++i)
+ {
+ var c = GetChar(data, i);
+
+ if((i % 4) == 0 && i != 0)
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i/4 - 1), part);
+ part = 0;
+ }
+
+ part |= (c << ((i % 4) * 8));
+ }
+
+ if(part != 0)
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i/4), part);
+ }
+
+ i = i/4 + 1;
+ while(GetPlrExtraData(plr, Format("%s%d", name, i)))
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i++), 0);
+ }
+}
+
+global func GetPlrExtraDataString(int plr, string name)
+{
+ var ret = "";
+ var part = true;
+ for(var i = 0; part != 0; ++i)
+ {
+ part = CastInt(GetPlrExtraData(plr, Format("%s%d", name, i)));
+ for(var j = 0; j < 4; ++j)
+ {
+ var c = (part >> (j * 8)) & 0xFF;
+ if(c == 0)
+ {
+ part = 0;
+ break;
+ }
+ else
+ {
+ ret = Format("%s%c", ret, c);
+ }
+ }
+ }
+ return ret;
+}
+
+global func SetPlrExtraDataArray(int plr, string name, array arr)
+{
+ return SetPlrExtraDataString(plr, name, Serialize(arr));
+}
+
+global func GetPlrExtraDataArray(int plr, string name)
+{
+ var function = GetPlrExtraDataString(plr, name);
+ if(function != "")
+ {
+ return eval(function);
+ }
+}
+
+global func EnableScripting()
+{
+ AddMsgBoardCmd("s", "ScriptingFunc(\"%s\", %player%)", C4MSGCMDR_Escaped);
+}
+
+global func ScriptingFunc(string script, int player)
+{
+ Log("-> %s", script);
+ Log("= %v (by %s)", eval(script), GetTaggedPlayerName(player));
+}
+
+global func ParseInt(string int, bool hex)
+{
+ if(GetLength(int) == 0)
+ {
+ return "";
+ }
+ var ret = 0, neg = false;
+ for(var i = 0; i < GetLength(int); ++i)
+ {
+ var c = GetChar(int, i);
+ if(i == 0)
+ {
+ if(c == 45) // "-"
+ {
+ neg = true;
+ continue;
+ }
+ else if(c == 43) // "+"
+ {
+ continue;
+ }
+ }
+ c -= 48; // "0" == 48
+ if(c < 0 || ((!hex && c > 9) || (hex && c > 54))) // "f" - "0" == 54
+ {
+ return "";
+ }
+ if(hex)
+ {
+ ret = ret << 4;
+ }
+ else
+ {
+ ret *= 10;
+ }
+ if(c > 9)
+ {
+ c -= 39;
+ }
+ ret += c;
+ }
+ return ret * (1 - (2 * neg));
+}
+
+global func ParseFloat(string float, int precision) // precision = number of digits after decimal point and multiplier of 10^precision
+{
+ precision = precision || 3;
+ if(GetLength(float) == 0)
+ {
+ return "";
+ }
+ var ret = 0, neg = false, decimalPoint = -1;
+ for(var i = 0; i < GetLength(float); ++i)
+ {
+ if(decimalPoint != -1 && decimalPoint + precision < i)
+ {
+ // out of precision
+ break;
+ }
+
+ var c = GetChar(float, i);
+ if(i == 0)
+ {
+ if(c == 45) // "-"
+ {
+ neg = true;
+ continue;
+ }
+ else if(c == 43) // "+"
+ {
+ continue;
+ }
+ }
+ c -= 48; // "0" == 48
+ if(c < 0 || c > 9)
+ {
+ if((c == -2 || c == -4) && decimalPoint == -1) // "." || ","
+ {
+ decimalPoint = i;
+ continue;
+ }
+ else
+ {
+ return "";
+ }
+ }
+ ret *= 10;
+ ret += c;
+ }
+ if(decimalPoint == -1)
+ {
+ decimalPoint = i - 1;
+ }
+
+ for(; i <= decimalPoint + precision; ++i)
+ {
+ ret *= 10;
+ }
+
+ return ret * (1 - (2 * neg));
+}
+
+global func CreateFilledArray()
+{
+ var ret = [];
+ for(var i = 9; i >= 0; --i)
+ {
+ if(Par(i))
+ {
+ ret[i] = Par(i);
+ }
+ }
+ return ret;
+}
+
+global func Find_Procedure(string proc)
+{
+ return [C4FO_Func, "Find_ProcedureCheck", proc];
+}
+
+global func Find_ProcedureCheck(string proc)
+{
+ return proc == GetProcedure();
+}
+
+global func GetSolidMask(object obj)
+{
+ obj = obj || this;
+ return
+ [
+ GetObjectVal("SolidMask", 0, obj, 0), GetObjectVal("SolidMask", 0, obj, 1),
+ GetObjectVal("SolidMask", 0, obj, 2), GetObjectVal("SolidMask", 0, obj, 3),
+ GetObjectVal("SolidMask", 0, obj, 4), GetObjectVal("SolidMask", 0, obj, 5)
+ ];
+}
+
+// getValue:
+// 0 - offset index (the preferred way is to always get the offset index first and to pass the result to following calls for performance reasons)
+// 1 - overlay ID
+// 2 - overlay mode
+// 3 - graphics definition ID
+// 4 - graphics name
+// 5 - action name
+// 6 - blit mode
+// 7 - index of Transformation-matrix
+// 8 - enumerated overlay object (i.e. object number of the overlay object); WARNING: this is only valid and true directly after saving or loading a savegame
+
+global func GetOverlayValueByIndex(int index, int startIndex, int getValue, object obj) // startIndex can be given as a valid index (retrieved with this function beforehand) and the asked index is used as offset (better performance for iterating over all indices)
+{
+ if(getValue < 0 || getValue > 8)
+ {
+ // invalid getValues could cause infinite loops
+ return -2;
+ }
+ obj = obj || this;
+ var found = false;
+ if(index == 0)
+ {
+ if(getValue == 0)
+ {
+ if(GetObjectVal("GfxOverlay", 0, obj, startIndex) == 0)
+ {
+ // there is no such overlay
+ return -1;
+ }
+ else
+ {
+ return startIndex;
+ }
+ }
+ else if(getValue == 1)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, startIndex);
+ }
+ found = true;
+ }
+ for(var i = startIndex + 1, foundIndex = 0; ;) // skip overlay ID
+ {
+ var val = GetObjectVal("GfxOverlay", 0, obj, i);
+ if(val == 0)
+ {
+ // there is no overlay left
+ return -1;
+ }
+ if(val == GFXOV_MODE_Object)
+ {
+ // this is the overlay mode GFXOV_MODE_Object. Graphics-Definition and -Name are left out
+ if(found && (getValue == 3 || getValue == 4))
+ {
+ return 0;
+ }
+ else if(found && getValue == 2)
+ {
+ return GFXOV_MODE_Object;
+ }
+ }
+ else if(GetType(val) == C4V_C4ID)
+ {
+ if(found)
+ {
+ if(getValue == 3)
+ {
+ return val;
+ }
+ else if(getValue == 4)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 1);
+ }
+ }
+ i += 2; // skip Graphics-Definition and -Name, so i points to OverlayMode here too
+ if(found && getValue == 2)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i);
+ }
+ }
+ else
+ {
+ //something went wrong already
+ return -3;
+ }
+
+ if(found)
+ {
+ if(getValue == 5)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 1);
+ }
+ else if(getValue == 6)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 2);
+ }
+ else if(getValue == 7)
+ {
+ return i + 4;
+ }
+ }
+
+ i += 10; // this is either the last row of the Transformation matrix or the Color-Modulation
+ val = GetObjectVal("GfxOverlay", 0, obj, i);
+
+ if(GetType(val) == C4V_String)
+ {
+ // part of the matrix, skip it
+ i += 3;
+ }
+
+ if(found && getValue == 8)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 2);
+ }
+
+ i += 3; // and the rest
+
+ if(++foundIndex == index)
+ {
+ if(GetObjectVal("GfxOverlay", 0, obj, i) == 0)
+ {
+ // there is no overlay left
+ return -1;
+ }
+ else
+ {
+ found = true;
+ if(getValue == 0)
+ {
+ return i;
+ }
+ else if(getValue == 1)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i);
+ }
+ }
+ }
+
+ ++i; // skip the next overlay ID
+ }
+}
+
+global func GetOverlayValueOffsetByID(int overlay, object obj)
+{
+ obj = obj || this;
+
+ if(overlay != 0)
+ {
+ var offset = 0, diffIndex = 0;
+ while((offset = obj->GetOverlayValueByIndex(diffIndex, offset)) >= 0)
+ {
+ if(GetOverlayValueByIndex(0, offset, 1) == overlay)
+ {
+ return offset;
+ }
+ diffIndex = 1;
+ }
+ }
+
+ return -1;
+}
+
+global func GetObjDrawTransform(int overlay, int overlayOffset, object obj)
+{
+ obj = obj || this;
+ if(overlay == 0)
+ {
+ var first = GetObjectVal("DrawTransform", 0, obj, 0);
+ if(first == 0)
+ {
+ return [1000, 0, 0, 0, 1000, 0];
+ }
+ var ret = [ParseFloat(first, 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 1), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 2), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 3), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 4), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 5), 3)];
+
+ var rest = GetObjectVal("DrawTransform", 0, obj, 7); // 6 is left out intentionally, because its only FlipDir (used internally for Actions with FlipDir)
+ if(GetType(rest) == C4V_String) // the last 3 matrix-values are only decompiled if they are not the default of [0, 0, 1.0]
+ {
+ SetLength(ret, 9);
+ ret[6] = ParseFloat(rest, 3);
+ ret[7] = ParseFloat(GetObjectVal("DrawTransform", 0, obj, 8), 3);
+ ret[8] = ParseFloat(GetObjectVal("DrawTransform", 0, obj, 9), 3);
+ }
+
+ return ret;
+ }
+ else
+ {
+ var index = 0;
+ if(overlay == -1)
+ {
+ index = overlayOffset;
+ }
+ else
+ {
+ index = obj->GetOverlayValueOffsetByID(overlay);
+ }
+ if(index < 0)
+ {
+ return index;
+ }
+ index = obj->GetOverlayValueByIndex(0, index, 7);
+ if(index < 0)
+ {
+ return index;
+ }
+
+ var ret = [ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 1), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 2), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 3), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 4), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 5), 3)];
+
+ var rest = GetObjectVal("GfxOverlay", 0, obj, index + 7); // 6 is left out intentionally, because its only FlipDir (used internally for Actions with FlipDir)
+ if(GetType(rest) == C4V_String) // the last 3 matrix-values are only decompiled if they are not the default of [0, 0, 1.0]
+ {
+ SetLength(ret, 9);
+ ret[6] = ParseFloat(rest, 3);
+ ret[7] = ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 8), 3);
+ ret[8] = ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 9), 3);
+ }
+
+ return ret;
+ }
+}