summaryrefslogtreecommitdiffstats
path: root/System.c4g
diff options
context:
space:
mode:
Diffstat (limited to 'System.c4g')
-rw-r--r--System.c4g/DTArrays.c52
-rw-r--r--System.c4g/DTCallback.c397
-rw-r--r--System.c4g/DTCrewSelection.c12
-rw-r--r--System.c4g/DTScopedVars.c108
-rw-r--r--System.c4g/DTUtility.c817
5 files changed, 1386 insertions, 0 deletions
diff --git a/System.c4g/DTArrays.c b/System.c4g/DTArrays.c
new file mode 100644
index 0000000..0b78ef4
--- /dev/null
+++ b/System.c4g/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/System.c4g/DTCallback.c b/System.c4g/DTCallback.c
new file mode 100644
index 0000000..91730d2
--- /dev/null
+++ b/System.c4g/DTCallback.c
@@ -0,0 +1,397 @@
+/*-- Extensible callback system --*/
+#strict 2
+
+static const CallbackTarget_Bind = -1;
+static const CallbackTarget_Global = -2;
+static const CallbackTarget_Scenario = -3;
+
+// NOTE: fast callbacks (fast = true for ObjectCallback or DefinitionCallback) use ObjectCall/DefinitionCall directly for greater performance if possible (refs == 0 and GetLength(args) <= 8 for CallA()) and thus must be real functions of the desired object/definition. safe mode is ignored for fast callbacks
+
+global func GlobalCallback(string name) { return [CallbackTarget_Global, name]; }
+global func ScenarioCallback(string name) { return [CallbackTarget_Scenario, name]; }
+global func ObjectCallback(string name, bool fast, object target) { return [target || this || FatalError("ObjectCallback without target object"), name, fast]; }
+global func DefinitionCallback(string name, bool fast, id target) { return [target || GetID() || FatalError("DefinitionCallback without target definition"), name, fast]; }
+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, true];
+ }
+
+ if(GetType(callback) != C4V_Array)
+ {
+ return _inherited(callback, args, ...);
+ }
+
+ var target = callback[0], function = callback[1];
+
+ if((GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID) && GetLength(callback) == 3 && callback[2] && (!refs || refs == []) && GetLength(args) <= 8) // fast callback
+ {
+ if(GetType(target) == C4V_C4Object)
+ {
+ return ObjectCall(target, function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
+ }
+ else if(GetType(target) == C4V_C4ID)
+ {
+ return DefinitionCall(target, function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
+ }
+ }
+
+ refs = refs || [];
+
+ 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(function, BindArgs(callback[2], args, safe, refs), safe, refs);
+ }
+
+ if(GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID || target == CallbackTarget_Global)
+ {
+ var safeModifier = "";
+ if(safe)
+ {
+ safeModifier = "~";
+ }
+ 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(GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID)
+ {
+ return eval(Format("%s->%s%s(%s)", Serialize(target), safeModifier, function, argsStr));
+ }
+ }
+ else
+ {
+ if(this)
+ {
+ return this->~CallCustom(callback, args, ...);
+ }
+ else if(GetID())
+ {
+ return GetID()->~CallCustom(callback, args, ...);
+ }
+ else
+ {
+ return CallCustom(callback, args, ...);
+ }
+ }
+}
+
+global func CallCustom() { return _inherited(...); }
+
+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)
+ {
+ if(this)
+ {
+ ret[pos] = this->~BindCustomArgs(bind, args, safe);
+ }
+ else if(GetID())
+ {
+ ret[pos] = GetID()->~BindCustomArgs(bind, args, safe);
+ }
+ else
+ {
+ 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 || (GetLength(callback) == 3 && (!callback[2] || GetType(callback[2]) == C4V_Bool))) // length 2 for callbacks created by Callback() or Object/DefinitionCallback() before fast was introduced
+ {
+ if(GetType(callback[1]) == C4V_String && callback[1] != "")
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ if(this)
+ {
+ return this->~CheckCustomCallback(callback, ...);
+ }
+ else if(GetID())
+ {
+ return GetID()->~CheckCustomCallback(callback, ...);
+ }
+ else
+ {
+ return CheckCustomCallback(callback, ...);
+ }
+}
+
+// to silence the error if no ScopedVars are included
+global func CheckScopedVar() { return _inherited(...); }
+
+global func CheckCustomCallback() { return _inherited(...); }
+
+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)
+ {
+ var ret;
+ if(this)
+ {
+ ret = this->~CheckBindCallbackCustomBinding(b);
+ }
+ else if(GetID())
+ {
+ ret = GetID()->~CheckBindCallbackCustomBinding(b);
+ }
+ else
+ {
+ ret = CheckBindCallbackCustomBinding(b);
+ }
+ if(!ret)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if(!CheckBindCallbackBinding(b))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if(b < 0)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+global func CheckBindCallbackCustomBinding() { return _inherited(...); } \ No newline at end of file
diff --git a/System.c4g/DTCrewSelection.c b/System.c4g/DTCrewSelection.c
new file mode 100644
index 0000000..4e12db1
--- /dev/null
+++ b/System.c4g/DTCrewSelection.c
@@ -0,0 +1,12 @@
+#strict 2
+#appendto CLNK
+
+func Initialize()
+{
+ if(!FindObject(CS7I))
+ {
+ CreateObject(CS7I, 0, 0, NO_OWNER);
+ }
+
+ return _inherited(...);
+} \ No newline at end of file
diff --git a/System.c4g/DTScopedVars.c b/System.c4g/DTScopedVars.c
new file mode 100644
index 0000000..0cde670
--- /dev/null
+++ b/System.c4g/DTScopedVars.c
@@ -0,0 +1,108 @@
+#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
+ {
+ if(this)
+ {
+ return ScopedVar(this->~CustomScopedVar(variable));
+ }
+ else if(GetID())
+ {
+ return ScopedVar(GetID()->~CustomScopedVar(variable));
+ }
+ 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;
+ }
+ }
+ }
+
+ if(this)
+ {
+ return this->~CheckCustomScopedVar(variable);
+ }
+ else if(GetID())
+ {
+ return GetID()->~CheckCustomScopedVar(variable);
+ }
+ else
+ {
+ return CheckCustomScopedVar(variable);
+ }
+}
+
+global func CheckCustomScopedVar() { return _inherited(...); } \ No newline at end of file
diff --git a/System.c4g/DTUtility.c b/System.c4g/DTUtility.c
new file mode 100644
index 0000000..d491567
--- /dev/null
+++ b/System.c4g/DTUtility.c
@@ -0,0 +1,817 @@
+#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 SetPlrExtraDataIntArray(int plr, string name, array arr)
+{
+ var lengthName = Format("%sL", name);
+
+ var oldLength = CastInt(GetPlrExtraData(plr, lengthName));
+
+ var length = GetLength(arr);
+
+ for(var i = 0; i < length; ++i)
+ {
+ var partName = Format("%s%d", name, i);
+ if(arr[i] != 0)
+ {
+ SetPlrExtraData(plr, partName, arr[i]);
+ }
+ else if(GetPlrExtraData(plr, partName) != 0)
+ {
+ SetPlrExtraData(plr, partName, 0);
+ }
+ }
+
+ SetPlrExtraData(plr, lengthName, length);
+
+ for(var i = length; i < oldLength; ++i)
+ {
+ var partName = Format("%s%d", name, i);
+ if(GetPlrExtraData(plr, partName) != 0)
+ {
+ SetPlrExtraData(plr, partName, 0);
+ }
+ }
+}
+
+global func GetPlrExtraDataIntArray(int plr, string name)
+{
+ var length = CastInt(GetPlrExtraData(plr, Format("%sL", name)));
+
+ var ret = CreateArray(length);
+
+ for(var i = 0; i < length; ++i)
+ {
+ ret[i] = CastInt(GetPlrExtraData(plr, Format("%s%d", name, i)));
+ }
+
+ return ret;
+}
+
+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;
+ }
+}
+
+global func GetShape(object obj)
+{
+ obj = obj || this;
+ return [GetObjectVal("Offset", 0, obj, 0), GetObjectVal("Offset", 0, obj, 1), GetObjectVal("Width", 0, obj), GetObjectVal("Height", 0, obj)];
+}
+
+global func InRect(array point, array rect)
+{
+ var x = point[0], y = point[1];
+ return rect[0] < x && x < rect[0] + rect[2] && rect[1] < y && y < rect[1] + rect[3];
+}
+
+global func GetPlayerByID(int id)
+{
+ for(var i = 0; i < GetPlayerCount(); ++i)
+ {
+ var plr = GetPlayerByIndex(i);
+ if(GetPlayerID(plr) == id)
+ {
+ return plr;
+ }
+ }
+
+ return NO_OWNER;
+}