1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Functions mapped by C4Script */
19 
20 #include "C4Include.h"
21 
22 #include "C4Version.h"
23 #include "lib/C4Random.h"
24 #include "script/C4AulExec.h"
25 #include "script/C4AulDefFunc.h"
26 #include "script/C4ScriptLibraries.h"
27 
28 //========================== Some Support Functions =======================================
29 
FnStringFormat(C4PropList * _this,C4String * szFormatPar,C4Value * Pars,int ParCount)30 StdStrBuf FnStringFormat(C4PropList * _this, C4String *szFormatPar, C4Value * Pars, int ParCount)
31 {
32 	int cPar=0;
33 
34 	StdStrBuf StringBuf("", false);
35 	const char * cpFormat = FnStringPar(szFormatPar);
36 	const char * cpType;
37 	char szField[20];
38 	while (*cpFormat)
39 	{
40 		// Copy normal stuff
41 		while (*cpFormat && (*cpFormat!='%'))
42 			StringBuf.AppendChar(*cpFormat++);
43 		// Field
44 		if (*cpFormat=='%')
45 		{
46 			// Scan field type
47 			for (cpType=cpFormat+1; *cpType && (*cpType == '+' || *cpType == '-' || *cpType == '.' || *cpType == '#' || *cpType == ' ' || Inside(*cpType,'0','9')); cpType++) {}
48 			// Copy field
49 			SCopy(cpFormat,szField,std::min<unsigned int>(sizeof szField - 1, cpType - cpFormat + 1));
50 			// Insert field by type
51 			switch (*cpType)
52 			{
53 				// number
54 			case 'd': case 'x': case 'X':
55 			{
56 				if (cPar >= ParCount) throw C4AulExecError("format placeholder without parameter");
57 				StringBuf.AppendFormat(szField, Pars[cPar++].getInt());
58 				cpFormat+=SLen(szField);
59 				break;
60 			}
61 			// character
62 			case 'c':
63 			{
64 				if (cPar >= ParCount) throw C4AulExecError("format placeholder without parameter");
65 				StringBuf.AppendCharacter(Pars[cPar++].getInt());
66 				cpFormat+=SLen(szField);
67 				break;
68 			}
69 			// C4ID
70 			case 'i':
71 			// C4Value
72 			case 'v':
73 			{
74 				if (cPar >= ParCount) throw C4AulExecError("format placeholder without parameter");
75 				StringBuf.Append(static_cast<const StdStrBuf&>(Pars[cPar++].GetDataString(10)));
76 				cpFormat+=SLen(szField);
77 				break;
78 			}
79 			// String
80 			case 's':
81 			{
82 				// get string
83 				if (cPar >= ParCount) throw C4AulExecError("format placeholder without parameter");
84 				const char *szStr = "(null)";
85 				if (Pars[cPar].GetData())
86 				{
87 					C4String * pStr = Pars[cPar].getStr();
88 					if (!pStr) throw C4AulExecError("string format placeholder without string");
89 					szStr = pStr->GetCStr();
90 				}
91 				++cPar;
92 				StringBuf.AppendFormat(szField, szStr);
93 				cpFormat+=SLen(szField);
94 				break;
95 			}
96 			case '%':
97 				StringBuf.AppendChar('%');
98 				cpFormat+=SLen(szField);
99 				break;
100 				// Undefined / Empty
101 			default:
102 				StringBuf.AppendChar('%');
103 				cpFormat++;
104 				break;
105 			}
106 		}
107 	}
108 	return StringBuf;
109 }
110 
C4AulDefFunc(C4PropListStatic * Parent,C4ScriptFnDef * pDef)111 C4AulDefFunc::C4AulDefFunc(C4PropListStatic * Parent, C4ScriptFnDef* pDef):
112 		C4AulFunc(Parent, pDef->Identifier), Def(pDef)
113 {
114 	Parent->SetPropertyByS(Name, C4VFunction(this));
115 }
116 
117 C4AulDefFunc::~C4AulDefFunc() = default;
118 
Exec(C4PropList * p,C4Value pPars[],bool fPassErrors)119 C4Value C4AulDefFunc::Exec(C4PropList * p, C4Value pPars[], bool fPassErrors)
120 {
121 	assert(Def->FunctionC4V);
122 	return Def->FunctionC4V(p, pPars);
123 }
124 
125 //=============================== C4Script Functions ====================================
126 
127 #define MAKE_AND_RETURN_ARRAY(values) do { \
128 	C4ValueArray *matrix = new C4ValueArray(sizeof(values) / sizeof(*values)); \
129 	for (size_t i = 0; i < sizeof(values) / sizeof(*values); ++i) \
130 		(*matrix)[i] = C4VInt(values[i]); \
131 	return matrix; \
132 } while (0)
133 
FnTrans_Identity(C4PropList * _this)134 static C4ValueArray *FnTrans_Identity(C4PropList * _this)
135 {
136 	long values[] =
137 	{
138 		1000, 0, 0, 0,
139 		0, 1000, 0, 0,
140 		0, 0, 1000, 0
141 	};
142 	MAKE_AND_RETURN_ARRAY(values);
143 }
144 
FnTrans_Translate(C4PropList * _this,long dx,long dy,long dz)145 static C4ValueArray *FnTrans_Translate(C4PropList * _this, long dx, long dy, long dz)
146 {
147 	long values[] =
148 	{
149 		1000, 0, 0, dx,
150 		0, 1000, 0, dy,
151 		0, 0, 1000, dz
152 	};
153 	MAKE_AND_RETURN_ARRAY(values);
154 }
155 
FnTrans_Scale(C4PropList * _this,long sx,long sy,long sz)156 static C4ValueArray *FnTrans_Scale(C4PropList * _this, long sx, long sy, long sz)
157 {
158 	if (sy == 0 && sz == 0)
159 		sy = sz = sx;
160 	long values[] =
161 	{
162 		sx, 0, 0, 0,
163 		0, sy, 0, 0,
164 		0, 0, sz, 0
165 	};
166 	MAKE_AND_RETURN_ARRAY(values);
167 }
168 
FnTrans_Rotate(C4PropList * _this,long angle,long rx,long ry,long rz)169 static C4ValueArray *FnTrans_Rotate(C4PropList * _this, long angle, long rx, long ry, long rz)
170 {
171 	long c = fixtoi(Cos(itofix(angle, 1)), 1000);
172 	long s = fixtoi(Sin(itofix(angle, 1)), 1000);
173 
174 	long sqrt_val = rx * rx + ry * ry + rz * rz;
175 	long n = long(sqrt(double(sqrt_val)));
176 	if (n * n < sqrt_val) n++;
177 	else if (n * n > sqrt_val) n--;
178 
179 	if (n == 0)
180 	{
181 		throw C4AulExecError("cannot rotate around a null vector");
182 	}
183 
184 	rx = (1000 * rx) / n;
185 	ry = (1000 * ry) / n;
186 	rz = (1000 * rz) / n;
187 
188 	long values[] =
189 	{
190 		rx*rx*(1000-c)/1000000+c, rx*ry*(1000-c)/1000000-rz*s/1000, rx*rz*(1000-c)/1000000+ry*s/1000, 0,
191 		ry*rx*(1000-c)/1000000+rz*s/1000, ry*ry*(1000-c)/1000000+c, ry*rz*(1000-c)/1000000-rx*s/1000, 0,
192 		rz*rx*(1000-c)/1000000-ry*s/1000, ry*rz*(1000-c)/1000000+rx*s/1000, rz*rz*(1000-c)/1000000+c, 0
193 	};
194 	MAKE_AND_RETURN_ARRAY(values);
195 }
196 
FnTrans_Mul(C4PropList * _this,C4Value * pars)197 static C4Value FnTrans_Mul(C4PropList * _this, C4Value *pars)
198 {
199 	const int32_t matrixSize = 12;
200 	long values[] =
201 	{
202 		0, 0, 0, 0,
203 		0, 0, 0, 0,
204 		0, 0, 0, 0
205 	};
206 
207 	// Read all parameters
208 	bool first = true;
209 	for (int32_t i = 0; i < C4AUL_MAX_Par; i++)
210 	{
211 		C4Value Data = *(pars++);
212 		// No data given?
213 		if (!Data) break;
214 		C4ValueArray *factorArray = Data.getArray();
215 		if (!factorArray || factorArray->GetSize() != matrixSize) continue;
216 
217 		if (first)
218 		{
219 			first = false;
220 
221 			for (int32_t c = 0; c < matrixSize; ++c)
222 				values[c] = (*factorArray)[c].getInt();
223 			continue;
224 		}
225 
226 		// multiply current matrix with new one
227 		long values_rhs[matrixSize], values_result[matrixSize];
228 		for (int32_t c = 0; c < matrixSize; ++c)
229 			values_rhs[c] = (*factorArray)[c].getInt();
230 
231 		// matrix multiplication
232 		values_result[ 0] = values[0]*values_rhs[0]/1000 + values[1]*values_rhs[4]/1000 + values[ 2]*values_rhs[ 8]/1000;
233 		values_result[ 1] = values[0]*values_rhs[1]/1000 + values[1]*values_rhs[5]/1000 + values[ 2]*values_rhs[ 9]/1000;
234 		values_result[ 2] = values[0]*values_rhs[2]/1000 + values[1]*values_rhs[6]/1000 + values[ 2]*values_rhs[10]/1000;
235 		values_result[ 3] = values[0]*values_rhs[3]/1000 + values[1]*values_rhs[7]/1000 + values[ 2]*values_rhs[11]/1000 + values[3];
236 		values_result[ 4] = values[4]*values_rhs[0]/1000 + values[5]*values_rhs[4]/1000 + values[ 6]*values_rhs[ 8]/1000;
237 		values_result[ 5] = values[4]*values_rhs[1]/1000 + values[5]*values_rhs[5]/1000 + values[ 6]*values_rhs[ 9]/1000;
238 		values_result[ 6] = values[4]*values_rhs[2]/1000 + values[5]*values_rhs[6]/1000 + values[ 6]*values_rhs[10]/1000;
239 		values_result[ 7] = values[4]*values_rhs[3]/1000 + values[5]*values_rhs[7]/1000 + values[ 6]*values_rhs[11]/1000 + values[7];
240 		values_result[ 8] = values[8]*values_rhs[0]/1000 + values[9]*values_rhs[4]/1000 + values[10]*values_rhs[ 8]/1000;
241 		values_result[ 9] = values[8]*values_rhs[1]/1000 + values[9]*values_rhs[5]/1000 + values[10]*values_rhs[ 9]/1000;
242 		values_result[10] = values[8]*values_rhs[2]/1000 + values[9]*values_rhs[6]/1000 + values[10]*values_rhs[10]/1000;
243 		values_result[11] = values[8]*values_rhs[3]/1000 + values[9]*values_rhs[7]/1000 + values[10]*values_rhs[11]/1000 + values[11];
244 
245 		for (int32_t c = 0; c < matrixSize; ++c)
246 			values[c] = values_result[c];
247 	}
248 
249 	// unlike in the other Trans_*-functions, we have to put the array into a C4Value manually here
250 	C4ValueArray *matrix = new C4ValueArray(sizeof(values) / sizeof(*values));
251 	for (size_t i = 0; i < sizeof(values) / sizeof(*values); ++i)
252 		(*matrix)[i] = C4VInt(values[i]);
253 	return C4VArray(matrix);
254 }
255 
256 #undef MAKE_AND_RETURN_ARRAY
257 
258 /* PropLists */
259 
FnCreatePropList(C4PropList * _this,C4PropList * prototype)260 static C4PropList * FnCreatePropList(C4PropList * _this, C4PropList * prototype)
261 {
262 	return C4PropList::New(prototype);
263 }
264 
FnGetProperty(C4PropList * _this,C4String * key,C4PropList * pObj)265 static C4Value FnGetProperty(C4PropList * _this, C4String * key, C4PropList * pObj)
266 {
267 	if (!pObj) pObj = _this;
268 	if (!pObj) return C4VNull;
269 	if (!key) return C4VNull;
270 	C4Value r;
271 	pObj->GetPropertyByS(key, &r);
272 	return r;
273 }
274 
FnSetProperty(C4PropList * _this,C4String * key,const C4Value & to,C4PropList * pObj)275 static bool FnSetProperty(C4PropList * _this, C4String * key, const C4Value & to, C4PropList * pObj)
276 {
277 	if (!pObj) pObj = _this;
278 	if (!pObj) return false;
279 	if (!key) return false;
280 	if (pObj->IsFrozen())
281 		throw C4AulExecError("proplist write: proplist is readonly");
282 	pObj->SetPropertyByS(key, to);
283 	return true;
284 }
285 
FnResetProperty(C4PropList * _this,C4String * key,C4PropList * pObj)286 static bool FnResetProperty(C4PropList * _this, C4String * key, C4PropList * pObj)
287 {
288 	if (!pObj) pObj = _this;
289 	if (!pObj) return false;
290 	if (!key) return false;
291 	if (!pObj->HasProperty(key)) return false;
292 	if (pObj->IsFrozen())
293 		throw C4AulExecError("proplist write: proplist is readonly");
294 	pObj->ResetProperty(key);
295 	return true;
296 }
297 
FnGetProperties(C4PropList * _this,C4PropList * p)298 static C4ValueArray * FnGetProperties(C4PropList * _this, C4PropList * p)
299 {
300 	if (!p) p = _this;
301 	if (!p) throw NeedNonGlobalContext("GetProperties");
302 	C4ValueArray * r = p->GetProperties();
303 	r->SortStrings();
304 	return r;
305 }
306 
FnGetPrototype(C4PropList * _this,C4PropList * p)307 static C4PropList * FnGetPrototype(C4PropList * _this, C4PropList * p)
308 {
309 	if (!p) p = _this;
310 	if (!p) throw NeedNonGlobalContext("GetPrototype");
311 	return p->GetPrototype();
312 }
313 
FnSetPrototype(C4PropList * _this,C4PropList * prototype,C4PropList * p)314 static void FnSetPrototype(C4PropList * _this, C4PropList * prototype, C4PropList * p)
315 {
316 	if (!p) p = _this;
317 	if (!p) throw NeedNonGlobalContext("GetPrototype");
318 	p->SetProperty(P_Prototype, C4Value(prototype));
319 }
320 
FnCall(C4PropList * _this,C4Value * Pars)321 static C4Value FnCall(C4PropList * _this, C4Value * Pars)
322 {
323 	if (!_this) _this = ::ScriptEngine.GetPropList();
324 	C4AulParSet ParSet;
325 	ParSet.Copy(&Pars[1], C4AUL_MAX_Par - 1);
326 	C4AulFunc * fn = Pars[0].getFunction();
327 	C4String * name;
328 	if (!fn)
329 	{
330 		name = Pars[0].getStr();
331 		if (name) fn = _this->GetFunc(name);
332 	}
333 	if (!fn)
334 	{
335 		const char * s = FnStringPar(name);
336 		if (s[0] == '~')
337 		{
338 			fn = _this->GetFunc(&s[1]);
339 			if (!fn)
340 				return C4Value();
341 		}
342 	}
343 	if (!fn)
344 		throw C4AulExecError(FormatString("Call: no function %s", Pars[0].GetDataString().getData()).getData());
345 	return fn->Exec(_this, &ParSet, true);
346 }
347 
FnGetName(C4PropList * _this,bool truename)348 static C4String *FnGetName(C4PropList * _this, bool truename)
349 {
350 	if (!_this)
351 		throw NeedNonGlobalContext("GetName");
352 	else if(truename)
353 		return _this->IsStatic() ? _this->IsStatic()->GetParentKeyName() : nullptr;
354 	else
355 		return String(_this->GetName());
356 }
357 
358 /* Effects */
359 
FnAddEffect(C4PropList * _this,C4String * szEffect,C4PropList * pTarget,int iPrio,int iTimerInterval,C4PropList * pCmdTarget,C4Def * idCmdTarget,const C4Value & Val1,const C4Value & Val2,const C4Value & Val3,const C4Value & Val4)360 static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * pTarget,
361                            int iPrio, int iTimerInterval, C4PropList * pCmdTarget, C4Def * idCmdTarget,
362                            const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4)
363 {
364 	// safety
365 	if (pTarget && !pTarget->Status) return C4Value();
366 	if (!szEffect || !*szEffect->GetCStr() || !iPrio) return C4Value();
367 	// create effect
368 	C4PropList * p = pCmdTarget;
369 	if (!p) p = idCmdTarget;
370 	if (!p) p = ::ScriptEngine.GetPropList();
371 	C4Effect * pEffect = C4Effect::New(pTarget, FnGetEffectsFor(pTarget),
372 			szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4);
373 	// return effect - may be 0 if the effect has been denied by another effect
374 	if (!pEffect) return C4Value();
375 	return C4VPropList(pEffect);
376 }
377 
FnCreateEffect(C4PropList * _this,C4PropList * prototype,int iPrio,int iTimerInterval,const C4Value & Val1,const C4Value & Val2,const C4Value & Val3,const C4Value & Val4)378 static C4Effect * FnCreateEffect(C4PropList * _this, C4PropList * prototype, int iPrio, int iTimerInterval,
379                               const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4)
380 {
381 	if (!prototype || !(prototype->GetName()[0])) throw C4AulExecError("CreateEffect needs a prototype with a name");
382 	if (!iPrio) throw C4AulExecError("CreateEffect needs a nonzero priority");
383 	// create effect
384 	C4Effect * pEffect = C4Effect::New(_this, FnGetEffectsFor(_this), prototype, iPrio, iTimerInterval,
385 	                                   Val1, Val2, Val3, Val4);
386 	// return effect - may be 0 if the effect has been denied by another effect
387 	return pEffect;
388 }
389 
FnGetEffect(C4PropList * _this,C4String * psEffectName,C4PropList * pTarget,int index,int iMaxPriority)390 static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, int index, int iMaxPriority)
391 {
392 	const char *szEffect = FnStringPar(psEffectName);
393 	// get effects
394 	C4Effect *pEffect = *FnGetEffectsFor(pTarget);
395 	if (!pEffect) return nullptr;
396 	// name/wildcard given: find effect by name and index
397 	if (szEffect && *szEffect)
398 		return pEffect->Get(szEffect, index, iMaxPriority);
399 	return nullptr;
400 }
401 
FnRemoveEffect(C4PropList * _this,C4String * psEffectName,C4PropList * pTarget,C4Effect * pEffect2,bool fDoNoCalls)402 static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, C4Effect * pEffect2, bool fDoNoCalls)
403 {
404 	// evaluate parameters
405 	const char *szEffect = FnStringPar(psEffectName);
406 	// if the user passed an effect, it can be used straight-away
407 	C4Effect *pEffect = pEffect2;
408 	// otherwise, the correct effect will be searched in the target's effects or in the global ones
409 	if (!pEffect)
410 	{
411 		pEffect = *FnGetEffectsFor(pTarget);
412 		// the object has no effects attached, nothing to look for
413 		if (!pEffect) return false;
414 		// name/wildcard given: find effect by name
415 		if (szEffect && *szEffect)
416 			pEffect = pEffect->Get(szEffect, 0);
417 	}
418 
419 	// neither passed nor found - nothing to remove!
420 	if (!pEffect) return false;
421 
422 	// kill it
423 	if (fDoNoCalls)
424 		pEffect->SetDead();
425 	else
426 		pEffect->Kill();
427 	// done, success
428 	return true;
429 }
430 
FnCheckEffect(C4PropList * _this,C4String * psEffectName,C4PropList * pTarget,int iPrio,int iTimerInterval,const C4Value & Val1,const C4Value & Val2,const C4Value & Val3,const C4Value & Val4)431 static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4PropList * pTarget,
432                              int iPrio, int iTimerInterval,
433                              const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4)
434 {
435 	const char *szEffect = FnStringPar(psEffectName);
436 	// safety
437 	if (pTarget && !pTarget->Status) return C4Value();
438 	if (!szEffect || !*szEffect) return C4Value();
439 	// get effects
440 	C4Effect *pEffect = *FnGetEffectsFor(pTarget);
441 	if (!pEffect) return C4Value();
442 	// let them check
443 	C4Effect * r = pEffect->Check(szEffect, iPrio, iTimerInterval, Val1, Val2, Val3, Val4);
444 	if (r == (C4Effect *)C4Fx_Effect_Deny) return C4VInt(C4Fx_Effect_Deny);
445 	if (r == (C4Effect *)C4Fx_Effect_Annul) return C4VInt(C4Fx_Effect_Annul);
446 	return C4VPropList(r);
447 }
448 
FnGetEffectCount(C4PropList * _this,C4String * psEffectName,C4PropList * pTarget,long iMaxPriority)449 static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, long iMaxPriority)
450 {
451 	// evaluate parameters
452 	const char *szEffect = FnStringPar(psEffectName);
453 	// get effects
454 	C4Effect *pEffect = *FnGetEffectsFor(pTarget);
455 	if (!pEffect) return false;
456 	// count effects
457 	if (!*szEffect) szEffect = nullptr;
458 	return pEffect->GetCount(szEffect, iMaxPriority);
459 }
460 
FnEffectCall(C4PropList * _this,C4Value * Pars)461 static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars)
462 {
463 	// evaluate parameters
464 	C4PropList *pTarget = Pars[0].getPropList();
465 	C4Effect * pEffect = Pars[1].getPropList() ? Pars[1].getPropList()->GetEffect() : nullptr;
466 	const char *szCallFn = FnStringPar(Pars[2].getStr());
467 	// safety
468 	if (pTarget && !pTarget->Status) return C4Value();
469 	if (!szCallFn || !*szCallFn) return C4Value();
470 	if (!pEffect) return C4Value();
471 	// do call
472 	return pEffect->DoCall(pTarget, szCallFn, Pars[3], Pars[4], Pars[5], Pars[6], Pars[7], Pars[8], Pars[9]);
473 }
474 
475 /* Regex */
476 
477 static const long
478 	Regex_CaseInsensitive = (1 << 0),
479 	Regex_FirstOnly       = (1 << 1);
480 
C4IntToSyntaxOption(long flags)481 static std::regex_constants::syntax_option_type C4IntToSyntaxOption(long flags)
482 {
483 	std::regex_constants::syntax_option_type out = std::regex::ECMAScript;
484 	if (flags & Regex_CaseInsensitive)
485 		out |= std::regex::icase;
486 	return out;
487 }
488 
C4IntToMatchFlag(long flags)489 static std::regex_constants::match_flag_type C4IntToMatchFlag(long flags)
490 {
491 	std::regex_constants::match_flag_type out = std::regex_constants::match_default;
492 	if (flags & Regex_FirstOnly)
493 		out |= std::regex_constants::format_first_only;
494 	return out;
495 }
496 
FnRegexReplace(C4PropList * _this,C4String * source,C4String * regex,C4String * replacement,long flags)497 static Nillable<C4String *> FnRegexReplace(C4PropList * _this, C4String *source, C4String *regex, C4String *replacement, long flags)
498 {
499 	if (!source || !regex || !replacement) return C4Void();
500 	try
501 	{
502 		std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
503 		std::string out = std::regex_replace(source->GetCStr(), re, replacement->GetCStr(), C4IntToMatchFlag(flags));
504 		return ::Strings.RegString(out.c_str());
505 	}
506 	catch (const std::regex_error& e)
507 	{
508 		throw C4AulExecError(FormatString("RegexReplace: %s", e.what()).getData());
509 	}
510 }
511 
512 
FnRegexSearch(C4PropList * _this,C4String * source,C4String * regex,long flags)513 static Nillable<C4ValueArray *> FnRegexSearch(C4PropList * _this, C4String *source, C4String *regex, long flags)
514 {
515 	if (!source || !regex) return C4Void();
516 	try
517 	{
518 		std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
519 		C4ValueArray *out = new C4ValueArray();
520 		const auto &data = source->GetData();
521 		size_t pos = 0;
522 		std::cmatch m;
523 		long i = 0;
524 		// std::regex_iterator would be the better way to do this, but is is broken in libc++ (see LLVM bug #21597).
525 		while (pos <= data.getLength() && std::regex_search(data.getData() + pos, data.getData() + data.getLength(), m, re))
526 		{
527 			int char_pos = GetCharacterCount(std::string(data.getData(), pos + m.position()).c_str());
528 			(*out)[i++] = C4VInt(char_pos);
529 			if (flags & Regex_FirstOnly) break;
530 			pos += m.position() + std::max<size_t>(m.length(), 1);
531 		}
532 		return out;
533 	}
534 	catch (const std::regex_error& e)
535 	{
536 		throw C4AulExecError(FormatString("RegexSearch: %s", e.what()).getData());
537 	}
538 }
539 
FnRegexMatch(C4PropList * _this,C4String * source,C4String * regex,long flags)540 static Nillable<C4ValueArray *> FnRegexMatch(C4PropList * _this, C4String *source, C4String *regex, long flags)
541 {
542 	if (!source || !regex) return C4Void();
543 	try
544 	{
545 		std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
546 		C4ValueArray *out = new C4ValueArray();
547 		const auto &data = source->GetData();
548 		size_t pos = 0;
549 		std::cmatch m;
550 		long i = 0;
551 		while (pos <= data.getLength() && std::regex_search(data.getData() + pos, data.getData() + data.getLength(), m, re))
552 		{
553 			C4ValueArray *match = new C4ValueArray(m.size());
554 			long j = 0;
555 			for (auto sm : m)
556 			{
557 				(*match)[j++] = C4VString(String(sm.str().c_str()));
558 			}
559 			(*out)[i++] = C4VArray(match);
560 			if (flags & Regex_FirstOnly) break;
561 			pos += m.position() + std::max<size_t>(m.length(), 1);
562 		}
563 		return out;
564 	}
565 	catch (const std::regex_error& e)
566 	{
567 		throw C4AulExecError(FormatString("RegexMatch: %s", e.what()).getData());
568 	}
569 }
570 
FnRegexSplit(C4PropList * _this,C4String * source,C4String * regex,long flags)571 static Nillable<C4ValueArray *> FnRegexSplit(C4PropList * _this, C4String *source, C4String *regex, long flags)
572 {
573 	if (!source || !regex) return C4Void();
574 	try
575 	{
576 		std::regex re(regex->GetCStr(), C4IntToSyntaxOption(flags));
577 		C4ValueArray *out = new C4ValueArray();
578 		const auto &data = source->GetData();
579 		size_t pos = 0;
580 		std::cmatch m;
581 		long i = 0;
582 		while (pos <= data.getLength() && std::regex_search(data.getData() + pos, data.getData() + data.getLength(), m, re))
583 		{
584 			// As we're advancing by one character for zero-length matches, always
585 			// include at least one character here.
586 			std::string substr(data.getData() + pos, std::max<size_t>(m.position(), 1));
587 			(*out)[i++] = C4VString(String(substr.c_str()));
588 			if (flags & Regex_FirstOnly) break;
589 			pos += m.position() + std::max<size_t>(m.length(), 1);
590 		}
591 		if (pos <= data.getLength())
592 		{
593 			std::string substr(data.getData() + pos, data.getLength() - pos);
594 			(*out)[i++] = C4VString(String(substr.c_str()));
595 		}
596 		return out;
597 	}
598 	catch (const std::regex_error& e)
599 	{
600 		throw C4AulExecError(FormatString("RegexSplit: %s", e.what()).getData());
601 	}
602 }
603 
604 
FnLog(C4PropList * _this,C4Value * Pars)605 static C4Value FnLog(C4PropList * _this, C4Value * Pars)
606 {
607 	Log(FnStringFormat(_this, Pars[0].getStr(), &Pars[1], 9).getData());
608 	return C4VBool(true);
609 }
610 
FnDebugLog(C4PropList * _this,C4Value * Pars)611 static C4Value FnDebugLog(C4PropList * _this, C4Value * Pars)
612 {
613 	DebugLog(FnStringFormat(_this, Pars[0].getStr(), &Pars[1], 9).getData());
614 	return C4VBool(true);
615 }
616 
FnFormat(C4PropList * _this,C4Value * Pars)617 static C4Value FnFormat(C4PropList * _this, C4Value * Pars)
618 {
619 	return C4VString(FnStringFormat(_this, Pars[0].getStr(), &Pars[1], 9));
620 }
621 
622 // Parse a string into an integer. Returns nil if the conversion fails.
FnParseInt(C4PropList * _this,C4String * str)623 static Nillable<int32_t> FnParseInt(C4PropList *_this, C4String *str)
624 {
625 	const char *cstr = str->GetCStr();
626 	const char *end = nullptr;
627 	int32_t result = StrToI32(cstr, 10, &end);
628 	if (end == cstr || *end != '\0') return C4Void();
629 	return result;
630 }
631 
FnAbs(C4PropList * _this,long iVal)632 static long FnAbs(C4PropList * _this, long iVal)
633 {
634 	return Abs(iVal);
635 }
636 
FnSin(C4PropList * _this,long iAngle,long iRadius,long iPrec)637 static long FnSin(C4PropList * _this, long iAngle, long iRadius, long iPrec)
638 {
639 	if (!iPrec) iPrec = 1;
640 	// Precalculate the modulo operation so the C4Fixed argument to Sin does not overflow
641 	iAngle %= 360 * iPrec;
642 	// Let itofix and fixtoi handle the division and multiplication because that can handle higher ranges
643 	return fixtoi(Sin(itofix(iAngle, iPrec)), iRadius);
644 }
645 
FnCos(C4PropList * _this,long iAngle,long iRadius,long iPrec)646 static long FnCos(C4PropList * _this, long iAngle, long iRadius, long iPrec)
647 {
648 	if (!iPrec) iPrec = 1;
649 	iAngle %= 360 * iPrec;
650 	return fixtoi(Cos(itofix(iAngle, iPrec)), iRadius);
651 }
652 
FnSqrt(C4PropList * _this,long iValue)653 static long FnSqrt(C4PropList * _this, long iValue)
654 {
655 	if (iValue<0) return 0;
656 	long iSqrt = long(sqrt(double(iValue)));
657 	if (iSqrt * iSqrt < iValue) iSqrt++;
658 	if (iSqrt * iSqrt > iValue) iSqrt--;
659 	return iSqrt;
660 }
661 
FnAngle(C4PropList * _this,long iX1,long iY1,long iX2,long iY2,long iPrec)662 static long FnAngle(C4PropList * _this, long iX1, long iY1, long iX2, long iY2, long iPrec)
663 {
664 	// Standard prec
665 	if (!iPrec) iPrec = 1;
666 	return Angle(iX1, iY1, iX2, iY2, iPrec);
667 }
668 
FnArcSin(C4PropList * _this,long iVal,long iRadius)669 static long FnArcSin(C4PropList * _this, long iVal, long iRadius)
670 {
671 	// safety
672 	if (!iRadius) return 0;
673 	if (iVal > iRadius) return 0;
674 	// calc arcsin
675 	double f1 = iVal;
676 	f1 = asin(f1/iRadius)*180.0/M_PI;
677 	// return rounded angle
678 	return (long) floor(f1+0.5);
679 }
680 
FnArcCos(C4PropList * _this,long iVal,long iRadius)681 static long FnArcCos(C4PropList * _this, long iVal, long iRadius)
682 {
683 	// safety
684 	if (!iRadius) return 0;
685 	if (iVal > iRadius) return 0;
686 	// calc arccos
687 	double f1 = iVal;
688 	f1 = acos(f1/iRadius)*180.0/M_PI;
689 	// return rounded angle
690 	return (long) floor(f1+0.5);
691 }
692 
minmax(const char * func,const C4Value & a_val,const Nillable<int32_t> & b_opt)693 static std::pair<Nillable<int32_t>, Nillable<int32_t>> minmax(const char *func, const C4Value &a_val, const Nillable<int32_t> &b_opt)
694 {
695 	if (a_val.CheckConversion(C4V_Int))
696 	{
697 		int32_t a = a_val.getInt();
698 		int32_t b = b_opt;
699 		if (a > b)
700 			std::swap(a, b);
701 		return std::make_pair(a, b);
702 	}
703 	else if (a_val.CheckConversion(C4V_Array))
704 	{
705 		const C4ValueArray *a = a_val.getArray();
706 		if (a->GetSize() == 0)
707 			return std::make_pair(nullptr, nullptr);
708 
709 		if (!a->GetItem(0).CheckConversion(C4V_Int))
710 		{
711 			throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but element %d of array is of type %s", func, 0, a->GetItem(0).GetTypeName()).getData());
712 		}
713 		int32_t min, max;
714 		min = max = a->GetItem(0).getInt();
715 
716 		for (int32_t i = 1; i < a->GetSize(); ++i)
717 		{
718 			if (!a->GetItem(i).CheckConversion(C4V_Int))
719 			{
720 				throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but element %d of array is of type %s", func, i, a->GetItem(i).GetTypeName()).getData());
721 			}
722 			int32_t value = a->GetItem(i).getInt();
723 			min = std::min(min, value);
724 			max = std::max(max, value);
725 		}
726 
727 		return std::make_pair(min, max);
728 	}
729 	else
730 	{
731 		throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but is of type %s", func, a_val.GetTypeName()).getData());
732 	}
733 }
734 
FnMin(C4PropList * _this,const C4Value & a,Nillable<int32_t> b)735 static Nillable<int32_t> FnMin(C4PropList * _this, const C4Value &a, Nillable<int32_t> b)
736 {
737 	return minmax("Min", a, b).first;
738 }
739 
FnMax(C4PropList * _this,const C4Value & a,Nillable<int32_t> b)740 static Nillable<int32_t> FnMax(C4PropList * _this, const C4Value &a, Nillable<int32_t> b)
741 {
742 	return minmax("Max", a, b).second;
743 }
744 
FnDistance(C4PropList * _this,long iX1,long iY1,long iX2,long iY2)745 static long FnDistance(C4PropList * _this, long iX1, long iY1, long iX2, long iY2)
746 {
747 	return Distance(iX1,iY1,iX2,iY2);
748 }
749 
FnBoundBy(C4PropList * _this,long iVal,long iRange1,long iRange2)750 static long FnBoundBy(C4PropList * _this, long iVal, long iRange1, long iRange2)
751 {
752 	return Clamp(iVal,iRange1,iRange2);
753 }
754 
FnInside(C4PropList * _this,long iVal,long iRange1,long iRange2)755 static bool FnInside(C4PropList * _this, long iVal, long iRange1, long iRange2)
756 {
757 	return Inside(iVal,iRange1,iRange2);
758 }
759 
FnRandom(C4PropList * _this,long iRange)760 static long FnRandom(C4PropList * _this, long iRange)
761 {
762 	return Random(iRange);
763 }
764 
FnGetType(C4PropList * _this,const C4Value & Value)765 static int FnGetType(C4PropList * _this, const C4Value & Value)
766 {
767 	// dynamic types
768 	if (Value.CheckConversion(C4V_Object)) return C4V_Object;
769 	if (Value.CheckConversion(C4V_Def)) return C4V_Def;
770 	if (Value.CheckConversion(C4V_Effect)) return C4V_Effect;
771 	// static types
772 	return Value.GetType();
773 }
774 
FnCreateArray(C4PropList * _this,int iSize)775 static C4ValueArray * FnCreateArray(C4PropList * _this, int iSize)
776 {
777 	return new C4ValueArray(iSize);
778 }
779 
FnGetLength(C4PropList * _this,const C4Value & Par)780 static int FnGetLength(C4PropList * _this, const C4Value & Par)
781 {
782 	// support GetLength() etc.
783 	C4ValueArray * pArray = Par.getArray();
784 	if (pArray)
785 		return pArray->GetSize();
786 	C4String * pStr = Par.getStr();
787 	if (pStr)
788 		return GetCharacterCount(pStr->GetData().getData());
789 	throw C4AulExecError("GetLength: parameter 0 cannot be converted to string or array");
790 }
791 
FnGetIndexOf(C4PropList * _this,C4ValueArray * pArray,const C4Value & Needle)792 static int FnGetIndexOf(C4PropList * _this, C4ValueArray * pArray, const C4Value & Needle)
793 {
794 	// find first occurance of first parameter in array
795 	// support GetIndexOf(0, x)
796 	if (!pArray) return -1;
797 	int32_t iSize = pArray->GetSize();
798 	for (int32_t i = 0; i < iSize; ++i)
799 		if (Needle.IsIdenticalTo(pArray->GetItem(i)))
800 			// element found
801 			return i;
802 	// element not found
803 	return -1;
804 }
805 
FnDeepEqual(C4PropList * _this,const C4Value & v1,const C4Value & v2)806 static bool FnDeepEqual(C4PropList * _this, const C4Value & v1, const C4Value & v2)
807 {
808 	// return if v1==v2 with deep comparison on arrays and proplists
809 	return v1 == v2;
810 }
811 
FnSetLength(C4PropList * _this,C4ValueArray * pArray,int iNewSize)812 static void FnSetLength(C4PropList * _this, C4ValueArray *pArray, int iNewSize)
813 {
814 	// safety
815 	if (iNewSize<0 || iNewSize > C4ValueArray::MaxSize)
816 		throw C4AulExecError(FormatString("SetLength: invalid array size (%d)", iNewSize).getData());
817 
818 	// set new size
819 	pArray->SetSize(iNewSize);
820 }
821 
FnGetChar(C4PropList * _this,C4String * pString,long iIndex)822 static Nillable<long> FnGetChar(C4PropList * _this, C4String *pString, long iIndex)
823 {
824 	const char *szText = FnStringPar(pString);
825 	if (!szText) return C4Void();
826 	// C4Strings are UTF-8 encoded, so decode to get the indicated character
827 	uint32_t c = GetNextCharacter(&szText);
828 	for (int i = 0; i < iIndex; ++i)
829 	{
830 		c = GetNextCharacter(&szText);
831 		if (!c) return C4Void();
832 	}
833 	return c;
834 }
835 
FnStringToIdentifier(C4PropList * _this,C4String * pString)836 static C4String *FnStringToIdentifier(C4PropList * _this, C4String *pString)
837 {
838 	// Change an arbitrary string so that it becomes an identifier
839 	const char *text = FnStringPar(pString);
840 	if (!text) return nullptr;
841 	StdStrBuf result;
842 	bool had_valid = false, had_invalid = false;
843 	const char *ptext = text, *t0 = text;
844 	uint32_t c = GetNextCharacter(&text);
845 	while (c)
846 	{
847 		if (isalnum(c) || c == '_')
848 		{
849 			// Starting with a digit? Needs to prepend a character
850 			if (isdigit(c) && !had_valid)
851 			{
852 				result.Append("_");
853 				had_invalid = true;
854 			}
855 			// Valid character: Append to result string if a modification had to be done
856 			if (had_invalid) result.Append(ptext, text - ptext);
857 			had_valid = true;
858 		}
859 		else
860 		{
861 			// Invalid character. Make sure result is created from previous valid characters
862 			if (!had_invalid)
863 			{
864 				result.Copy(t0, ptext - t0);
865 				had_invalid = true;
866 			}
867 		}
868 		ptext = text;
869 		c = GetNextCharacter(&text);
870 	}
871 	// Make sure no empty string is returned
872 	if (!had_valid) return ::Strings.RegString("_");
873 	// Return either modified string or the original if no modifications were needed
874 	return had_invalid ? ::Strings.RegString(result) : pString;
875 }
876 
Fneval(C4PropList * _this,C4String * strScript,bool dont_pass_errors)877 static C4Value Fneval(C4PropList * _this, C4String *strScript, bool dont_pass_errors)
878 {
879 	return ::AulExec.DirectExec(_this, FnStringPar(strScript), "eval", !dont_pass_errors);
880 }
881 
FnLocateFunc(C4PropList * _this,C4String * funcname,C4PropList * p)882 static bool FnLocateFunc(C4PropList * _this, C4String *funcname, C4PropList * p)
883 {
884 	// safety
885 	if (!funcname || !funcname->GetCStr())
886 	{
887 		Log("No func name");
888 		return false;
889 	}
890 	if (!p) p = _this;
891 	// get function by name
892 	C4AulFunc *pFunc = p->GetFunc(funcname);
893 	if (!pFunc)
894 	{
895 		LogF("Func %s not found", funcname->GetCStr());
896 	}
897 	else
898 	{
899 		const char *szPrefix = "";
900 		while (pFunc)
901 		{
902 			C4AulScriptFunc *pSFunc = pFunc->SFunc();
903 			if (!pSFunc)
904 			{
905 				LogF("%s%s (engine)", szPrefix, pFunc->GetName());
906 			}
907 			else if (!pSFunc->pOrgScript)
908 			{
909 				LogF("%s%s (no owner)", szPrefix, pSFunc->GetName());
910 			}
911 			else
912 			{
913 				int32_t iLine = SGetLine(pSFunc->pOrgScript->GetScript(), pSFunc->Script);
914 				LogF("%s%s (%s:%d)", szPrefix, pFunc->GetName(), pSFunc->pOrgScript->ScriptName.getData(), (int)iLine);
915 			}
916 			// next func in overload chain
917 			pFunc = pSFunc ? pSFunc->OwnerOverloaded : nullptr;
918 			szPrefix = "overloads ";
919 		}
920 	}
921 	return true;
922 }
923 
FnModulateColor(C4PropList * _this,long iClr1,long iClr2)924 static long FnModulateColor(C4PropList * _this, long iClr1, long iClr2)
925 {
926 	DWORD dwClr1 = iClr1;
927 	DWORD dwClr2 = iClr2;
928 	// default color
929 	if (!dwClr1) dwClr1 = 0xffffff;
930 	// get alpha
931 	long iA1=dwClr1>>24, iA2=dwClr2>>24;
932 	// modulate color values; mod alpha upwards
933 	DWORD r = (((dwClr1     & 0xff) * (dwClr2    &   0xff))    >>  8)   | // blue
934 	          (((dwClr1>> 8 & 0xff) * (dwClr2>>8 &   0xff)) &   0xff00) | // green
935 	          (((dwClr1>>16 & 0xff) * (dwClr2>>8 & 0xff00)) & 0xff0000) | // red
936 	          (std::min<long>(iA1+iA2 - ((iA1*iA2)>>8), 255)           << 24); // alpha
937 	return r;
938 }
939 
FnWildcardMatch(C4PropList * _this,C4String * psString,C4String * psWildcard)940 static long FnWildcardMatch(C4PropList * _this, C4String *psString, C4String *psWildcard)
941 {
942 	return SWildcardMatchEx(FnStringPar(psString), FnStringPar(psWildcard));
943 }
944 
FnFatalError(C4PropList * _this,C4String * pErrorMsg)945 static bool FnFatalError(C4PropList * _this, C4String *pErrorMsg)
946 {
947 	throw C4AulExecError(FormatString("script: %s", pErrorMsg ? pErrorMsg->GetCStr() : "(no error)").getData());
948 }
949 
FnStartCallTrace(C4PropList * _this)950 static bool FnStartCallTrace(C4PropList * _this)
951 {
952 	AulExec.StartTrace();
953 	return true;
954 }
955 
FnStartScriptProfiler(C4PropList * _this,C4Def * pDef)956 static bool FnStartScriptProfiler(C4PropList * _this, C4Def * pDef)
957 {
958 	// get script to profile
959 	C4ScriptHost *pScript;
960 	if (pDef)
961 		pScript = &pDef->Script;
962 	else
963 		pScript = nullptr;
964 	// profile it
965 	C4AulProfiler::StartProfiling(pScript);
966 	return true;
967 }
968 
FnStopScriptProfiler(C4PropList * _this)969 static bool FnStopScriptProfiler(C4PropList * _this)
970 {
971 	C4AulProfiler::StopProfiling();
972 	return true;
973 }
974 
FnGetConstantNameByValue(C4PropList * _this,int value,Nillable<C4String * > name_prefix,int idx)975 static Nillable<C4String *> FnGetConstantNameByValue(C4PropList * _this, int value, Nillable<C4String *> name_prefix, int idx)
976 {
977 	C4String *name_prefix_s = name_prefix;
978 	// find a constant that has the specified value and prefix
979 	for (int32_t i = 0; i < ::ScriptEngine.GlobalConsts.GetAnzItems(); ++i)
980 	{
981 		if (::ScriptEngine.GlobalConsts[i].getInt() == value)
982 		{
983 			const char *const_name = ::ScriptEngine.GlobalConstNames.GetItemUnsafe(i);
984 			if (!name_prefix_s || SEqual2(const_name, name_prefix_s->GetCStr()))
985 				if (!idx--)
986 					// indexed constant found. return name minus prefix
987 					return String(const_name + (name_prefix_s ? name_prefix_s->GetData().getLength() : 0));
988 		}
989 	}
990 	// nothing found (at index)
991 	return C4Void();
992 }
993 
FnReplaceString(C4PropList * _this,C4String * source,C4String * from,C4String * to)994 static Nillable<C4String *> FnReplaceString(C4PropList * _this, C4String *source, C4String *from, C4String *to)
995 {
996 	if (!from) return source;
997 	if (!source) return C4Void();
998 	const char *szto = to ? to->GetCStr() : "";
999 	const char *szfrom = from->GetCStr();
1000 	StdStrBuf s(source->GetData(), true);
1001 	if (s.Replace(szfrom, szto))
1002 	{
1003 		return ::Strings.RegString(s.getData());
1004 	}
1005 	else
1006 	{
1007 		return source;
1008 	}
1009 }
1010 
FnSortArray(C4PropList * _this,C4ValueArray * pArray,bool descending)1011 static bool FnSortArray(C4PropList * _this, C4ValueArray *pArray, bool descending)
1012 {
1013 	if (!pArray) throw C4AulExecError("SortArray: no array given");
1014 	if (pArray->IsFrozen()) throw C4AulExecError("array sort: array is readonly");
1015 	// sort array by its members
1016 	pArray->Sort(descending);
1017 	return true;
1018 }
1019 
FnSortArrayByProperty(C4PropList * _this,C4ValueArray * pArray,C4String * prop_name,bool descending)1020 static bool FnSortArrayByProperty(C4PropList * _this, C4ValueArray *pArray, C4String *prop_name, bool descending)
1021 {
1022 	if (!pArray) throw C4AulExecError("SortArrayByProperty: no array given");
1023 	if (!prop_name) throw C4AulExecError("SortArrayByProperty: no property name given");
1024 	if (pArray->IsFrozen()) throw C4AulExecError("array sort: array is readonly");
1025 	// sort array by property
1026 	if (!pArray->SortByProperty(prop_name, descending)) throw C4AulExecError("SortArrayByProperty: not all array elements are proplists");
1027 	return true;
1028 }
1029 
FnSortArrayByArrayElement(C4PropList * _this,C4ValueArray * pArray,int32_t element_index,bool descending)1030 static bool FnSortArrayByArrayElement(C4PropList * _this, C4ValueArray *pArray, int32_t element_index, bool descending)
1031 {
1032 	if (!pArray) throw C4AulExecError("SortArrayByArrayElement: no array given");
1033 	if (element_index<0) throw C4AulExecError("SortArrayByArrayElement: element index must be >=0");
1034 	if (pArray->IsFrozen()) throw C4AulExecError("array sort: array is readonly");
1035 	// sort array by array element
1036 	if (!pArray->SortByArrayElement(element_index, descending)) throw C4AulExecError("SortArrayByArrayElement: not all array elements are arrays of sufficient length");
1037 	return true;
1038 }
1039 
FnFileWrite(C4PropList * _this,int32_t file_handle,C4String * data)1040 static bool FnFileWrite(C4PropList * _this, int32_t file_handle, C4String *data)
1041 {
1042 	// resolve file handle to user file
1043 	C4AulUserFile *file = ::ScriptEngine.GetUserFile(file_handle);
1044 	if (!file) throw C4AulExecError("FileWrite: invalid file handle");
1045 	// prepare string to write
1046 	if (!data) return false; // write nullptr? No.
1047 	// write it
1048 	file->Write(data->GetCStr(), data->GetData().getLength());
1049 	return true;
1050 }
1051 
1052 //=========================== C4Script Function Map ===================================
1053 
1054 C4ScriptConstDef C4ScriptConstMap[]=
1055 {
1056 	{ "FX_OK"                     ,C4V_Int,      C4Fx_OK                    }, // generic standard behaviour for all effect callbacks
1057 	{ "FX_Effect_Deny"            ,C4V_Int,      C4Fx_Effect_Deny           }, // delete effect
1058 	{ "FX_Effect_Annul"           ,C4V_Int,      C4Fx_Effect_Annul          }, // delete effect, because it has annulled a countereffect
1059 	{ "FX_Effect_AnnulDoCalls"    ,C4V_Int,      C4Fx_Effect_AnnulCalls     }, // delete effect, because it has annulled a countereffect; temp readd countereffect
1060 	{ "FX_Execute_Kill"           ,C4V_Int,      C4Fx_Execute_Kill          }, // execute callback: Remove effect now
1061 	{ "FX_Stop_Deny"              ,C4V_Int,      C4Fx_Stop_Deny             }, // deny effect removal
1062 	{ "FX_Start_Deny"             ,C4V_Int,      C4Fx_Start_Deny            }, // deny effect start
1063 
1064 	{ "FX_Call_Normal"            ,C4V_Int,      C4FxCall_Normal            }, // normal call; effect is being added or removed
1065 	{ "FX_Call_Temp"              ,C4V_Int,      C4FxCall_Temp              }, // temp call; effect is being added or removed in responce to a lower-level effect change
1066 	{ "FX_Call_TempAddForRemoval" ,C4V_Int,      C4FxCall_TempAddForRemoval }, // temp call; effect is being added because it had been temp removed and is now removed forever
1067 	{ "FX_Call_RemoveClear"       ,C4V_Int,      C4FxCall_RemoveClear       }, // effect is being removed because object is being removed
1068 	{ "FX_Call_RemoveDeath"       ,C4V_Int,      C4FxCall_RemoveDeath       }, // effect is being removed because object died - return -1 to avoid removal
1069 	{ "FX_Call_DmgScript"         ,C4V_Int,      C4FxCall_DmgScript         }, // damage through script call
1070 	{ "FX_Call_DmgBlast"          ,C4V_Int,      C4FxCall_DmgBlast          }, // damage through blast
1071 	{ "FX_Call_DmgFire"           ,C4V_Int,      C4FxCall_DmgFire           }, // damage through fire
1072 	{ "FX_Call_DmgChop"           ,C4V_Int,      C4FxCall_DmgChop           }, // damage through chopping
1073 	{ "FX_Call_Energy"            ,C4V_Int,      32                         }, // bitmask for generic energy loss
1074 	{ "FX_Call_EngScript"         ,C4V_Int,      C4FxCall_EngScript         }, // energy loss through script call
1075 	{ "FX_Call_EngBlast"          ,C4V_Int,      C4FxCall_EngBlast          }, // energy loss through blast
1076 	{ "FX_Call_EngObjHit"         ,C4V_Int,      C4FxCall_EngObjHit         }, // energy loss through object hitting the living
1077 	{ "FX_Call_EngFire"           ,C4V_Int,      C4FxCall_EngFire           }, // energy loss through fire
1078 	{ "FX_Call_EngBaseRefresh"    ,C4V_Int,      C4FxCall_EngBaseRefresh    }, // energy reload in base (also by base object, but that's normally not called)
1079 	{ "FX_Call_EngAsphyxiation"   ,C4V_Int,      C4FxCall_EngAsphyxiation   }, // energy loss through asphyxiaction
1080 	{ "FX_Call_EngCorrosion"      ,C4V_Int,      C4FxCall_EngCorrosion      }, // energy loss through corrosion (acid)
1081 	{ "FX_Call_EngGetPunched"     ,C4V_Int,      C4FxCall_EngGetPunched     }, // energy loss from punch
1082 
1083 	{ "Regex_CaseInsensitive"     ,C4V_Int,      Regex_CaseInsensitive      },
1084 	{ "Regex_FirstOnly"           ,C4V_Int,      Regex_FirstOnly            },
1085 
1086 	{ "C4V_Nil",         C4V_Int, C4V_Nil},
1087 	{ "C4V_Int",         C4V_Int, C4V_Int},
1088 	{ "C4V_Bool",        C4V_Int, C4V_Bool},
1089 	{ "C4V_C4Object",    C4V_Int, C4V_Object},
1090 	{ "C4V_Effect",      C4V_Int, C4V_Effect},
1091 	{ "C4V_Def",         C4V_Int, C4V_Def},
1092 	{ "C4V_String",      C4V_Int, C4V_String},
1093 	{ "C4V_Array",       C4V_Int, C4V_Array},
1094 	{ "C4V_Function",    C4V_Int, C4V_Function},
1095 	{ "C4V_PropList",    C4V_Int, C4V_PropList},
1096 
1097 	{ "C4X_Ver1",        C4V_Int, C4XVER1},
1098 	{ "C4X_Ver2",        C4V_Int, C4XVER2},
1099 
1100 	{ nullptr, C4V_Nil, 0}
1101 };
1102 
1103 C4ScriptFnDef C4ScriptFnMap[]=
1104 {
1105 	{ "Call",          true, C4V_Any,    { C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnCall     },
1106 	{ "EffectCall",    true, C4V_Any,    { C4V_Object  ,C4V_PropList,C4V_String  ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnEffectCall    },
1107 	{ "Log",           true, C4V_Bool,   { C4V_String  ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnLog      },
1108 	{ "DebugLog",      true, C4V_Bool,   { C4V_String  ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnDebugLog },
1109 	{ "Format",        true, C4V_String, { C4V_String  ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnFormat   },
1110 	{ "Trans_Mul",     true, C4V_Array,  { C4V_Array   ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any     ,C4V_Any    ,C4V_Any    ,C4V_Any    ,C4V_Any}, FnTrans_Mul},
1111 
1112 	{ nullptr,            false, C4V_Nil,    { C4V_Nil     ,C4V_Nil     ,C4V_Nil     ,C4V_Nil     ,C4V_Nil     ,C4V_Nil     ,C4V_Nil    ,C4V_Nil    ,C4V_Nil    ,C4V_Nil}, nullptr          }
1113 };
1114 
InitCoreFunctionMap(C4AulScriptEngine * pEngine)1115 void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
1116 {
1117 	C4ScriptLibrary::InstantiateAllLibraries(pEngine);
1118 	// add all def constants (all Int)
1119 	for (C4ScriptConstDef *pCDef = &C4ScriptConstMap[0]; pCDef->Identifier; pCDef++)
1120 	{
1121 		assert(pCDef->ValType == C4V_Int); // only int supported currently
1122 		pEngine->RegisterGlobalConstant(pCDef->Identifier, C4VInt(pCDef->Data));
1123 	}
1124 
1125 	C4PropListStatic * p = pEngine->GetPropList();
1126 	// add all def script funcs
1127 	for (C4ScriptFnDef *pDef = &C4ScriptFnMap[0]; pDef->Identifier; pDef++)
1128 		new C4AulDefFunc(p, pDef);
1129 #define F(f) ::AddFunc(p, #f, Fn##f)
1130 	F(ParseInt);
1131 	F(Abs);
1132 	F(Min);
1133 	F(Max);
1134 	F(Sin);
1135 	F(Cos);
1136 	F(Sqrt);
1137 	F(ArcSin);
1138 	F(ArcCos);
1139 	F(BoundBy);
1140 	F(Inside);
1141 	F(Random);
1142 
1143 	F(CreateArray);
1144 	F(CreatePropList);
1145 	F(GetProperties);
1146 	F(GetProperty);
1147 	F(SetProperty);
1148 	F(GetPrototype);
1149 	F(SetPrototype);
1150 	F(ResetProperty);
1151 	F(GetName);
1152 	F(AddEffect);
1153 	F(CreateEffect);
1154 	F(CheckEffect);
1155 	F(RemoveEffect);
1156 	F(GetEffect);
1157 	F(GetEffectCount);
1158 	F(RegexReplace);
1159 	F(RegexSearch);
1160 	F(RegexMatch);
1161 	F(RegexSplit);
1162 	F(Distance);
1163 	F(Angle);
1164 	F(GetChar);
1165 	F(GetType);
1166 	F(ModulateColor);
1167 	F(WildcardMatch);
1168 	F(GetLength);
1169 	F(SetLength);
1170 	F(GetIndexOf);
1171 	F(DeepEqual);
1172 	F(FatalError);
1173 	F(StartCallTrace);
1174 	F(StartScriptProfiler);
1175 	F(StopScriptProfiler);
1176 	F(SortArray);
1177 	F(SortArrayByProperty);
1178 	F(SortArrayByArrayElement);
1179 	F(Trans_Identity);
1180 	F(Trans_Translate);
1181 	F(Trans_Scale);
1182 	F(Trans_Rotate);
1183 	F(LocateFunc);
1184 	F(FileWrite);
1185 	F(eval);
1186 	F(StringToIdentifier);
1187 	F(GetConstantNameByValue);
1188 	F(ReplaceString);
1189 
1190 	::AddFunc(p, "Translate", C4AulExec::FnTranslate);
1191 	::AddFunc(p, "LogCallStack", C4AulExec::FnLogCallStack);
1192 #undef F
1193 }
1194