1/*
2 * LegacyClonk
3 *
4 * Copyright (C) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017, The OpenClonk Team and contributors
6 * Copyright (c) 2017-2022, The LegacyClonk 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/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
19
20/* Functions mapped by C4Script */
21
22#include <C4Include.h>
23#include <C4Script.h>
24#include <C4Version.h>
25
26#include <C4Application.h>
27#include <C4Object.h>
28#include <C4ObjectInfo.h>
29#include <C4ObjectCom.h>
30#include <C4Random.h>
31#include <C4Command.h>
32#include <C4Console.h>
33#include <C4Viewport.h>
34#include <C4Wrappers.h>
35#include <C4ObjectInfoList.h>
36#include <C4Player.h>
37#include <C4ObjectMenu.h>
38#include <C4ValueHash.h>
39#include <C4NetworkRestartInfos.h>
40#include <C4SoundSystem.h>
41
42#include <array>
43#include <cinttypes>
44#include <numbers>
45#include <optional>
46#include <type_traits>
47#include <utility>
48
49#ifndef _WIN32
50#include <sys/time.h>
51#endif
52
53// Some Support Functions
54
55static void Warn(C4Object *const obj, const std::string_view message)
56{
57 C4AulExecError{obj, message}.show();
58}
59
60template<typename... Args>
61static void StrictError(C4AulContext *const context, C4AulScriptStrict errorSince, const std::format_string<Args...> message, Args &&... args)
62{
63 const auto strictness = context->Caller ? context->Caller->Func->Owner->Strict : C4AulScriptStrict::NONSTRICT;
64
65 const std::string result{std::format(message, std::forward<Args>(args)...)};
66 if (strictness < errorSince)
67 {
68 Warn(obj: context->Obj, message: result);
69 }
70 else
71 {
72 throw C4AulExecError{context->Obj, result};
73 }
74}
75
76const C4ValueInt MaxFnStringParLen = 500;
77
78inline const static char *FnStringPar(const C4String *const pString)
79{
80 return pString ? pString->Data.getData() : "";
81}
82
83inline C4String *String(const char *str)
84{
85 return str ? new C4String((str), &Game.ScriptEngine.Strings) : nullptr;
86}
87
88inline C4String *String(StdStrBuf &&str)
89{
90 return str ? new C4String(std::forward<StdStrBuf>(t&: str), &Game.ScriptEngine.Strings) : nullptr;
91}
92
93static std::string FnStringFormat(C4AulContext *cthr, const char *szFormatPar, C4Value *Par0 = nullptr, C4Value *Par1 = nullptr, C4Value *Par2 = nullptr, C4Value *Par3 = nullptr,
94 C4Value *Par4 = nullptr, C4Value *Par5 = nullptr, C4Value *Par6 = nullptr, C4Value *Par7 = nullptr, C4Value *Par8 = nullptr, C4Value *Par9 = nullptr)
95{
96 C4Value *Par[11];
97 Par[0] = Par0; Par[1] = Par1; Par[2] = Par2; Par[3] = Par3; Par[4] = Par4;
98 Par[5] = Par5; Par[6] = Par6; Par[7] = Par7; Par[8] = Par8; Par[9] = Par9;
99 Par[10] = nullptr;
100 int cPar = 0;
101
102 std::string stringBuf;
103 const char *cpFormat = szFormatPar;
104 const char *cpType;
105 char szField[MaxFnStringParLen + 1];
106 while (*cpFormat)
107 {
108 // Copy normal stuff
109 while (*cpFormat && (*cpFormat != '%'))
110 stringBuf += *cpFormat++;
111 // Field
112 if (*cpFormat == '%')
113 {
114 // Scan field type
115 for (cpType = cpFormat + 1; *cpType && (*cpType == '.' || Inside(ival: *cpType, lbound: '0', rbound: '9')); cpType++);
116 // Copy field
117 SCopy(szSource: cpFormat, sTarget: szField, iMaxL: std::min<unsigned int>(a: sizeof(szField) - 1, b: cpType - cpFormat + 1));
118 // Insert field by type
119 switch (*cpType)
120 {
121 // number
122 case 'd': case 'x': case 'X': case 'c':
123 {
124 if (!Par[cPar]) throw C4AulExecError(cthr->Obj, "format placeholder without parameter");
125 stringBuf += fmt::sprintf(fmt: szField, args: Par[cPar++]->getInt());
126 cpFormat += SLen(sptr: szField);
127 break;
128 }
129 // C4ID
130 case 'i':
131 {
132 if (!Par[cPar]) throw C4AulExecError(cthr->Obj, "format placeholder without parameter");
133 C4ID id = Par[cPar++]->getC4ID();
134 stringBuf += C4IdText(id);
135 cpFormat += SLen(sptr: szField);
136 break;
137 }
138 // C4Value
139 case 'v':
140 {
141 if (!Par[cPar]) throw C4AulExecError(cthr->Obj, "format placeholder without parameter");
142 if (!Par[cPar]->_getRaw() && !cthr->CalledWithStrictNil())
143 {
144 stringBuf += '0';
145 }
146 else
147 {
148 stringBuf += Par[cPar++]->GetDataString();
149 }
150 cpFormat += SLen(sptr: szField);
151 break;
152 }
153 // String
154 case 's':
155 {
156 // get string
157 if (!Par[cPar]) throw C4AulExecError(cthr->Obj, "format placeholder without parameter");
158 const char *szStr = "(null)";
159 if (Par[cPar]->GetData())
160 {
161 C4String *pStr = Par[cPar++]->getStr();
162 if (!pStr) throw C4AulExecError(cthr->Obj, "string format placeholder without string");
163 szStr = pStr->Data.getData();
164 }
165 stringBuf += fmt::sprintf(fmt: szField, args: szStr);
166 cpFormat += SLen(sptr: szField);
167 break;
168 }
169 case '%':
170 stringBuf += '%';
171 cpFormat += SLen(sptr: szField);
172 break;
173 // Undefined / Empty
174 default:
175 stringBuf += '%';
176 cpFormat++;
177 break;
178 }
179 }
180 }
181 return stringBuf;
182}
183
184bool CheckEnergyNeedChain(C4Object *pObj, C4ObjectList &rEnergyChainChecked)
185{
186 if (!pObj) return false;
187
188 // No recursion, flag check
189 if (rEnergyChainChecked.GetLink(pObj)) return false;
190 rEnergyChainChecked.Add(nObj: pObj, eSort: C4ObjectList::stNone);
191
192 // This object needs energy
193 if (pObj->Def->LineConnect & C4D_Power_Consumer)
194 if (pObj->NeedEnergy)
195 return true;
196
197 // Check all power line connected structures
198 C4Object *cline; C4ObjectLink *clnk;
199 for (clnk = Game.Objects.First; clnk && (cline = clnk->Obj); clnk = clnk->Next)
200 if (cline->Status) if (cline->Def->id == C4ID_PowerLine)
201 if (cline->Action.Target == pObj)
202 if (CheckEnergyNeedChain(pObj: cline->Action.Target2, rEnergyChainChecked))
203 return true;
204
205 return false;
206}
207
208uint32_t StringBitEval(const char *str)
209{
210 uint32_t rval = 0;
211 for (int cpos = 0; str && str[cpos]; cpos++)
212 if ((str[cpos] != '_') && (str[cpos] != ' '))
213 rval += 1 << cpos;
214 return rval;
215}
216
217// C4Script Functions
218
219static C4Object *Fn_this(C4AulContext *cthr)
220{
221 return cthr->Obj;
222}
223
224static C4ValueInt Fn_goto(C4AulContext *cthr, C4ValueInt iCounter)
225{
226 Game.Script.Counter = iCounter;
227 return iCounter;
228}
229
230static bool FnChangeDef(C4AulContext *cthr, C4ID to_id, C4Object *pObj)
231{
232 if (!pObj) pObj = cthr->Obj;
233 if (!pObj) return false;
234 return pObj->ChangeDef(idNew: to_id);
235}
236
237static bool FnExplode(C4AulContext *cthr, C4ValueInt iLevel, C4Object *pObj, C4ID idEffect, C4String *szEffect)
238{
239 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
240 pObj->Explode(iLevel, idEffect, szEffect: FnStringPar(pString: szEffect));
241 return true;
242}
243
244static bool FnIncinerate(C4AulContext *cthr, C4Object *pObj)
245{
246 if (!pObj) pObj = cthr->Obj;
247 if (!pObj) return false;
248 C4ValueInt iCausedBy = NO_OWNER;
249 if (cthr->Obj) iCausedBy = cthr->Obj->Controller;
250 return pObj->Incinerate(iCausedBy);
251}
252
253static bool FnIncinerateLandscape(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY)
254{
255 if (cthr->Obj)
256 {
257 iX += cthr->Obj->x;
258 iY += cthr->Obj->y;
259 }
260 return Game.Landscape.Incinerate(x: iX, y: iY);
261}
262
263static bool FnExtinguish(C4AulContext *cthr, C4Object *pObj)
264{
265 if (!pObj) pObj = cthr->Obj;
266 if (!pObj) return false;
267 // extinguish all fires
268 return pObj->Extinguish(iFireNumber: 0);
269}
270
271static bool FnSetSolidMask(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4ValueInt iTX, C4ValueInt iTY, C4Object *pObj)
272{
273 if (!pObj) pObj = cthr->Obj;
274 if (!pObj) return false;
275 pObj->SetSolidMask(iX, iY, iWdt, iHgt, iTX, iTY);
276 return true;
277}
278
279static void FnSetGravity(C4AulContext *cthr, C4ValueInt iGravity)
280{
281 Game.Landscape.Gravity = itofix(x: BoundBy<C4ValueInt>(bval: iGravity, lbound: -300, rbound: 300)) / 500;
282}
283
284static C4ValueInt FnGetGravity(C4AulContext *cthr)
285{
286 return fixtoi(x: Game.Landscape.Gravity * 500);
287}
288
289template<int N>
290static void DeathAnnounceMessageHelper(C4Object *const obj, const int n)
291{
292 if (N == n)
293 {
294 GameMsgObject(szText: LoadResStr(id: static_cast<C4ResStrTableKey>(std::to_underlying(value: C4ResStrTableKey::IDS_OBJ_DEATH1) + N), args: obj->GetName()).c_str(), pTarget: obj);
295 }
296 else if constexpr (N > 0)
297 {
298 DeathAnnounceMessageHelper<N - 1>(obj, n);
299 }
300}
301
302static bool FnDeathAnnounce(C4AulContext *cthr)
303{
304 static constexpr int MaxDeathMsg{7};
305 if (!cthr->Obj) return false;
306 if (Game.C4S.Head.Film) return true;
307 // Check if crew member has an own death message
308 if (cthr->Obj->Info && *(cthr->Obj->Info->DeathMessage))
309 {
310 GameMsgObject(szText: cthr->Obj->Info->DeathMessage, pTarget: cthr->Obj);
311 }
312 else
313 {
314 DeathAnnounceMessageHelper<MaxDeathMsg - 1>(obj: cthr->Obj, n: SafeRandom(range: MaxDeathMsg));
315 }
316 return true;
317}
318
319static bool FnGrabContents(C4AulContext *cthr, C4Object *from, C4Object *pTo)
320{
321 if (!pTo && !(pTo = cthr->Obj)) return false;
322 if (!from) return false;
323 if (pTo == from) return false;
324 pTo->GrabContents(pFrom: from);
325 return true;
326}
327
328static bool FnPunch(C4AulContext *cthr, C4Object *target, C4ValueInt punch)
329{
330 if (!cthr->Obj) return false;
331 return ObjectComPunch(cObj: cthr->Obj, pTarget: target, iPunch: punch);
332}
333
334static bool FnKill(C4AulContext *cthr, C4Object *pObj, bool fForced)
335{
336 if (!pObj) pObj = cthr->Obj;
337 if (!pObj) return false;
338 if (!pObj->GetAlive()) return false;
339 // Trace kills by player-owned objects
340 // Do not trace for NO_OWNER, because that would include e.g. the Suicide-rule
341 if (cthr->Obj && ValidPlr(plr: cthr->Obj->Controller)) pObj->UpdatLastEnergyLossCause(iNewCausePlr: cthr->Obj->Controller);
342 // Do the kill
343 pObj->AssignDeath(fForced);
344 return true;
345}
346
347static bool FnFling(C4AulContext *cthr, C4Object *pObj, C4ValueInt iXDir, C4ValueInt iYDir, C4ValueInt iPrec, bool fAddSpeed)
348{
349 if (!pObj) return false;
350 if (!iPrec) iPrec = 1;
351 pObj->Fling(txdir: itofix(x: iXDir, prec: iPrec), tydir: itofix(x: iYDir, prec: iPrec), fAddSpeed, byPlayer: cthr->Obj ? cthr->Obj->Controller : NO_OWNER);
352 // unstick from ground, because Fling command may be issued in an Action-callback,
353 // where attach-values have already been determined for that frame
354 pObj->Action.t_attach = 0;
355 return true;
356}
357
358static bool FnJump(C4AulContext *cthr, C4Object *pObj)
359{
360 if (!pObj) pObj = cthr->Obj;
361 if (!pObj) return false;
362 return ObjectComJump(cObj: pObj);
363}
364
365static bool FnEnter(C4AulContext *cthr, C4Object *pTarget, C4Object *pObj)
366{
367 if (!pObj) pObj = cthr->Obj;
368 if (!pObj) return false;
369 return pObj->Enter(pTarget);
370}
371
372static bool FnExit(C4AulContext *cthr, C4Object *pObj, C4ValueInt tx, C4ValueInt ty, C4ValueInt tr, C4ValueInt txdir, C4ValueInt tydir, C4ValueInt trdir)
373{
374 if (!pObj) pObj = cthr->Obj;
375 if (!pObj) return false;
376 if (cthr->Obj)
377 {
378 tx += cthr->Obj->x;
379 ty += cthr->Obj->y;
380 }
381 if (tr == -1) tr = Random(iRange: 360);
382 ObjectComCancelAttach(cObj: pObj);
383 return pObj->Exit(iX: tx,
384 iY: ty + pObj->Shape.y,
385 iR: tr,
386 iXDir: itofix(x: txdir), iYDir: itofix(x: tydir),
387 iRDir: itofix(x: trdir) / 10);
388}
389
390static bool FnCollect(C4AulContext *cthr, C4Object *pItem, C4Object * pCollector)
391{
392 // local call / safety
393 if (!pCollector) pCollector = cthr->Obj;
394 if (!pItem || !pCollector) return false;
395 // Script function Collect ignores NoCollectDelay
396 int32_t iOldNoCollectDelay = pCollector->NoCollectDelay;
397 if (iOldNoCollectDelay)
398 {
399 pCollector->NoCollectDelay = 0;
400 pCollector->UpdateOCF();
401 }
402
403 bool success = false;
404 // check OCF of collector (MaxCarry)
405 if (pCollector->OCF & OCF_Collection)
406 // collect
407 success = pCollector->Collect(pObj: pItem);
408 // restore NoCollectDelay
409 if (iOldNoCollectDelay > pCollector->NoCollectDelay) pCollector->NoCollectDelay = iOldNoCollectDelay;
410 // failure
411 return success;
412}
413
414static bool FnSplit2Components(C4AulContext *cthr, C4Object *pObj)
415{
416 C4Object *pThing, *pNew, *pContainer;
417 size_t cnt, cnt2;
418 // Pointer required
419 if (!pObj) pObj = cthr->Obj;
420 if (!pObj) return false;
421 // Store container
422 pContainer = pObj->Contained;
423 // Contents: exit / transfer to container
424 while (pThing = pObj->Contents.GetObject())
425 if (pContainer) pThing->Enter(pTarget: pContainer);
426 else pThing->Exit(iX: pThing->x, iY: pThing->y);
427 // Destroy the object, create its components
428 C4IDList ObjComponents;
429 pObj->Def->GetComponents(pOutList: &ObjComponents, pObjInstance: pObj, pBuilder: cthr->Obj);
430 if (pObj->Contained) pObj->Exit(iX: pObj->x, iY: pObj->y);
431 for (cnt = 0; ObjComponents.GetID(index: cnt); cnt++)
432 {
433 for (cnt2 = 0; cnt2 < ObjComponents.GetCount(index: cnt); cnt2++)
434 {
435 // force argument evaluation order
436 const auto r4 = itofix(x: Rnd3());
437 const auto r3 = itofix(x: Rnd3());
438 const auto r2 = itofix(x: Rnd3());
439 const auto r1 = Random(iRange: 360);
440 if (pNew = Game.CreateObject(type: ObjComponents.GetID(index: cnt),
441 pCreator: pObj,
442 owner: pObj->Owner,
443 x: pObj->x, y: pObj->y,
444 r: r1, xdir: r2, ydir: r3, rdir: r4))
445 {
446 if (pObj->GetOnFire()) pNew->Incinerate(iCausedBy: pObj->Owner);
447 if (pContainer) pNew->Enter(pTarget: pContainer);
448 }
449 }
450 }
451 pObj->AssignRemoval();
452 return true;
453}
454
455static bool FnRemoveObject(C4AulContext *cthr, C4Object *pObj, bool fEjectContents)
456{
457 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
458 pObj->AssignRemoval(fExitContents: fEjectContents);
459 return true;
460}
461
462static bool FnSetPosition(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4Object *pObj, bool fCheckBounds)
463{
464 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
465
466 if (fCheckBounds)
467 {
468 // BoundsCheck takes ref to int32_t and not to C4ValueInt
469 int32_t i_x = iX, i_y = iY;
470 pObj->BoundsCheck(ctcox&: i_x, ctcoy&: i_y);
471 iX = i_x; iY = i_y;
472 }
473 pObj->ForcePosition(tx: iX, ty: iY);
474 // update liquid
475 pObj->UpdateInLiquid();
476 return true;
477}
478
479static bool FnDoCon(C4AulContext *cthr, C4ValueInt iChange, C4Object *pObj) // in percent
480{
481 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
482 pObj->DoCon(iChange: FullCon * iChange / 100);
483 return true;
484}
485
486static C4ValueInt FnGetCon(C4AulContext *cthr, C4Object *pObj) // in percent
487{
488 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
489 return 100 * pObj->GetCon() / FullCon;
490}
491
492static bool FnDoEnergy(C4AulContext *cthr, C4ValueInt iChange, C4Object *pObj, bool fExact, C4ValueInt iEngType, C4ValueInt iCausedByPlusOne)
493{
494 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
495 if (!iEngType) iEngType = C4FxCall_EngScript;
496 C4ValueInt iCausedBy = iCausedByPlusOne - 1; if (!iCausedByPlusOne && cthr->Obj) iCausedBy = cthr->Obj->Controller;
497 pObj->DoEnergy(iChange, fExact: !!fExact, iCause: iEngType, iCausedByPlr: iCausedBy);
498 return true;
499}
500
501static bool FnDoBreath(C4AulContext *cthr, C4ValueInt iChange, C4Object *pObj)
502{
503 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
504 pObj->DoBreath(iChange);
505 return true;
506}
507
508static bool FnDoDamage(C4AulContext *cthr, C4ValueInt iChange, C4Object *pObj, C4ValueInt iDmgType, C4ValueInt iCausedByPlusOne)
509{
510 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
511 C4ValueInt iCausedBy = iCausedByPlusOne - 1; if (!iCausedByPlusOne && cthr->Obj) iCausedBy = cthr->Obj->Controller;
512 if (!iDmgType) iDmgType = C4FxCall_DmgScript;
513 pObj->DoDamage(iLevel: iChange, iCausedByPlr: iCausedBy, iCause: iDmgType);
514 return true;
515}
516
517static bool FnDoMagicEnergy(C4AulContext *cthr, C4ValueInt iChange, C4Object *pObj, bool fAllowPartial)
518{
519 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
520 // Physical modification factor
521 iChange *= MagicPhysicalFactor;
522 // Maximum load
523 if (iChange > 0)
524 if (pObj->MagicEnergy + iChange > pObj->GetPhysical()->Magic)
525 {
526 if (!fAllowPartial) return false;
527 iChange = pObj->GetPhysical()->Magic - pObj->MagicEnergy;
528 if (!iChange) return false;
529 // partial change to max allowed
530 }
531 // Insufficient load
532 if (iChange < 0)
533 if (pObj->MagicEnergy + iChange < 0)
534 {
535 if (!fAllowPartial) return false;
536 iChange = -pObj->MagicEnergy;
537 if (!iChange) return false;
538 // partial change to zero allowed
539 }
540 // Change energy level
541 pObj->MagicEnergy = BoundBy<C4ValueInt>(bval: pObj->MagicEnergy + iChange, lbound: 0, rbound: pObj->GetPhysical()->Magic);
542 pObj->ViewEnergy = C4ViewDelay;
543 return true;
544}
545
546static C4ValueInt FnGetMagicEnergy(C4AulContext *cthr, C4Object *pObj)
547{
548 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
549 return pObj->MagicEnergy / MagicPhysicalFactor;
550}
551
552const int32_t PHYS_Current = 0,
553 PHYS_Permanent = 1,
554 PHYS_Temporary = 2,
555 PHYS_StackTemporary = 3;
556
557static bool FnSetPhysical(C4AulContext *cthr, C4String *szPhysical, C4ValueInt iValue, C4ValueInt iMode, C4Object *pObj)
558{
559 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
560 // Get physical offset
561 C4PhysicalInfo::Offset off;
562 if (!C4PhysicalInfo::GetOffsetByName(szPhysicalName: FnStringPar(pString: szPhysical), pmpiOut: &off)) return false;
563 // Set by mode
564 switch (iMode)
565 {
566 // Currently active physical
567 case PHYS_Current:
568 // Info objects or temporary mode only
569 if (!pObj->PhysicalTemporary) if (!pObj->Info || Game.Parameters.UseFairCrew) return false;
570 // Set physical
571 pObj->GetPhysical()->*off = iValue;
572 return true;
573 // Permanent physical
574 case PHYS_Permanent:
575 // Info objects only
576 if (!pObj->Info) return false;
577 // In fair crew mode, changing the permanent physicals is only allowed via TrainPhysical
578 // Otherwise, stuff like SetPhysical(..., GetPhysical(...)+1, ...) would screw up the crew in fair crew mode
579 if (Game.Parameters.UseFairCrew) return false;
580 // Set physical
581 pObj->Info->Physical.*off = iValue;
582 return true;
583 // Temporary physical
584 case PHYS_Temporary:
585 case PHYS_StackTemporary:
586 // Automatically switch to temporary mode
587 if (!pObj->PhysicalTemporary)
588 {
589 pObj->TemporaryPhysical = *(pObj->GetPhysical());
590 pObj->PhysicalTemporary = true;
591 }
592 // if old value is to be remembered, register the change
593 if (iMode == PHYS_StackTemporary)
594 pObj->TemporaryPhysical.RegisterChange(mpiOffset: off);
595 // Set physical
596 pObj->TemporaryPhysical.*off = iValue;
597 return true;
598 }
599 // Invalid mode
600 return false;
601}
602
603static bool FnTrainPhysical(C4AulContext *cthr, C4String *szPhysical, C4ValueInt iTrainBy, C4ValueInt iMaxTrain, C4Object *pObj)
604{
605 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
606 // Get physical offset
607 C4PhysicalInfo::Offset off;
608 if (!C4PhysicalInfo::GetOffsetByName(szPhysicalName: FnStringPar(pString: szPhysical), pmpiOut: &off)) return false;
609 // train it
610 return !!pObj->TrainPhysical(mpiOffset: off, iTrainBy, iMaxTrain);
611}
612
613static bool FnResetPhysical(C4AulContext *cthr, C4Object *pObj, C4String *sPhysical)
614{
615 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
616 const char *szPhysical = FnStringPar(pString: sPhysical);
617
618 // Reset to permanent physical
619 if (!pObj->PhysicalTemporary) return false;
620
621 // reset specified physical only?
622 if (szPhysical && *szPhysical)
623 {
624 C4PhysicalInfo::Offset off;
625 if (!C4PhysicalInfo::GetOffsetByName(szPhysicalName: szPhysical, pmpiOut: &off)) return false;
626 if (!pObj->TemporaryPhysical.ResetPhysical(mpiOffset: off)) return false;
627 // if other physical changes remain, do not reset complete physicals
628 if (pObj->TemporaryPhysical.HasChanges(pRefPhysical: pObj->GetPhysical(fPermanent: true))) return true;
629 }
630
631 // actual reset of temp physicals
632 pObj->PhysicalTemporary = false;
633 pObj->TemporaryPhysical.Default();
634
635 return true;
636}
637
638static std::optional<C4ValueInt> FnGetPhysical(C4AulContext *cthr, C4String *szPhysical, C4ValueInt iMode, C4Object *pObj, C4ID idDef)
639{
640 // Get physical offset
641 C4PhysicalInfo::Offset off;
642 if (!C4PhysicalInfo::GetOffsetByName(szPhysicalName: FnStringPar(pString: szPhysical), pmpiOut: &off)) return {};
643 // no object given?
644 if (!pObj)
645 {
646 // def given?
647 if (idDef)
648 {
649 // get def
650 C4Def *pDef = Game.Defs.ID2Def(id: idDef); if (!pDef) return {};
651 // return physical value
652 return {pDef->Physical.*off};
653 }
654 // local call?
655 pObj = cthr->Obj; if (!pObj) return {};
656 }
657
658 // Get by mode
659 switch (iMode)
660 {
661 // Currently active physical
662 case PHYS_Current:
663 // Get physical
664 return {pObj->GetPhysical()->*off};
665 // Permanent physical
666 case PHYS_Permanent:
667 // Info objects only
668 if (!pObj->Info) return {};
669 // In fair crew mode, scripts may not read permanent physical values - fallback to fair def physical instead!
670 if (Game.Parameters.UseFairCrew)
671 if (pObj->Info->pDef)
672 return {pObj->Info->pDef->GetFairCrewPhysicals()->*off};
673 else
674 return {pObj->Def->GetFairCrewPhysicals()->*off};
675 // Get physical
676 return {pObj->Info->Physical.*off};
677 // Temporary physical
678 case PHYS_Temporary:
679 // Info objects only
680 if (!pObj->Info) return {};
681 // Only if in temporary mode
682 if (!pObj->PhysicalTemporary) return {};
683 // Get physical
684 return {pObj->TemporaryPhysical.*off};
685 }
686 // Invalid mode
687 return {};
688}
689
690static bool FnSetEntrance(C4AulContext *cthr, bool enabled, C4Object *pObj)
691{
692 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
693 pObj->EntranceStatus = enabled;
694 return true;
695}
696
697static bool FnSetXDir(C4AulContext *cthr, C4ValueInt nxdir, C4Object *pObj, C4ValueInt iPrec)
698{
699 // safety
700 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
701 // precision (default 10.0)
702 if (!iPrec) iPrec = 10;
703 // update xdir
704 pObj->xdir = itofix(x: nxdir, prec: iPrec);
705 pObj->Mobile = 1;
706 // success
707 return true;
708}
709
710static bool FnSetRDir(C4AulContext *cthr, C4ValueInt nrdir, C4Object *pObj, C4ValueInt iPrec)
711{
712 // safety
713 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
714 // precision (default 10.0)
715 if (!iPrec) iPrec = 10;
716 // update rdir
717 pObj->rdir = itofix(x: nrdir, prec: iPrec);
718 pObj->Mobile = 1;
719 // success
720 return true;
721}
722
723static bool FnSetYDir(C4AulContext *cthr, C4ValueInt nydir, C4Object *pObj, C4ValueInt iPrec)
724{
725 // safety
726 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
727 // precision (default 10.0)
728 if (!iPrec) iPrec = 10;
729 // update ydir
730 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
731 pObj->ydir = itofix(x: nydir, prec: iPrec);
732 pObj->Mobile = 1;
733 // success
734 return true;
735}
736
737static bool FnSetR(C4AulContext *cthr, C4ValueInt nr, C4Object *pObj)
738{
739 // safety
740 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
741 // set rotation
742 pObj->SetRotation(nr);
743 // success
744 return true;
745}
746
747static bool FnSetAction(C4AulContext *cthr, C4String *szAction,
748 C4Object *pTarget, C4Object *pTarget2, bool fDirect)
749{
750 if (!cthr->Obj) return false;
751 if (!szAction) return false;
752 return !!cthr->Obj->SetActionByName(szActName: FnStringPar(pString: szAction), pTarget, pTarget2,
753 iCalls: C4Object::SAC_StartCall | C4Object::SAC_AbortCall, fForce: !!fDirect);
754}
755
756static bool FnSetBridgeActionData(C4AulContext *cthr, C4ValueInt iBridgeLength, bool fMoveClonk, bool fWall, C4ValueInt iBridgeMaterial, C4Object *pObj)
757{
758 if (!pObj) pObj = cthr->Obj; if (!pObj || !pObj->Status) return false;
759 // action must be BRIDGE
760 if (pObj->Action.Act <= ActIdle) return false;
761 if (pObj->Def->ActMap[pObj->Action.Act].Procedure != DFA_BRIDGE) return false;
762 // set data
763 pObj->Action.SetBridgeData(iBridgeTime: iBridgeLength, fMoveClonk, fWall, iBridgeMaterial);
764 return true;
765}
766
767static bool FnSetActionData(C4AulContext *cthr, C4ValueInt iData, C4Object *pObj)
768{
769 if (!pObj) pObj = cthr->Obj; if (!pObj || !pObj->Status) return false;
770 // bridge: Convert from old style
771 if ((pObj->Action.Act > ActIdle) && (pObj->Def->ActMap[pObj->Action.Act].Procedure == DFA_BRIDGE))
772 return FnSetBridgeActionData(cthr, iBridgeLength: 0, fMoveClonk: false, fWall: false, iBridgeMaterial: iData, pObj);
773 // attach: check for valid vertex indices
774 if ((pObj->Action.Act > ActIdle) && (pObj->Def->ActMap[pObj->Action.Act].Procedure == DFA_ATTACH)) // Fixed Action.Act check here... matthes
775 if (((iData & 255) >= C4D_MaxVertex) || ((iData >> 8) >= C4D_MaxVertex))
776 return false;
777 // set data
778 pObj->Action.Data = iData;
779 return true;
780}
781
782static bool FnObjectSetAction(C4AulContext *cthr, C4Object *pObj, C4String *szAction,
783 C4Object *pTarget, C4Object *pTarget2, bool fDirect)
784{
785 if (!szAction || !pObj) return false;
786 // regular action change
787 return !!pObj->SetActionByName(szActName: FnStringPar(pString: szAction), pTarget, pTarget2,
788 iCalls: C4Object::SAC_StartCall | C4Object::SAC_AbortCall, fForce: !!fDirect);
789}
790
791static bool FnSetComDir(C4AulContext *cthr, C4ValueInt ncomdir, C4Object *pObj)
792{
793 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
794 pObj->Action.ComDir = ncomdir;
795 return true;
796}
797
798static bool FnSetDir(C4AulContext *cthr, C4ValueInt ndir, C4Object *pObj)
799{
800 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
801 pObj->SetDir(ndir);
802 return true;
803}
804
805static bool FnSetCategory(C4AulContext *cthr, C4ValueInt iCategory, C4Object *pObj)
806{
807 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
808 if (!(iCategory & C4D_SortLimit)) iCategory |= (pObj->Category & C4D_SortLimit);
809 pObj->SetCategory(iCategory);
810 return true;
811}
812
813static bool FnSetAlive(C4AulContext *cthr, bool nalv, C4Object *pObj)
814{
815 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
816 pObj->SetAlive(nalv);
817 return true;
818}
819
820static bool FnSetOwner(C4AulContext *cthr, C4ValueInt iOwner, C4Object *pObj)
821{
822 // Object safety
823 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
824 // Set owner
825 return !!pObj->SetOwner(iOwner);
826}
827
828static bool FnSetPhase(C4AulContext *cthr, C4ValueInt iVal, C4Object *pObj)
829{
830 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
831 return !!pObj->SetPhase(iVal);
832}
833
834static bool FnExecuteCommand(C4AulContext *cthr, C4Object *pObj)
835{
836 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
837 return !!pObj->ExecuteCommand();
838}
839
840static bool FnSetCommand(C4AulContext *cthr, C4Object *pObj, C4String *szCommand, C4Object *pTarget, C4Value Tx, C4ValueInt iTy, C4Object *pTarget2, C4Value data, C4ValueInt iRetries)
841{
842 // Object
843 if (!pObj) pObj = cthr->Obj;
844 if (!pObj || !szCommand) return false;
845 // Command
846 C4ValueInt iCommand = CommandByName(szCommand: FnStringPar(pString: szCommand));
847 if (!iCommand)
848 {
849 pObj->ClearCommands();
850 return false;
851 }
852 // Special: convert iData to szText
853 const char *szText = nullptr;
854 int32_t iData = 0;
855 if (iCommand == C4CMD_Call)
856 {
857 szText = FnStringPar(pString: data.getStr());
858 }
859 else
860 {
861 iData = data.getIntOrID();
862 Tx.ConvertTo(vtToType: C4V_Int);
863 }
864 // Set
865 pObj->SetCommand(iCommand, pTarget, iTx: Tx, iTy, pTarget2, fControl: false, iData, iRetries, szText);
866 // Success
867 return true;
868}
869
870static bool FnAddCommand(C4AulContext *cthr, C4Object *pObj, C4String *szCommand, C4Object *pTarget, C4Value Tx, C4ValueInt iTy, C4Object *pTarget2, C4ValueInt iUpdateInterval, C4Value data, C4ValueInt iRetries, C4ValueInt iBaseMode)
871{
872 // Object
873 if (!pObj) pObj = cthr->Obj;
874 if (!pObj || !szCommand) return false;
875 // Command
876 C4ValueInt iCommand = CommandByName(szCommand: FnStringPar(pString: szCommand));
877 if (!iCommand) return false;
878 // Special: convert iData to szText
879 const char *szText = nullptr;
880 int32_t iData = 0;
881 if (iCommand == C4CMD_Call)
882 {
883 szText = FnStringPar(pString: data.getStr());
884 }
885 else
886 {
887 iData = data.getIntOrID();
888 Tx.ConvertTo(vtToType: C4V_Int);
889 }
890 // Add
891 return pObj->AddCommand(iCommand, pTarget, iTx: Tx, iTy, iUpdateInterval, pTarget2, fInitEvaluation: true, iData, fAppend: false, iRetries, szText, iBaseMode);
892}
893
894static bool FnAppendCommand(C4AulContext *cthr, C4Object *pObj, C4String *szCommand, C4Object *pTarget, C4Value Tx, C4ValueInt iTy, C4Object *pTarget2, C4ValueInt iUpdateInterval, C4Value Data, C4ValueInt iRetries, C4ValueInt iBaseMode)
895{
896 // Object
897 if (!pObj) pObj = cthr->Obj;
898 if (!pObj || !szCommand) return false;
899 // Command
900 C4ValueInt iCommand = CommandByName(szCommand: FnStringPar(pString: szCommand));
901 if (!iCommand) return false;
902 // Special: convert iData to szText
903 const char *szText = nullptr;
904 int32_t iData = 0;
905 if (iCommand == C4CMD_Call)
906 {
907 szText = FnStringPar(pString: Data.getStr());
908 }
909 else
910 {
911 iData = Data.getIntOrID();
912 Tx.ConvertTo(vtToType: C4V_Int);
913 }
914 // Add
915 return pObj->AddCommand(iCommand, pTarget, iTx: Tx, iTy, iUpdateInterval, pTarget2, fInitEvaluation: true, iData, fAppend: true, iRetries, szText, iBaseMode);
916}
917
918static C4Value FnGetCommand(C4AulContext *cthr, C4Object *pObj, C4ValueInt iElement, C4ValueInt iCommandNum)
919{
920 if (!pObj) pObj = cthr->Obj;
921 if (!pObj) return C4VNull;
922 C4Command *Command = pObj->Command;
923 // Move through list to Command iCommandNum
924 while (Command && iCommandNum--) Command = Command->Next;
925 // Object has no command or iCommandNum was to high or < 0
926 if (!Command) return C4VNull;
927 // Return command element
928 switch (iElement)
929 {
930 case 0: // Name
931 return C4VString(strString: CommandName(iCommand: Command->Command));
932 case 1: // Target
933 return C4VObj(pObj: Command->Target);
934 case 2: // Tx
935 return Command->Tx;
936 case 3: // Ty
937 return C4VInt(iVal: Command->Ty);
938 case 4: // Target2
939 return C4VObj(pObj: Command->Target2);
940 case 5: // Data
941 return C4Value(Command->Data, C4V_Any);
942 }
943 // Undefined element
944 return C4VNull;
945}
946
947static bool FnFinishCommand(C4AulContext *cthr, C4Object *pObj, bool fSuccess, C4ValueInt iCommandNum)
948{
949 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
950 C4Command *Command = pObj->Command;
951 // Move through list to Command iCommandNum
952 while (Command && iCommandNum--) Command = Command->Next;
953 // Object has no command or iCommandNum was to high or < 0
954 if (!Command) return false;
955 if (!fSuccess) ++(Command->Failures);
956 else Command->Finished = true;
957 return true;
958}
959
960static bool FnPlayerObjectCommand(C4AulContext *cthr, C4ValueInt iPlr, C4String *szCommand, C4Object *pTarget, C4Value Tx, C4ValueInt iTy, C4Object *pTarget2, C4Value data)
961{
962 // Player
963 if (!ValidPlr(plr: iPlr) || !szCommand) return false;
964 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
965 // Command
966 C4ValueInt iCommand = CommandByName(szCommand: FnStringPar(pString: szCommand));
967 if (!iCommand) return false;
968
969 std::int32_t iData{0};
970 const std::int32_t iTx{Tx.getInt()};
971 if (iCommand == C4CMD_Call)
972 {
973 StrictError(context: cthr, errorSince: C4AulScriptStrict::STRICT3, message: "PlayerObjectCommand: Command \"Call\" not supported");
974 }
975 else
976 {
977 iData = data.getIntOrID();
978 }
979 // Set
980 pPlr->ObjectCommand(iCommand, pTarget, iTx, iTy, pTarget2, iData, iAddMode: C4P_Command_Set);
981 // Success
982 return true;
983}
984
985static C4String *FnGetAction(C4AulContext *cthr, C4Object *pObj)
986{
987 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
988 if (pObj->Action.Act <= ActIdle) return String(str: "Idle");
989 return String(str: pObj->Def->ActMap[pObj->Action.Act].Name);
990}
991
992static C4String *FnGetName(C4AulContext *cthr, C4Object *pObj, C4ID idDef)
993{
994 // Def name
995 C4Def *pDef;
996 if (idDef)
997 {
998 pDef = C4Id2Def(id: idDef);
999 if (pDef) return String(str: pDef->GetName());
1000 return nullptr;
1001 }
1002 // Object name
1003 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1004 return String(str: pObj->GetName());
1005}
1006
1007static bool FnSetName(C4AulContext *cthr, C4String *pNewName, C4Object *pObj, C4ID idDef, bool fSetInInfo, bool fMakeValidIfExists)
1008{
1009 // safety
1010 if (fSetInInfo && idDef) return false;
1011
1012 // Def name
1013 C4Def *pDef;
1014
1015 if (idDef)
1016 if (pDef = C4Id2Def(id: idDef))
1017 pDef->Name.Copy(pnData: FnStringPar(pString: pNewName));
1018 else
1019 return false;
1020 else
1021 {
1022 // Object name
1023 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1024 if (fSetInInfo)
1025 {
1026 // setting name in info
1027 C4ObjectInfo *pInfo = pObj->Info;
1028 if (!pInfo) return false;
1029 const char *szName = pNewName->Data.getData();
1030 // empty names are bad; e.g., could cause problems in savegames
1031 if (!szName || !*szName) return false;
1032 // name must not be too C4ValueInt
1033 if (SLen(sptr: szName) > C4MaxName) return false;
1034 // any change at all?
1035 if (SEqual(szStr1: szName, szStr2: pInfo->Name)) return true;
1036 // make sure names in info list aren't duplicated
1037 // querying owner info list here isn't 100% accurate, as infos might have been stolen by other players
1038 // however, there is no good way to track the original list ATM
1039 C4ObjectInfoList *pInfoList = nullptr;
1040 C4Player *pOwner = Game.Players.Get(iPlayer: pObj->Owner);
1041 if (pOwner) pInfoList = &pOwner->CrewInfoList;
1042 char NameBuf[C4MaxName + 1];
1043 if (pInfoList) if (pInfoList->NameExists(szName))
1044 {
1045 if (!fMakeValidIfExists) return false;
1046 SCopy(szSource: szName, sTarget: NameBuf, iMaxL: C4MaxName);
1047 pInfoList->MakeValidName(sName: NameBuf);
1048 szName = NameBuf;
1049 }
1050 SCopy(szSource: szName, sTarget: pInfo->Name, iMaxL: C4MaxName);
1051 pObj->SetName(); // make sure object uses info name
1052 }
1053 else
1054 {
1055 if (!pNewName) pObj->SetName();
1056 else pObj->SetName(pNewName->Data.getData());
1057 }
1058 }
1059 return true;
1060}
1061
1062static C4String *FnGetDesc(C4AulContext *cthr, C4Object *pObj, C4ID idDef)
1063{
1064 C4Def *pDef;
1065 // find def
1066 if (!pObj && !idDef) pObj = cthr->Obj;
1067 if (pObj)
1068 pDef = pObj->Def;
1069 else
1070 pDef = Game.Defs.ID2Def(id: idDef);
1071 // nothing found?
1072 if (!pDef) return nullptr;
1073 // return desc
1074 return String(str: pDef->GetDesc());
1075}
1076
1077static C4String *FnGetPlayerName(C4AulContext *cthr, C4ValueInt iPlayer)
1078{
1079 if (!ValidPlr(plr: iPlayer)) return nullptr;
1080 return String(str: Game.Players.Get(iPlayer)->GetName());
1081}
1082
1083static C4String *FnGetTaggedPlayerName(C4AulContext *cthr, C4ValueInt iPlayer)
1084{
1085 C4Player *pPlr = Game.Players.Get(iPlayer);
1086 if (!pPlr) return nullptr;
1087 uint32_t dwClr = pPlr->ColorDw; C4GUI::MakeColorReadableOnBlack(rdwClr&: dwClr);
1088 static char szFnFormatBuf[1024 + 1];
1089 FormatWithNull(buf&: szFnFormatBuf, fmt: "<c {:x}>{}</c>", args: dwClr & 0xffffff, args: pPlr->GetName());
1090 return String(str: szFnFormatBuf);
1091}
1092
1093static std::optional<C4ValueInt> FnGetPlayerType(C4AulContext *cthr, C4ValueInt iPlayer)
1094{
1095 C4Player *pPlr = Game.Players.Get(iPlayer);
1096 if (!pPlr) return {};
1097 return {pPlr->GetType()};
1098}
1099
1100static C4Object *FnGetActionTarget(C4AulContext *cthr, C4ValueInt target_index, C4Object *pObj)
1101{
1102 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1103 if (target_index == 0) return pObj->Action.Target;
1104 if (target_index == 1) return pObj->Action.Target2;
1105 return nullptr;
1106}
1107
1108static bool FnSetActionTargets(C4AulContext *cthr, C4Object *pTarget1, C4Object *pTarget2, C4Object *pObj)
1109{
1110 // safety
1111 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1112 // set targets
1113 pObj->Action.Target = pTarget1;
1114 pObj->Action.Target2 = pTarget2;
1115 return true;
1116}
1117
1118static std::optional<C4ValueInt> FnGetDir(C4AulContext *cthr, C4Object *pObj)
1119{
1120 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1121 return {pObj->Action.Dir};
1122}
1123
1124static std::optional<C4ValueInt> FnGetEntrance(C4AulContext *cthr, C4Object *pObj)
1125{
1126 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1127 return {pObj->EntranceStatus};
1128}
1129
1130static std::optional<C4ValueInt> FnGetPhase(C4AulContext *cthr, C4Object *pObj)
1131{
1132 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1133 return {pObj->Action.Phase};
1134}
1135
1136static std::optional<C4ValueInt> FnGetEnergy(C4AulContext *cthr, C4Object *pObj)
1137{
1138 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1139 return {100 * pObj->Energy / C4MaxPhysical};
1140}
1141
1142static std::optional<C4ValueInt> FnGetBreath(C4AulContext *cthr, C4Object *pObj)
1143{
1144 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1145 return {100 * pObj->Breath / C4MaxPhysical};
1146}
1147
1148static std::optional<C4ValueInt> FnGetMass(C4AulContext *cthr, C4Object *pObj, C4ID idDef)
1149{
1150 if (idDef)
1151 {
1152 C4Def *pDef = Game.Defs.ID2Def(id: idDef);
1153 if (!pDef) return {};
1154 return pDef->Mass;
1155 }
1156 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1157 return {pObj->Mass};
1158}
1159
1160static std::optional<C4ValueInt> FnGetRDir(C4AulContext *cthr, C4Object *pObj, C4ValueInt iPrec)
1161{
1162 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1163 if (!iPrec) iPrec = 10;
1164 return {fixtoi(x: pObj->rdir, prec: iPrec)};
1165}
1166
1167static std::optional<C4ValueInt> FnGetXDir(C4AulContext *cthr, C4Object *pObj, C4ValueInt iPrec)
1168{
1169 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1170 if (!iPrec) iPrec = 10;
1171 return {fixtoi(x: pObj->xdir, prec: iPrec)};
1172}
1173
1174static std::optional<C4ValueInt> FnGetYDir(C4AulContext *cthr, C4Object *pObj, C4ValueInt iPrec)
1175{
1176 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1177 if (!iPrec) iPrec = 10;
1178 return {fixtoi(x: pObj->ydir, prec: iPrec)};
1179}
1180
1181static std::optional<C4ValueInt> FnGetR(C4AulContext *cthr, C4Object *pObj)
1182{
1183 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1184 // Adjust range
1185 C4ValueInt iR = pObj->r;
1186 while (iR > 180) iR -= 360;
1187 while (iR < -180) iR += 360;
1188 return {iR};
1189}
1190
1191static std::optional<C4ValueInt> FnGetComDir(C4AulContext *cthr, C4Object *pObj)
1192{
1193 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1194 return {pObj->Action.ComDir};
1195}
1196
1197static std::optional<C4ValueInt> FnGetX(C4AulContext *cthr, C4Object *pObj)
1198{
1199 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1200 return {pObj->x};
1201}
1202
1203static std::optional<C4ValueInt> FnGetVertexNum(C4AulContext *cthr, C4Object *pObj)
1204{
1205 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1206 return {pObj->Shape.VtxNum};
1207}
1208
1209static const C4ValueInt VTX_X = 0, // vertex data indices
1210 VTX_Y = 1,
1211 VTX_CNAT = 2,
1212 VTX_Friction = 3,
1213 VTX_SetPermanent = 1,
1214 VTX_SetPermanentUpd = 2;
1215
1216static std::optional<C4ValueInt> FnGetVertex(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt iValueToGet, C4Object *pObj)
1217{
1218 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1219 if (pObj->Shape.VtxNum < 1) return {};
1220 iIndex = std::min<C4ValueInt>(a: iIndex, b: pObj->Shape.VtxNum - 1);
1221 switch (iValueToGet)
1222 {
1223 case VTX_X: return {pObj->Shape.VtxX[iIndex]}; break;
1224 case VTX_Y: return {pObj->Shape.VtxY[iIndex]}; break;
1225 case VTX_CNAT: return {pObj->Shape.VtxCNAT[iIndex]}; break;
1226 case VTX_Friction: return {pObj->Shape.VtxFriction[iIndex]}; break;
1227 default:
1228 // old-style behaviour for any value != 0 (normally not used)
1229 DebugLog(fmt: "GetVertex: Unknown vertex attribute: {}; getting VtxY", args&: iValueToGet);
1230 return {pObj->Shape.VtxY[iIndex]};
1231 break;
1232 }
1233 // impossible mayhem!
1234 return {};
1235}
1236
1237static bool FnSetVertex(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt iValueToSet, C4ValueInt iValue, C4Object *pObj, C4ValueInt iOwnVertexMode)
1238{
1239 // local call / safety
1240 if (!pObj) pObj = cthr->Obj; if (!pObj || !pObj->Status) return false;
1241 // own vertex mode?
1242 if (iOwnVertexMode)
1243 {
1244 // enter own custom vertex mode if not already set
1245 if (!pObj->fOwnVertices)
1246 {
1247 pObj->Shape.CreateOwnOriginalCopy(rFrom&: pObj->Def->Shape);
1248 pObj->fOwnVertices = 1;
1249 }
1250 // set vertices at end of buffer
1251 iIndex += C4D_VertexCpyPos;
1252 }
1253 // range check
1254 if (!Inside<C4ValueInt>(ival: iIndex, lbound: 0, rbound: C4D_MaxVertex - 1)) return false;
1255 // set desired value
1256 switch (iValueToSet)
1257 {
1258 case VTX_X: pObj->Shape.VtxX[iIndex] = iValue; break;
1259 case VTX_Y: pObj->Shape.VtxY[iIndex] = iValue; break;
1260 case VTX_CNAT: pObj->Shape.VtxCNAT[iIndex] = iValue; break;
1261 case VTX_Friction: pObj->Shape.VtxFriction[iIndex] = iValue; break;
1262 default:
1263 // old-style behaviour for any value != 0 (normally not used)
1264 pObj->Shape.VtxY[iIndex] = iValue;
1265 DebugLog(fmt: "SetVertex: Unknown vertex attribute: {}; setting VtxY", args&: iValueToSet);
1266 break;
1267 }
1268 // vertex update desired?
1269 if (iOwnVertexMode == VTX_SetPermanentUpd) pObj->UpdateShape(bUpdateVertices: true);
1270 return true;
1271}
1272
1273static bool FnAddVertex(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4Object *pObj)
1274{
1275 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1276 return !!pObj->Shape.AddVertex(iX, iY);
1277}
1278
1279static bool FnRemoveVertex(C4AulContext *cthr, C4ValueInt iIndex, C4Object *pObj)
1280{
1281 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1282 return !!pObj->Shape.RemoveVertex(iPos: iIndex);
1283}
1284
1285static bool FnSetContactDensity(C4AulContext *cthr, C4ValueInt iDensity, C4Object *pObj)
1286{
1287 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1288 pObj->Shape.ContactDensity = iDensity;
1289 return true;
1290}
1291
1292static std::optional<C4ValueInt> FnGetY(C4AulContext *cthr, C4Object *pObj)
1293{
1294 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1295 return {pObj->y};
1296}
1297
1298static std::optional<C4ValueInt> FnGetAlive(C4AulContext *cthr, C4Object *pObj)
1299{
1300 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1301 return pObj->GetAlive();
1302}
1303
1304static C4ValueInt FnGetOwner(C4AulContext *cthr, C4Object *pObj)
1305{
1306 if (!pObj) pObj = cthr->Obj; if (!pObj) return NO_OWNER;
1307 return pObj->Owner;
1308}
1309
1310static std::optional<C4ValueInt> FnCrewMember(C4AulContext *cthr, C4Object *pObj)
1311{
1312 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1313 return pObj->Def->CrewMember;
1314}
1315
1316static C4ValueInt FnGetController(C4AulContext *cthr, C4Object *pObj)
1317{
1318 if (!pObj) pObj = cthr->Obj; if (!pObj) return NO_OWNER;
1319 return pObj->Controller;
1320}
1321
1322static bool FnSetController(C4AulContext *cthr, C4ValueInt iNewController, C4Object *pObj)
1323{
1324 // validate player
1325 if (iNewController != NO_OWNER && !ValidPlr(plr: iNewController)) return false;
1326 // Object safety
1327 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1328 // Set controller
1329 pObj->Controller = iNewController;
1330 return true;
1331}
1332
1333static C4ValueInt FnGetKiller(C4AulContext *cthr, C4Object *pObj)
1334{
1335 if (!pObj) pObj = cthr->Obj; if (!pObj) return NO_OWNER;
1336 return pObj->LastEnergyLossCausePlayer;
1337}
1338
1339static bool FnSetKiller(C4AulContext *cthr, C4ValueInt iNewKiller, C4Object *pObj)
1340{
1341 // validate player
1342 if (iNewKiller != NO_OWNER && !ValidPlr(plr: iNewKiller)) return false;
1343 // object safety
1344 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1345 // set killer as last energy loss cause
1346 pObj->LastEnergyLossCausePlayer = iNewKiller;
1347 return true;
1348}
1349
1350static std::optional<C4ValueInt> FnGetCategory(C4AulContext *cthr, C4Object *pObj, C4ID idDef)
1351{
1352 // Def category
1353 C4Def *pDef;
1354 if (idDef) if (pDef = C4Id2Def(id: idDef)) return {pDef->Category};
1355 // Object category
1356 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1357 return {pObj->Category};
1358}
1359
1360static std::optional<C4ValueInt> FnGetOCF(C4AulContext *cthr, C4Object *pObj)
1361{
1362 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1363 return {pObj->OCF};
1364}
1365
1366static std::optional<C4ValueInt> FnGetDamage(C4AulContext *cthr, C4Object *pObj)
1367{
1368 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1369 return {pObj->Damage};
1370}
1371
1372static std::optional<C4ValueInt> FnGetValue(C4AulContext *cthr, C4Object *pObj, C4ID idDef, C4Object *pInBase, C4ValueInt iForPlayer)
1373{
1374 // Def value
1375 C4Def *pDef;
1376 if (idDef)
1377 // return Def value or 0 if def unloaded
1378 if (pDef = C4Id2Def(id: idDef)) return pDef->GetValue(pInBase, iBuyPlayer: iForPlayer); else return {};
1379 // Object value
1380 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1381 return {pObj->GetValue(pInBase, iForPlayer)};
1382}
1383
1384static std::optional<C4ValueInt> FnGetRank(C4AulContext *cthr, C4Object *pObj)
1385{
1386 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1387 if (!pObj->Info) return {};
1388 return {pObj->Info->Rank};
1389}
1390
1391static std::optional<C4ValueInt> FnValue(C4AulContext *cthr, C4ID id)
1392{
1393 C4Def *pDef = C4Id2Def(id);
1394 if (pDef) return {pDef->Value};
1395 return {};
1396}
1397
1398static std::optional<C4ValueInt> FnGetActTime(C4AulContext *cthr, C4Object *pObj)
1399{
1400 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1401 return {pObj->Action.Time};
1402}
1403
1404static C4ID FnGetID(C4AulContext *cthr, C4Object *pObj)
1405{
1406 C4Def *pDef = pObj ? pObj->Def : cthr->Def;
1407 if (!pDef) return 0;
1408 // return id of object
1409 return pDef->id;
1410}
1411
1412static C4ValueInt FnGetBase(C4AulContext *cthr, C4Object *pObj)
1413{
1414 if (!pObj) pObj = cthr->Obj; if (!pObj) return -1;
1415 return pObj->Base;
1416}
1417
1418static C4ID FnGetMenu(C4AulContext *cthr, C4Object *pObj)
1419{
1420 if (!pObj) pObj = cthr->Obj; if (!pObj) return C4ID(-1);
1421 if (pObj->Menu && pObj->Menu->IsActive())
1422 return pObj->Menu->GetIdentification();
1423 return C4MN_None;
1424}
1425
1426static bool FnCreateMenu(C4AulContext *cthr, C4ID iSymbol, C4Object *pMenuObj, C4Object *pCommandObj,
1427 C4ValueInt iExtra, C4String *szCaption, C4ValueInt iExtraData,
1428 C4ValueInt iStyle, bool fPermanent, C4ID idMenuID)
1429{
1430 if (!pMenuObj) { pMenuObj = cthr->Obj; if (!pMenuObj) return false; }
1431 if (!pCommandObj) pCommandObj = cthr->Obj;
1432 if (pCommandObj)
1433 {
1434 // object menu: Validate object
1435 if (!pCommandObj->Status) return false;
1436 }
1437 else
1438 {
1439 // scenario script callback: No command object OK
1440 }
1441
1442 // Create symbol
1443 C4Def *pDef;
1444 C4FacetExSurface fctSymbol;
1445 fctSymbol.Create(iWdt: C4SymbolSize, iHgt: C4SymbolSize);
1446 if (pDef = C4Id2Def(id: iSymbol)) pDef->Draw(cgo&: fctSymbol);
1447
1448 // Clear any old menu, init new menu
1449 if (!pMenuObj->CloseMenu(fForce: false)) return false;
1450 if (!pMenuObj->Menu) pMenuObj->Menu = new C4ObjectMenu; else pMenuObj->Menu->ClearItems(fResetSelection: true);
1451 pMenuObj->Menu->Init(fctSymbol, szEmpty: FnStringPar(pString: szCaption), pObject: pCommandObj, iExtra, iExtraData, iId: idMenuID ? idMenuID : iSymbol, iStyle, fUserMenu: true);
1452
1453 // Set permanent
1454 pMenuObj->Menu->SetPermanent(fPermanent);
1455
1456 return true;
1457}
1458
1459const C4ValueInt C4MN_Add_ImgRank = 1,
1460 C4MN_Add_ImgIndexed = 2,
1461 C4MN_Add_ImgObjRank = 3,
1462 C4MN_Add_ImgObject = 4,
1463 C4MN_Add_ImgTextSpec = 5,
1464 C4MN_Add_ImgColor = 6,
1465 C4MN_Add_ImgIndexedColor = 7,
1466 C4MN_Add_MaxImage = 127, // mask for param which decides what to draw as the menu symbol
1467 C4MN_Add_PassValue = 128,
1468 C4MN_Add_ForceCount = 256,
1469 C4MN_Add_ForceNoDesc = 512;
1470
1471static bool FnAddMenuItem(C4AulContext *cthr, C4String *szCaption, C4String *szCommand, C4ID idItem, C4Object *pMenuObj, C4ValueInt iCount, C4Value Parameter, C4String *szInfoCaption, C4ValueInt iExtra, C4Value XPar, C4Value XPar2)
1472{
1473 if (!pMenuObj) pMenuObj = cthr->Obj;
1474 if (!pMenuObj) return false;
1475 if (!pMenuObj->Menu) return false;
1476
1477 char caption[256 + 1];
1478 char parameter[256 + 1];
1479 char dummy[256 + 1];
1480 std::string command;
1481 std::string command2;
1482 char infocaption[C4MaxTitle + 1];
1483
1484 // get needed symbol size
1485 const auto iSymbolSize = pMenuObj->Menu->GetSymbolSize();
1486
1487 // Check specified def
1488 C4Def *pDef = C4Id2Def(id: idItem);
1489 if (!pDef) pDef = pMenuObj->Def;
1490
1491 // Compose caption with def name
1492 if (szCaption)
1493 {
1494 const char *s = FnStringPar(pString: szCaption);
1495 const char *sep = strstr(haystack: s, needle: "%s");
1496 if (sep)
1497 {
1498 strncpy(dest: caption, src: s, n: std::min<intptr_t>(a: sep - s, b: 256));
1499 caption[std::min<intptr_t>(a: sep - s, b: 256)] = 0;
1500 strncat(dest: caption, src: pDef->GetName(), n: 256);
1501 strncat(dest: caption, src: sep + 2, n: 256);
1502 }
1503 else
1504 {
1505 strncpy(dest: caption, src: s, n: 256);
1506 caption[256] = 0;
1507 }
1508 }
1509 else
1510 caption[0] = 0;
1511
1512 // create string to include type-information in command
1513 switch (Parameter.GetType())
1514 {
1515 case C4V_Int:
1516 FormatWithNull(buf&: parameter, fmt: "{}", args: Parameter.getInt());
1517 break;
1518 case C4V_Bool:
1519 SCopy(szSource: Parameter.getBool() ? "true" : "false", sTarget: parameter);
1520 break;
1521 case C4V_C4ID:
1522 FormatWithNull(buf&: parameter, fmt: "{}", args: C4IdText(id: Parameter.getC4ID()));
1523 break;
1524 case C4V_C4Object:
1525 FormatWithNull(buf&: parameter, fmt: "Object({})", args&: Parameter.getObj()->Number);
1526 break;
1527 case C4V_String:
1528 // note this breaks if there is '"' in the string.
1529 parameter[0] = '"';
1530 SCopy(szSource: Parameter.getStr()->Data.getData(), sTarget: parameter + 1, iMaxL: sizeof(parameter) - 3);
1531 SAppendChar(cChar: '"', szStr: parameter);
1532 break;
1533 case C4V_Any:
1534 FormatWithNull(buf&: parameter, fmt: "CastAny({})", args: Parameter._getRaw());
1535 break;
1536 case C4V_Array:
1537 // Arrays were never allowed, so tell the scripter
1538 throw C4AulExecError(cthr->Obj, "array as parameter to AddMenuItem");
1539 case C4V_Map:
1540 // Maps are not allowed either
1541 throw C4AulExecError(cthr->Obj, "map as parameter to AddMenuItem");
1542 default:
1543 return false;
1544 }
1545
1546 // own value
1547 bool fOwnValue = false; C4ValueInt iValue = 0;
1548 if (iExtra & C4MN_Add_PassValue)
1549 {
1550 fOwnValue = true;
1551 iValue = XPar2.getInt();
1552 }
1553
1554 // New Style: native script command
1555 int i = 0;
1556 for (; i < SLen(sptr: FnStringPar(pString: szCommand)); i++)
1557 if (!IsIdentifier(cChar: FnStringPar(pString: szCommand)[i]))
1558 break;
1559 if (i < SLen(sptr: FnStringPar(pString: szCommand)))
1560 {
1561 // Search for "%d" an replace it by "%s" for insertion of formatted parameter
1562 SCopy(szSource: FnStringPar(pString: szCommand), sTarget: dummy, iMaxL: 256);
1563 char *pFound = const_cast<char *>(SSearch(szString: dummy, szIndex: "%d"));
1564 if (pFound != nullptr)
1565 *(pFound - 1) = 's';
1566 // Compose left-click command
1567 command = fmt::sprintf(fmt: dummy, args: parameter, args: 0);
1568 command2 = fmt::sprintf(fmt: dummy, args: parameter, args: 1);
1569 }
1570
1571 // Old style: function name with id and parameter
1572 else
1573 {
1574 const char *szScriptCom = FnStringPar(pString: szCommand);
1575 if (szScriptCom && *szScriptCom)
1576 {
1577 if (iExtra & C4MN_Add_PassValue)
1578 {
1579 // with value
1580 command = std::format(fmt: "{}({},{},0,{})", args&: szScriptCom, args: C4IdText(id: idItem), args: +parameter, args&: iValue);
1581 command2 = std::format(fmt: "{}({},{},1,{})", args&: szScriptCom, args: C4IdText(id: idItem), args: +parameter, args&: iValue);
1582 }
1583 else
1584 {
1585 // without value
1586 command = std::format(fmt: "{}({},{})", args&: szScriptCom, args: C4IdText(id: idItem), args: +parameter);
1587 command2 = std::format(fmt: "{}({},{},1)", args&: szScriptCom, args: C4IdText(id: idItem), args: +parameter);
1588 }
1589 }
1590 else
1591 {
1592 // no command
1593 }
1594 }
1595
1596 // Info caption
1597 SCopy(szSource: FnStringPar(pString: szInfoCaption), sTarget: infocaption, iMaxL: C4MaxTitle);
1598 // Default info caption by def desc
1599 if (!infocaption[0] && !(iExtra & C4MN_Add_ForceNoDesc)) SCopy(szSource: pDef->GetDesc(), sTarget: infocaption, iMaxL: C4MaxTitle);
1600
1601 // Create symbol
1602 C4FacetExSurface fctSymbol;
1603 switch (iExtra & C4MN_Add_MaxImage)
1604 {
1605 case C4MN_Add_ImgRank:
1606 {
1607 // symbol by rank
1608 C4FacetEx *pfctRankSym = &Game.GraphicsResource.fctRank;
1609 int32_t iRankSymNum = Game.GraphicsResource.iNumRanks;
1610 if (pDef && pDef->pRankSymbols)
1611 {
1612 pfctRankSym = pDef->pRankSymbols;
1613 iRankSymNum = pDef->iNumRankSymbols;
1614 }
1615 C4RankSystem::DrawRankSymbol(fctSymbol: &fctSymbol, iRank: iCount, pfctRankSymbols: pfctRankSym, iRankSymbolCount: iRankSymNum, fOwnSurface: true);
1616 iCount = 0;
1617 break;
1618 }
1619 case C4MN_Add_ImgIndexed:
1620 // use indexed facet
1621 pDef->Picture2Facet(cgo&: fctSymbol, color: 0, xPhase: XPar.getInt());
1622 break;
1623 case C4MN_Add_ImgObjRank:
1624 {
1625 // draw current gfx of XPar_C4V including rank
1626 if (XPar.GetType() != C4V_C4Object) return false;
1627 C4Object *pGfxObj = XPar.getObj();
1628 if (pGfxObj && pGfxObj->Status)
1629 {
1630 // create graphics
1631 // get rank gfx
1632 C4FacetEx *pRankRes = &Game.GraphicsResource.fctRank;
1633 C4ValueInt iRankCnt = Game.GraphicsResource.iNumRanks;
1634 C4Def *pDef = pGfxObj->Def;
1635 if (pDef->pRankSymbols)
1636 {
1637 pRankRes = pDef->pRankSymbols;
1638 iRankCnt = pDef->iNumRankSymbols;
1639 }
1640 // context menu
1641 C4Facet fctRank;
1642 if (pMenuObj->Menu->IsContextMenu())
1643 {
1644 // context menu entry: left object gfx
1645 C4ValueInt C4MN_SymbolSize = pMenuObj->Menu->GetItemHeight();
1646 fctSymbol.Create(iWdt: C4MN_SymbolSize * 2, iHgt: C4MN_SymbolSize);
1647 fctSymbol.Wdt = C4MN_SymbolSize;
1648 pGfxObj->Def->Draw(cgo&: fctSymbol, fSelected: false, iColor: pGfxObj->Color, pObj: pGfxObj);
1649 // right of it the rank
1650 fctRank = fctSymbol;
1651 fctRank.X = C4MN_SymbolSize;
1652 fctSymbol.Wdt *= 2;
1653 }
1654 else
1655 {
1656 // regular menu: draw object picture
1657 fctSymbol.Create(iWdt: iSymbolSize, iHgt: iSymbolSize);
1658 pGfxObj->Def->Draw(cgo&: fctSymbol, fSelected: false, iColor: pGfxObj->Color, pObj: pGfxObj);
1659 // rank at top-right corner
1660 fctRank = fctSymbol;
1661 fctRank.X = fctRank.Wdt - pRankRes->Wdt;
1662 fctRank.Wdt = pRankRes->Wdt;
1663 fctRank.Hgt = pRankRes->Hgt;
1664 }
1665 // draw rank
1666 if (pGfxObj->Info)
1667 {
1668 C4Facet fctBackup = static_cast<const C4Facet &>(fctSymbol);
1669 fctSymbol.Set(fctRank);
1670 C4RankSystem::DrawRankSymbol(fctSymbol: &fctSymbol, iRank: pGfxObj->Info->Rank, pfctRankSymbols: pRankRes, iRankSymbolCount: iRankCnt, fOwnSurface: true);
1671 fctSymbol.Set(fctBackup);
1672 }
1673 }
1674 }
1675 break;
1676 case C4MN_Add_ImgObject:
1677 {
1678 // draw object picture
1679 if (XPar.GetType() != C4V_C4Object) return false;
1680 C4Object *pGfxObj = XPar.getObj();
1681 fctSymbol.Wdt = fctSymbol.Hgt = iSymbolSize;
1682 pGfxObj->Picture2Facet(cgo&: fctSymbol);
1683 }
1684 break;
1685
1686 case C4MN_Add_ImgTextSpec:
1687 {
1688 C4FacetExSurface fctSymSpec;
1689 uint32_t dwClr = XPar.getInt();
1690 if (!szCaption || !Game.DrawTextSpecImage(fctTarget&: fctSymSpec, szSpec: caption, dwClr: dwClr ? dwClr : 0xff))
1691 return false;
1692 fctSymbol.Create(iWdt: iSymbolSize, iHgt: iSymbolSize);
1693 fctSymSpec.Draw(cgo&: fctSymbol, fAspect: true);
1694 *caption = '\0';
1695 }
1696 break;
1697
1698 case C4MN_Add_ImgColor:
1699 // set colored def facet
1700 pDef->Picture2Facet(cgo&: fctSymbol, color: XPar.getInt());
1701 break;
1702
1703 case C4MN_Add_ImgIndexedColor:
1704 if (iExtra & C4MN_Add_PassValue)
1705 {
1706 throw C4AulExecError{cthr->Obj, "AddMenuItem: C4MN_Add_ImgIndexedColor can not be used together with C4MN_Add_PassValue!"};
1707 }
1708 pDef->Picture2Facet(cgo&: fctSymbol, color: XPar2.getInt(), xPhase: XPar.getInt());
1709 break;
1710
1711 default:
1712 // default: by def, if it is not specifically NONE
1713 if (idItem != C4Id(str: "NONE"))
1714 {
1715 pDef->Picture2Facet(cgo&: fctSymbol);
1716 }
1717 else
1718 {
1719 // otherwise: Clear symbol!
1720 }
1721 break;
1722 }
1723
1724 // Convert default zero count to no count
1725 if (iCount == 0 && !(iExtra & C4MN_Add_ForceCount)) iCount = C4MN_Item_NoCount;
1726
1727 // menuitems without commands are never selectable
1728 bool fIsSelectable = !command.empty();
1729
1730 // Add menu item
1731 pMenuObj->Menu->Add(szCaption: caption, fctSymbol, szCommand: command.c_str(), iCount, pObject: nullptr, szInfoCaption: infocaption, idID: idItem, szCommand2: command2.c_str(), fOwnValue, iValue, fIsSelectable);
1732
1733 return true;
1734}
1735
1736static bool FnSelectMenuItem(C4AulContext *cthr, C4ValueInt iItem, C4Object *pMenuObj)
1737{
1738 if (!pMenuObj) pMenuObj = cthr->Obj; if (!pMenuObj) return false;
1739 if (!pMenuObj->Menu) return false;
1740 return !!pMenuObj->Menu->SetSelection(iSelection: iItem, fAdjustPosition: false, fDoCalls: true);
1741}
1742
1743static bool FnSetMenuDecoration(C4AulContext *cthr, C4ID idNewDeco, C4Object *pMenuObj)
1744{
1745 if (!pMenuObj || !pMenuObj->Menu) return false;
1746 C4GUI::FrameDecoration *pNewDeco = new C4GUI::FrameDecoration();
1747 if (!pNewDeco->SetByDef(idNewDeco))
1748 {
1749 delete pNewDeco;
1750 return false;
1751 }
1752 pMenuObj->Menu->SetFrameDeco(pNewDeco);
1753 return true;
1754}
1755
1756static bool FnSetMenuTextProgress(C4AulContext *cthr, C4ValueInt iNewProgress, C4Object *pMenuObj)
1757{
1758 if (!pMenuObj || !pMenuObj->Menu) return false;
1759 return pMenuObj->Menu->SetTextProgress(iToProgress: iNewProgress, fAdd: false);
1760}
1761
1762// Check / Status
1763
1764static C4Object *FnContained(C4AulContext *cthr, C4Object *pObj)
1765{
1766 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1767 return pObj->Contained;
1768}
1769
1770static C4Object *FnContents(C4AulContext *cthr, C4ValueInt index, C4Object *pObj, bool returnAttached)
1771{
1772 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1773 // Special: objects attaching to another object
1774 // cannot be accessed by FnContents, unless returnAttached is true
1775 C4Object *cobj;
1776 while (cobj = pObj->Contents.GetObject(Index: index))
1777 {
1778 if (cobj->GetProcedure() != DFA_ATTACH || returnAttached) return cobj;
1779 index++;
1780 }
1781 return nullptr;
1782}
1783
1784static bool FnShiftContents(C4AulContext *cthr, C4Object *pObj, bool fShiftBack, C4ID idTarget, bool fDoCalls)
1785{
1786 // local call/safety
1787 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
1788 // regular shift
1789 if (!idTarget) return !!pObj->ShiftContents(fShiftBack, fDoCalls);
1790 // check if ID is present within target
1791 C4Object *pNewFront = pObj->Contents.Find(id: idTarget);
1792 if (!pNewFront) return false;
1793 // select it
1794 pObj->DirectComContents(pTarget: pNewFront, fDoCalls);
1795 // done, success
1796 return true;
1797}
1798
1799static C4Object *FnScrollContents(C4AulContext *cthr, C4Object *pObj)
1800{
1801 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1802
1803 C4Object *pMove = pObj->Contents.GetObject();
1804 if (pMove)
1805 {
1806 pObj->Contents.Remove(pObj: pMove);
1807 pObj->Contents.Add(nObj: pMove, eSort: C4ObjectList::stNone);
1808 }
1809
1810 return pObj->Contents.GetObject();
1811}
1812
1813static std::optional<C4ValueInt> FnContentsCount(C4AulContext *cthr, C4ID id, C4Object *pObj)
1814{
1815 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1816 return {pObj->Contents.ObjectCount(id)};
1817}
1818
1819static C4Object *FnFindContents(C4AulContext *cthr, C4ID c_id, C4Object *pObj)
1820{
1821 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1822 return pObj->Contents.Find(id: c_id);
1823}
1824
1825static C4Object *FnFindOtherContents(C4AulContext *cthr, C4ID c_id, C4Object *pObj)
1826{
1827 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1828 return pObj->Contents.FindOther(id: c_id);
1829}
1830
1831static std::optional<bool> FnActIdle(C4AulContext *cthr, C4Object *pObj)
1832{
1833 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1834 if (pObj->Action.Act == ActIdle) return {true};
1835 return {false};
1836}
1837
1838static std::optional<bool> FnCheckEnergyNeedChain(C4AulContext *cthr, C4Object *pObj)
1839{
1840 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1841 C4ObjectList EnergyChainChecked;
1842 return {CheckEnergyNeedChain(pObj, rEnergyChainChecked&: EnergyChainChecked)};
1843}
1844
1845static std::optional<bool> FnEnergyCheck(C4AulContext *cthr, C4ValueInt energy, C4Object *pObj)
1846{
1847 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1848 if (!(Game.Rules & C4RULE_StructuresNeedEnergy)
1849 || (pObj->Energy >= energy)
1850 || !(pObj->Def->LineConnect & C4D_Power_Consumer))
1851 {
1852 pObj->NeedEnergy = 0; return {true};
1853 }
1854 pObj->NeedEnergy = 1;
1855 return {false};
1856}
1857
1858static std::optional<bool> FnStuck(C4AulContext *cthr, C4Object *pObj)
1859{
1860 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1861 return {!!pObj->Shape.CheckContact(cx: pObj->x, cy: pObj->y)};
1862}
1863
1864static std::optional<bool> FnInLiquid(C4AulContext *cthr, C4Object *pObj)
1865{
1866 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1867 return {pObj->InLiquid};
1868}
1869
1870static std::optional<bool> FnOnFire(C4AulContext *cthr, C4Object *pObj)
1871{
1872 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
1873 if (pObj->GetOnFire()) return {true};
1874 // check for effect
1875 if (!pObj->pEffects) return {false};
1876 return {!!pObj->pEffects->Get(C4Fx_AnyFire)};
1877}
1878
1879static std::optional<bool> FnComponentAll(C4AulContext *cthr, C4Object *pObj, C4ID c_id)
1880{
1881 C4ValueInt cnt;
1882 if (!pObj) return {};
1883 C4IDList Components;
1884 pObj->Def->GetComponents(pOutList: &Components, pObjInstance: pObj, pBuilder: cthr->Obj);
1885 for (cnt = 0; Components.GetID(index: cnt); cnt++)
1886 if (Components.GetID(index: cnt) != c_id)
1887 if (Components.GetCount(index: cnt) > 0)
1888 return {false};
1889 return {true};
1890}
1891
1892static C4Object *FnCreateObject(C4AulContext *cthr,
1893 C4ID id, C4ValueInt iXOffset, C4ValueInt iYOffset, C4ValueInt iOwner)
1894{
1895 if (cthr->Obj) // Local object calls override
1896 {
1897 iXOffset += cthr->Obj->x;
1898 iYOffset += cthr->Obj->y;
1899 if (!cthr->Caller || cthr->Caller->Func->Owner->Strict == C4AulScriptStrict::NONSTRICT)
1900 iOwner = cthr->Obj->Owner;
1901 }
1902
1903 C4Object *pNewObj = Game.CreateObject(type: id, pCreator: cthr->Obj, owner: iOwner, x: iXOffset, y: iYOffset);
1904
1905 // Set initial controller to creating controller, so more complicated cause-effect-chains can be traced back to the causing player
1906 if (pNewObj && cthr->Obj && cthr->Obj->Controller > NO_OWNER) pNewObj->Controller = cthr->Obj->Controller;
1907
1908 return pNewObj;
1909}
1910
1911static C4Object *FnCreateConstruction(C4AulContext *cthr,
1912 C4ID id, C4ValueInt iXOffset, C4ValueInt iYOffset, C4ValueInt iOwner,
1913 C4ValueInt iCompletion, bool fTerrain, bool fCheckSite)
1914{
1915 // Local object calls override position offset, owner
1916 if (cthr->Obj)
1917 {
1918 iXOffset += cthr->Obj->x;
1919 iYOffset += cthr->Obj->y;
1920 if (!cthr->Caller || cthr->Caller->Func->Owner->Strict == C4AulScriptStrict::NONSTRICT)
1921 iOwner = cthr->Obj->Owner;
1922 }
1923
1924 // Check site
1925 if (fCheckSite)
1926 if (!ConstructionCheck(id, iX: iXOffset, iY: iYOffset, pByObj: cthr->Obj))
1927 return nullptr;
1928
1929 // Create site object
1930 C4Object *pNewObj = Game.CreateObjectConstruction(type: id, pCreator: cthr->Obj, owner: iOwner, ctx: iXOffset, bty: iYOffset, con: iCompletion * FullCon / 100, terrain: fTerrain);
1931
1932 // Set initial controller to creating controller, so more complicated cause-effect-chains can be traced back to the causing player
1933 if (pNewObj && cthr->Obj && cthr->Obj->Controller > NO_OWNER) pNewObj->Controller = cthr->Obj->Controller;
1934
1935 return pNewObj;
1936}
1937
1938static C4Object *FnCreateContents(C4AulContext *cthr, C4ID c_id, C4Object *pObj, C4ValueInt iCount)
1939{
1940 // local call / safety
1941 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1942 // default amount parameter
1943 if (!iCount) ++iCount;
1944 // create objects
1945 C4Object *pNewObj = nullptr;
1946 while (iCount-- > 0) pNewObj = pObj->CreateContents(n_id: c_id);
1947 // controller will automatically be set upon entrance
1948 // return last created
1949 return pNewObj;
1950}
1951
1952static C4Object *FnComposeContents(C4AulContext *cthr, C4ID c_id, C4Object *pObj)
1953{
1954 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
1955 return pObj->ComposeContents(id: c_id);
1956}
1957
1958static std::optional<bool> FnFindConstructionSite(C4AulContext *cthr, C4ID id, C4ValueInt iVarX, C4ValueInt iVarY)
1959{
1960 // Get def (Old-style implementation (fixed)...)
1961 C4Def *pDef;
1962 if (!(pDef = C4Id2Def(id))) return {};
1963 // Var indices out of range
1964 if (!Inside<C4ValueInt>(ival: iVarX, lbound: 0, C4AUL_MAX_Par - 1) || !Inside<C4ValueInt>(ival: iVarY, lbound: 0, C4AUL_MAX_Par - 1)) return {};
1965 // Get thread vars
1966 if (!cthr->Caller) return {};
1967 C4Value &V1 = cthr->Caller->NumVars[iVarX];
1968 C4Value &V2 = cthr->Caller->NumVars[iVarY];
1969 // Construction check at starting position
1970 if (ConstructionCheck(id, iX: V1.getInt(), iY: V2.getInt()))
1971 return {true};
1972 // Search for real
1973 int32_t v1 = V1.getInt(), v2 = V2.getInt();
1974 bool result = !!FindConSiteSpot(rx&: v1, ry&: v2,
1975 wdt: pDef->Shape.Wdt, hgt: pDef->Shape.Hgt,
1976 category: pDef->Category,
1977 hrange: 20);
1978 V1 = C4VInt(iVal: v1); V2 = C4VInt(iVal: v2);
1979 return {result};
1980}
1981
1982static C4Object *FnFindBase(C4AulContext *cthr, C4ValueInt iOwner, C4ValueInt iIndex)
1983{
1984 if (!ValidPlr(plr: iOwner)) return nullptr;
1985 return Game.FindBase(iPlayer: iOwner, iIndex);
1986}
1987
1988C4FindObject *CreateCriterionsFromPars(const C4Value *pPars, C4FindObject **pFOs, C4SortObject **pSOs)
1989{
1990 int i, iCnt = 0, iSortCnt = 0;
1991 // Read all parameters
1992 for (i = 0; i < C4AUL_MAX_Par; i++)
1993 {
1994 const C4Value &Data = pPars[i].GetRefVal();
1995 // No data given?
1996 if (!Data) break;
1997 // Construct
1998 C4SortObject *pSO = nullptr;
1999 C4FindObject *pFO = C4FindObject::CreateByValue(Data, ppSortObj: pSOs ? &pSO : nullptr);
2000 // Add FindObject
2001 if (pFO)
2002 {
2003 pFOs[iCnt++] = pFO;
2004 }
2005 // Add SortObject
2006 if (pSO)
2007 {
2008 pSOs[iSortCnt++] = pSO;
2009 }
2010 }
2011 // No criterions?
2012 if (!iCnt)
2013 {
2014 for (i = 0; i < iSortCnt; ++i) delete pSOs[i];
2015 return nullptr;
2016 }
2017 // create sort criterion
2018 C4SortObject *pSO = nullptr;
2019 if (iSortCnt)
2020 {
2021 if (iSortCnt == 1)
2022 pSO = pSOs[0];
2023 else
2024 pSO = new C4SortObjectMultiple(iSortCnt, pSOs, false);
2025 }
2026 // Create search object
2027 C4FindObject *pFO;
2028 if (iCnt == 1)
2029 pFO = pFOs[0];
2030 else
2031 pFO = new C4FindObjectAnd(iCnt, pFOs, false);
2032 if (pSO) pFO->SetSort(pSO);
2033 return pFO;
2034}
2035
2036static C4Value FnObjectCount2(C4AulContext *cthr, const C4Value *pPars)
2037{
2038 // Create FindObject-structure
2039 C4FindObject *pFOs[C4AUL_MAX_Par];
2040 C4FindObject *pFO = CreateCriterionsFromPars(pPars, pFOs, pSOs: nullptr);
2041 // Error?
2042 if (!pFO)
2043 throw C4AulExecError(cthr->Obj, "ObjectCount: No valid search criterions supplied!");
2044 // Search
2045 int32_t iCnt = pFO->Count(Objs: Game.Objects, Sct: Game.Objects.Sectors);
2046 // Free
2047 delete pFO;
2048 // Return
2049 return C4VInt(iVal: iCnt);
2050}
2051
2052static C4Value FnFindObject2(C4AulContext *cthr, const C4Value *pPars)
2053{
2054 // Create FindObject-structure
2055 C4FindObject *pFOs[C4AUL_MAX_Par];
2056 C4SortObject *pSOs[C4AUL_MAX_Par];
2057 C4FindObject *pFO = CreateCriterionsFromPars(pPars, pFOs, pSOs);
2058 // Error?
2059 if (!pFO)
2060 throw C4AulExecError(cthr->Obj, "FindObject: No valid search criterions supplied!");
2061 // Search
2062 C4Object *pObj = pFO->Find(Objs: Game.Objects, Sct: Game.Objects.Sectors);
2063 // Free
2064 delete pFO;
2065 // Return
2066 return C4VObj(pObj);
2067}
2068
2069static C4Value FnFindObjects(C4AulContext *cthr, const C4Value *pPars)
2070{
2071 // Create FindObject-structure
2072 C4FindObject *pFOs[C4AUL_MAX_Par];
2073 C4SortObject *pSOs[C4AUL_MAX_Par];
2074 C4FindObject *pFO = CreateCriterionsFromPars(pPars, pFOs, pSOs);
2075 // Error?
2076 if (!pFO)
2077 throw C4AulExecError(cthr->Obj, "FindObjects: No valid search criterions supplied!");
2078 // Search
2079 C4ValueArray *pResult = pFO->FindMany(Objs: Game.Objects, Sct: Game.Objects.Sectors);
2080 // Free
2081 delete pFO;
2082 // Return
2083 return C4VArray(pArray: pResult);
2084}
2085
2086static C4ValueInt FnObjectCount(C4AulContext *cthr, C4ID id, C4ValueInt x, C4ValueInt y, C4ValueInt wdt, C4ValueInt hgt, C4ValueInt dwOCF, C4String *szAction, C4Object *pActionTarget, C4Value vContainer, C4ValueInt iOwner)
2087{
2088 // Local call adjust coordinates
2089 if (cthr->Obj && (x || y || wdt || hgt)) // if not default full range
2090 {
2091 x += cthr->Obj->x;
2092 y += cthr->Obj->y;
2093 }
2094 // Adjust default ocf
2095 if (dwOCF == 0) dwOCF = OCF_All;
2096 // Adjust default owner
2097 if (iOwner == 0) iOwner = ANY_OWNER; // imcomplete useless implementation
2098 // NO_CONTAINER/ANY_CONTAINER
2099 C4Object *pContainer = vContainer.getObj();
2100 if (vContainer.getInt() == NO_CONTAINER)
2101 pContainer = reinterpret_cast<C4Object *>(NO_CONTAINER);
2102 if (vContainer.getInt() == ANY_CONTAINER)
2103 pContainer = reinterpret_cast<C4Object *>(ANY_CONTAINER);
2104 // Find object
2105 return Game.ObjectCount(id, x, y, wdt, hgt, ocf: dwOCF,
2106 szAction: FnStringPar(pString: szAction), pActionTarget,
2107 pExclude: cthr->Obj, // Local calls exclude self
2108 pContainer,
2109 iOwner);
2110}
2111
2112static C4Object *FnFindObject(C4AulContext *cthr, C4ID id, C4ValueInt x, C4ValueInt y, C4ValueInt wdt, C4ValueInt hgt, C4ValueInt dwOCF, C4String *szAction, C4Object *pActionTarget, C4Value vContainer, C4Object *pFindNext)
2113{
2114 // Local call adjust coordinates
2115 if (cthr->Obj)
2116 if (x || y || wdt || hgt) // if not default full range
2117 {
2118 x += cthr->Obj->x; y += cthr->Obj->y;
2119 }
2120 // Adjust default ocf
2121 if (dwOCF == 0) dwOCF = OCF_All;
2122 // NO_CONTAINER/ANY_CONTAINER
2123 C4Object *pContainer = vContainer.getObj();
2124 if (vContainer.getInt() == NO_CONTAINER)
2125 pContainer = reinterpret_cast<C4Object *>(NO_CONTAINER);
2126 if (vContainer.getInt() == ANY_CONTAINER)
2127 pContainer = reinterpret_cast<C4Object *>(ANY_CONTAINER);
2128 // Find object
2129 return Game.FindObject(id, iX: x, iY: y, iWdt: wdt, iHgt: hgt, ocf: dwOCF,
2130 szAction: FnStringPar(pString: szAction), pActionTarget,
2131 pExclude: cthr->Obj, // Local calls exclude self
2132 pContainer,
2133 iOwner: ANY_OWNER,
2134 pFindNext);
2135}
2136
2137static C4Object *FnFindObjectOwner(C4AulContext *cthr,
2138 C4ID id,
2139 C4ValueInt iOwner,
2140 C4ValueInt x, C4ValueInt y, C4ValueInt wdt, C4ValueInt hgt,
2141 C4ValueInt dwOCF,
2142 C4String *szAction, C4Object *pActionTarget,
2143 C4Object *pFindNext)
2144{
2145 // invalid owner?
2146 if (!ValidPlr(plr: iOwner) && iOwner != NO_OWNER) return nullptr;
2147 // Local call adjust coordinates
2148 if (cthr->Obj)
2149 if (x || y || wdt || hgt) // if not default full range
2150 {
2151 x += cthr->Obj->x; y += cthr->Obj->y;
2152 }
2153 // Adjust default ocf
2154 if (dwOCF == 0) dwOCF = OCF_All;
2155 // Find object
2156 return Game.FindObject(id, iX: x, iY: y, iWdt: wdt, iHgt: hgt, ocf: dwOCF,
2157 szAction: FnStringPar(pString: szAction), pActionTarget,
2158 pExclude: cthr->Obj, // Local calls exclude self
2159 pContainer: nullptr,
2160 iOwner,
2161 pFindNext);
2162}
2163
2164static bool FnMakeCrewMember(C4AulContext *cthr, C4Object *pObj, C4ValueInt iPlayer)
2165{
2166 if (!ValidPlr(plr: iPlayer)) return false;
2167 return !!Game.Players.Get(iPlayer)->MakeCrewMember(pObj);
2168}
2169
2170static bool FnGrabObjectInfo(C4AulContext *cthr, C4Object *pFrom, C4Object *pTo)
2171{
2172 // local call, safety
2173 if (!pFrom) return false; if (!pTo) { pTo = cthr->Obj; if (!pTo) return false; }
2174 // grab info
2175 return !!pTo->GrabInfo(pFrom);
2176}
2177
2178static bool FnFlameConsumeMaterial(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2179{
2180 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2181 C4ValueInt mat = GBackMat(x, y);
2182 if (!MatValid(mat)) return false;
2183 if (!Game.Material.Map[mat].Inflammable) return false;
2184 if (Game.Landscape.ExtractMaterial(fx: x, fy: y) == MNone) return false;
2185 return true;
2186}
2187
2188static void FnSmoke(C4AulContext *cthr, C4ValueInt tx, C4ValueInt ty, C4ValueInt level, C4ValueInt dwClr)
2189{
2190 if (cthr->Obj) { tx += cthr->Obj->x; ty += cthr->Obj->y; }
2191 Smoke(tx, ty, level, dwClr);
2192}
2193
2194static void FnBubble(C4AulContext *cthr, C4ValueInt tx, C4ValueInt ty)
2195{
2196 if (cthr->Obj) { tx += cthr->Obj->x; ty += cthr->Obj->y; }
2197 BubbleOut(tx, ty);
2198}
2199
2200static C4ValueInt FnExtractLiquid(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2201{
2202 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2203 if (!GBackLiquid(x, y)) return MNone;
2204 return Game.Landscape.ExtractMaterial(fx: x, fy: y);
2205}
2206
2207static bool FnInsertMaterial(C4AulContext *cthr, C4ValueInt mat, C4ValueInt x, C4ValueInt y, C4ValueInt vx, C4ValueInt vy)
2208{
2209 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2210 return !!Game.Landscape.InsertMaterial(mat, tx: x, ty: y, vx, vy);
2211}
2212
2213static C4ValueInt FnGetMaterialCount(C4AulContext *cthr, C4ValueInt iMaterial, bool fReal)
2214{
2215 if (!MatValid(mat: iMaterial)) return -1;
2216 if (fReal || !Game.Material.Map[iMaterial].MinHeightCount)
2217 return Game.Landscape.MatCount[iMaterial];
2218 else
2219 return Game.Landscape.EffectiveMatCount[iMaterial];
2220}
2221
2222static C4ValueInt FnGetMaterial(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2223{
2224 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2225 return GBackMat(x, y);
2226}
2227
2228static C4String *FnGetTexture(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2229{
2230 // Get texture
2231 int32_t iTex = PixCol2Tex(GBackPix(x, y));
2232 if (!iTex) return nullptr;
2233 // Get material-texture mapping
2234 const C4TexMapEntry *pTex = Game.TextureMap.GetEntry(iIndex: iTex);
2235 if (!pTex) return nullptr;
2236 // Return tex name
2237 return String(str: pTex->GetTextureName());
2238}
2239
2240static bool FnGBackSolid(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2241{
2242 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2243 return GBackSolid(x, y);
2244}
2245
2246static bool FnGBackSemiSolid(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2247{
2248 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2249 return GBackSemiSolid(x, y);
2250}
2251
2252static bool FnGBackLiquid(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2253{
2254 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2255 return GBackLiquid(x, y);
2256}
2257
2258static bool FnGBackSky(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
2259{
2260 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2261 return !GBackIFT(x, y);
2262}
2263
2264static C4ValueInt FnExtractMaterialAmount(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, C4ValueInt mat, C4ValueInt amount)
2265{
2266 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
2267 C4ValueInt extracted = 0; for (; extracted < amount; extracted++)
2268 {
2269 if (GBackMat(x, y) != mat) return extracted;
2270 if (Game.Landscape.ExtractMaterial(fx: x, fy: y) != mat) return extracted;
2271 }
2272 return extracted;
2273}
2274
2275static void FnBlastObjects(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iLevel, C4Object *pInObj, C4ValueInt iCausedByPlusOne)
2276{
2277 C4ValueInt iCausedBy = iCausedByPlusOne - 1; if (!iCausedByPlusOne && cthr->Obj) iCausedBy = cthr->Obj->Controller;
2278 Game.BlastObjects(tx: iX, ty: iY, level: iLevel, inobj: pInObj, iCausedBy, pByObj: cthr->Obj);
2279}
2280
2281static bool FnBlastObject(C4AulContext *cthr, C4ValueInt iLevel, C4Object *pObj, C4ValueInt iCausedByPlusOne)
2282{
2283 C4ValueInt iCausedBy = iCausedByPlusOne - 1; if (!iCausedByPlusOne && cthr->Obj) iCausedBy = cthr->Obj->Controller;
2284 if (!pObj) if (!(pObj = cthr->Obj)) return false;
2285 if (!pObj->Status) return false;
2286 pObj->Blast(iLevel, iCausedBy);
2287 return true;
2288}
2289
2290static void FnBlastFree(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iLevel, C4ValueInt iCausedByPlusOne)
2291{
2292 C4ValueInt iCausedBy = iCausedByPlusOne - 1;
2293 if (!iCausedByPlusOne && cthr->Obj)
2294 {
2295 iCausedBy = cthr->Obj->Controller;
2296 iX += cthr->Obj->x;
2297 iY += cthr->Obj->y;
2298 }
2299 C4ValueInt grade = BoundBy<C4ValueInt>(bval: (iLevel / 10) - 1, lbound: 1, rbound: 3);
2300 Game.Landscape.BlastFree(tx: iX, ty: iY, rad: iLevel, grade, iByPlayer: iCausedBy);
2301}
2302
2303static bool FnSound(C4AulContext *cthr, C4String *szSound, bool fGlobal, C4Object *pObj, C4ValueInt iLevel, C4ValueInt iAtPlayer, C4ValueInt iLoop, bool fMultiple, C4ValueInt iCustomFalloffDistance)
2304{
2305 // play here?
2306 if (iAtPlayer)
2307 {
2308 // get player to play at
2309 C4Player *pPlr = Game.Players.Get(iPlayer: iAtPlayer - 1);
2310 // not existent? fail
2311 if (!pPlr) return false;
2312 // network client: don't play here
2313 // return true for network sync
2314 if (!pPlr->LocalControl && !Game.GraphicsSystem.GetViewport(iPlayer: iAtPlayer - 1)) return true;
2315 }
2316 // even less than nothing?
2317 if (iLevel < 0) return true;
2318 // default sound level
2319 if (!iLevel || iLevel > 100)
2320 iLevel = 100;
2321 // target object
2322 if (fGlobal) pObj = nullptr; else if (!pObj) pObj = cthr->Obj;
2323 // already playing?
2324 if (iLoop >= 0 && !fMultiple && IsSoundPlaying(name: FnStringPar(pString: szSound), obj: pObj))
2325 return true;
2326 // try to play effect
2327 if (iLoop >= 0)
2328 StartSoundEffect(name: FnStringPar(pString: szSound), loop: !!iLoop, volume: iLevel, obj: pObj, falloffDistance: iCustomFalloffDistance);
2329 else
2330 StopSoundEffect(name: FnStringPar(pString: szSound), obj: pObj);
2331 // always return true (network safety!)
2332 return true;
2333}
2334
2335static void FnMusic(C4AulContext *cthr, C4String *szSongname, bool fLoop)
2336{
2337 if (!szSongname)
2338 {
2339 Game.IsMusicEnabled = false;
2340 Application.MusicSystem->Stop();
2341 }
2342 else
2343 {
2344 Game.IsMusicEnabled = true;
2345 Application.MusicSystem->Play(songname: FnStringPar(pString: szSongname), loop: fLoop);
2346 }
2347}
2348
2349static C4ValueInt FnMusicLevel(C4AulContext *cthr, C4ValueInt iLevel)
2350{
2351 Game.SetMusicLevel(iLevel);
2352 return Game.iMusicLevel;
2353}
2354
2355static std::optional<C4ValueInt> FnSetPlayList(C4AulContext *cth, C4String *szPlayList, bool fRestartMusic)
2356{
2357 C4ValueInt iFilesInPlayList = Application.MusicSystem->SetPlayList(FnStringPar(pString: szPlayList));
2358 Game.PlayList.Copy(pnData: FnStringPar(pString: szPlayList));
2359 if (fRestartMusic) Application.MusicSystem->Play();
2360 if (Game.Control.SyncMode()) return {};
2361 return {iFilesInPlayList};
2362}
2363
2364static void FnSoundLevel(C4AulContext *cthr, C4String *szSound, C4ValueInt iLevel, C4Object *pObj)
2365{
2366 SoundLevel(name: FnStringPar(pString: szSound), obj: pObj, iLevel);
2367}
2368
2369static bool FnGameOver(C4AulContext *cthr, C4ValueInt iGameOverValue /* provided for future compatibility */)
2370{
2371 return !!Game.DoGameOver();
2372}
2373
2374static bool FnGainMissionAccess(C4AulContext *cthr, C4String *szPassword)
2375{
2376 if (SLen(sptr: Config.General.MissionAccess) + SLen(sptr: FnStringPar(pString: szPassword)) + 3 > CFG_MaxString) return false;
2377 SAddModule(szList: Config.General.MissionAccess, szModule: FnStringPar(pString: szPassword));
2378 return true;
2379}
2380
2381static void FnLog(C4AulContext *cthr, C4String *szMessage, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7, C4Value iPar8)
2382{
2383 LogNTr(message: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7, Par8: &iPar8));
2384}
2385
2386static void FnDebugLog(C4AulContext *cthr, C4String *szMessage, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7, C4Value iPar8)
2387{
2388 DebugLog(message: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7, Par8: &iPar8));
2389}
2390
2391static C4String *FnFormat(C4AulContext *cthr, C4String *szFormat, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7, C4Value iPar8)
2392{
2393 return String(str: StdStrBuf{FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szFormat), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7, Par8: &iPar8).c_str()});
2394}
2395
2396static C4ID FnC4Id(C4AulContext *cthr, C4String *szID)
2397{
2398 return C4Id(str: FnStringPar(pString: szID));
2399}
2400
2401static bool FnPlayerMessage(C4AulContext *cthr, C4ValueInt iPlayer, C4String *szMessage, C4Object *pObj, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6)
2402{
2403 char buf[MaxFnStringParLen + 1];
2404 if (!szMessage) return false;
2405
2406 // Speech
2407 bool fSpoken = false;
2408 if (SCopySegment(fstr: FnStringPar(pString: szMessage), segn: 1, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2409 if (StartSoundEffect(name: buf, loop: false, volume: 100, obj: pObj ? pObj : cthr->Obj))
2410 fSpoken = true;
2411
2412 // Text
2413 if (!fSpoken)
2414 if (SCopySegment(fstr: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6).c_str(), segn: 0, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2415 if (pObj) GameMsgObjectPlayer(szText: buf, pTarget: pObj, iPlayer);
2416 else GameMsgPlayer(szText: buf, iPlayer);
2417
2418 return true;
2419}
2420
2421static bool FnMessage(C4AulContext *cthr, C4String *szMessage, C4Object *pObj, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7)
2422{
2423 char buf[MaxFnStringParLen + 1];
2424 if (!szMessage) return false;
2425
2426 // Speech
2427 bool fSpoken = false;
2428 if (SCopySegment(fstr: FnStringPar(pString: szMessage), segn: 1, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2429 if (StartSoundEffect(name: buf, loop: false, volume: 100, obj: cthr->Obj))
2430 fSpoken = true;
2431
2432 // Text
2433 if (!fSpoken)
2434 if (SCopySegment(fstr: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7).c_str(), segn: 0, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2435 if (pObj) GameMsgObject(szText: buf, pTarget: pObj);
2436 else GameMsgGlobal(szText: buf);
2437
2438 return true;
2439}
2440
2441static bool FnAddMessage(C4AulContext *cthr, C4String *szMessage, C4Object *pObj, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7)
2442{
2443 if (!szMessage) return false;
2444
2445 if (pObj) Game.Messages.Append(iType: C4GM_Target, szText: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7).c_str(), pTarget: pObj, iPlayer: NO_OWNER, iX: 0, iY: 0, bCol: FWhite);
2446 else Game.Messages.Append(iType: C4GM_Global, szText: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7).c_str(), pTarget: nullptr, iPlayer: ANY_OWNER, iX: 0, iY: 0, bCol: FWhite);
2447
2448 return true;
2449}
2450
2451static bool FnPlrMessage(C4AulContext *cthr, C4String *szMessage, C4ValueInt iPlr, C4Value iPar0, C4Value iPar1, C4Value iPar2, C4Value iPar3, C4Value iPar4, C4Value iPar5, C4Value iPar6, C4Value iPar7)
2452{
2453 char buf[MaxFnStringParLen + 1];
2454 if (!szMessage) return false;
2455
2456 // Speech
2457 bool fSpoken = false;
2458 if (SCopySegment(fstr: FnStringPar(pString: szMessage), segn: 1, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2459 if (StartSoundEffect(name: buf, loop: false, volume: 100, obj: cthr->Obj))
2460 fSpoken = true;
2461
2462 // Text
2463 if (!fSpoken)
2464 if (SCopySegment(fstr: FnStringFormat(cthr, szFormatPar: FnStringPar(pString: szMessage), Par0: &iPar0, Par1: &iPar1, Par2: &iPar2, Par3: &iPar3, Par4: &iPar4, Par5: &iPar5, Par6: &iPar6, Par7: &iPar7).c_str(), segn: 0, tstr: buf, sepa: '$', iMaxL: MaxFnStringParLen))
2465 if (ValidPlr(plr: iPlr)) GameMsgPlayer(szText: buf, iPlayer: iPlr);
2466 else GameMsgGlobal(szText: buf);
2467
2468 return true;
2469}
2470
2471static void FnScriptGo(C4AulContext *cthr, bool go)
2472{
2473 Game.Script.Go = !!go;
2474}
2475
2476static void FnCastPXS(C4AulContext *cthr, C4String *mat_name, C4ValueInt amt, C4ValueInt level, C4ValueInt tx, C4ValueInt ty)
2477{
2478 if (cthr->Obj) { tx += cthr->Obj->x; ty += cthr->Obj->y; }
2479 Game.PXS.Cast(mat: Game.Material.Get(szMaterial: FnStringPar(pString: mat_name)), num: amt, tx, ty, level);
2480}
2481
2482static void FnCastObjects(C4AulContext *cthr, C4ID id, C4ValueInt amt, C4ValueInt level, C4ValueInt tx, C4ValueInt ty)
2483{
2484 if (cthr->Obj) { tx += cthr->Obj->x; ty += cthr->Obj->y; }
2485 Game.CastObjects(id, pCreator: cthr->Obj, num: amt, level, tx, ty, iOwner: cthr->Obj ? cthr->Obj->Owner : NO_OWNER, iController: cthr->Obj ? cthr->Obj->Controller : NO_OWNER);
2486}
2487
2488static C4ValueInt FnMaterial(C4AulContext *cthr, C4String *mat_name)
2489{
2490 return Game.Material.Get(szMaterial: FnStringPar(pString: mat_name));
2491}
2492
2493C4Object *FnPlaceVegetation(C4AulContext *cthr, C4ID id, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4ValueInt iGrowth)
2494{
2495 // Local call: relative coordinates
2496 if (cthr->Obj) { iX += cthr->Obj->x; iY += cthr->Obj->y; }
2497 // Place vegetation
2498 return Game.PlaceVegetation(id, iX, iY, iWdt, iHgt, iGrowth);
2499}
2500
2501C4Object *FnPlaceAnimal(C4AulContext *cthr, C4ID id)
2502{
2503 return Game.PlaceAnimal(idAnimal: id);
2504}
2505
2506static void FnDrawVolcanoBranch(C4AulContext *cthr, C4ValueInt mat, C4ValueInt fx, C4ValueInt fy, C4ValueInt tx, C4ValueInt ty, C4ValueInt size)
2507{
2508 C4ValueInt cx, cx2, cy;
2509 for (cy = ty; cy < fy; cy++)
2510 {
2511 cx = fx + (tx - fx) * (cy - fy) / (ty - fy);
2512 for (cx2 = cx - size / 2; cx2 < cx + size / 2; cx2++)
2513 SBackPix(x: cx2, y: cy, npix: Mat2PixColDefault(mat) + GBackIFT(x: cx2, y: cy));
2514 }
2515}
2516
2517static bool FnHostile(C4AulContext *cthr, C4ValueInt iPlr1, C4ValueInt iPlr2, bool fCheckOneWayOnly)
2518{
2519 if (fCheckOneWayOnly)
2520 {
2521 return Game.Players.HostilityDeclared(iPlayer1: iPlr1, iPlayer2: iPlr2);
2522 }
2523 else
2524 return !!Hostile(plr1: iPlr1, plr2: iPlr2);
2525}
2526
2527static bool FnSetHostility(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iPlr2, bool fHostile, bool fSilent, bool fNoCalls)
2528{
2529 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2530 if (!pPlr) return false;
2531 // do rejection test first
2532 if (!fNoCalls)
2533 {
2534 if (Game.Script.GRBroadcast(PSF_RejectHostilityChange, pPars: {C4VInt(iVal: iPlr), C4VInt(iVal: iPlr2), C4VBool(fVal: fHostile)}, fPassError: true, fRejectTest: true))
2535 return false;
2536 }
2537 // OK; set hostility
2538 bool fOldHostility = Game.Players.HostilityDeclared(iPlayer1: iPlr, iPlayer2: iPlr2);
2539 if (!pPlr->SetHostility(iOpponent: iPlr2, iHostility: fHostile, fSilent)) return false;
2540 // calls afterwards
2541 Game.Script.GRBroadcast(PSF_OnHostilityChange, pPars: {C4VInt(iVal: iPlr), C4VInt(iVal: iPlr2), C4VBool(fVal: fHostile), C4VBool(fVal: fOldHostility)}, fPassError: true);
2542 return true;
2543}
2544
2545static bool FnSetPlrView(C4AulContext *cthr, C4ValueInt iPlr, C4Object *tobj)
2546{
2547 if (!ValidPlr(plr: iPlr)) return false;
2548 Game.Players.Get(iPlayer: iPlr)->SetViewMode(iMode: C4PVM_Target, pTarget: tobj);
2549 return true;
2550}
2551
2552static bool FnSetPlrShowControl(C4AulContext *cthr, C4ValueInt iPlr, C4String *defstring)
2553{
2554 if (!ValidPlr(plr: iPlr)) return false;
2555 Game.Players.Get(iPlayer: iPlr)->ShowControl = StringBitEval(str: FnStringPar(pString: defstring));
2556 return true;
2557}
2558
2559static bool FnSetPlrShowCommand(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iCom)
2560{
2561 if (!ValidPlr(plr: iPlr)) return false;
2562 Game.Players.Get(iPlayer: iPlr)->FlashCom = iCom;
2563 if (!Config.Graphics.ShowCommands) Config.Graphics.ShowCommands = true;
2564 return true;
2565}
2566
2567static bool FnSetPlrShowControlPos(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt pos)
2568{
2569 if (!ValidPlr(plr: iPlr)) return false;
2570 Game.Players.Get(iPlayer: iPlr)->ShowControlPos = pos;
2571 return true;
2572}
2573
2574static C4String *FnGetPlrControlName(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iCon, bool fShort)
2575{
2576 return String(str: PlrControlKeyName(iPlayer: iPlr, iControl: iCon, fShort).c_str());
2577}
2578
2579static C4ValueInt FnGetPlrJumpAndRunControl(C4AulContext *cthr, C4ValueInt iPlr)
2580{
2581 C4Player *plr = Game.Players.Get(iPlayer: iPlr);
2582 return plr ? plr->ControlStyle : -1;
2583}
2584
2585static C4ValueInt FnGetPlrViewMode(C4AulContext *cthr, C4ValueInt iPlr)
2586{
2587 if (!ValidPlr(plr: iPlr)) return -1;
2588 if (Game.Control.SyncMode()) return -1;
2589 return Game.Players.Get(iPlayer: iPlr)->ViewMode;
2590}
2591
2592static C4Object *FnGetPlrView(C4AulContext *cthr, C4ValueInt iPlr)
2593{
2594 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2595 if (!pPlr || pPlr->ViewMode != C4PVM_Target) return nullptr;
2596 return pPlr->ViewTarget;
2597}
2598
2599static bool FnDoHomebaseMaterial(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iChange)
2600{
2601 // validity check
2602 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2603 if (!pPlr) return false;
2604 C4Def *pDef = C4Id2Def(id);
2605 if (!pDef) return false;
2606 // add to material
2607 C4ValueInt iLastcount = pPlr->HomeBaseMaterial.GetIDCount(id);
2608 if (!pPlr->HomeBaseMaterial.SetIDCount(id, count: iLastcount + iChange, addNewID: true)) return false;
2609 if (Game.Rules & C4RULE_TeamHombase) pPlr->SyncHomebaseMaterialToTeam();
2610 return true;
2611}
2612
2613static bool FnDoHomebaseProduction(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iChange)
2614{
2615 // validity check
2616 if (!ValidPlr(plr: iPlr)) return false;
2617 C4Def *pDef = C4Id2Def(id);
2618 if (!pDef) return false;
2619 // add to material
2620 C4ValueInt iLastcount = Game.Players.Get(iPlayer: iPlr)->HomeBaseProduction.GetIDCount(id);
2621 return Game.Players.Get(iPlayer: iPlr)->HomeBaseProduction.SetIDCount(id, count: iLastcount + iChange, addNewID: true);
2622}
2623
2624static std::optional<C4ValueInt> FnGetPlrDownDouble(C4AulContext *cthr, C4ValueInt iPlr)
2625{
2626 if (!ValidPlr(plr: iPlr)) return {};
2627 return Game.Players.Get(iPlayer: iPlr)->LastComDownDouble;
2628}
2629
2630static bool FnClearLastPlrCom(C4AulContext *cthr, C4ValueInt iPlr)
2631{
2632 // get player
2633 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2634 if (!pPlr) return false;
2635 // reset last coms
2636 pPlr->LastCom = COM_None;
2637 pPlr->LastComDownDouble = 0;
2638 // done, success
2639 return true;
2640}
2641
2642static bool FnSetPlrKnowledge(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, bool fRemove)
2643{
2644 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2645 if (!pPlr) return false;
2646 if (fRemove)
2647 {
2648 C4ValueInt iIndex = pPlr->Knowledge.GetIndex(id);
2649 if (iIndex < 0) return false;
2650 return pPlr->Knowledge.DeleteItem(iIndex);
2651 }
2652 else
2653 {
2654 if (!C4Id2Def(id)) return false;
2655 return pPlr->Knowledge.SetIDCount(id, count: 1, addNewID: true);
2656 }
2657}
2658
2659static bool FnSetComponent(C4AulContext *cthr, C4ID idComponent, C4ValueInt iCount, C4Object *pObj)
2660{
2661 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
2662 return pObj->Component.SetIDCount(id: idComponent, count: iCount, addNewID: true);
2663}
2664
2665static C4Value FnGetPlrKnowledge(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iIndex, C4ValueInt dwCategory)
2666{
2667 if (!ValidPlr(plr: iPlr)) return C4VNull;
2668 // Search by id, check if available, return bool
2669 if (id) return C4VBool(fVal: Game.Players.Get(iPlayer: iPlr)->Knowledge.GetIDCount(id, zeroDefVal: 1) != 0);
2670 // Search indexed item of given category, return C4ID
2671 return C4VID(idVal: Game.Players.Get(iPlayer: iPlr)->Knowledge.GetID(rDefs&: Game.Defs, dwCategory, index: iIndex));
2672}
2673
2674static C4ID FnGetDefinition(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt dwCategory)
2675{
2676 C4Def *pDef;
2677 // Default: all categories
2678 if (!dwCategory) dwCategory = C4D_All;
2679 // Get def
2680 if (!(pDef = Game.Defs.GetDef(index: iIndex, category: dwCategory))) return C4ID_None;
2681 // Return id
2682 return pDef->id;
2683}
2684
2685static C4Value FnGetComponent(C4AulContext *cthr, C4ID idComponent, C4ValueInt iIndex, C4Object *pObj, C4ID idDef)
2686{
2687 // Def component - as seen by scope object as builder
2688 if (idDef)
2689 {
2690 // Get def
2691 C4Def *pDef = C4Id2Def(id: idDef);
2692 if (!pDef) return C4VNull;
2693 // Component count
2694 if (idComponent) return C4VInt(iVal: pDef->GetComponentCount(idComponent, pBuilder: cthr->Obj));
2695 // Indexed component
2696 return C4VID(idVal: pDef->GetIndexedComponent(idx: iIndex, pBuilder: cthr->Obj));
2697 }
2698 // Object component
2699 else
2700 {
2701 // Get object
2702 if (!pObj) pObj = cthr->Obj;
2703 if (!pObj) return C4VNull;
2704 // Component count
2705 if (idComponent) return C4VInt(iVal: pObj->Component.GetIDCount(id: idComponent));
2706 // Indexed component
2707 return C4VID(idVal: pObj->Component.GetID(index: iIndex));
2708 }
2709}
2710
2711static C4Value FnGetHomebaseMaterial(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iIndex, C4ValueInt dwCategory)
2712{
2713 if (!ValidPlr(plr: iPlr)) return C4VNull;
2714 // Search by id, return available count
2715 if (id) return C4VInt(iVal: Game.Players.Get(iPlayer: iPlr)->HomeBaseMaterial.GetIDCount(id));
2716 // Search indexed item of given category, return C4ID
2717 return C4VID(idVal: Game.Players.Get(iPlayer: iPlr)->HomeBaseMaterial.GetID(rDefs&: Game.Defs, dwCategory, index: iIndex));
2718}
2719
2720static C4Value FnGetHomebaseProduction(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iIndex, C4ValueInt dwCategory)
2721{
2722 if (!ValidPlr(plr: iPlr)) return C4VNull;
2723 // Search by id, return available count
2724 if (id) return C4VInt(iVal: Game.Players.Get(iPlayer: iPlr)->HomeBaseProduction.GetIDCount(id));
2725 // Search indexed item of given category, return C4ID
2726 return C4VID(idVal: Game.Players.Get(iPlayer: iPlr)->HomeBaseProduction.GetID(rDefs&: Game.Defs, dwCategory, index: iIndex));
2727}
2728
2729static C4ValueInt FnSetPlrMagic(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, bool fRemove)
2730{
2731 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2732 if (!pPlr) return false;
2733 if (fRemove)
2734 {
2735 C4ValueInt iIndex = pPlr->Magic.GetIndex(id);
2736 if (iIndex < 0) return false;
2737 return pPlr->Magic.DeleteItem(iIndex);
2738 }
2739 else
2740 {
2741 if (!C4Id2Def(id)) return false;
2742 return pPlr->Magic.SetIDCount(id, count: 1, addNewID: true);
2743 }
2744}
2745
2746static C4Value FnGetPlrMagic(C4AulContext *cthr, C4ValueInt iPlr, C4ID id, C4ValueInt iIndex)
2747{
2748 if (!ValidPlr(plr: iPlr)) return C4VNull;
2749 // Search by id, check if available, return bool
2750 if (id) return C4VBool(fVal: Game.Players.Get(iPlayer: iPlr)->Magic.GetIDCount(id, zeroDefVal: 1) != 0);
2751 // Search indexed item of given category, return C4ID
2752 return C4VID(idVal: Game.Players.Get(iPlayer: iPlr)->Magic.GetID(rDefs&: Game.Defs, dwCategory: C4D_Magic, index: iIndex));
2753}
2754
2755static std::optional<C4ValueInt> FnGetWealth(C4AulContext *cthr, C4ValueInt iPlr)
2756{
2757 if (!ValidPlr(plr: iPlr)) return {};
2758 return {Game.Players.Get(iPlayer: iPlr)->Wealth};
2759}
2760
2761static bool FnSetWealth(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iValue)
2762{
2763 if (!ValidPlr(plr: iPlr)) return false;
2764 Game.Players.Get(iPlayer: iPlr)->Wealth = BoundBy<C4ValueInt>(bval: iValue, lbound: 0, rbound: 100000);
2765 return true;
2766}
2767
2768static C4ValueInt FnDoScore(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iChange)
2769{
2770 if (!ValidPlr(plr: iPlr)) return false;
2771 return Game.Players.Get(iPlayer: iPlr)->DoPoints(iChange);
2772}
2773
2774static std::optional<C4ValueInt> FnGetPlrValue(C4AulContext *cthr, C4ValueInt iPlr)
2775{
2776 if (!ValidPlr(plr: iPlr)) return {};
2777 return {Game.Players.Get(iPlayer: iPlr)->Value};
2778}
2779
2780static std::optional<C4ValueInt> FnGetPlrValueGain(C4AulContext *cthr, C4ValueInt iPlr)
2781{
2782 if (!ValidPlr(plr: iPlr)) return {};
2783 return {Game.Players.Get(iPlayer: iPlr)->ValueGain};
2784}
2785
2786static std::optional<C4ValueInt> FnGetScore(C4AulContext *cthr, C4ValueInt iPlr)
2787{
2788 if (!ValidPlr(plr: iPlr)) return {};
2789 return {Game.Players.Get(iPlayer: iPlr)->Points};
2790}
2791
2792static C4Object *FnGetHiRank(C4AulContext *cthr, C4ValueInt iPlr)
2793{
2794 if (!ValidPlr(plr: iPlr)) return nullptr;
2795 return Game.Players.Get(iPlayer: iPlr)->GetHiRankActiveCrew(fSelectedOnly: false);
2796}
2797
2798static C4Object *FnGetCrew(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt index)
2799{
2800 if (!ValidPlr(plr: iPlr)) return nullptr;
2801 return Game.Players.Get(iPlayer: iPlr)->Crew.GetObject(Index: index);
2802}
2803
2804static std::optional<C4ValueInt> FnGetCrewCount(C4AulContext *cthr, C4ValueInt iPlr)
2805{
2806 if (!ValidPlr(plr: iPlr)) return {};
2807 return {Game.Players.Get(iPlayer: iPlr)->Crew.ObjectCount()};
2808}
2809
2810static C4ValueInt FnGetPlayerCount(C4AulContext *cthr, C4ValueInt iType)
2811{
2812 if (!iType)
2813 return Game.Players.GetCount();
2814 else
2815 return Game.Players.GetCount(eType: static_cast<C4PlayerType>(iType));
2816}
2817
2818static C4ValueInt FnGetPlayerByIndex(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt iType)
2819{
2820 C4Player *pPlayer;
2821 if (iType)
2822 pPlayer = Game.Players.GetByIndex(iIndex, eType: static_cast<C4PlayerType>(iType));
2823 else
2824 pPlayer = Game.Players.GetByIndex(iIndex);
2825 if (!pPlayer) return NO_OWNER;
2826 return pPlayer->Number;
2827}
2828
2829static C4ValueInt FnEliminatePlayer(C4AulContext *cthr, C4ValueInt iPlr, bool fRemoveDirect)
2830{
2831 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2832 if (!pPlr) return false;
2833 // direct removal?
2834 if (fRemoveDirect)
2835 {
2836 // do direct removal (no fate)
2837 if (Game.Control.isCtrlHost()) Game.Players.CtrlRemove(iPlayer: iPlr, fDisonnected: false);
2838 return true;
2839 }
2840 else
2841 {
2842 // do regular elimination
2843 if (pPlr->Eliminated) return false;
2844 pPlr->Eliminate();
2845 }
2846 return true;
2847}
2848
2849static bool FnSurrenderPlayer(C4AulContext *cthr, C4ValueInt iPlr)
2850{
2851 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2852 if (!pPlr) return false;
2853 if (pPlr->Eliminated) return false;
2854 pPlr->Surrender();
2855 return true;
2856}
2857
2858static bool FnSetLeaguePerformance(C4AulContext *cthr, C4ValueInt iScore, C4ValueInt idPlayer)
2859{
2860 if (!Game.Parameters.isLeague()) return false;
2861 if (idPlayer && !Game.PlayerInfos.GetPlayerInfoByID(id: idPlayer)) return false;
2862 Game.RoundResults.SetLeaguePerformance(iNewPerf: iScore, idPlayer);
2863 return true;
2864}
2865
2866static bool FnSetLeagueProgressData(C4AulContext *cthr, C4String *pNewData, C4ValueInt idPlayer)
2867{
2868 if (!Game.Parameters.League.getLength()) return false;
2869 C4PlayerInfo *info = Game.PlayerInfos.GetPlayerInfoByID(id: idPlayer);
2870 if (!info) return false;
2871 info->SetLeagueProgressData(pNewData ? pNewData->Data.getData() : nullptr);
2872 return true;
2873}
2874
2875static C4String *FnGetLeagueProgressData(C4AulContext *cthr, C4ValueInt idPlayer)
2876{
2877 if (!Game.Parameters.League.getLength()) return nullptr;
2878 C4PlayerInfo *info = Game.PlayerInfos.GetPlayerInfoByID(id: idPlayer);
2879 if (!info) return nullptr;
2880 return String(str: info->GetLeagueProgressData());
2881}
2882
2883static const int32_t CSPF_FixedAttributes = 1 << 0,
2884 CSPF_NoScenarioInit = 1 << 1,
2885 CSPF_NoEliminationCheck = 1 << 2,
2886 CSPF_Invisible = 1 << 3;
2887
2888static bool FnCreateScriptPlayer(C4AulContext *cthr, C4String *szName, C4ValueInt dwColor, C4ValueInt idTeam, C4ValueInt dwFlags, C4ID idExtra)
2889{
2890 // safety
2891 if (!szName || !szName->Data.getLength()) return false;
2892 // this script command puts a new script player info into the list
2893 // the actual join will be delayed and synchronized via queue
2894 // processed by control host only - clients/replay/etc. will perform the join via queue
2895 if (!Game.Control.isCtrlHost()) return true;
2896 C4PlayerInfo *pScriptPlrInfo = new C4PlayerInfo();
2897 uint32_t dwInfoFlags = 0u;
2898 if (dwFlags & CSPF_FixedAttributes) dwInfoFlags |= C4PlayerInfo::PIF_AttributesFixed;
2899 if (dwFlags & CSPF_NoScenarioInit) dwInfoFlags |= C4PlayerInfo::PIF_NoScenarioInit;
2900 if (dwFlags & CSPF_NoEliminationCheck) dwInfoFlags |= C4PlayerInfo::PIF_NoEliminationCheck;
2901 if (dwFlags & CSPF_Invisible) dwInfoFlags |= C4PlayerInfo::PIF_Invisible;
2902 pScriptPlrInfo->SetAsScriptPlayer(szName: szName->Data.getData(), dwColor, dwFlags: dwInfoFlags, idExtra);
2903 pScriptPlrInfo->SetTeam(idTeam);
2904 C4ClientPlayerInfos JoinPkt(nullptr, true, pScriptPlrInfo);
2905 // add to queue!
2906 Game.PlayerInfos.DoPlayerInfoUpdate(pUpdate: &JoinPkt);
2907 // always successful for sync reasons
2908 return true;
2909}
2910
2911static C4Object *FnGetCursor(C4AulContext *cthr, C4ValueInt iPlr, C4ValueInt iIndex)
2912{
2913 // get player
2914 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2915 // invalid player?
2916 if (!pPlr) return nullptr;
2917 // first index is always the cursor
2918 if (!iIndex) return pPlr->Cursor;
2919 // iterate through selected crew for iIndex times
2920 // status needs not be checked, as dead objects are never in Crew list
2921 C4Object *pCrew;
2922 for (C4ObjectLink *pLnk = pPlr->Crew.First; pLnk; pLnk = pLnk->Next)
2923 // get crew object
2924 if (pCrew = pLnk->Obj)
2925 // is it selected?
2926 if (pCrew->Select)
2927 // is it not the cursor? (which is always first)
2928 if (pCrew != pPlr->Cursor)
2929 // enough searched?
2930 if (!--iIndex)
2931 // return it
2932 return pCrew;
2933 // nothing found at that index
2934 return nullptr;
2935}
2936
2937static C4Object *FnGetViewCursor(C4AulContext *cthr, C4ValueInt iPlr)
2938{
2939 // get player
2940 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2941 // get viewcursor
2942 return pPlr ? pPlr->ViewCursor.Object() : nullptr;
2943}
2944
2945static C4Object *FnGetCaptain(C4AulContext *cthr, C4ValueInt iPlr)
2946{
2947 if (!ValidPlr(plr: iPlr)) return nullptr;
2948 return Game.Players.Get(iPlayer: iPlr)->Captain;
2949}
2950
2951static bool FnSetCursor(C4AulContext *cthr, C4ValueInt iPlr, C4Object *pObj, bool fNoSelectMark, bool fNoSelectArrow, bool fNoSelectCrew)
2952{
2953 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2954 if (!pPlr || (pObj && !pObj->Status)) return false;
2955 pPlr->SetCursor(pObj, fSelectFlash: !fNoSelectMark, fSelectArrow: !fNoSelectArrow);
2956 if (!fNoSelectCrew) pPlr->SelectCrew(pObj, fSelect: true);
2957 return true;
2958}
2959
2960static bool FnSetViewCursor(C4AulContext *cthr, C4ValueInt iPlr, C4Object *pObj)
2961{
2962 // get player
2963 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2964 // invalid player?
2965 if (!pPlr) return false;
2966 // set viewcursor
2967 pPlr->ViewCursor = pObj;
2968 return true;
2969}
2970
2971static bool FnSelectCrew(C4AulContext *cthr, C4ValueInt iPlr, C4Object *pObj, bool fSelect, bool fNoCursorAdjust)
2972{
2973 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2974 if (!pPlr || !pObj) return false;
2975 if (fNoCursorAdjust)
2976 {
2977 if (fSelect) pObj->DoSelect(); else pObj->UnSelect();
2978 }
2979 else
2980 pPlr->SelectCrew(pObj, fSelect);
2981 return true;
2982}
2983
2984static std::optional<C4ValueInt> FnGetSelectCount(C4AulContext *cthr, C4ValueInt iPlr)
2985{
2986 if (!ValidPlr(plr: iPlr)) return {};
2987 return {Game.Players.Get(iPlayer: iPlr)->SelectCount};
2988}
2989
2990static C4ValueInt FnSetCrewStatus(C4AulContext *cthr, C4ValueInt iPlr, bool fInCrew, C4Object *pObj)
2991{
2992 // validate player
2993 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
2994 if (!pPlr) return false;
2995 // validate object / local call
2996 if (!pObj) if (!(pObj = cthr->Obj)) return false;
2997 // set crew status
2998 return pPlr->SetObjectCrewStatus(pCrew: pObj, fNewStatus: fInCrew);
2999}
3000
3001static C4ValueInt FnGetWind(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, bool fGlobal)
3002{
3003 // global wind
3004 if (fGlobal) return Game.Weather.Wind;
3005 // local wind
3006 if (cthr->Obj) { x += cthr->Obj->x; y += cthr->Obj->y; }
3007 return Game.Weather.GetWind(x, y);
3008}
3009
3010static void FnSetWind(C4AulContext *cthr, C4ValueInt iWind)
3011{
3012 Game.Weather.SetWind(iWind);
3013}
3014
3015static void FnSetTemperature(C4AulContext *cthr, C4ValueInt iTemperature)
3016{
3017 Game.Weather.SetTemperature(iTemperature);
3018}
3019
3020static C4ValueInt FnGetTemperature(C4AulContext *cthr)
3021{
3022 return Game.Weather.GetTemperature();
3023}
3024
3025static void FnSetSeason(C4AulContext *cthr, C4ValueInt iSeason)
3026{
3027 Game.Weather.SetSeason(iSeason);
3028}
3029
3030static C4ValueInt FnGetSeason(C4AulContext *cthr)
3031{
3032 return Game.Weather.GetSeason();
3033}
3034
3035static void FnSetClimate(C4AulContext *cthr, C4ValueInt iClimate)
3036{
3037 Game.Weather.SetClimate(iClimate);
3038}
3039
3040static C4ValueInt FnGetClimate(C4AulContext *cthr)
3041{
3042 return Game.Weather.GetClimate();
3043}
3044
3045static void FnSetSkyFade(C4AulContext *cthr, C4ValueInt iFromRed, C4ValueInt iFromGreen, C4ValueInt iFromBlue, C4ValueInt iToRed, C4ValueInt iToGreen, C4ValueInt iToBlue)
3046{
3047 // newgfx: set modulation
3048 uint32_t dwBack, dwMod = GetClrModulation(src: Game.Landscape.Sky.FadeClr1, C4RGB(iFromRed, iFromGreen, iFromBlue), back&: dwBack);
3049 Game.Landscape.Sky.SetModulation(dwWithClr: dwMod, dwBackClr: dwBack);
3050}
3051
3052static void FnSetSkyColor(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt iRed, C4ValueInt iGreen, C4ValueInt iBlue)
3053{
3054 // set first index only
3055 if (iIndex) return;
3056 // get color difference
3057 uint32_t dwBack, dwMod = GetClrModulation(src: Game.Landscape.Sky.FadeClr1, C4RGB(iRed, iGreen, iBlue), back&: dwBack);
3058 Game.Landscape.Sky.SetModulation(dwWithClr: dwMod, dwBackClr: dwBack);
3059 // success
3060}
3061
3062static C4ValueInt FnGetSkyColor(C4AulContext *cthr, C4ValueInt iIndex, C4ValueInt iRGB)
3063{
3064 // relict from OldGfx
3065 if (iIndex || !Inside<C4ValueInt>(ival: iRGB, lbound: 0, rbound: 2)) return 0;
3066 uint32_t dwClr = Game.Landscape.Sky.FadeClr1;
3067 BltAlpha(dst&: dwClr, src: Game.Landscape.Sky.FadeClr2 | ((iIndex * 0xff / 19) << 24));
3068 switch (iRGB)
3069 {
3070 case 0: return (dwClr >> 16) & 0xff;
3071 case 1: return (dwClr >> 8) & 0xff;
3072 case 2: return dwClr & 0xff;
3073 default: return 0;
3074 }
3075}
3076
3077static C4ValueInt FnLandscapeWidth(C4AulContext *cthr)
3078{
3079 return GBackWdt;
3080}
3081
3082static C4ValueInt FnLandscapeHeight(C4AulContext *cthr)
3083{
3084 return GBackHgt;
3085}
3086
3087static C4ValueInt FnLaunchLightning(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, C4ValueInt xdir, C4ValueInt xrange, C4ValueInt ydir, C4ValueInt yrange, bool fDoGamma)
3088{
3089 return Game.Weather.LaunchLightning(x, y, xdir, xrange, ydir, yrange, fDoGamma);
3090}
3091
3092static C4ValueInt FnLaunchVolcano(C4AulContext *cthr, C4ValueInt x)
3093{
3094 return Game.Weather.LaunchVolcano(
3095 mat: Game.Material.Get(szMaterial: "Lava"),
3096 x, GBackHgt - 1,
3097 size: BoundBy(bval: 15 * GBackHgt / 500 + Random(iRange: 10), lbound: 10, rbound: 60));
3098}
3099
3100static void FnLaunchEarthquake(C4AulContext *cthr, C4ValueInt x, C4ValueInt y)
3101{
3102 Game.Weather.LaunchEarthquake(iX: x, iY: y);
3103}
3104
3105static void FnShakeFree(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, C4ValueInt rad)
3106{
3107 Game.Landscape.ShakeFree(tx: x, ty: y, rad);
3108}
3109
3110static void FnShakeObjects(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, C4ValueInt rad)
3111{
3112 Game.ShakeObjects(tx: x, ry: y, range: rad, iCausedBy: cthr->Obj ? cthr->Obj->Controller : NO_OWNER);
3113}
3114
3115static void FnDigFree(C4AulContext *cthr, C4ValueInt x, C4ValueInt y, C4ValueInt rad, bool fRequest)
3116{
3117 Game.Landscape.DigFree(tx: x, ty: y, rad, fRequest, pByObj: cthr->Obj);
3118}
3119
3120static void FnDigFreeRect(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, bool fRequest)
3121{
3122 Game.Landscape.DigFreeRect(tx: iX, ty: iY, wdt: iWdt, hgt: iHgt, fRequest, pByObj: cthr->Obj);
3123}
3124
3125static void FnFreeRect(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4ValueInt iFreeDensity)
3126{
3127 if (iFreeDensity)
3128 Game.Landscape.ClearRectDensity(iTx: iX, iTy: iY, iWdt, iHgt, iOfDensity: iFreeDensity);
3129 else
3130 Game.Landscape.ClearRect(iTx: iX, iTy: iY, iWdt, iHgt);
3131}
3132
3133static bool FnPathFree(C4AulContext *cthr, C4ValueInt X1, C4ValueInt Y1, C4ValueInt X2, C4ValueInt Y2)
3134{
3135 return !!PathFree(x1: X1, y1: Y1, x2: X2, y2: Y2);
3136}
3137
3138static bool FnPathFree2(C4AulContext *cthr, C4Value *X1, C4Value *Y1, C4ValueInt X2, C4ValueInt Y2)
3139{
3140 int32_t x = -1, y = -1;
3141 // Do not use getInt on the references, because it destroys them.
3142 bool r = PathFree(x1: X1->GetRefVal().getInt(), y1: Y1->GetRefVal().getInt(), x2: X2, y2: Y2, ix: &x, iy: &y);
3143 if (!r)
3144 {
3145 *X1 = C4VInt(iVal: x);
3146 *Y1 = C4VInt(iVal: y);
3147 }
3148 return r;
3149}
3150
3151static C4ValueInt FnSetTransferZone(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4Object *pObj)
3152{
3153 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3154 iX += pObj->x; iY += pObj->y;
3155 return Game.TransferZones.Set(iX, iY, iWdt, iHgt, pObj);
3156}
3157
3158static bool FnNot(C4AulContext *cthr, bool fCondition)
3159{
3160 return !fCondition;
3161}
3162
3163static bool FnOr(C4AulContext *cthr, bool fCon1, bool fCon2, bool fCon3, bool fCon4, bool fCon5)
3164{
3165 return (fCon1 || fCon2 || fCon3 || fCon4 || fCon5);
3166}
3167
3168static bool FnAnd(C4AulContext *cthr, bool fCon1, bool fCon2)
3169{
3170 return (fCon1 && fCon2);
3171}
3172
3173static C4ValueInt FnBitAnd(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3174{
3175 return (iVal1 & iVal2);
3176}
3177
3178static bool FnEqual(C4AulContext *cthr, C4Value Val1, C4Value Val2)
3179{
3180 return Val1.GetData() == Val2.GetData();
3181}
3182
3183static C4ValueInt FnLessThan(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3184{
3185 return (iVal1 < iVal2);
3186}
3187
3188static C4ValueInt FnGreaterThan(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3189{
3190 return (iVal1 > iVal2);
3191}
3192
3193static C4ValueInt FnSum(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2, C4ValueInt iVal3, C4ValueInt iVal4)
3194{
3195 return (iVal1 + iVal2 + iVal3 + iVal4);
3196}
3197
3198static C4ValueInt FnSub(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2, C4ValueInt iVal3, C4ValueInt iVal4)
3199{
3200 return (iVal1 - iVal2 - iVal3 - iVal4);
3201}
3202
3203static C4ValueInt FnAbs(C4AulContext *cthr, C4ValueInt iVal)
3204{
3205 return Abs(val: iVal);
3206}
3207
3208static C4ValueInt FnMul(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3209{
3210 return (iVal1 * iVal2);
3211}
3212
3213static C4ValueInt FnDiv(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3214{
3215 if (!iVal2) return 0;
3216 return (iVal1 / iVal2);
3217}
3218
3219static C4ValueInt FnMod(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3220{
3221 if (!iVal2) return 0;
3222 return (iVal1 % iVal2);
3223}
3224
3225static C4ValueInt FnPow(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3226{
3227 return Pow(base: iVal1, exponent: iVal2);
3228}
3229
3230static C4ValueInt FnSin(C4AulContext *cthr, C4ValueInt iAngle, C4ValueInt iRadius, C4ValueInt iPrec)
3231{
3232 if (!iPrec) iPrec = 1;
3233 // Precalculate the modulo operation so the C4Fixed argument to Sin does not overflow
3234 iAngle %= 360 * iPrec;
3235 // Let itofix and fixtoi handle the division and multiplication because that can handle higher ranges
3236 return fixtoi(x: Sin(fAngle: itofix(x: iAngle, prec: iPrec)), prec: iRadius);
3237}
3238
3239static C4ValueInt FnCos(C4AulContext *cthr, C4ValueInt iAngle, C4ValueInt iRadius, C4ValueInt iPrec)
3240{
3241 if (!iPrec) iPrec = 1;
3242 iAngle %= 360 * iPrec;
3243 return fixtoi(x: Cos(fAngle: itofix(x: iAngle, prec: iPrec)), prec: iRadius);
3244}
3245
3246static C4ValueInt FnSqrt(C4AulContext *cthr, C4ValueInt iValue)
3247{
3248 if (iValue < 0) return 0;
3249 C4ValueInt iSqrt = C4ValueInt(sqrt(x: double(iValue)));
3250 if (iSqrt * iSqrt < iValue) iSqrt++;
3251 if (iSqrt * iSqrt > iValue) iSqrt--;
3252 return iSqrt;
3253}
3254
3255static C4ValueInt FnAngle(C4AulContext *cthr, C4ValueInt iX1, C4ValueInt iY1, C4ValueInt iX2, C4ValueInt iY2, C4ValueInt iPrec)
3256{
3257 C4ValueInt iAngle;
3258
3259 // Standard prec
3260 if (!iPrec) iPrec = 1;
3261
3262 C4ValueInt dx = iX2 - iX1, dy = iY2 - iY1;
3263 if (!dx) if (dy > 0) return 180 * iPrec; else return 0;
3264 if (!dy) if (dx > 0) return 90 * iPrec; else return 270 * iPrec;
3265
3266 iAngle = static_cast<C4ValueInt>(180.0 * iPrec * atan2(y: static_cast<double>(Abs(val: dy)), x: static_cast<double>(Abs(val: dx))) * std::numbers::inv_pi);
3267
3268 if (iX2 > iX1)
3269 {
3270 if (iY2 < iY1) iAngle = (90 * iPrec) - iAngle;
3271 else iAngle = (90 * iPrec) + iAngle;
3272 }
3273 else
3274 {
3275 if (iY2 < iY1) iAngle = (270 * iPrec) + iAngle;
3276 else iAngle = (270 * iPrec) - iAngle;
3277 }
3278
3279 return iAngle;
3280}
3281
3282static C4ValueInt FnArcSin(C4AulContext *cthr, C4ValueInt iVal, C4ValueInt iRadius)
3283{
3284 // safety
3285 if (!iRadius) return 0;
3286 if (iVal > iRadius) return 0;
3287 // calc arcsin
3288 double f1 = iVal;
3289 f1 = asin(x: f1 / iRadius) * 180.0 * std::numbers::inv_pi;
3290 // return rounded angle
3291 return static_cast<C4ValueInt>(floor(x: f1 + 0.5));
3292}
3293
3294static C4ValueInt FnArcCos(C4AulContext *cthr, C4ValueInt iVal, C4ValueInt iRadius)
3295{
3296 // safety
3297 if (!iRadius) return 0;
3298 if (iVal > iRadius) return 0;
3299 // calc arccos
3300 double f1 = iVal;
3301 f1 = acos(x: f1 / iRadius) * 180.0 * std::numbers::inv_pi;
3302 // return rounded angle
3303 return static_cast<C4ValueInt>(floor(x: f1 + 0.5));
3304}
3305
3306static C4ValueInt FnMin(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3307{
3308 return (std::min)(a: iVal1, b: iVal2);
3309}
3310
3311static C4ValueInt FnMax(C4AulContext *cthr, C4ValueInt iVal1, C4ValueInt iVal2)
3312{
3313 return (std::max)(a: iVal1, b: iVal2);
3314}
3315
3316static C4ValueInt FnDistance(C4AulContext *cthr, C4ValueInt iX1, C4ValueInt iY1, C4ValueInt iX2, C4ValueInt iY2)
3317{
3318 return Distance(iX1, iY1, iX2, iY2);
3319}
3320
3321static std::optional<C4ValueInt> FnObjectDistance(C4AulContext *cthr, C4Object *pObj2, C4Object *pObj)
3322{
3323 if (!pObj) pObj = cthr->Obj; if (!pObj || !pObj2) return {};
3324 return {Distance(iX1: pObj->x, iY1: pObj->y, iX2: pObj2->x, iY2: pObj2->y)};
3325}
3326
3327static std::optional<C4ValueInt> FnObjectNumber(C4AulContext *cthr, C4Object *pObj)
3328{
3329 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
3330 return {pObj->Number};
3331}
3332
3333static C4Object *FnObject(C4AulContext *cthr, C4ValueInt iNumber)
3334{
3335 return Game.Objects.SafeObjectPointer(iNumber);
3336}
3337
3338static C4ValueInt FnShowInfo(C4AulContext *cthr, C4Object *pObj)
3339{
3340 if (!cthr->Obj) return false;
3341 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3342 return cthr->Obj->ActivateMenu(iMenu: C4MN_Info, iMenuSelect: 0, iMenuData: 0, iMenuPosition: 0, pTarget: pObj);
3343}
3344
3345static C4ValueInt FnBoundBy(C4AulContext *cthr, C4ValueInt iVal, C4ValueInt iRange1, C4ValueInt iRange2)
3346{
3347 return BoundBy(bval: iVal, lbound: iRange1, rbound: iRange2);
3348}
3349
3350static bool FnInside(C4AulContext *cthr, C4ValueInt iVal, C4ValueInt iRange1, C4ValueInt iRange2)
3351{
3352 return Inside(ival: iVal, lbound: iRange1, rbound: iRange2);
3353}
3354
3355static C4ValueInt FnSEqual(C4AulContext *cthr, C4String *szString1, C4String *szString2)
3356{
3357 if (szString1 == szString2) return true;
3358 return SEqual(szStr1: FnStringPar(pString: szString1), szStr2: FnStringPar(pString: szString2));
3359}
3360
3361static C4ValueInt FnRandom(C4AulContext *cthr, C4ValueInt iRange)
3362{
3363 return Random(iRange);
3364}
3365
3366static C4ValueInt FnAsyncRandom(C4AulContext *cthr, C4ValueInt iRange)
3367{
3368 return SafeRandom(range: iRange);
3369}
3370
3371static C4Value FnSetVar(C4AulContext *cthr, C4ValueInt iVarIndex, C4Value iValue)
3372{
3373 if (!cthr->Caller) return C4VNull;
3374 cthr->Caller->NumVars[iVarIndex] = iValue;
3375 return iValue;
3376}
3377
3378static C4Value FnIncVar(C4AulContext *cthr, C4ValueInt iVarIndex)
3379{
3380 if (!cthr->Caller) return C4VNull;
3381 return ++cthr->Caller->NumVars[iVarIndex];
3382}
3383
3384static C4Value FnDecVar(C4AulContext *cthr, C4ValueInt iVarIndex)
3385{
3386 if (!cthr->Caller) return C4VNull;
3387 return --cthr->Caller->NumVars[iVarIndex];
3388}
3389
3390static C4Value FnVar(C4AulContext *cthr, C4ValueInt iVarIndex)
3391{
3392 if (!cthr->Caller) return C4VNull;
3393 // Referenz zurückgeben
3394 return cthr->Caller->NumVars[iVarIndex].GetRef();
3395}
3396
3397static C4Value FnSetGlobal(C4AulContext *cthr, C4ValueInt iVarIndex, C4Value iValue)
3398{
3399 Game.ScriptEngine.Global[iVarIndex] = iValue;
3400 return iValue;
3401}
3402
3403static C4Value FnGlobal(C4AulContext *cthr, C4ValueInt iVarIndex)
3404{
3405 return Game.ScriptEngine.Global[iVarIndex].GetRef();
3406}
3407
3408static C4Value FnSetLocal(C4AulContext *cthr, C4ValueInt iVarIndex, C4Value iValue, C4Object *pObj)
3409{
3410 if (!pObj) pObj = cthr->Obj;
3411 if (!pObj) return C4VFalse;
3412 pObj->Local[iVarIndex] = iValue;
3413 return iValue;
3414}
3415
3416static C4Value FnLocal(C4AulContext *cthr, C4ValueInt iIndex, C4Object *pObj)
3417{
3418 if (!pObj) pObj = cthr->Obj;
3419 if (!pObj) return C4VNull;
3420 if (iIndex < 0) return C4VNull;
3421 return pObj->Local[iIndex].GetRef();
3422}
3423
3424static C4Value FnCall(C4AulContext *cthr, C4String *szFunction,
3425 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3426 C4Value par5, C4Value par6, C4Value par7, C4Value par8)
3427{
3428 if (!szFunction || !cthr->Obj) return C4VNull;
3429 C4AulParSet Pars;
3430 Copy2ParSet9(Pars, par);
3431 return cthr->Obj->Call(szFunctionCall: FnStringPar(pString: szFunction), pPars: Pars, fPassError: true, convertNilToIntBool: !cthr->CalledWithStrictNil());
3432}
3433
3434static C4Value FnObjectCall(C4AulContext *cthr,
3435 C4Object *pObj, C4String *szFunction,
3436 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3437 C4Value par5, C4Value par6, C4Value par7)
3438{
3439 if (!pObj || !szFunction) return C4VNull;
3440 if (!pObj->Def) return C4VNull;
3441 // get func
3442 C4AulFunc *f;
3443 if (!(f = pObj->Def->Script.GetSFunc(pIdtf: FnStringPar(pString: szFunction), AccNeeded: AA_PUBLIC, fFailSafe: true))) return C4VNull;
3444 // copy pars
3445 C4AulParSet Pars;
3446 Copy2ParSet8(Pars, par);
3447 // exec
3448 return f->Exec(pObj, pPars: Pars, fPassErrors: true, nonStrict3WarnConversionOnly: true, convertNilToIntBool: !cthr->CalledWithStrictNil());
3449}
3450
3451static C4Value FnDefinitionCall(C4AulContext *cthr,
3452 C4ID idID, C4String *szFunction,
3453 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3454 C4Value par5, C4Value par6, C4Value par7)
3455{
3456 if (!idID || !szFunction) return C4VNull;
3457 // Make failsafe
3458 char szFunc2[C4AUL_MAX_Identifier + 1];
3459 FormatWithNull(buf&: szFunc2, fmt: "~{}", args: FnStringPar(pString: szFunction));
3460 // Get definition
3461 C4Def *pDef;
3462 if (!(pDef = C4Id2Def(id: idID))) return C4VNull;
3463 // copy parameters
3464 C4AulParSet Pars;
3465 Copy2ParSet8(Pars, par);
3466 // Call
3467 return pDef->Script.Call(szFunction: szFunc2, pPars: Pars, fPassError: true, convertNilToIntBool: !cthr->CalledWithStrictNil());
3468}
3469
3470static C4Value FnGameCall(C4AulContext *cthr,
3471 C4String *szFunction,
3472 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3473 C4Value par5, C4Value par6, C4Value par7, C4Value par8)
3474{
3475 if (!szFunction) return C4VNull;
3476 // Make failsafe
3477 char szFunc2[C4AUL_MAX_Identifier + 1];
3478 FormatWithNull(buf&: szFunc2, fmt: "~{}", args: FnStringPar(pString: szFunction));
3479 // copy parameters
3480 C4AulParSet Pars;
3481 Copy2ParSet9(Pars, par);
3482 // Call
3483 return Game.Script.Call(szFunction: szFunc2, pPars: Pars, fPassError: true, convertNilToIntBool: !cthr->CalledWithStrictNil());
3484}
3485
3486static C4Value FnGameCallEx(C4AulContext *cthr,
3487 C4String *szFunction,
3488 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3489 C4Value par5, C4Value par6, C4Value par7, C4Value par8)
3490{
3491 if (!szFunction) return C4VNull;
3492 // Make failsafe
3493 char szFunc2[C4AUL_MAX_Identifier + 1];
3494 FormatWithNull(buf&: szFunc2, fmt: "~{}", args: FnStringPar(pString: szFunction));
3495 // copy parameters
3496 C4AulParSet Pars;
3497 Copy2ParSet9(Pars, par);
3498 // Call
3499 return Game.Script.GRBroadcast(szFunction: szFunc2, pPars: Pars, fPassError: true, fRejectTest: false, convertNilToIntBool: !cthr->CalledWithStrictNil());
3500}
3501
3502static C4Value FnProtectedCall(C4AulContext *cthr,
3503 C4Object *pObj, C4String *szFunction,
3504 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3505 C4Value par5, C4Value par6, C4Value par7)
3506{
3507 if (!pObj || !szFunction) return C4VNull;
3508 if (!pObj->Def) return C4VNull;
3509 // get func
3510 C4AulScriptFunc *f;
3511 if (!(f = pObj->Def->Script.GetSFunc(pIdtf: FnStringPar(pString: szFunction), AccNeeded: AA_PROTECTED, fFailSafe: true))) return C4VNull;
3512 // copy parameters
3513 C4AulParSet Pars;
3514 Copy2ParSet8(Pars, par);
3515 // exec
3516 return f->Exec(pObj, pPars: Pars, fPassErrors: true, nonStrict3WarnConversionOnly: !cthr->CalledWithStrictNil());
3517}
3518
3519static C4Value FnPrivateCall(C4AulContext *cthr,
3520 C4Object *pObj, C4String *szFunction,
3521 C4Value par0, C4Value par1, C4Value par2, C4Value par3, C4Value par4,
3522 C4Value par5, C4Value par6, C4Value par7)
3523{
3524 if (!pObj || !szFunction) return C4VNull;
3525 if (!pObj->Def) return C4VNull;
3526 // get func
3527 C4AulScriptFunc *f;
3528 if (!(f = pObj->Def->Script.GetSFunc(pIdtf: FnStringPar(pString: szFunction), AccNeeded: AA_PRIVATE, fFailSafe: true))) return C4VNull;
3529 // copy parameters
3530 C4AulParSet Pars;
3531 Copy2ParSet8(Pars, par);
3532 // exec
3533 return f->Exec(pObj, pPars: Pars, fPassErrors: true, nonStrict3WarnConversionOnly: !cthr->CalledWithStrictNil());
3534}
3535
3536static C4Object *FnEditCursor(C4AulContext *cth)
3537{
3538 if (Game.Control.SyncMode()) return nullptr;
3539 return Console.EditCursor.GetTarget();
3540}
3541
3542static void FnResort(C4AulContext *cthr, C4Object *pObj)
3543{
3544 if (!pObj) pObj = cthr->Obj;
3545 // Resort single object
3546 if (pObj)
3547 pObj->Resort();
3548 // Resort object list
3549 else
3550 Game.Objects.SortByCategory();
3551}
3552
3553static bool FnIsNetwork(C4AulContext *cthr) { return Game.Parameters.IsNetworkGame; }
3554
3555static C4String *FnGetLeague(C4AulContext *cthr, C4ValueInt idx)
3556{
3557 // get indexed league
3558 StdStrBuf sIdxLeague;
3559 if (!Game.Parameters.League.GetSection(idx, psOutSection: &sIdxLeague)) return nullptr;
3560 return String(str: sIdxLeague.getData());
3561}
3562
3563static std::optional<bool> FnTestMessageBoard(C4AulContext *cthr, C4ValueInt iForPlr, bool fTestIfInUse)
3564{
3565 // multi-query-MessageBoard is always available if the player is valid =)
3566 // (but it won't do anything in developer mode...)
3567 C4Player *pPlr = Game.Players.Get(iPlayer: iForPlr);
3568 if (!pPlr) return {};
3569 if (!fTestIfInUse) return {true};
3570 // single query only if no query is scheduled
3571 return {pPlr->HasMessageBoardQuery()};
3572}
3573
3574static bool FnCallMessageBoard(C4AulContext *cthr, C4Object *pObj, bool fUpperCase, C4String *szQueryString, C4ValueInt iForPlr)
3575{
3576 if (!pObj) pObj = cthr->Obj;
3577 if (pObj && !pObj->Status) return false;
3578 // check player
3579 C4Player *pPlr = Game.Players.Get(iPlayer: iForPlr);
3580 if (!pPlr) return false;
3581 // remove any previous
3582 pPlr->CallMessageBoard(pForObj: pObj, sQueryString: StdStrBuf::MakeRef(str: FnStringPar(pString: szQueryString)), fUppercase: !!fUpperCase);
3583 return true;
3584}
3585
3586static bool FnAbortMessageBoard(C4AulContext *cthr, C4Object *pObj, C4ValueInt iForPlr)
3587{
3588 if (!pObj) pObj = cthr->Obj;
3589 // check player
3590 C4Player *pPlr = Game.Players.Get(iPlayer: iForPlr);
3591 if (!pPlr) return false;
3592 // close TypeIn if active
3593 Game.MessageInput.AbortMsgBoardQuery(pObj, iPlr: iForPlr);
3594 // abort for it
3595 return pPlr->RemoveMessageBoardQuery(pForObj: pObj);
3596}
3597
3598static bool FnOnMessageBoardAnswer(C4AulContext *cthr, C4Object *pObj, C4ValueInt iForPlr, C4String *szAnswerString)
3599{
3600 // remove query
3601 // fail if query doesn't exist to prevent any doubled answers
3602 C4Player *pPlr = Game.Players.Get(iPlayer: iForPlr);
3603 if (!pPlr) return false;
3604 if (!pPlr->RemoveMessageBoardQuery(pForObj: pObj)) return false;
3605 // if no answer string is provided, the user did not answer anything
3606 // just remove the query
3607 if (!szAnswerString || !szAnswerString->Data.getData()) return true;
3608 // get script
3609 C4ScriptHost *scr;
3610 if (pObj) scr = &pObj->Def->Script; else scr = &Game.Script;
3611 // exec func
3612 return static_cast<bool>(scr->ObjectCall(pCaller: nullptr, pObj, PSF_InputCallback, pPars: {C4VString(strString: FnStringPar(pString: szAnswerString)), C4VInt(iVal: iForPlr)}, fPassError: true));
3613}
3614
3615static C4ValueInt FnScriptCounter(C4AulContext *cthr)
3616{
3617 return Game.Script.Counter;
3618}
3619
3620static C4ValueInt FnSetMass(C4AulContext *cthr, C4ValueInt iValue, C4Object *pObj)
3621{
3622 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3623 pObj->OwnMass = iValue - pObj->Def->Mass;
3624 pObj->UpdateMass();
3625 return true;
3626}
3627
3628static C4ValueInt FnGetColor(C4AulContext *cthr, C4Object *pObj)
3629{
3630 // oldgfx
3631 return 0;
3632}
3633
3634static C4ValueInt FnSetColor(C4AulContext *cthr, C4ValueInt iValue, C4Object *pObj)
3635{
3636 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3637 if (!Inside<C4ValueInt>(ival: iValue, lbound: 0, rbound: C4MaxColor - 1)) return false;
3638 iValue = Application.DDraw->Pal.GetClr(byCol: FColors[FPlayer + iValue]);
3639 pObj->Color = iValue;
3640 pObj->UpdateGraphics(fGraphicsChanged: false);
3641 pObj->UpdateFace(bUpdateShape: false);
3642 return true;
3643}
3644
3645static std::optional<C4ValueInt> FnGetColorDw(C4AulContext *cthr, C4Object *pObj)
3646{
3647 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
3648 return {pObj->Color};
3649}
3650
3651static std::optional<C4ValueInt> FnGetPlrColorDw(C4AulContext *cthr, C4ValueInt iPlr)
3652{
3653 // get player
3654 C4Player *pPlr = Game.Players.Get(iPlayer: iPlr);
3655 // safety
3656 if (!pPlr) return {};
3657 // return player color
3658 return {pPlr->ColorDw};
3659}
3660
3661static bool FnSetColorDw(C4AulContext *cthr, C4ValueInt iValue, C4Object *pObj)
3662{
3663 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3664 pObj->Color = iValue;
3665 pObj->UpdateGraphics(fGraphicsChanged: false);
3666 pObj->UpdateFace(bUpdateShape: false);
3667 return true;
3668}
3669
3670static C4ValueInt FnSetFoW(C4AulContext *cthr, bool fEnabled, C4ValueInt iPlr)
3671{
3672 // safety
3673 if (!ValidPlr(plr: iPlr)) return false;
3674 // set enabled
3675 Game.Players.Get(iPlayer: iPlr)->SetFoW(!!fEnabled);
3676 // success
3677 return true;
3678}
3679
3680static C4ValueInt FnSetPlrViewRange(C4AulContext *cthr, C4ValueInt iRange, C4Object *pObj, bool fExact)
3681{
3682 // local/safety
3683 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3684 // backwards compatibility for low ranges
3685 if (!fExact && iRange < 128 && iRange > 0) iRange = 128;
3686 // set range
3687 pObj->SetPlrViewRange(iRange);
3688 // success
3689 return true;
3690}
3691
3692static C4ValueInt FnGetMaxPlayer(C4AulContext *cthr)
3693{
3694 return Game.Parameters.MaxPlayers;
3695}
3696
3697static C4ValueInt FnSetMaxPlayer(C4AulContext *cthr, C4ValueInt iTo)
3698{
3699 // think positive! :)
3700 if (iTo < 0) return false;
3701 // script functions don't need to pass ControlQueue!
3702 Game.Parameters.MaxPlayers = iTo;
3703 // success
3704 return true;
3705}
3706
3707static C4ValueInt FnSetPicture(C4AulContext *cthr, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4Object *pObj)
3708{
3709 // local/safety
3710 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3711 // set new picture rect
3712 pObj->PictureRect.Set(iX, iY, iWdt, iHgt);
3713 // success
3714 return true;
3715}
3716
3717static C4String *FnGetProcedure(C4AulContext *cthr, C4Object *pObj)
3718{
3719 // local/safety
3720 if (!pObj) pObj = cthr->Obj; if (!pObj) return nullptr;
3721 // no action?
3722 if (pObj->Action.Act <= ActIdle) return nullptr;
3723 // get proc
3724 C4ValueInt iProc = pObj->Def->ActMap[pObj->Action.Act].Procedure;
3725 // NONE?
3726 if (iProc <= DFA_NONE) return nullptr;
3727 // return procedure name
3728 return String(str: ProcedureName[iProc]);
3729}
3730
3731static C4Object *FnBuy(C4AulContext *cthr, C4ID idBuyObj, C4ValueInt iForPlr, C4ValueInt iPayPlr, C4Object *pToBase, bool fShowErrors)
3732{
3733 // safety
3734 if (!ValidPlr(plr: iForPlr) || !ValidPlr(plr: iPayPlr)) return nullptr;
3735 // buy
3736 C4Object *pThing;
3737 if (!(pThing = Game.Players.Get(iPlayer: iPayPlr)->Buy(id: idBuyObj, fShowErrors, iForPlr, pBuyObj: pToBase ? pToBase : cthr->Obj))) return nullptr;
3738 // enter object, if supplied
3739 if (pToBase)
3740 {
3741 pThing->Enter(pTarget: pToBase, fCalls: true);
3742 }
3743 else
3744 {
3745 // no target object? get local pos
3746 if (cthr->Obj) pThing->ForcePosition(tx: cthr->Obj->x, ty: cthr->Obj->y);
3747 }
3748 // done
3749 return pThing;
3750}
3751
3752static bool FnSell(C4AulContext *cthr, C4ValueInt iToPlr, C4Object *pObj)
3753{
3754 // local/safety
3755 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3756 if (!ValidPlr(plr: iToPlr)) return false;
3757 // sell
3758 return Game.Players.Get(iPlayer: iToPlr)->Sell2Home(tobj: pObj);
3759}
3760
3761// ** additional funcs for references/type info
3762
3763static C4Value FnSet(C4AulContext *cthr, C4Value *Dest, C4Value Src)
3764{
3765 *Dest = Src;
3766 return *Dest;
3767}
3768
3769static C4Value FnInc(C4AulContext *cthr, C4Value *Value, C4Value Diff)
3770{
3771 if (!Value->GetRefVal().ConvertTo(vtToType: C4V_Int))
3772 return C4VNull;
3773
3774 *Value += Diff.getInt();
3775
3776 return *Value;
3777}
3778
3779static C4Value FnDec(C4AulContext *cthr, C4Value *Value, C4Value Diff)
3780{
3781 if (!Value->GetRefVal().ConvertTo(vtToType: C4V_Int))
3782 return C4VNull;
3783
3784 *Value -= Diff.getInt();
3785
3786 return *Value;
3787}
3788
3789static bool FnIsRef(C4AulContext *cthr, C4Value Value)
3790{
3791 return Value.IsRef();
3792}
3793
3794static C4ValueInt FnGetType(C4AulContext *cthr, C4Value Value)
3795{
3796 if (!Value._getBool() && cthr->Caller && !cthr->CalledWithStrictNil()) return C4V_Any;
3797 return Value.GetType();
3798}
3799
3800static C4ValueArray *FnCreateArray(C4AulContext *cthr, C4ValueInt iSize)
3801{
3802 return new C4ValueArray(iSize);
3803}
3804
3805static std::optional<C4ValueInt> FnGetLength(C4AulContext *cthr, C4Value pPar)
3806{
3807 // support GetLength() etc.
3808 if (!pPar) return {};
3809 if (auto map = pPar.getMap())
3810 return {map->size()};
3811 C4ValueArray *pArray = pPar.getArray();
3812 if (pArray)
3813 return {pArray->GetSize()};
3814 C4String *pStr = pPar.getStr();
3815 if (pStr)
3816 return {pStr->Data.getLength()};
3817 throw C4AulExecError(cthr->Obj, "func \"GetLength\" par 0 cannot be converted to string or array or map");
3818}
3819
3820static C4ValueInt FnGetIndexOf(C4AulContext *cthr, C4Value searchVal, C4ValueArray *pArray)
3821{
3822 // find first occurance of first parameter in array
3823 // support GetIndexOf(x, 0)
3824 if (!pArray) return -1;
3825 // find the element by comparing data only - this may result in bogus results if an object ptr array is searched for an int
3826 // however, that's rather unlikely and strange scripting style
3827 int32_t iSize = pArray->GetSize();
3828
3829 if (!cthr->Caller || cthr->Caller->Func->pOrgScript->Strict >= C4AulScriptStrict::STRICT2)
3830 {
3831 auto strict = cthr->Caller->Func->pOrgScript->Strict;
3832 const auto &val = searchVal.GetRefVal();
3833 for (int32_t i = 0; i < iSize; ++i)
3834 if (val.Equals(other: pArray->GetItem(index: i), strict))
3835 // element found
3836 return i;
3837 }
3838 else
3839 {
3840 const auto cmp = searchVal._getRaw();
3841 for (int32_t i = 0; i < iSize; ++i)
3842 if (cmp == pArray->GetItem(index: i)._getRaw())
3843 // element found
3844 return i;
3845 }
3846 // element not found
3847 return -1;
3848}
3849
3850static void FnSetLength(C4AulContext *cthr, C4Value *pArrayRef, C4ValueInt iNewSize)
3851{
3852 // safety
3853 if (iNewSize < 0 || iNewSize > C4ValueList::MaxSize)
3854 throw C4AulExecError(cthr->Obj, std::format(fmt: "SetLength: invalid array size ({})", args&: iNewSize));
3855
3856 // set new size
3857 pArrayRef->SetArrayLength(size: iNewSize, cthr);
3858}
3859
3860static bool FnSetVisibility(C4AulContext *cthr, C4ValueInt iVisibility, C4Object *pObj)
3861{
3862 // local call/safety
3863 if (!pObj) pObj = cthr->Obj;
3864 if (!pObj) return false;
3865
3866 pObj->Visibility = iVisibility;
3867
3868 return true;
3869}
3870
3871static std::optional<C4ValueInt> FnGetVisibility(C4AulContext *cthr, C4Object *pObj)
3872{
3873 if (!pObj) pObj = cthr->Obj;
3874 if (!pObj) return {};
3875
3876 return {pObj->Visibility};
3877}
3878
3879static bool FnSetClrModulation(C4AulContext *cthr, C4ValueInt dwClr, C4Object *pObj, C4ValueInt iOverlayID)
3880{
3881 // local call/safety
3882 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
3883 // overlay?
3884 if (iOverlayID)
3885 {
3886 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
3887 if (!pOverlay)
3888 {
3889 DebugLog(level: spdlog::level::err, fmt: "SetClrModulation: Overlay {} not defined for object {} ({})", args: static_cast<int>(iOverlayID), args: static_cast<int>(pObj->Number), args: pObj->GetName());
3890 return false;
3891 }
3892 pOverlay->SetClrModulation(dwClr);
3893 }
3894 else
3895 {
3896 // set it
3897 pObj->ColorMod = dwClr;
3898 }
3899 // success
3900 return true;
3901}
3902
3903static std::optional<C4ValueInt> FnGetClrModulation(C4AulContext *cthr, C4Object *pObj, C4ValueInt iOverlayID)
3904{
3905 // local call/safety
3906 if (!pObj) pObj = cthr->Obj; if (!pObj) return {};
3907 // overlay?
3908 if (iOverlayID)
3909 {
3910 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
3911 if (!pOverlay)
3912 {
3913 DebugLog(level: spdlog::level::err, fmt: "GetClrModulation: Overlay {} not defined for object {} ({})", args: static_cast<int>(iOverlayID), args: static_cast<int>(pObj->Number), args: pObj->GetName());
3914 return {};
3915 }
3916 return {pOverlay->GetClrModulation()};
3917 }
3918 else
3919 // get it
3920 return {pObj->ColorMod};
3921}
3922
3923static bool FnGetMissionAccess(C4AulContext *cthr, C4String *strMissionAccess)
3924{
3925 // safety
3926 if (!strMissionAccess) return false;
3927
3928 // non-sync mode: warn
3929 if (Game.Control.SyncMode())
3930 LogNTr(level: spdlog::level::warn, message: "using GetMissionAccess may cause desyncs when playing records!");
3931
3932 return SIsModule(szList: Config.General.MissionAccess, szString: FnStringPar(pString: strMissionAccess));
3933}
3934
3935// Helper to read or write a value from/to a structure. Must be two
3936class C4ValueCompiler : public StdCompiler
3937{
3938public:
3939 C4ValueCompiler(const char **pszNames, int iNameCnt, int iEntryNr)
3940 : pszNames(pszNames), iNameCnt(iNameCnt), iEntryNr(iEntryNr) {}
3941
3942 virtual bool isCompiler() override { return false; }
3943 virtual bool hasNaming() override { return true; }
3944 virtual bool isVerbose() override { return false; }
3945
3946 virtual NameGuard Name(const char *szName) override
3947 {
3948 // match possible? (no match yet / continued match)
3949 if (!iMatchStart || haveCurrentMatch())
3950 // already got all names?
3951 if (!haveCompleteMatch())
3952 // check name
3953 if (SEqual(szStr1: pszNames[iMatchCount], szStr2: szName))
3954 {
3955 // got match
3956 if (!iMatchCount) iMatchStart = iDepth + 1;
3957 iMatchCount++;
3958 }
3959 iDepth++;
3960 return {this, true};
3961 }
3962
3963 virtual bool Default(const char *szName) override
3964 {
3965 // Always process values even if they are default!
3966 return false;
3967 }
3968
3969 virtual void NameEnd(bool fBreak = false) override
3970 {
3971 // end of matched name section?
3972 if (haveCurrentMatch())
3973 {
3974 iMatchCount--;
3975 if (!iMatchCount) iMatchStart = 0;
3976 }
3977 iDepth--;
3978 }
3979
3980 virtual void Begin() override
3981 {
3982 // set up
3983 iDepth = iMatchStart = iMatchCount = 0;
3984 }
3985
3986protected:
3987 // value function forward to be overwritten by get or set compiler
3988 virtual void ProcessInt(int32_t &rInt) = 0;
3989 virtual void ProcessBool(bool &rBool) = 0;
3990 virtual void ProcessChar(char &rChar) = 0;
3991 virtual void ProcessString(char *szString, size_t iMaxLength, bool fIsID) = 0;
3992 virtual void ProcessString(std::string &str, bool isID) = 0;
3993
3994public:
3995 // value functions
3996 virtual void QWord(int64_t &rInt) override { if (haveCompleteMatch()) if (!iEntryNr--) { auto i = static_cast<int32_t>(rInt); ProcessInt(rInt&: i); rInt = i; } }
3997 virtual void QWord(uint64_t &rInt) override { if (haveCompleteMatch()) if (!iEntryNr--) { auto i = static_cast<int32_t>(rInt); ProcessInt(rInt&: i); rInt = i; } }
3998 virtual void DWord(int32_t &rInt) override { if (haveCompleteMatch()) if (!iEntryNr--) ProcessInt(rInt); }
3999 virtual void DWord(uint32_t &rInt) override { if (haveCompleteMatch()) if (!iEntryNr--) { int32_t i = rInt; ProcessInt(rInt&: i); rInt = i; } }
4000 virtual void Word(int16_t &rShort) override { if (haveCompleteMatch()) if (!iEntryNr--) { int32_t i = rShort; ProcessInt(rInt&: i); rShort = i; } }
4001 virtual void Word(uint16_t &rShort) override { if (haveCompleteMatch()) if (!iEntryNr--) { int32_t i = rShort; ProcessInt(rInt&: i); rShort = i; } }
4002 virtual void Byte(int8_t &rByte) override { if (haveCompleteMatch()) if (!iEntryNr--) { int32_t i = rByte; ProcessInt(rInt&: i); rByte = i; } }
4003 virtual void Byte(uint8_t &rByte) override { if (haveCompleteMatch()) if (!iEntryNr--) { int32_t i = rByte; ProcessInt(rInt&: i); rByte = i; } }
4004 virtual void Boolean(bool &rBool) override { if (haveCompleteMatch()) if (!iEntryNr--) ProcessBool(rBool); }
4005 virtual void Character(char &rChar) override { if (haveCompleteMatch()) if (!iEntryNr--) ProcessChar(rChar); }
4006
4007 // The C4ID-Adaptor will set RCT_ID for it's strings (see C4Id.h), so we don't have to guess the type.
4008 virtual void String(char *szString, size_t iMaxLength, RawCompileType eType) override
4009 {
4010 if (haveCompleteMatch()) if (!iEntryNr--) ProcessString(szString, iMaxLength, fIsID: eType == StdCompiler::RCT_ID);
4011 }
4012 virtual void String(std::string &str, RawCompileType type) override
4013 {
4014 if (haveCompleteMatch()) if (!iEntryNr--) ProcessString(str, isID: type == StdCompiler::RCT_ID);
4015 }
4016 virtual void Raw(void *pData, size_t iSize, RawCompileType eType = RCT_Escaped) override
4017 {
4018 /* C4Script can't handle this */
4019 }
4020
4021private:
4022 // Name(s) of the entry to read
4023 const char **pszNames;
4024 int iNameCnt;
4025 // Number of the element that is to be read
4026 int iEntryNr;
4027
4028 // current depth
4029 int iDepth;
4030 // match start (where did the first name match?),
4031 // match count (how many names did match, from that point?)
4032 int iMatchStart, iMatchCount;
4033
4034private:
4035 // match active?
4036 bool haveCurrentMatch() const { return iDepth + 1 == iMatchStart + iMatchCount; }
4037 // match complete?
4038 bool haveCompleteMatch() const { return haveCurrentMatch() && iMatchCount == iNameCnt; }
4039};
4040
4041class C4ValueGetCompiler : public C4ValueCompiler
4042{
4043private:
4044 // Result
4045 C4Value Res;
4046
4047public:
4048 C4ValueGetCompiler(const char **pszNames, int iNameCnt, int iEntryNr)
4049 : C4ValueCompiler(pszNames, iNameCnt, iEntryNr) {}
4050
4051 // Result-getter
4052 const C4Value &getResult() const { return Res; }
4053
4054protected:
4055 // get values as C4Value
4056 virtual void ProcessInt(int32_t &rInt) override { Res = C4VInt(iVal: rInt); }
4057 virtual void ProcessBool(bool &rBool) override { Res = C4VBool(fVal: rBool); }
4058 virtual void ProcessChar(char &rChar) override { Res = C4VString(strString: std::format(fmt: "{}", args&: rChar).c_str()); }
4059
4060 virtual void ProcessString(char *szString, size_t iMaxLength, bool fIsID) override
4061 {
4062 Res = (fIsID ? C4VID(idVal: C4Id(str: szString)) : C4VString(strString: szString));
4063 }
4064 virtual void ProcessString(std::string &str, bool fIsID) override
4065 {
4066 Res = (fIsID ? C4VID(idVal: C4Id(str: str.c_str())) : C4VString(strString: str.c_str()));
4067 }
4068};
4069
4070class C4ValueSetCompiler : public C4ValueCompiler
4071{
4072private:
4073 C4Value Val; // value to which the setting should be set
4074 bool fSuccess; // set if the value could be set successfully
4075 int32_t iRuntimeWriteAllowed; // if >0, runtime writing of values is allowed
4076
4077public:
4078 C4ValueSetCompiler(const char **pszNames, int iNameCnt, int iEntryNr, const C4Value &rSetVal)
4079 : C4ValueCompiler(pszNames, iNameCnt, iEntryNr), Val(rSetVal), fSuccess(false), iRuntimeWriteAllowed(0) {}
4080
4081 // Query successful setting
4082 bool getSuccess() const { return fSuccess; }
4083
4084 virtual void setRuntimeWritesAllowed(int32_t iChange) override { iRuntimeWriteAllowed += iChange; }
4085
4086protected:
4087 // set values as C4Value, only if type matches or is convertible
4088 virtual void ProcessInt(int32_t &rInt) override { if (iRuntimeWriteAllowed > 0 && Val.ConvertTo(vtToType: C4V_Int)) { rInt = Val.getInt(); fSuccess = true; } }
4089 virtual void ProcessBool(bool &rBool) override { if (iRuntimeWriteAllowed > 0 && Val.ConvertTo(vtToType: C4V_Bool)) { rBool = Val.getBool(); fSuccess = true; } }
4090 virtual void ProcessChar(char &rChar) override { C4String *s; if (iRuntimeWriteAllowed > 0 && (s = Val.getStr())) { rChar = s->Data.getData() ? *s->Data.getData() : 0; fSuccess = true; } }
4091
4092 virtual void ProcessString(char *szString, size_t iMaxLength, bool fIsID) override
4093 {
4094 if (iRuntimeWriteAllowed <= 0 || !Val.ConvertTo(vtToType: fIsID ? C4V_C4ID : C4V_String)) return;
4095 if (fIsID)
4096 {
4097 assert(iMaxLength >= 4); // fields that should carry an ID are guaranteed to have a buffer that's large enough
4098 GetC4IdText(id: Val.getC4ID(), sBuf: szString);
4099 }
4100 else
4101 {
4102 C4String *s = Val.getStr(); if (!s) return;
4103 StdStrBuf &rsBuf = s->Data;
4104 if (rsBuf.getData()) SCopy(szSource: rsBuf.getData(), sTarget: szString, iMaxL: iMaxLength); else *szString = 0;
4105 }
4106 fSuccess = true;
4107 }
4108
4109 virtual void ProcessString(std::string &str, bool isID) override
4110 {
4111 if (iRuntimeWriteAllowed <= 0 || !Val.ConvertTo(vtToType: isID ? C4V_C4ID : C4V_String)) return;
4112 if (isID)
4113 {
4114 assert(str.size() >= 4); // fields that should carry an ID are guaranteed to have a buffer that's large enough
4115 char buf[5];
4116 GetC4IdText(id: Val.getC4ID(), sBuf: buf);
4117 str = buf;
4118 }
4119 else
4120 {
4121 C4String *s = Val.getStr(); if (!s) return;
4122 str = s->Data.getData();
4123 }
4124 fSuccess = true;
4125 }
4126};
4127
4128// Use the compiler to find a named value in a structure
4129template <class T>
4130C4Value GetValByStdCompiler(const char *strEntry, const char *strSection, int iEntryNr, const T &rFrom)
4131{
4132 // Set up name array, create compiler
4133 const char *szNames[2] = { strSection ? strSection : strEntry, strSection ? strEntry : nullptr };
4134 C4ValueGetCompiler Comp(szNames, strSection ? 2 : 1, iEntryNr);
4135
4136 // Compile
4137 try
4138 {
4139 Comp.Decompile(rFrom);
4140 return Comp.getResult();
4141 }
4142 // Should not happen, catch it anyway.
4143 catch (const StdCompiler::Exception &)
4144 {
4145 return C4VNull;
4146 }
4147}
4148
4149// Use the compiler to set a named value in a structure
4150template <class T>
4151bool SetValByStdCompiler(const char *strEntry, const char *strSection, int iEntryNr, const T &rTo, const C4Value &rvNewVal)
4152{
4153 // Set up name array, create compiler
4154 const char *szNames[2] = { strSection ? strSection : strEntry, strSection ? strEntry : nullptr };
4155 C4ValueSetCompiler Comp(szNames, strSection ? 2 : 1, iEntryNr, rvNewVal);
4156
4157 // Compile
4158 try
4159 {
4160 Comp.Decompile(rTo);
4161 return Comp.getSuccess();
4162 }
4163 // Should not happen, catch it anyway.
4164 catch (const StdCompiler::Exception &)
4165 {
4166 return false;
4167 }
4168}
4169
4170static C4Value FnGetDefCoreVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4ID idDef, C4ValueInt iEntryNr)
4171{
4172 const char *strSection = FnStringPar(pString: section);
4173 if (strSection && !*strSection) strSection = nullptr;
4174
4175 if (!idDef && cthr->Def) idDef = cthr->Def->id;
4176 if (!idDef) return C4VNull;
4177
4178 C4Def *pDef = C4Id2Def(id: idDef);
4179 if (!pDef) return C4VNull;
4180
4181 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: mkNamingAdapt(rValue&: *pDef, szName: "DefCore"));
4182}
4183
4184static C4Value FnGetObjectVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4Object *pObj, C4ValueInt iEntryNr)
4185{
4186 const char *strSection = FnStringPar(pString: section);
4187 if (!*strSection) strSection = nullptr;
4188
4189 if (!pObj) pObj = cthr->Obj;
4190 if (!pObj) return C4VNull;
4191
4192 // get value
4193 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: mkNamingAdapt(rValue&: *pObj, szName: "Object"));
4194}
4195
4196static C4Value FnGetObjectInfoCoreVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4Object *pObj, C4ValueInt iEntryNr)
4197{
4198 const char *strSection = FnStringPar(pString: section);
4199 if (strSection && !*strSection) strSection = nullptr;
4200
4201 if (!pObj) pObj = cthr->Obj;
4202 if (!pObj) return C4VNull;
4203
4204 // get obj info
4205 C4ObjectInfo *pObjInfo = pObj->Info;
4206
4207 if (!pObjInfo) return C4VNull;
4208
4209 // get obj info core
4210 C4ObjectInfoCore *pObjInfoCore = static_cast<C4ObjectInfoCore *>(pObjInfo);
4211
4212 // get value
4213 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: mkNamingAdapt(rValue&: *pObjInfoCore, szName: "ObjectInfo"));
4214}
4215
4216static C4Value FnGetActMapVal(C4AulContext *cthr, C4String *strEntry, C4String *action, C4ID idDef, C4ValueInt iEntryNr)
4217{
4218 if (!idDef && cthr->Def) idDef = cthr->Def->id;
4219 if (!idDef) return C4VNull;
4220
4221 C4Def *pDef = C4Id2Def(id: idDef);
4222
4223 if (!pDef) return C4VNull;
4224
4225 C4ActionDef *pAct = pDef->ActMap;
4226
4227 if (!pAct) return C4VNull;
4228
4229 const char *strAction = FnStringPar(pString: action);
4230 C4ValueInt iAct;
4231 for (iAct = 0; iAct < pDef->ActNum; iAct++, pAct++)
4232 if (SEqual(szStr1: pAct->Name, szStr2: strAction))
4233 break;
4234
4235 // not found?
4236 if (iAct >= pDef->ActNum)
4237 return C4VNull;
4238
4239 // get value
4240 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection: nullptr, iEntryNr, rFrom: *pAct);
4241}
4242
4243static C4Value FnGetScenarioVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4ValueInt iEntryNr)
4244{
4245 const char *strSection = FnStringPar(pString: section);
4246 if (strSection && !*strSection) strSection = nullptr;
4247
4248 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: mkParAdapt(rObj&: Game.C4S, rPar: false));
4249}
4250
4251static C4Value FnGetPlayerVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4ValueInt iPlr, C4ValueInt iEntryNr)
4252{
4253 const char *strSection = FnStringPar(pString: section);
4254 if (strSection && !*strSection) strSection = nullptr;
4255
4256 if (!ValidPlr(plr: iPlr)) return C4VNull;
4257
4258 // get player
4259 C4Player *pPlayer = Game.Players.Get(iPlayer: iPlr);
4260
4261 // get value
4262 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: mkNamingAdapt(rValue&: *pPlayer, szName: "Player"));
4263}
4264
4265static C4Value FnGetPlayerInfoCoreVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4ValueInt iPlr, C4ValueInt iEntryNr)
4266{
4267 const char *strSection = FnStringPar(pString: section);
4268 if (strSection && !*strSection) strSection = nullptr;
4269
4270 if (!ValidPlr(plr: iPlr)) return C4VNull;
4271
4272 // get player
4273 C4Player *pPlayer = Game.Players.Get(iPlayer: iPlr);
4274
4275 // get plr info core
4276 C4PlayerInfoCore *pPlayerInfoCore = static_cast<C4PlayerInfoCore *>(pPlayer);
4277
4278 // get value
4279 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection, iEntryNr, rFrom: *pPlayerInfoCore);
4280}
4281
4282static C4Value FnGetMaterialVal(C4AulContext *cthr, C4String *strEntry, C4String *section, C4ValueInt iMat, C4ValueInt iEntryNr)
4283{
4284 const char *strSection = FnStringPar(pString: section);
4285 if (strSection && !*strSection) strSection = nullptr;
4286
4287 if (iMat < 0 || iMat >= Game.Material.Num) return C4VNull;
4288
4289 // get material
4290 C4Material *pMaterial = &Game.Material.Map[iMat];
4291
4292 // get plr info core
4293 C4MaterialCore *pMaterialCore = static_cast<C4MaterialCore *>(pMaterial);
4294
4295 // material core implicates section "Material"
4296 if (!SEqual(szStr1: strSection, szStr2: "Material")) return C4VNull;
4297
4298 // get value
4299 return GetValByStdCompiler(strEntry: FnStringPar(pString: strEntry), strSection: nullptr, iEntryNr, rFrom: *pMaterialCore);
4300}
4301
4302static bool FnCloseMenu(C4AulContext *cthr, C4Object *pObj)
4303{
4304 if (!pObj) pObj = cthr->Obj;
4305 if (!pObj) return false;
4306 return pObj->CloseMenu(fForce: true);
4307}
4308
4309static C4ValueInt FnGetMenuSelection(C4AulContext *cthr, C4Object *pObj)
4310{
4311 if (!pObj) pObj = cthr->Obj;
4312 if (!pObj) return -1;
4313 if (!pObj->Menu || !pObj->Menu->IsActive()) return -1;
4314 return pObj->Menu->GetSelection();
4315}
4316
4317static bool FnResortObjects(C4AulContext *cthr, C4String *szFunc, C4ValueInt Category)
4318{
4319 // safety
4320 if (!szFunc) return false;
4321 if (!cthr->Caller) return false;
4322 // default category
4323 if (!Category) Category = C4D_SortLimit;
4324 // get function
4325 C4AulFunc *pFn = cthr->Caller->Func->GetLocalSFunc(szIdtf: FnStringPar(pString: szFunc));
4326 if (!pFn)
4327 throw C4AulExecError(cthr->Obj, std::format(fmt: "ResortObjects: Resort function {} not found", args: FnStringPar(pString: szFunc)));
4328 // create object resort
4329 C4ObjResort *pObjRes = new C4ObjResort();
4330 pObjRes->Category = Category;
4331 pObjRes->OrderFunc = pFn;
4332 // insert into game resort proc list
4333 pObjRes->Next = Game.Objects.ResortProc;
4334 Game.Objects.ResortProc = pObjRes;
4335 // success, so far
4336 return true;
4337}
4338
4339static bool FnResortObject(C4AulContext *cthr, C4String *szFunc, C4Object *pObj)
4340{
4341 // safety
4342 if (!szFunc) return false;
4343 if (!cthr->Caller) return false;
4344 if (!pObj) if (!(pObj = cthr->Obj)) return false;
4345 // get function
4346 C4AulFunc *pFn = cthr->Caller->Func->GetLocalSFunc(szIdtf: FnStringPar(pString: szFunc));
4347 if (!pFn)
4348 throw C4AulExecError(cthr->Obj, std::format(fmt: "ResortObjects: Resort function {} not found", args: FnStringPar(pString: szFunc)));
4349 // create object resort
4350 C4ObjResort *pObjRes = new C4ObjResort();
4351 pObjRes->OrderFunc = pFn;
4352 pObjRes->pSortObj = pObj;
4353 // insert into game resort proc list
4354 pObjRes->Next = Game.Objects.ResortProc;
4355 Game.Objects.ResortProc = pObjRes;
4356 // success, so far
4357 return true;
4358}
4359
4360static std::optional<C4ValueInt> FnGetChar(C4AulContext *cthr, C4String *pString, C4ValueInt iIndex)
4361{
4362 const char *szText = FnStringPar(pString);
4363 if (!szText) return {};
4364 // loop and check for end of string
4365 for (C4ValueInt i = 0; i < iIndex; i++, szText++)
4366 if (!*szText) return 0;
4367 // return indiced character value
4368 return static_cast<unsigned char>(*szText);
4369}
4370
4371static bool FnSetGraphics(C4AulContext *pCtx, C4String *pGfxName, C4Object *pObj, C4ID idSrcGfx, C4ValueInt iOverlayID, C4ValueInt iOverlayMode, C4String *pAction, C4ValueInt dwBlitMode, C4Object *pOverlayObject)
4372{
4373 // safety
4374 if (!pObj) if (!(pObj = pCtx->Obj)) return false;
4375 if (!pObj->Status) return false;
4376 // get def for source graphics
4377 C4Def *pSrcDef = nullptr;
4378 if (idSrcGfx) if (!(pSrcDef = C4Id2Def(id: idSrcGfx))) return false;
4379 // setting overlay?
4380 if (iOverlayID)
4381 {
4382 // any overlays must be positive for now
4383 if (iOverlayID < 0) { LogNTr(level: spdlog::level::err, message: "SetGraphics: Background overlays not implemented!"); return false; }
4384 // deleting overlay?
4385 C4DefGraphics *pGrp;
4386 if (iOverlayMode == C4GraphicsOverlay::MODE_Object)
4387 {
4388 if (!pOverlayObject) return pObj->RemoveGraphicsOverlay(iOverlayID);
4389 }
4390 else
4391 {
4392 if (!pSrcDef) return pObj->RemoveGraphicsOverlay(iOverlayID);
4393 pGrp = pSrcDef->Graphics.Get(szGrpName: FnStringPar(pString: pGfxName));
4394 if (!pGrp) return false;
4395 }
4396 // adding/setting
4397 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: true);
4398 switch (iOverlayMode)
4399 {
4400 case C4GraphicsOverlay::MODE_Base:
4401 pOverlay->SetAsBase(pBaseGfx: pGrp, dwBMode: dwBlitMode);
4402 break;
4403
4404 case C4GraphicsOverlay::MODE_Action:
4405 pOverlay->SetAsAction(pBaseGfx: pGrp, szAction: FnStringPar(pString: pAction), dwBMode: dwBlitMode);
4406 break;
4407
4408 case C4GraphicsOverlay::MODE_IngamePicture:
4409 pOverlay->SetAsIngamePicture(pBaseGfx: pGrp, dwBMode: dwBlitMode);
4410 break;
4411
4412 case C4GraphicsOverlay::MODE_Picture:
4413 pOverlay->SetAsPicture(pBaseGfx: pGrp, dwBMode: dwBlitMode);
4414 break;
4415
4416 case C4GraphicsOverlay::MODE_Object:
4417 if (pOverlayObject && !pOverlayObject->Status) pOverlayObject = nullptr;
4418 pOverlay->SetAsObject(pOverlayObj: pOverlayObject, dwBMode: dwBlitMode);
4419 break;
4420
4421 case C4GraphicsOverlay::MODE_ExtraGraphics:
4422 pOverlay->SetAsExtraGraphics(pGfx: pGrp, dwBMode: dwBlitMode);
4423 break;
4424
4425 default:
4426 DebugLog(message: "SetGraphics: Invalid overlay mode");
4427 pOverlay->SetAsBase(pBaseGfx: nullptr, dwBMode: 0); // make invalid, so it will be removed
4428 break;
4429 }
4430 // remove if invalid
4431 if (!pOverlay->IsValid(pForObj: pObj))
4432 {
4433 pObj->RemoveGraphicsOverlay(iOverlayID);
4434 return false;
4435 }
4436 // Okay, valid overlay set!
4437 return true;
4438 }
4439 // no overlay: Base graphics
4440 // set graphics - pSrcDef==nullptr defaults to pObj->pDef
4441 return pObj->SetGraphics(szGraphicsName: FnStringPar(pString: pGfxName), pSourceDef: pSrcDef);
4442}
4443
4444static std::optional<C4ValueInt> FnGetDefBottom(C4AulContext *cthr, C4Object *pObj)
4445{
4446 if (!pObj) if (!(pObj = cthr->Obj)) return {};
4447 return pObj->y + pObj->Def->Shape.y + pObj->Def->Shape.Hgt;
4448}
4449
4450static bool FnSetMaterialColor(C4AulContext *cthr, C4ValueInt iMat, C4ValueInt iClr1R, C4ValueInt iClr1G, C4ValueInt iClr1B, C4ValueInt iClr2R, C4ValueInt iClr2G, C4ValueInt iClr2B, C4ValueInt iClr3R, C4ValueInt iClr3G, C4ValueInt iClr3B)
4451{
4452 // get mat
4453 if (!MatValid(mat: iMat)) return false;
4454 C4Material *pMat = &Game.Material.Map[iMat];
4455 // newgfx: emulate by landscape modulation - enlightment not allowed...
4456 uint32_t dwBack, dwMod = GetClrModulation(C4RGB(pMat->Color[0], pMat->Color[1], pMat->Color[2]), C4RGB(iClr1R, iClr1G, iClr1B), back&: dwBack);
4457 dwMod &= 0xffffff;
4458 if (!dwMod) dwMod = 1;
4459 if (dwMod == 0xffffff) dwMod = 0;
4460 Game.Landscape.SetModulation(dwMod);
4461 // done
4462 return true;
4463}
4464
4465static std::optional<C4ValueInt> FnGetMaterialColor(C4AulContext *cthr, C4ValueInt iMat, C4ValueInt iNum, C4ValueInt iChannel)
4466{
4467 // get mat
4468 if (!MatValid(mat: iMat)) return {};
4469 C4Material *pMat = &Game.Material.Map[iMat];
4470 // get color
4471 return pMat->Color[iNum * 3 + iChannel];
4472}
4473
4474static C4String *FnMaterialName(C4AulContext *cthr, C4ValueInt iMat)
4475{
4476 // mat valid?
4477 if (!MatValid(mat: iMat)) return nullptr;
4478 // return mat name
4479 return String(str: Game.Material.Map[iMat].Name);
4480}
4481
4482static bool FnSetMenuSize(C4AulContext *cthr, C4ValueInt iCols, C4ValueInt iRows, C4Object *pObj)
4483{
4484 // get object
4485 if (!pObj) pObj = cthr->Obj; if (!pObj) return false;
4486 // get menu
4487 C4Menu *pMnu = pObj->Menu;
4488 if (!pMnu || !pMnu->IsActive()) return false;
4489 pMnu->SetSize(iToWdt: BoundBy<C4ValueInt>(bval: iCols, lbound: 0, rbound: 50), iToHgt: BoundBy<C4ValueInt>(bval: iRows, lbound: 0, rbound: 50));
4490 return true;
4491}
4492
4493static C4String *FnGetNeededMatStr(C4AulContext *cthr, C4Object *pObj)
4494{
4495 // local/safety
4496 if (!pObj) if (!(pObj = cthr->Obj)) return nullptr;
4497 return String(str: pObj->GetNeededMatStr(pBuilder: cthr->Obj).c_str());
4498}
4499
4500static C4Value FnEval(C4AulContext *cthr, C4String *strScript)
4501{
4502 // execute script in the same object
4503 C4AulScriptStrict Strict = C4AulScriptStrict::MAXSTRICT;
4504 if (cthr->Caller)
4505 Strict = cthr->Caller->Func->pOrgScript->Strict;
4506 if (cthr->Obj)
4507 return cthr->Obj->Def->Script.DirectExec(pObj: cthr->Obj, szScript: FnStringPar(pString: strScript), szContext: "eval", fPassErrors: true, Strict);
4508 else if (cthr->Def)
4509 return cthr->Def->Script.DirectExec(pObj: nullptr, szScript: FnStringPar(pString: strScript), szContext: "eval", fPassErrors: true, Strict);
4510 else
4511 return Game.Script.DirectExec(pObj: nullptr, szScript: FnStringPar(pString: strScript), szContext: "eval", fPassErrors: true, Strict);
4512}
4513
4514static bool FnLocateFunc(C4AulContext *cthr, C4String *funcname, C4Object *pObj, C4ID idDef)
4515{
4516 // safety
4517 if (!funcname || !funcname->Data.getData())
4518 {
4519 LogNTr(level: spdlog::level::err, message: "No func name");
4520 return false;
4521 }
4522 // determine script context
4523 C4AulScript *pCheckScript;
4524 if (pObj)
4525 {
4526 pCheckScript = &pObj->Def->Script;
4527 }
4528 else if (idDef)
4529 {
4530 C4Def *pDef = C4Id2Def(id: idDef);
4531 if (!pDef) { LogNTr(level: spdlog::level::err, message: "Invalid or unloaded def"); return false; }
4532 pCheckScript = &pDef->Script;
4533 }
4534 else
4535 {
4536 if (!cthr || !cthr->Caller || !cthr->Caller->Func || !cthr->Caller->Func->Owner)
4537 {
4538 LogNTr(level: spdlog::level::err, message: "No valid script context");
4539 return false;
4540 }
4541 pCheckScript = cthr->Caller->Func->Owner;
4542 }
4543 // get function by name
4544 C4AulFunc *pFunc = pCheckScript->GetFuncRecursive(pIdtf: funcname->Data.getData());
4545 if (!pFunc)
4546 {
4547 LogNTr(level: spdlog::level::err, fmt: "Func {} not found", args: funcname->Data.getData());
4548 }
4549 else
4550 {
4551 const char *szPrefix = "";
4552 while (pFunc)
4553 {
4554 C4AulScriptFunc *pSFunc = pFunc->SFunc();
4555 if (!pSFunc)
4556 {
4557 LogNTr(fmt: "{}{} (engine)", args&: szPrefix, args: +pFunc->Name);
4558 }
4559 else if (!pSFunc->pOrgScript)
4560 {
4561 LogNTr(fmt: "{}{} (no owner)", args&: szPrefix, args: +pSFunc->Name);
4562 }
4563 else
4564 {
4565 int32_t iLine = SGetLine(szText: pSFunc->pOrgScript->GetScript(), cpPosition: pSFunc->Script);
4566 LogNTr(fmt: "{}{} ({}:{})", args&: szPrefix, args: +pFunc->Name, args: pSFunc->pOrgScript->ScriptName.c_str(), args: static_cast<int>(iLine));
4567 }
4568 // next func in overload chain
4569 pFunc = pSFunc ? pSFunc->OwnerOverloaded : nullptr;
4570 szPrefix = "overloads ";
4571 }
4572 }
4573 return true;
4574}
4575
4576static C4Value FnVarN(C4AulContext *cthr, C4String *name)
4577{
4578 const char *strName = FnStringPar(pString: name);
4579
4580 if (!cthr->Caller) return C4VNull;
4581
4582 // find variable
4583 int32_t iID = cthr->Caller->Func->VarNamed.GetItemNr(strName);
4584 if (iID < 0)
4585 return C4VNull;
4586
4587 // return reference on variable
4588 return cthr->Caller->Vars[iID].GetRef();
4589}
4590
4591static C4Value FnLocalN(C4AulContext *cthr, C4String *name, C4Object *pObj)
4592{
4593 const char *strName = FnStringPar(pString: name);
4594 if (!pObj) pObj = cthr->Obj;
4595 if (!pObj) return C4VNull;
4596
4597 // find variable
4598 C4Value *pVarN = pObj->LocalNamed.GetItem(strName);
4599
4600 if (!pVarN) return C4VNull;
4601
4602 // return reference on variable
4603 return pVarN->GetRef();
4604}
4605
4606static C4Value FnGlobalN(C4AulContext *cthr, C4String *name)
4607{
4608 const char *strName = FnStringPar(pString: name);
4609
4610 // find variable
4611 C4Value *pVarN = Game.ScriptEngine.GlobalNamed.GetItem(strName);
4612
4613 if (!pVarN) return C4VNull;
4614
4615 // return reference on variable
4616 return pVarN->GetRef();
4617}
4618
4619static void FnSetSkyAdjust(C4AulContext *cthr, C4ValueInt dwAdjust, C4ValueInt dwBackClr)
4620{
4621 // set adjust
4622 Game.Landscape.Sky.SetModulation(dwWithClr: dwAdjust, dwBackClr);
4623}
4624
4625static void FnSetMatAdjust(C4AulContext *cthr, C4ValueInt dwAdjust)
4626{
4627 // set adjust
4628 Game.Landscape.SetModulation(dwAdjust);
4629}
4630
4631static C4ValueInt FnGetSkyAdjust(C4AulContext *cthr, bool fBackColor)
4632{
4633 // get adjust
4634 return Game.Landscape.Sky.GetModulation(fBackClr: !!fBackColor);
4635}
4636
4637static C4ValueInt FnGetMatAdjust(C4AulContext *cthr)
4638{
4639 // get adjust
4640 return Game.Landscape.GetModulation();
4641}
4642
4643static C4ValueInt FnAnyContainer(C4AulContext *) { return ANY_CONTAINER; }
4644static C4ValueInt FnNoContainer(C4AulContext *) { return NO_CONTAINER; }
4645
4646static std::optional<C4ValueInt> FnGetTime(C4AulContext *)
4647{
4648 // check network, record, etc
4649 if (Game.Control.SyncMode()) return {};
4650 return {timeGetTime()};
4651}
4652
4653static std::optional<C4ValueInt> FnGetSystemTime(C4AulContext *cthr, C4ValueInt iWhat)
4654{
4655 // check network, record, etc
4656 if (Game.Control.SyncMode()) return {};
4657 // check bounds
4658 if (!Inside<C4ValueInt>(ival: iWhat, lbound: 0, rbound: 7)) return {};
4659#ifdef _WIN32
4660 SYSTEMTIME time;
4661 GetLocalTime(&time);
4662 // return queried value
4663 return {*(((WORD *)&time) + iWhat)};
4664#else
4665 struct timeval tv;
4666 if (gettimeofday(tv: &tv, tz: nullptr)) return {};
4667 if (iWhat == 7) return tv.tv_usec / 1000;
4668 struct tm *time;
4669 time = localtime(timer: &tv.tv_sec);
4670 switch (iWhat)
4671 {
4672 case 0: return {time->tm_year + 1900};
4673 case 1: return {time->tm_mon + 1};
4674 case 2: return {time->tm_wday};
4675 case 3: return {time->tm_mday};
4676 case 4: return {time->tm_hour};
4677 case 5: return {time->tm_min};
4678 case 6: return {time->tm_sec};
4679 }
4680
4681 return {};
4682#endif
4683}
4684
4685static C4Value FnSetPlrExtraData(C4AulContext *cthr, C4ValueInt iPlayer, C4String *DataName, C4Value Data)
4686{
4687 const char *strDataName = FnStringPar(pString: DataName);
4688
4689 if (!strDataName || !strDataName[0]) return C4VNull;
4690 if (!StdCompiler::IsIdentifier(str: strDataName))
4691 {
4692 StdStrBuf name{strDataName};
4693 name.EscapeString();
4694 DebugLog(level: spdlog::level::warn, fmt: "SetPlrExtraData: Ignoring invalid data name \"{}\"! Only alphanumerics, _ and - are allowed.", args: name.getData());
4695 return C4VNull;
4696 }
4697
4698 // valid player? (for great nullpointer prevention)
4699 if (!ValidPlr(plr: iPlayer)) return C4VNull;
4700 // do not allow data type C4V_String or C4V_C4Object
4701 if (Data.GetType() != C4V_Any &&
4702 Data.GetType() != C4V_Int &&
4703 Data.GetType() != C4V_Bool &&
4704 Data.GetType() != C4V_C4ID) return C4VNull;
4705 // get pointer on player...
4706 C4Player *pPlayer = Game.Players.Get(iPlayer);
4707 // no name list created yet?
4708 if (!pPlayer->ExtraData.pNames)
4709 // create name list
4710 pPlayer->ExtraData.CreateTempNameList();
4711 // data name already exists?
4712 C4ValueInt ival;
4713 if ((ival = pPlayer->ExtraData.pNames->GetItemNr(strName: strDataName)) != -1)
4714 pPlayer->ExtraData[ival] = Data;
4715 else
4716 {
4717 // add name
4718 pPlayer->ExtraData.pNames->AddName(pnName: strDataName);
4719 // get val id & set
4720 if ((ival = pPlayer->ExtraData.pNames->GetItemNr(strName: strDataName)) == -1) return C4VNull;
4721 pPlayer->ExtraData[ival] = Data;
4722 }
4723 // ok, return the value that has been set
4724 return Data;
4725}
4726
4727static C4Value FnGetPlrExtraData(C4AulContext *cthr, C4ValueInt iPlayer, C4String *DataName)
4728{
4729 const char *strDataName = FnStringPar(pString: DataName);
4730 // valid player?
4731 if (!ValidPlr(plr: iPlayer)) return C4VNull;
4732 // get pointer on player...
4733 C4Player *pPlayer = Game.Players.Get(iPlayer);
4734 // no name list?
4735 if (!pPlayer->ExtraData.pNames) return C4VNull;
4736 C4ValueInt ival;
4737 if ((ival = pPlayer->ExtraData.pNames->GetItemNr(strName: strDataName)) == -1) return C4VNull;
4738 // return data
4739 return pPlayer->ExtraData[ival];
4740}
4741
4742static C4Value FnSetCrewExtraData(C4AulContext *cthr, C4Object *pCrew, C4String *dataName, C4Value Data)
4743{
4744 if (!pCrew) pCrew = cthr->Obj;
4745 const char *strDataName = FnStringPar(pString: dataName);
4746
4747 if (!strDataName || !strDataName[0]) return C4VNull;
4748 if (!StdCompiler::IsIdentifier(str: strDataName))
4749 {
4750 StdStrBuf name{strDataName};
4751 name.EscapeString();
4752 DebugLog(level: spdlog::level::err, fmt: "SetCrewExtraData: Ignoring invalid data name \"{}\"! Only alphanumerics, _ and - are allowed.", args: name.getData());
4753 return C4VNull;
4754 }
4755
4756 // valid crew with info? (for great nullpointer prevention)
4757 if (!pCrew || !pCrew->Info) return C4VNull;
4758 // do not allow data type C4V_String or C4V_C4Object
4759 if (Data.GetType() != C4V_Any &&
4760 Data.GetType() != C4V_Int &&
4761 Data.GetType() != C4V_Bool &&
4762 Data.GetType() != C4V_C4ID) return C4VNull;
4763 // get pointer on info...
4764 C4ObjectInfo *pInfo = pCrew->Info;
4765 // no name list created yet?
4766 if (!pInfo->ExtraData.pNames)
4767 // create name list
4768 pInfo->ExtraData.CreateTempNameList();
4769 // data name already exists?
4770 C4ValueInt ival;
4771 if ((ival = pInfo->ExtraData.pNames->GetItemNr(strName: strDataName)) != -1)
4772 pInfo->ExtraData[ival] = Data;
4773 else
4774 {
4775 // add name
4776 pInfo->ExtraData.pNames->AddName(pnName: strDataName);
4777 // get val id & set
4778 if ((ival = pInfo->ExtraData.pNames->GetItemNr(strName: strDataName)) == -1) return C4VNull;
4779 pInfo->ExtraData[ival] = Data;
4780 }
4781 // ok, return the value that has been set
4782 return Data;
4783}
4784
4785static C4Value FnGetCrewExtraData(C4AulContext *cthr, C4Object *pCrew, C4String *dataName)
4786{
4787 if (!pCrew) pCrew = cthr->Obj;
4788 const char *strDataName = FnStringPar(pString: dataName);
4789 // valid crew with info?
4790 if (!pCrew || !pCrew->Info) return C4VNull;
4791 // get pointer on info...
4792 C4ObjectInfo *pInfo = pCrew->Info;
4793 // no name list?
4794 if (!pInfo->ExtraData.pNames) return C4VNull;
4795 C4ValueInt ival;
4796 if ((ival = pInfo->ExtraData.pNames->GetItemNr(strName: strDataName)) == -1) return C4VNull;
4797 // return data
4798 return pInfo->ExtraData[ival];
4799}
4800
4801static C4ValueInt FnDrawMatChunks(C4AulContext *cctx, C4ValueInt tx, C4ValueInt ty, C4ValueInt twdt, C4ValueInt thgt, C4ValueInt icntx, C4ValueInt icnty, C4String *strMaterial, C4String *strTexture, bool bIFT)
4802{
4803 return Game.Landscape.DrawChunks(tx, ty, wdt: twdt, hgt: thgt, icntx, icnty, szMaterial: FnStringPar(pString: strMaterial), szTexture: FnStringPar(pString: strTexture), bIFT: bIFT != 0);
4804}
4805
4806static std::optional<bool> FnGetCrewEnabled(C4AulContext *cctx, C4Object *pObj)
4807{
4808 // local/safety
4809 if (!pObj) pObj = cctx->Obj; if (!pObj) return {};
4810 // return status
4811 return !pObj->CrewDisabled;
4812}
4813
4814static bool FnSetCrewEnabled(C4AulContext *cctx, bool fEnabled, C4Object *pObj)
4815{
4816 // local/safety
4817 if (!pObj) pObj = cctx->Obj; if (!pObj) return false;
4818 // set status
4819 pObj->CrewDisabled = !fEnabled;
4820 // deselect
4821 if (!fEnabled)
4822 {
4823 pObj->Select = false;
4824 C4Player *pOwner;
4825 if (pOwner = Game.Players.Get(iPlayer: pObj->Owner))
4826 {
4827 // if viewed player cursor gets deactivated and no new cursor is found, follow the old in target mode
4828 bool fWasCursorMode = (pOwner->ViewMode == C4PVM_Cursor);
4829 if (pOwner->Cursor == pObj)
4830 pOwner->AdjustCursorCommand();
4831 if (!pOwner->ViewCursor && !pOwner->Cursor && fWasCursorMode)
4832 pOwner->SetViewMode(iMode: C4PVM_Target, pTarget: pObj);
4833 }
4834 }
4835 // success
4836 return true;
4837}
4838
4839static bool FnUnselectCrew(C4AulContext *cctx, C4ValueInt iPlayer)
4840{
4841 // get player
4842 C4Player *pPlr = Game.Players.Get(iPlayer);
4843 if (!pPlr) return false;
4844 // unselect crew
4845 pPlr->UnselectCrew();
4846 // success
4847 return true;
4848}
4849
4850static C4ValueInt FnDrawMap(C4AulContext *cctx, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4String *szMapDef)
4851{
4852 // draw it!
4853 return Game.Landscape.DrawMap(iX, iY, iWdt, iHgt, szMapDef: FnStringPar(pString: szMapDef));
4854}
4855
4856static C4ValueInt FnDrawDefMap(C4AulContext *cctx, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4String *szMapDef)
4857{
4858 // draw it!
4859 return Game.Landscape.DrawDefMap(iX, iY, iWdt, iHgt, szMapDef: FnStringPar(pString: szMapDef));
4860}
4861
4862static bool FnCreateParticle(C4AulContext *cthr, C4String *szName, C4ValueInt iX, C4ValueInt iY, C4ValueInt iXDir, C4ValueInt iYDir, C4ValueInt a, C4ValueInt b, C4Object *pObj, bool fBack)
4863{
4864 // safety
4865 if (pObj && !pObj->Status) return false;
4866 // local offset
4867 if (cthr->Obj)
4868 {
4869 iX += cthr->Obj->x;
4870 iY += cthr->Obj->y;
4871 }
4872 // get particle
4873 C4ParticleDef *pDef = Game.Particles.GetDef(szName: FnStringPar(pString: szName));
4874 if (!pDef) return false;
4875 // create
4876 Game.Particles.Create(pOfDef: pDef, x: static_cast<float>(iX), y: static_cast<float>(iY), xdir: static_cast<float>(iXDir) / 10.0f, ydir: static_cast<float>(iYDir) / 10.0f, a: static_cast<float>(a) / 10.0f, b, pPxList: pObj ? (fBack ? &pObj->BackParticles : &pObj->FrontParticles) : nullptr, pObj);
4877 // success, even if not created
4878 return true;
4879}
4880
4881static bool FnCastAParticles(C4AulContext *cthr, C4String *szName, C4ValueInt iAmount, C4ValueInt iLevel, C4ValueInt iX, C4ValueInt iY, C4ValueInt a0, C4ValueInt a1, C4ValueInt b0, C4ValueInt b1, C4Object *pObj, bool fBack)
4882{
4883 // safety
4884 if (pObj && !pObj->Status) return false;
4885 // local offset
4886 if (cthr->Obj)
4887 {
4888 iX += cthr->Obj->x;
4889 iY += cthr->Obj->y;
4890 }
4891 // get particle
4892 C4ParticleDef *pDef = Game.Particles.GetDef(szName: FnStringPar(pString: szName));
4893 if (!pDef) return false;
4894 // cast
4895 Game.Particles.Cast(pOfDef: pDef, iAmount, x: static_cast<float>(iX), y: static_cast<float>(iY), level: iLevel, a0: static_cast<float>(a0) / 10.0f, b0, a1: static_cast<float>(a1) / 10.0f, b1, pPxList: pObj ? (fBack ? &pObj->BackParticles : &pObj->FrontParticles) : nullptr, pObj);
4896 // success, even if not created
4897 return true;
4898}
4899
4900static bool FnCastParticles(C4AulContext *cthr, C4String *szName, C4ValueInt iAmount, C4ValueInt iLevel, C4ValueInt iX, C4ValueInt iY, C4ValueInt a0, C4ValueInt a1, C4ValueInt b0, C4ValueInt b1, C4Object *pObj)
4901{
4902 return FnCastAParticles(cthr, szName, iAmount, iLevel, iX, iY, a0, a1, b0, b1, pObj, fBack: false);
4903}
4904
4905static bool FnCastBackParticles(C4AulContext *cthr, C4String *szName, C4ValueInt iAmount, C4ValueInt iLevel, C4ValueInt iX, C4ValueInt iY, C4ValueInt a0, C4ValueInt a1, C4ValueInt b0, C4ValueInt b1, C4Object *pObj)
4906{
4907 return FnCastAParticles(cthr, szName, iAmount, iLevel, iX, iY, a0, a1, b0, b1, pObj, fBack: true);
4908}
4909
4910static bool FnPushParticles(C4AulContext *cthr, C4String *szName, C4ValueInt iAX, C4ValueInt iAY)
4911{
4912 // particle given?
4913 C4ParticleDef *pDef = nullptr;
4914 if (szName)
4915 {
4916 pDef = Game.Particles.GetDef(szName: FnStringPar(pString: szName));
4917 if (!pDef) return false;
4918 }
4919 // push them
4920 Game.Particles.Push(pOfDef: pDef, dxdir: static_cast<float>(iAX) / 10.0f, dydir: static_cast<float>(iAY) / 10.0f);
4921 // success
4922 return true;
4923}
4924
4925static bool FnClearParticles(C4AulContext *cthr, C4String *szName, C4Object *pObj)
4926{
4927 // particle given?
4928 C4ParticleDef *pDef = nullptr;
4929 if (szName)
4930 {
4931 pDef = Game.Particles.GetDef(szName: FnStringPar(pString: szName));
4932 if (!pDef) return false;
4933 }
4934 // delete them
4935 if (pObj)
4936 {
4937 pObj->FrontParticles.Remove(pOfDef: pDef);
4938 pObj->BackParticles.Remove(pOfDef: pDef);
4939 }
4940 else
4941 Game.Particles.GlobalParticles.Remove(pOfDef: pDef);
4942 // success
4943 return true;
4944}
4945
4946static bool FnIsNewgfx(C4AulContext *) { return true; }
4947
4948#define SkyPar_KEEP -163764
4949
4950static void FnSetSkyParallax(C4AulContext *ctx, C4ValueInt iMode, C4ValueInt iParX, C4ValueInt iParY, C4ValueInt iXDir, C4ValueInt iYDir, C4ValueInt iX, C4ValueInt iY)
4951{
4952 // set all parameters that aren't SkyPar_KEEP
4953 if (iMode != SkyPar_KEEP)
4954 if (Inside<C4ValueInt>(ival: iMode, lbound: 0, rbound: 1)) Game.Landscape.Sky.ParallaxMode = iMode;
4955 if (iParX != SkyPar_KEEP && iParX) Game.Landscape.Sky.ParX = iParX;
4956 if (iParY != SkyPar_KEEP && iParY) Game.Landscape.Sky.ParY = iParY;
4957 if (iXDir != SkyPar_KEEP) Game.Landscape.Sky.xdir = itofix(x: iXDir);
4958 if (iYDir != SkyPar_KEEP) Game.Landscape.Sky.ydir = itofix(x: iYDir);
4959 if (iX != SkyPar_KEEP) Game.Landscape.Sky.x = itofix(x: iX);
4960 if (iY != SkyPar_KEEP) Game.Landscape.Sky.y = itofix(x: iY);
4961}
4962
4963static bool FnDoCrewExp(C4AulContext *ctx, C4ValueInt iChange, C4Object *pObj)
4964{
4965 // local call/safety
4966 if (!pObj) pObj = ctx->Obj; if (!pObj) return false;
4967 // do exp
4968 pObj->DoExperience(change: iChange);
4969 // success
4970 return true;
4971}
4972
4973static C4ValueInt FnReloadDef(C4AulContext *ctx, C4ID idDef)
4974{
4975 // get def
4976 C4Def *pDef = nullptr;
4977 if (!idDef)
4978 {
4979 // no def given: local def
4980 if (ctx->Obj) pDef = ctx->Obj->Def;
4981 }
4982 else
4983 // def by ID
4984 pDef = Game.Defs.ID2Def(id: idDef);
4985 // safety
4986 if (!pDef) return false;
4987 // perform reload
4988 return Game.ReloadDef(id: pDef->id, reloadWhat: C4D_Load_RX);
4989}
4990
4991static C4ValueInt FnReloadParticle(C4AulContext *ctx, C4String *szParticleName)
4992{
4993 // perform reload
4994 return Game.ReloadParticle(szName: FnStringPar(pString: szParticleName));
4995}
4996
4997static void FnSetGamma(C4AulContext *ctx, C4ValueInt dwClr1, C4ValueInt dwClr2, C4ValueInt dwClr3, C4ValueInt iRampIndex)
4998{
4999 Game.GraphicsSystem.SetGamma(dwClr1, dwClr2, dwClr3, iRampIndex);
5000}
5001
5002static void FnResetGamma(C4AulContext *ctx, C4ValueInt iRampIndex)
5003{
5004 Game.GraphicsSystem.SetGamma(dwClr1: 0x000000, dwClr2: 0x808080, dwClr3: 0xffffff, iRampIndex);
5005}
5006
5007static C4ValueInt FnFrameCounter(C4AulContext *) { return Game.FrameCounter; }
5008
5009static C4ValueHash *FnGetPath(C4AulContext *ctx, C4ValueInt iFromX, C4ValueInt iFromY, C4ValueInt iToX, C4ValueInt iToY)
5010{
5011 struct Waypoint
5012 {
5013 int32_t x = 0;
5014 int32_t y = 0;
5015 C4Object *obj = nullptr;
5016 };
5017
5018 struct PathInfo
5019 {
5020 std::vector<Waypoint> path;
5021 int32_t length = 0;
5022 };
5023
5024 auto SetWaypoint = [](int32_t x, int32_t y, intptr_t transferTarget, intptr_t pathInfo) -> bool
5025 {
5026 auto *target = reinterpret_cast<C4Object *>(transferTarget);
5027 auto *pathinfo = reinterpret_cast<PathInfo *>(pathInfo);
5028
5029 const Waypoint &last = pathinfo->path.back();
5030 pathinfo->length += Distance(iX1: last.x, iY1: last.y, iX2: x, iY2: y);
5031
5032 pathinfo->path.push_back(x: {.x: x, .y: y, .obj: target});
5033 return true;
5034 };
5035
5036 PathInfo pathinfo;
5037 pathinfo.path.push_back(x: {.x: static_cast<int32_t>(iFromX), .y: static_cast<int32_t>(iFromY), .obj: nullptr});
5038
5039 if (!Game.PathFinder.Find(iFromX, iFromY, iToX, iToY, fnSetWaypoint: SetWaypoint, iWaypointParameter: reinterpret_cast<intptr_t>(&pathinfo)))
5040 {
5041 return nullptr;
5042 }
5043
5044 SetWaypoint(static_cast<int32_t>(iToX), static_cast<int32_t>(iToY), reinterpret_cast<intptr_t>(nullptr), reinterpret_cast<intptr_t>(&pathinfo));
5045
5046 auto *hash = new C4ValueHash;
5047 (*hash)[C4VString(strString: "Length")] = C4VInt(iVal: pathinfo.length);
5048
5049 auto *array = new C4ValueArray(static_cast<int32_t>(pathinfo.path.size()));
5050
5051 if (!pathinfo.path.empty())
5052 {
5053 for (size_t i = 0; i < pathinfo.path.size(); ++i)
5054 {
5055 auto *waypoint = new C4ValueHash;
5056 (*waypoint)[C4VString(strString: "X")] = C4VInt(iVal: pathinfo.path[i].x);
5057 (*waypoint)[C4VString(strString: "Y")] = C4VInt(iVal: pathinfo.path[i].y);
5058 if (pathinfo.path[i].obj)
5059 (*waypoint)[C4VString(strString: "TransferTarget")] = C4VObj(pObj: pathinfo.path[i].obj);
5060
5061 (*array)[static_cast<int32_t>(i)] = C4VMap(pMap: waypoint);
5062 }
5063 }
5064
5065 (*hash)[C4VString(strString: "Waypoints")] = C4VArray(pArray: array);
5066
5067 return hash;
5068}
5069
5070static C4ValueInt FnSetTextureIndex(C4AulContext *ctx, C4String *psMatTex, C4ValueInt iNewIndex, bool fInsert)
5071{
5072 if (!Inside(ival: iNewIndex, lbound: C4ValueInt{0}, rbound: C4ValueInt{255})) return false;
5073 return Game.Landscape.SetTextureIndex(szMatTex: FnStringPar(pString: psMatTex), iNewIndex: uint8_t(iNewIndex), fInsert: !!fInsert);
5074}
5075
5076static void FnRemoveUnusedTexMapEntries(C4AulContext *ctx)
5077{
5078 Game.Landscape.RemoveUnusedTexMapEntries();
5079}
5080
5081static void FnSetLandscapePixel(C4AulContext *ctx, C4ValueInt iX, C4ValueInt iY, C4ValueInt dwValue)
5082{
5083 // local call
5084 if (ctx->Obj) { iX += ctx->Obj->x; iY += ctx->Obj->y; }
5085 // set pixel in 32bit-sfc only
5086 Game.Landscape.SetPixDw(x: iX, y: iY, dwPix: dwValue);
5087}
5088
5089static bool FnSetObjectOrder(C4AulContext *ctx, C4Object *pObjBeforeOrAfter, C4Object *pSortObj, bool fSortAfter)
5090{
5091 // local call/safety
5092 if (!pSortObj) pSortObj = ctx->Obj; if (!pSortObj) return false;
5093 if (!pObjBeforeOrAfter) return false;
5094 // don't sort an object before or after itself, it messes up the object list and causes infinite loops
5095 if (pObjBeforeOrAfter == pSortObj) return false;
5096 // note that no category check is done, so this call might corrupt the main list!
5097 // the scripter must be wise enough not to call it for objects with different categories
5098 // create object resort
5099 C4ObjResort *pObjRes = new C4ObjResort();
5100 pObjRes->pSortObj = pSortObj;
5101 pObjRes->pObjBefore = pObjBeforeOrAfter;
5102 pObjRes->fSortAfter = fSortAfter;
5103 // insert into game resort proc list
5104 pObjRes->Next = Game.Objects.ResortProc;
5105 Game.Objects.ResortProc = pObjRes;
5106 // done, success so far
5107 return true;
5108}
5109
5110static bool FnDrawMaterialQuad(C4AulContext *ctx, C4String *szMaterial, C4ValueInt iX1, C4ValueInt iY1, C4ValueInt iX2, C4ValueInt iY2, C4ValueInt iX3, C4ValueInt iY3, C4ValueInt iX4, C4ValueInt iY4, bool fSub)
5111{
5112 const char *szMat = FnStringPar(pString: szMaterial);
5113 return !!Game.Landscape.DrawQuad(iX1, iY1, iX2, iY2, iX3, iY3, iX4, iY4, szMaterial: szMat, bIFT: fSub);
5114}
5115
5116static bool FnFightWith(C4AulContext *ctx, C4Object *pTarget, C4Object *pClonk)
5117{
5118 // local call/safety
5119 if (!pTarget) return false;
5120 if (!pClonk) if (!(pClonk = ctx->Obj)) return false;
5121 // check OCF
5122 if (~(pTarget->OCF & pClonk->OCF) & OCF_FightReady) return false;
5123 // RejectFight callback
5124 if (pTarget->Call(PSF_RejectFight, pPars: {C4VObj(pObj: pTarget)}, fPassError: true).getBool()) return false;
5125 if (pClonk->Call(PSF_RejectFight, pPars: {C4VObj(pObj: pClonk)}, fPassError: true).getBool()) return false;
5126 // begin fighting
5127 ObjectActionFight(cObj: pClonk, pTarget);
5128 ObjectActionFight(cObj: pTarget, pTarget: pClonk);
5129 // success
5130 return true;
5131}
5132
5133static bool FnSetFilmView(C4AulContext *ctx, C4ValueInt iToPlr)
5134{
5135 // check player
5136 if (!ValidPlr(plr: iToPlr) && iToPlr != NO_OWNER) return false;
5137 // real switch in replays only
5138 if (!Game.Control.isReplay()) return true;
5139 // set new target plr
5140 if (const auto &viewports = Game.GraphicsSystem.GetViewports(); viewports.size() > 0)
5141 {
5142 viewports.front()->Init(iPlayer: iToPlr, fSetTempOnly: true);
5143 }
5144 // done, always success (sync)
5145 return true;
5146}
5147
5148static bool FnClearMenuItems(C4AulContext *ctx, C4Object *pObj)
5149{
5150 // local call/safety
5151 if (!pObj) pObj = ctx->Obj; if (!pObj) return false;
5152 // check menu
5153 if (!pObj->Menu) return false;
5154 // clear the items
5155 pObj->Menu->ClearItems(fResetSelection: true);
5156 // success
5157 return true;
5158}
5159
5160static C4Object *FnGetObjectLayer(C4AulContext *ctx, C4Object *pObj)
5161{
5162 // local call/safety
5163 if (!pObj) if (!(pObj = ctx->Obj)) return nullptr;
5164 // get layer object
5165 return pObj->pLayer;
5166}
5167
5168static bool FnSetObjectLayer(C4AulContext *ctx, C4Object *pNewLayer, C4Object *pObj)
5169{
5170 // local call/safety
5171 if (!pObj) if (!(pObj = ctx->Obj)) return false;
5172 // set layer object
5173 pObj->pLayer = pNewLayer;
5174 // set for all contents as well
5175 for (C4ObjectLink *pLnk = pObj->Contents.First; pLnk; pLnk = pLnk->Next)
5176 if ((pObj = pLnk->Obj) && pObj->Status)
5177 pObj->pLayer = pNewLayer;
5178 // success
5179 return true;
5180}
5181
5182static bool FnSetShape(C4AulContext *ctx, C4ValueInt iX, C4ValueInt iY, C4ValueInt iWdt, C4ValueInt iHgt, C4Object *pObj)
5183{
5184 // local call / safety
5185 if (!pObj) if (!(pObj = ctx->Obj)) return false;
5186 // update shape
5187 pObj->Shape.x = iX;
5188 pObj->Shape.y = iY;
5189 pObj->Shape.Wdt = iWdt;
5190 pObj->Shape.Hgt = iHgt;
5191 // section list needs refresh
5192 pObj->UpdatePos();
5193 // done, success
5194 return true;
5195}
5196
5197static bool FnAddMsgBoardCmd(C4AulContext *ctx, C4String *pstrCommand, C4String *pstrScript, C4ValueInt iRestriction)
5198{
5199 // safety
5200 if (!pstrCommand || !pstrScript) return false;
5201 // unrestricted commands cannot be set by direct-exec script (like /script).
5202 if (iRestriction != C4MessageBoardCommand::C4MSGCMDR_Identifier)
5203 if (!ctx->Caller || !*ctx->Caller->Func->Name)
5204 return false;
5205 C4MessageBoardCommand::Restriction eRestriction;
5206 switch (iRestriction)
5207 {
5208 case C4MessageBoardCommand::C4MSGCMDR_Escaped: eRestriction = C4MessageBoardCommand::C4MSGCMDR_Escaped; break;
5209 case C4MessageBoardCommand::C4MSGCMDR_Plain: eRestriction = C4MessageBoardCommand::C4MSGCMDR_Plain; break;
5210 case C4MessageBoardCommand::C4MSGCMDR_Identifier: eRestriction = C4MessageBoardCommand::C4MSGCMDR_Identifier; break;
5211 default: return false;
5212 }
5213 // add command
5214 Game.MessageInput.AddCommand(strCommand: FnStringPar(pString: pstrCommand), strScript: FnStringPar(pString: pstrScript), eRestriction);
5215 return true;
5216}
5217
5218static bool FnSetGameSpeed(C4AulContext *ctx, C4ValueInt iSpeed)
5219{
5220 // league games: disable direct exec (like /speed)
5221 if (Game.Parameters.isLeague())
5222 if (!ctx->Caller || ctx->Caller->TemporaryScript)
5223 return false;
5224 // safety
5225 if (iSpeed) if (!Inside<C4ValueInt>(ival: iSpeed, lbound: 0, rbound: 1000)) return false;
5226 if (!iSpeed) iSpeed = 38;
5227 // set speed, restart timer
5228 Application.SetGameTickDelay(1000 / iSpeed);
5229 return true;
5230}
5231
5232static bool FnSetObjDrawTransform(C4AulContext *ctx, C4ValueInt iA, C4ValueInt iB, C4ValueInt iC, C4ValueInt iD, C4ValueInt iE, C4ValueInt iF, C4Object *pObj, C4ValueInt iOverlayID)
5233{
5234 // local call / safety
5235 if (!pObj) { if (!(pObj = ctx->Obj)) return false; }
5236 C4DrawTransform *pTransform;
5237 // overlay?
5238 if (iOverlayID)
5239 {
5240 // set overlay transform
5241 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
5242 if (!pOverlay) return false;
5243 pTransform = pOverlay->GetTransform();
5244 }
5245 else
5246 {
5247 // set base transform
5248 pTransform = pObj->pDrawTransform;
5249 // del transform?
5250 if (!iB && !iC && !iD && !iF && iA == iE && (!iA || iA == 1000))
5251 {
5252 // identity/0 and no transform defined: nothing to do
5253 if (!pTransform) return true;
5254 // transform has no flipdir?
5255 if (pTransform->FlipDir == 1)
5256 {
5257 // kill identity-transform, then
5258 delete pTransform;
5259 pObj->pDrawTransform = nullptr;
5260 return true;
5261 }
5262 // flipdir must remain: set identity
5263 pTransform->Set(fA: 1, fB: 0, fC: 0, fD: 0, fE: 1, fF: 0, fG: 0, fH: 0, fI: 1);
5264 return true;
5265 }
5266 // create draw transform if not already present
5267 if (!pTransform) pTransform = pObj->pDrawTransform = new C4DrawTransform();
5268 }
5269 // assign values
5270 pTransform->Set(fA: static_cast<float>(iA) / 1000, fB: static_cast<float>(iB) / 1000, fC: static_cast<float>(iC) / 1000, fD: static_cast<float>(iD) / 1000, fE: static_cast<float>(iE) / 1000, fF: static_cast<float>(iF) / 1000, fG: 0, fH: 0, fI: 1);
5271 // done, success
5272 return true;
5273}
5274
5275static bool FnSetObjDrawTransform2(C4AulContext *ctx, C4ValueInt iA, C4ValueInt iB, C4ValueInt iC, C4ValueInt iD, C4ValueInt iE, C4ValueInt iF, C4ValueInt iG, C4ValueInt iH, C4ValueInt iI, C4ValueInt iOverlayID)
5276{
5277 // local call / safety
5278 C4Object *pObj = ctx->Obj;
5279 if (!pObj) return false;
5280 C4DrawTransform *pTransform;
5281 // overlay?
5282 if (iOverlayID)
5283 {
5284 // set overlay transform
5285 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
5286 if (!pOverlay) return false;
5287 pTransform = pOverlay->GetTransform();
5288 }
5289 else
5290 {
5291 // set base transform
5292 pTransform = pObj->pDrawTransform;
5293 // create draw transform if not already present
5294 if (!pTransform) pTransform = pObj->pDrawTransform = new C4DrawTransform(1);
5295 }
5296 // assign values
5297#define L2F(l) (static_cast<float>(l)/1000)
5298 CBltTransform matrix;
5299 matrix.Set(L2F(iA), L2F(iB), L2F(iC), L2F(iD), L2F(iE), L2F(iF), L2F(iG), L2F(iH), L2F(iI));
5300 *pTransform *= matrix;
5301#undef L2F
5302 // done, success
5303 return true;
5304}
5305
5306bool SimFlight(C4Fixed &x, C4Fixed &y, C4Fixed &xdir, C4Fixed &ydir, int32_t iDensityMin, int32_t iDensityMax, int32_t iIter);
5307
5308static std::optional<bool> FnSimFlight(C4AulContext *ctx, C4Value *pvrX, C4Value *pvrY, C4Value *pvrXDir, C4Value *pvrYDir, std::optional<C4ValueInt> oiDensityMin, std::optional<C4ValueInt> oiDensityMax, std::optional<C4ValueInt> oiIter, std::optional<C4ValueInt> oiPrec)
5309{
5310 // check and copy parameters
5311 if (!pvrX || !pvrY || !pvrXDir || !pvrYDir) return {};
5312
5313 C4ValueInt iDensityMin = oiDensityMin.value_or(u: C4M_Solid);
5314 C4ValueInt iDensityMax = oiDensityMax.value_or(u: 100);
5315 C4ValueInt iIter = oiIter.value_or(u: -1);
5316 C4ValueInt iPrec = oiPrec.value_or(u: 10);
5317
5318 // convert to C4Fixed
5319 C4Fixed x = itofix(x: pvrX->getInt()), y = itofix(x: pvrY->getInt()),
5320 xdir = itofix(x: pvrXDir->getInt(), prec: iPrec), ydir = itofix(x: pvrYDir->getInt(), prec: iPrec);
5321
5322 // simulate
5323 if (!SimFlight(x, y, xdir, ydir, iDensityMin, iDensityMax, iIter))
5324 return {false};
5325
5326 // write results back
5327 *pvrX = C4VInt(iVal: fixtoi(x)); *pvrY = C4VInt(iVal: fixtoi(x: y));
5328 *pvrXDir = C4VInt(iVal: fixtoi(x: xdir * iPrec)); *pvrYDir = C4VInt(iVal: fixtoi(x: ydir * iPrec));
5329
5330 return {true};
5331}
5332
5333static bool FnSetPortrait(C4AulContext *ctx, C4String *pstrPortrait, C4Object *pTarget, C4ID idSourceDef, bool fPermanent, bool fCopyGfx)
5334{
5335 // safety
5336 const char *szPortrait;
5337 if (!pstrPortrait || !*(szPortrait = FnStringPar(pString: pstrPortrait))) return false;
5338 if (!pTarget) if (!(pTarget = ctx->Obj)) return false;
5339 if (!pTarget->Status || !pTarget->Info) return false;
5340 // special case: clear portrait
5341 if (SEqual(szStr1: szPortrait, C4Portrait_None)) return pTarget->Info->ClearPortrait(fPermanently: !!fPermanent);
5342 // get source def for portrait
5343 C4Def *pSourceDef;
5344 if (idSourceDef) pSourceDef = Game.Defs.ID2Def(id: idSourceDef); else pSourceDef = pTarget->Def;
5345 if (!pSourceDef) return false;
5346 // special case: random portrait
5347 if (SEqual(szStr1: szPortrait, C4Portrait_Random)) return pTarget->Info->SetRandomPortrait(idSourceDef: pSourceDef->id, fAssignPermanently: !!fPermanent, fCopyFile: !!fCopyGfx);
5348 // try to set portrait
5349 return pTarget->Info->SetPortrait(szPortraitName: szPortrait, pSourceDef, fAssignPermanently: !!fPermanent, fCopyFile: !!fCopyGfx);
5350}
5351
5352static C4Value FnGetPortrait(C4AulContext *ctx, C4Object *pObj, bool fGetID, bool fGetPermanent)
5353{
5354 // check valid object with info section
5355 if (!pObj) if (!(pObj = ctx->Obj)) return C4VNull;
5356 if (!pObj->Status || !pObj->Info) return C4VNull;
5357 // get portrait to examine
5358 C4Portrait *pPortrait;
5359 if (fGetPermanent)
5360 {
5361 // permanent: new portrait assigned?
5362 if (!(pPortrait = pObj->Info->pNewPortrait))
5363 {
5364 // custom portrait?
5365 if (pObj->Info->pCustomPortrait)
5366 if (fGetID) return C4VNull;
5367 else
5368 return C4VString(C4Portrait_Custom);
5369 // portrait string from info?
5370 const char *szPortrait = pObj->Info->PortraitFile;
5371 // no portrait string: portrait undefined ("none" would mean no portrait)
5372 if (!*szPortrait) return C4VNull;
5373 // evaluate portrait string
5374 C4ID idPortraitSource = 0;
5375 szPortrait = C4Portrait::EvaluatePortraitString(szPortrait, rIDOut&: idPortraitSource, idDefaultID: pObj->Info->id, pdwClrOut: nullptr);
5376 // return desired value
5377 if (fGetID)
5378 return idPortraitSource ? C4VID(idVal: idPortraitSource) : C4VNull;
5379 else
5380 return szPortrait ? C4VString(strString: szPortrait) : C4VNull;
5381 }
5382 }
5383 else
5384 // get current portrait
5385 pPortrait = &(pObj->Info->Portrait);
5386 // get portrait graphics
5387 C4DefGraphics *pPortraitGfx = pPortrait->GetGfx();
5388 // no portrait?
5389 if (!pPortraitGfx) return C4VNull;
5390 // get def or name
5391 if (fGetID)
5392 return (pPortraitGfx->pDef ? C4VID(idVal: pPortraitGfx->pDef->id) : C4VNull);
5393 else
5394 {
5395 const char *szPortraitName = pPortraitGfx->GetName();
5396 return C4VString(strString: szPortraitName ? szPortraitName : C4Portrait_Custom);
5397 }
5398}
5399
5400static C4ValueInt FnLoadScenarioSection(C4AulContext *ctx, C4String *pstrSection, C4ValueInt dwFlags)
5401{
5402 // safety
5403 const char *szSection;
5404 if (!pstrSection || !*(szSection = FnStringPar(pString: pstrSection))) return false;
5405 // try to load it
5406 return Game.LoadScenarioSection(szSection, dwFlags);
5407}
5408
5409static bool FnSetObjectStatus(C4AulContext *ctx, C4ValueInt iNewStatus, C4Object *pObj, bool fClearPointers)
5410{
5411 // local call / safety
5412 if (!pObj) { if (!(pObj = ctx->Obj)) return false; }
5413 if (!pObj->Status) return false;
5414 // no change
5415 if (pObj->Status == iNewStatus) return true;
5416 // set new status
5417 switch (iNewStatus)
5418 {
5419 case C4OS_NORMAL: return pObj->StatusActivate(); break;
5420 case C4OS_INACTIVE: return pObj->StatusDeactivate(fClearPointers: !!fClearPointers); break;
5421 default: return false; // status unknown
5422 }
5423}
5424
5425static std::optional<C4ValueInt> FnGetObjectStatus(C4AulContext *ctx, C4Object *pObj)
5426{
5427 // local call / safety
5428 if (!pObj) { if (!(pObj = ctx->Obj)) return {}; }
5429 return {pObj->Status};
5430}
5431
5432static bool FnAdjustWalkRotation(C4AulContext *ctx, C4ValueInt iRangeX, C4ValueInt iRangeY, C4ValueInt iSpeed, C4Object *pObj)
5433{
5434 // local call / safety
5435 if (!pObj) { if (!(pObj = ctx->Obj)) return false; }
5436 // must be rotateable and attached to solid ground
5437 if (!pObj->Def->Rotateable || ~pObj->Action.t_attach & CNAT_Bottom || pObj->Shape.AttachMat == MNone)
5438 return false;
5439 // adjust rotation
5440 return pObj->AdjustWalkRotation(iRangeX, iRangeY, iSpeed);
5441}
5442
5443static C4ValueInt FnAddEffect(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iPrio, C4ValueInt iTimerIntervall, C4Object *pCmdTarget, C4ID idCmdTarget, C4Value pvVal1, C4Value pvVal2, C4Value pvVal3, C4Value pvVal4)
5444{
5445 const char *szEffect = FnStringPar(pString: psEffectName);
5446 // safety
5447 if (pTarget && !pTarget->Status) return 0;
5448 if (!szEffect || !*szEffect || !iPrio) return 0;
5449 // create effect
5450 int32_t iEffectNumber;
5451 new C4Effect(pTarget, szEffect, iPrio, iTimerIntervall, pCmdTarget, idCmdTarget, pvVal1, pvVal2, pvVal3, pvVal4, true, iEffectNumber, true);
5452 // return assigned effect number - may be 0 if he effect has been denied by another effect
5453 // may also be the number of another effect
5454 return iEffectNumber;
5455}
5456
5457static C4Value FnGetEffect(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iIndex, C4ValueInt iQueryValue, C4ValueInt iMaxPriority)
5458{
5459 const char *szEffect = FnStringPar(pString: psEffectName);
5460 // get effects
5461 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5462 if (!pEffect) return C4VNull;
5463 // name/wildcard given: find effect by name and index
5464 if (szEffect && *szEffect)
5465 pEffect = pEffect->Get(szName: szEffect, iIndex, iMaxPriority);
5466 else
5467 // otherwise, get by number
5468 pEffect = pEffect->Get(iNumber: iIndex, fIncludeDead: true, iMaxPriority);
5469 // effect found?
5470 if (!pEffect) return C4VNull;
5471 // evaluate desired value
5472 switch (iQueryValue)
5473 {
5474 case 0: return C4VInt(iVal: pEffect->iNumber); // 0: number
5475 case 1: return C4VString(strString: pEffect->Name); // 1: name
5476 case 2: return C4VInt(iVal: Abs(val: pEffect->iPriority)); // 2: priority (may be negative for deactivated effects)
5477 case 3: return C4VInt(iVal: pEffect->iIntervall); // 3: timer intervall
5478 case 4: return C4VObj(pObj: pEffect->pCommandTarget); // 4: command target
5479 case 5: return C4VID(idVal: pEffect->idCommandTarget); // 5: command target ID
5480 case 6: return C4VInt(iVal: pEffect->iTime); // 6: effect time
5481 }
5482 // invalid data queried
5483 return C4VNull;
5484}
5485
5486static bool FnRemoveEffect(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iIndex, bool fDoNoCalls)
5487{
5488 // evaluate parameters
5489 const char *szEffect = FnStringPar(pString: psEffectName);
5490 // get effects
5491 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5492 if (!pEffect) return false;
5493 // name/wildcard given: find effect by name and index
5494 if (szEffect && *szEffect)
5495 pEffect = pEffect->Get(szName: szEffect, iIndex);
5496 else
5497 // otherwise, get by number
5498 pEffect = pEffect->Get(iNumber: iIndex, fIncludeDead: false);
5499 // effect found?
5500 if (!pEffect) return false;
5501 // kill it
5502 if (fDoNoCalls)
5503 pEffect->SetDead();
5504 else
5505 pEffect->Kill(pObj: pTarget);
5506 // done, success
5507 return true;
5508}
5509
5510static bool FnChangeEffect(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iIndex, C4String *psNewEffectName, C4ValueInt iNewTimer)
5511{
5512 // evaluate parameters
5513 const char *szEffect = FnStringPar(pString: psEffectName);
5514 const char *szNewEffect = FnStringPar(pString: psNewEffectName);
5515 if (!szNewEffect || !*szNewEffect) return false;
5516 // get effects
5517 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5518 if (!pEffect) return false;
5519 // name/wildcard given: find effect by name and index
5520 if (szEffect && *szEffect)
5521 pEffect = pEffect->Get(szName: szEffect, iIndex);
5522 else
5523 // otherwise, get by number
5524 pEffect = pEffect->Get(iNumber: iIndex, fIncludeDead: false);
5525 // effect found?
5526 if (!pEffect) return false;
5527 // set new name
5528 SCopy(szSource: szNewEffect, sTarget: pEffect->Name, iMaxL: C4MaxName);
5529 pEffect->ReAssignCallbackFunctions();
5530 // set new timer
5531 if (iNewTimer >= 0)
5532 {
5533 pEffect->iIntervall = iNewTimer;
5534 pEffect->iTime = 0;
5535 }
5536 // done, success
5537 return true;
5538}
5539
5540static std::optional<C4ValueInt> FnCheckEffect(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iPrio, C4ValueInt iTimerIntervall, C4Value pvVal1, C4Value pvVal2, C4Value pvVal3, C4Value pvVal4)
5541{
5542 const char *szEffect = FnStringPar(pString: psEffectName);
5543 // safety
5544 if (pTarget && !pTarget->Status) return {};
5545 if (!szEffect || !*szEffect) return {};
5546 // get effects
5547 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5548 if (!pEffect) return {};
5549 // let them check
5550 return {pEffect->Check(pForObj: pTarget, szCheckEffect: szEffect, iPrio, iTimer: iTimerIntervall, rVal1: pvVal1, rVal2: pvVal2, rVal3: pvVal3, rVal4: pvVal4, passErrors: true)};
5551}
5552
5553static C4ValueInt FnGetEffectCount(C4AulContext *ctx, C4String *psEffectName, C4Object *pTarget, C4ValueInt iMaxPriority)
5554{
5555 // evaluate parameters
5556 const char *szEffect = FnStringPar(pString: psEffectName);
5557 // get effects
5558 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5559 if (!pEffect) return false;
5560 // count effects
5561 if (!*szEffect) szEffect = nullptr;
5562 return pEffect->GetCount(szMask: szEffect, iMaxPriority);
5563}
5564
5565static C4Value FnEffectVar(C4AulContext *cthr, C4ValueInt iVarIndex, C4Object *pObj, C4ValueInt iEffectNumber)
5566{
5567 // safety
5568 if (iVarIndex < 0) return C4VNull;
5569 // get effect
5570 C4Effect *pEffect = pObj ? pObj->pEffects : Game.pGlobalEffects;
5571 if (!pEffect) return C4VNull;
5572 if (!(pEffect = pEffect->Get(iNumber: iEffectNumber, fIncludeDead: true))) return C4VNull;
5573 // return ref to var
5574 return pEffect->EffectVars[iVarIndex].GetRef();
5575}
5576
5577static C4Value FnEffectCall(C4AulContext *ctx, C4Object *pTarget, C4ValueInt iNumber, C4String *psCallFn, C4Value vVal1, C4Value vVal2, C4Value vVal3, C4Value vVal4, C4Value vVal5, C4Value vVal6, C4Value vVal7)
5578{
5579 const char *szCallFn = FnStringPar(pString: psCallFn);
5580 // safety
5581 if (pTarget && !pTarget->Status) return C4VNull;
5582 if (!szCallFn || !*szCallFn) return C4VNull;
5583 // get effect
5584 C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects;
5585 if (!pEffect) return C4VNull;
5586 if (!(pEffect = pEffect->Get(iNumber, fIncludeDead: true))) return C4VNull;
5587 // do call
5588 return pEffect->DoCall(pObj: pTarget, szFn: szCallFn, rVal1: vVal1, rVal2: vVal2, rVal3: vVal3, rVal4: vVal4, rVal5: vVal5, rVal6: vVal6, rVal7: vVal7, passErrors: true, convertNilToIntBool: !ctx->CalledWithStrictNil());
5589}
5590
5591static C4ValueInt FnModulateColor(C4AulContext *cthr, std::optional<C4ValueInt> iClr1, C4ValueInt iClr2)
5592{
5593 // default color
5594 uint32_t dwClr1 = iClr1.value_or(u: 0xffffff);
5595 uint32_t dwClr2 = iClr2;
5596 // get alpha
5597 C4ValueInt iA1 = dwClr1 >> 24, iA2 = dwClr2 >> 24;
5598 // modulate color values; mod alpha upwards
5599 uint32_t r = ((dwClr1 & 0xff) * (dwClr2 & 0xff)) >> 8 | // blue
5600 ((dwClr1 >> 8 & 0xff) * (dwClr2 >> 8 & 0xff)) & 0xff00 | // green
5601 ((dwClr1 >> 16 & 0xff) * (dwClr2 >> 8 & 0xff00)) & 0xff0000 | // red
5602 std::min<C4ValueInt>(a: iA1 + iA2 - ((iA1 * iA2) >> 8), b: 255) << 24; // alpha
5603 return r;
5604}
5605
5606static C4ValueInt FnWildcardMatch(C4AulContext *ctx, C4String *psString, C4String *psWildcard)
5607{
5608 return SWildcardMatchEx(szString: FnStringPar(pString: psString), szWildcard: FnStringPar(pString: psWildcard));
5609}
5610
5611static std::optional<C4ValueInt> FnGetContact(C4AulContext *ctx, C4Object *pObj, C4ValueInt iVertex, C4ValueInt dwCheck)
5612{
5613 // local call / safety
5614 if (!pObj) if (!(pObj = ctx->Obj)) return {};
5615 // vertex not specified: check all
5616 if (iVertex == -1)
5617 {
5618 C4ValueInt iResult = 0;
5619 for (std::int32_t i = 0; i < pObj->Shape.VtxNum; ++i)
5620 iResult |= pObj->Shape.GetVertexContact(iVtx: i, dwCheckMask: dwCheck, tx: pObj->x, ty: pObj->y);
5621 return iResult;
5622 }
5623 // vertex specified: check it
5624 if (!Inside<C4ValueInt>(ival: iVertex, lbound: 0, rbound: pObj->Shape.VtxNum - 1)) return {};
5625 return pObj->Shape.GetVertexContact(iVtx: iVertex, dwCheckMask: dwCheck, tx: pObj->x, ty: pObj->y);
5626}
5627
5628static std::optional<C4ValueInt> FnSetObjectBlitMode(C4AulContext *ctx, C4ValueInt dwNewBlitMode, C4Object *pObj, C4ValueInt iOverlayID)
5629{
5630 // local call / safety
5631 if (!pObj) if (!(pObj = ctx->Obj)) return {};
5632 // overlay?
5633 if (iOverlayID)
5634 {
5635 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
5636 if (!pOverlay)
5637 {
5638 DebugLog(level: spdlog::level::err, fmt: "SetObjectBlitMode: Overlay {} not defined for object {} ({})", args: static_cast<int>(iOverlayID), args: static_cast<int>(pObj->Number), args: pObj->GetName());
5639 return {};
5640 }
5641 pOverlay->SetBlitMode(dwNewBlitMode);
5642 return true;
5643 }
5644 // get prev blit mode
5645 uint32_t dwPrevMode = pObj->BlitMode;
5646 // iNewBlitMode = 0: reset to definition default
5647 if (!dwNewBlitMode)
5648 pObj->BlitMode = pObj->Def->BlitMode;
5649 else
5650 // otherwise, set the desired value
5651 // also ensure that the custom flag is set
5652 pObj->BlitMode = dwNewBlitMode | C4GFXBLIT_CUSTOM;
5653 // return previous value
5654 return dwPrevMode;
5655}
5656
5657static std::optional<C4ValueInt> FnGetObjectBlitMode(C4AulContext *ctx, C4Object *pObj, C4ValueInt iOverlayID)
5658{
5659 // local call / safety
5660 if (!pObj) if (!(pObj = ctx->Obj)) return {};
5661 // overlay?
5662 if (iOverlayID)
5663 {
5664 C4GraphicsOverlay *pOverlay = pObj->GetGraphicsOverlay(iForID: iOverlayID, fCreate: false);
5665 if (!pOverlay)
5666 {
5667 DebugLog(level: spdlog::level::err, fmt: "SetObjectBlitMode: Overlay {} not defined for object {} ({})", args: static_cast<int>(iOverlayID), args: static_cast<int>(pObj->Number), args: pObj->GetName());
5668 return {};
5669 }
5670 return {pOverlay->GetBlitMode()};
5671 }
5672 // get blitting mode
5673 return {pObj->BlitMode};
5674}
5675
5676static bool FnSetViewOffset(C4AulContext *ctx, C4ValueInt iPlayer, C4ValueInt iX, C4ValueInt iY)
5677{
5678 if (!ValidPlr(plr: iPlayer)) return false;
5679 // get player viewport
5680 C4Viewport *pView = Game.GraphicsSystem.GetViewport(iPlayer);
5681 if (!pView) return true; // sync safety
5682 // set
5683 pView->ViewOffsX = iX;
5684 pView->ViewOffsY = iY;
5685 // ok
5686 return true;
5687}
5688
5689static bool FnSetPreSend(C4AulContext *cthr, C4ValueInt iToVal, C4String *pNewName)
5690{
5691 if (iToVal < 0) return false;
5692 if (!Game.Control.isNetwork()) return true;
5693 if (iToVal == 0) iToVal = C4GameControlNetwork::DefaultTargetFPS;
5694 // dbg: manual presend
5695 const char *szClient = FnStringPar(pString: pNewName);
5696 if (!szClient || !*szClient || WildcardMatch(szFName1: szClient, szFName2: Game.Clients.getLocalName()))
5697 {
5698 Game.Control.Network.setTargetFPS(iToVal);
5699 Game.GraphicsSystem.FlashMessage(szMessage: ("TargetFPS: " + std::to_string(val: iToVal)).c_str());
5700 }
5701 return true;
5702}
5703
5704static std::optional<C4ValueInt> FnGetPlayerID(C4AulContext *cthr, C4ValueInt iPlayer)
5705{
5706 C4Player *pPlr = Game.Players.Get(iPlayer);
5707 return pPlr ? std::make_optional(t&: pPlr->ID) : std::nullopt;
5708}
5709
5710static std::optional<C4ValueInt> FnGetPlayerTeam(C4AulContext *cthr, C4ValueInt iPlayer)
5711{
5712 // get player
5713 C4Player *pPlr = Game.Players.Get(iPlayer);
5714 if (!pPlr) return {};
5715 // search team containing this player
5716 C4Team *pTeam = Game.Teams.GetTeamByPlayerID(iID: pPlr->ID);
5717 if (pTeam) return {pTeam->GetID()};
5718 // special value of -1 indicating that the team is still to be chosen
5719 if (pPlr->IsChosingTeam()) return {-1};
5720 // No team.
5721 return {0};
5722}
5723
5724static bool FnSetPlayerTeam(C4AulContext *cthr, C4ValueInt iPlayer, C4ValueInt idNewTeam, bool fNoCalls)
5725{
5726 // no team changing in league games
5727 if (Game.Parameters.isLeague()) return false;
5728 // get player
5729 C4Player *pPlr = Game.Players.Get(iPlayer);
5730 if (!pPlr) return false;
5731 C4PlayerInfo *pPlrInfo = pPlr->GetInfo();
5732 if (!pPlrInfo) return false;
5733 // already in that team?
5734 if (pPlr->Team == idNewTeam) return true;
5735 // ask team setting if it's allowed (also checks for valid team)
5736 if (!Game.Teams.IsJoin2TeamAllowed(idTeam: idNewTeam)) return false;
5737 // ask script if it's allowed
5738 if (!fNoCalls)
5739 {
5740 if (Game.Script.GRBroadcast(PSF_RejectTeamSwitch, pPars: {C4VInt(iVal: iPlayer), C4VInt(iVal: idNewTeam)}, fPassError: true, fRejectTest: true))
5741 return false;
5742 }
5743 // exit previous team
5744 C4Team *pOldTeam = Game.Teams.GetTeamByPlayerID(iID: pPlr->ID);
5745 int32_t idOldTeam = 0;
5746 if (pOldTeam)
5747 {
5748 idOldTeam = pOldTeam->GetID();
5749 pOldTeam->RemovePlayerByID(iID: pPlr->ID);
5750 }
5751 // enter new team
5752 if (idNewTeam)
5753 {
5754 C4Team *pNewTeam = Game.Teams.GetGenerateTeamByID(iID: idNewTeam);
5755 if (pNewTeam)
5756 {
5757 pNewTeam->AddPlayer(rInfo&: *pPlrInfo, fAdjustPlayer: true);
5758 idNewTeam = pNewTeam->GetID();
5759 // Update common home base material
5760 if (Game.Rules & C4RULE_TeamHombase && !fNoCalls) pPlr->SyncHomebaseMaterialFromTeam();
5761 }
5762 else
5763 {
5764 // unknown error
5765 pPlr->Team = idNewTeam = 0;
5766 }
5767 }
5768 // update hositlities if this is not a "silent" change
5769 if (!fNoCalls)
5770 {
5771 pPlr->SetTeamHostility();
5772 }
5773 // do callback to reflect change in scenario
5774 if (!fNoCalls)
5775 Game.Script.GRBroadcast(PSF_OnTeamSwitch, pPars: {C4VInt(iVal: iPlayer), C4VInt(iVal: idNewTeam), C4VInt(iVal: idOldTeam)}, fPassError: true);
5776 return true;
5777}
5778
5779static std::optional<C4ValueInt> FnGetTeamConfig(C4AulContext *cthr, C4ValueInt iConfigValue)
5780{
5781 // query value
5782 switch (iConfigValue)
5783 {
5784 case C4TeamList::TEAM_Custom: return {Game.Teams.IsCustom()};
5785 case C4TeamList::TEAM_Active: return {Game.Teams.IsMultiTeams()};
5786 case C4TeamList::TEAM_AllowHostilityChange: return {Game.Teams.IsHostilityChangeAllowed()};
5787 case C4TeamList::TEAM_Dist: return {Game.Teams.GetTeamDist()};
5788 case C4TeamList::TEAM_AllowTeamSwitch: return {Game.Teams.IsTeamSwitchAllowed()};
5789 case C4TeamList::TEAM_AutoGenerateTeams: return {Game.Teams.IsAutoGenerateTeams()};
5790 case C4TeamList::TEAM_TeamColors: return {Game.Teams.IsTeamColors()};
5791 }
5792 // undefined value
5793 DebugLog(level: spdlog::level::err, fmt: "GetTeamConfig: Unknown config value: {}", args&: iConfigValue);
5794 return {};
5795}
5796
5797static C4String *FnGetTeamName(C4AulContext *cthr, C4ValueInt iTeam)
5798{
5799 C4Team *pTeam = Game.Teams.GetTeamByID(iID: iTeam);
5800 if (!pTeam) return nullptr;
5801 return String(str: pTeam->GetName());
5802}
5803
5804static std::optional<C4ValueInt> FnGetTeamColor(C4AulContext *cthr, C4ValueInt iTeam)
5805{
5806 C4Team *pTeam = Game.Teams.GetTeamByID(iID: iTeam);
5807 return pTeam ? std::make_optional(t: pTeam->GetColor()) : std::nullopt;
5808}
5809
5810static std::optional<C4ValueInt> FnGetTeamByIndex(C4AulContext *cthr, C4ValueInt iIndex)
5811{
5812 C4Team *pTeam = Game.Teams.GetTeamByIndex(iIndex);
5813 return pTeam ? std::make_optional(t: pTeam->GetID()) : std::nullopt;
5814}
5815
5816static C4ValueInt FnGetTeamCount(C4AulContext *cthr)
5817{
5818 return Game.Teams.GetTeamCount();
5819}
5820
5821static bool FnInitScenarioPlayer(C4AulContext *cthr, C4ValueInt iPlayer, C4ValueInt idTeam)
5822{
5823 C4Player *pPlr = Game.Players.Get(iPlayer);
5824 if (!pPlr) return false;
5825 return pPlr->ScenarioAndTeamInit(idTeam);
5826}
5827
5828static bool FnOnOwnerRemoved(C4AulContext *cthr)
5829{
5830 // safety
5831 C4Object *pObj = cthr->Obj; if (!pObj) return false;
5832 C4Player *pPlr = Game.Players.Get(iPlayer: pObj->Owner); if (!pPlr) return false;
5833 if (pPlr->Crew.IsContained(pObj))
5834 {
5835 // crew members: Those are removed later (AFTER the player has been removed, for backwards compatiblity with relaunch scripting)
5836 }
5837 else if ((~pObj->Category & C4D_StaticBack) || (pObj->id == C4ID_Flag))
5838 {
5839 // Regular objects: Try to find a new, suitable owner from the same team
5840 // Ignore StaticBack, because this would not be backwards compatible with many internal objects such as team account
5841 // Do not ignore flags which might be StaticBack if being attached to castle parts
5842 int32_t iNewOwner = NO_OWNER;
5843 C4Team *pTeam;
5844 if (pPlr->Team) if (pTeam = Game.Teams.GetTeamByID(iID: pPlr->Team))
5845 {
5846 for (int32_t i = 0; i < pTeam->GetPlayerCount(); ++i)
5847 {
5848 int32_t iPlrID = pTeam->GetIndexedPlayer(iIndex: i);
5849 if (iPlrID && iPlrID != pPlr->ID)
5850 {
5851 C4PlayerInfo *pPlrInfo = Game.PlayerInfos.GetPlayerInfoByID(id: iPlrID);
5852 if (pPlrInfo) if (pPlrInfo->IsJoined())
5853 {
5854 // this looks like a good new owner
5855 iNewOwner = pPlrInfo->GetInGameNumber();
5856 break;
5857 }
5858 }
5859 }
5860 }
5861 // if noone from the same team was found, try to find another non-hostile player
5862 // (necessary for cooperative rounds without teams)
5863 if (iNewOwner == NO_OWNER)
5864 for (C4Player *pOtherPlr = Game.Players.First; pOtherPlr; pOtherPlr = pOtherPlr->Next)
5865 if (pOtherPlr != pPlr) if (!pOtherPlr->Eliminated)
5866 if (!Game.Players.Hostile(iPlayer1: pOtherPlr->Number, iPlayer2: pPlr->Number))
5867 iNewOwner = pOtherPlr->Number;
5868
5869 // set this owner
5870 pObj->SetOwner(iNewOwner);
5871 }
5872 return true;
5873}
5874
5875static void FnSetScoreboardData(C4AulContext *cthr, C4ValueInt iRowID, C4ValueInt iColID, C4String *pText, C4ValueInt iData)
5876{
5877 Game.Scoreboard.SetCell(iColKey: iColID, iRowKey: iRowID, szValue: pText ? pText->Data.getData() : nullptr, iValue: iData);
5878}
5879
5880static C4String *FnGetScoreboardString(C4AulContext *cthr, C4ValueInt iRowID, C4ValueInt iColID)
5881{
5882 return String(str: Game.Scoreboard.GetCellString(iColKey: iColID, iRowKey: iRowID));
5883}
5884
5885static int32_t FnGetScoreboardData(C4AulContext *cthr, C4ValueInt iRowID, C4ValueInt iColID)
5886{
5887 return Game.Scoreboard.GetCellData(iColKey: iColID, iRowKey: iRowID);
5888}
5889
5890static bool FnDoScoreboardShow(C4AulContext *cthr, C4ValueInt iChange, C4ValueInt iForPlr)
5891{
5892 C4Player *pPlr;
5893 if (iForPlr)
5894 {
5895 // abort if the specified player is not local - but always return if the player exists,
5896 // to ensure sync safety
5897 if (!(pPlr = Game.Players.Get(iPlayer: iForPlr - 1))) return false;
5898 if (!pPlr->LocalControl) return true;
5899 }
5900 Game.Scoreboard.DoDlgShow(iChange, fUserToggle: false);
5901 return true;
5902}
5903
5904static bool FnSortScoreboard(C4AulContext *cthr, C4ValueInt iByColID, bool fReverse)
5905{
5906 return Game.Scoreboard.SortBy(iColKey: iByColID, fReverse: !!fReverse);
5907}
5908
5909static bool FnAddEvaluationData(C4AulContext *cthr, C4String *pText, C4ValueInt idPlayer)
5910{
5911 // safety
5912 if (!pText) return false;
5913 if (!pText->Data.getLength()) return false;
5914 if (idPlayer && !Game.PlayerInfos.GetPlayerInfoByID(id: idPlayer)) return false;
5915 // add data
5916 Game.RoundResults.AddCustomEvaluationString(szCustomString: pText->Data.getData(), idPlayer);
5917 return true;
5918}
5919
5920static std::optional<int32_t> FnGetLeagueScore(C4AulContext *cthr, C4ValueInt idPlayer)
5921{
5922 // security
5923 if (idPlayer < 1) return {};
5924 // get info
5925 C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(id: idPlayer);
5926 if (!pInfo) return {};
5927 // get league score
5928 return {pInfo->getLeagueScore()};
5929}
5930
5931static void FnHideSettlementScoreInEvaluation(C4AulContext *cthr, bool fHide)
5932{
5933 Game.RoundResults.HideSettlementScore(fHide);
5934}
5935
5936static std::optional<C4ValueInt> FnGetUnusedOverlayID(C4AulContext *ctx, C4ValueInt iBaseIndex, C4Object *pObj)
5937{
5938 // local call / safety
5939 if (!iBaseIndex) return {};
5940 if (!pObj) if (!(pObj = ctx->Obj)) return {};
5941 // find search first unused index from there on
5942 int iSearchDir = (iBaseIndex < 0) ? -1 : 1;
5943 while (pObj->GetGraphicsOverlay(iForID: iBaseIndex, fCreate: false)) iBaseIndex += iSearchDir;
5944 return iBaseIndex;
5945}
5946
5947static C4ValueInt FnActivateGameGoalMenu(C4AulContext *ctx, C4ValueInt iPlayer)
5948{
5949 // get target player
5950 C4Player *pPlr = Game.Players.Get(iPlayer);
5951 if (!pPlr) return false;
5952 // open menu
5953 return pPlr->Menu.ActivateGoals(iPlayer: pPlr->Number, fDoActivate: pPlr->LocalControl && !Game.Control.isReplay());
5954}
5955
5956static void FnFatalError(C4AulContext *ctx, C4String *pErrorMsg)
5957{
5958 throw C4AulExecError(ctx->Obj, std::format(fmt: "User error: {}", args: pErrorMsg ? pErrorMsg->Data.getData() : "(no error)"));
5959}
5960
5961static void FnStartCallTrace(C4AulContext *ctx)
5962{
5963 extern void C4AulStartTrace();
5964 C4AulStartTrace();
5965}
5966
5967static bool FnStartScriptProfiler(C4AulContext *ctx, C4ID idScript)
5968{
5969 // get script to profile
5970 C4AulScript *pScript;
5971 if (idScript)
5972 {
5973 C4Def *pDef = C4Id2Def(id: idScript);
5974 if (!pDef) return false;
5975 pScript = &pDef->Script;
5976 }
5977 else
5978 pScript = &Game.ScriptEngine;
5979 // profile it
5980 C4AulProfiler::StartProfiling(pScript);
5981 return true;
5982}
5983
5984static void FnStopScriptProfiler(C4AulContext *ctx)
5985{
5986 C4AulProfiler::StopProfiling();
5987}
5988
5989static bool FnCustomMessage(C4AulContext *ctx, C4String *pMsg, C4Object *pObj, C4ValueInt iOwner, C4ValueInt iOffX, C4ValueInt iOffY, std::optional<C4ValueInt> clr, C4ID idDeco, C4String *sPortrait, C4ValueInt dwFlags, C4ValueInt iHSize)
5990{
5991 // safeties
5992 if (!pMsg) return false;
5993 if (pObj && !pObj->Status) return false;
5994 const char *szMsg = pMsg->Data.getData();
5995 if (!szMsg) return false;
5996 if (idDeco && !C4Id2Def(id: idDeco)) return false;
5997 // only one positioning flag per direction allowed
5998 uint32_t hpos = dwFlags & (C4GM_Left | C4GM_HCenter | C4GM_Right);
5999 uint32_t vpos = dwFlags & (C4GM_Top | C4GM_VCenter | C4GM_Bottom);
6000 if (((hpos | hpos - 1) + 1) >> 1 != hpos)
6001 {
6002 throw C4AulExecError(ctx->Obj, "CustomMessage: Only one horizontal positioning flag allowed!");
6003 }
6004 if (((vpos | vpos - 1) + 1) >> 1 != vpos)
6005 {
6006 throw C4AulExecError(ctx->Obj, "CustomMessage: Only one vertical positioning flag allowed!");
6007 }
6008
6009 uint32_t alignment = dwFlags & (C4GM_ALeft | C4GM_ACenter | C4GM_ARight);
6010 if (((alignment | alignment - 1) + 1) >> 1 != alignment)
6011 {
6012 throw C4AulExecError(ctx->Obj, "CustomMessage: Only one text alignment flag allowed!");
6013 }
6014
6015 // message color
6016 const auto dwClr = InvertRGBAAlpha(dwFromClr: clr.value_or(u: 0xffffff));
6017 // message type
6018 int32_t iType;
6019 if (pObj)
6020 if (iOwner != NO_OWNER)
6021 iType = C4GM_TargetPlayer;
6022 else
6023 iType = C4GM_Target;
6024 else if (iOwner != NO_OWNER)
6025 iType = C4GM_GlobalPlayer;
6026 else
6027 iType = C4GM_Global;
6028 // remove speech?
6029 StdStrBuf sMsg;
6030 sMsg.Ref(pnData: szMsg);
6031 if (dwFlags & C4GM_DropSpeech) sMsg.SplitAtChar(cSplit: '$', psSplit: nullptr);
6032 // create it!
6033 return Game.Messages.New(iType, Text: sMsg, pTarget: pObj, iPlayer: iOwner, iX: iOffX, iY: iOffY, dwClr: static_cast<uint32_t>(dwClr), idDecoID: idDeco, szPortraitDef: sPortrait ? sPortrait->Data.getData() : nullptr, dwFlags, width: iHSize);
6034}
6035
6036static void FnPauseGame(C4AulContext *ctx, bool fToggle)
6037{
6038 // not in replay (film)
6039 if (Game.Control.isReplay()) return;
6040 // script method for halting game (for films)
6041 if (fToggle)
6042 Console.TogglePause();
6043 else
6044 Console.DoHalt();
6045}
6046
6047static void FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4String *szNextMissionText, C4String *szNextMissionDesc)
6048{
6049 if (!szNextMission || !szNextMission->Data.getLength())
6050 {
6051 // param empty: clear next mission
6052 Game.NextMission.Clear();
6053 Game.NextMissionText.Clear();
6054 }
6055 else
6056 {
6057 // set next mission, button and button desc if given
6058 Game.NextMission.Copy(Buf2: szNextMission->Data);
6059 if (szNextMissionText && szNextMissionText->Data.getData())
6060 {
6061 Game.NextMissionText.Copy(Buf2: szNextMissionText->Data);
6062 }
6063 else
6064 {
6065 Game.NextMissionText.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_BTN_NEXTSCENARIO));
6066 }
6067 if (szNextMissionDesc && szNextMissionDesc->Data.getData())
6068 {
6069 Game.NextMissionDesc.Copy(Buf2: szNextMissionDesc->Data);
6070 }
6071 else
6072 {
6073 Game.NextMissionDesc.Copy(pnData: LoadResStr(id: C4ResStrTableKey::IDS_DESC_NEXTSCENARIO));
6074 }
6075 }
6076}
6077
6078static C4ValueArray *FnGetKeys(C4AulContext *ctx, C4ValueHash *map)
6079{
6080 if (!map) throw C4AulExecError(ctx->Obj, "GetKeys(): map expected, got 0");
6081
6082 C4ValueArray *keys = new C4ValueArray(map->size());
6083
6084 size_t i = 0;
6085 for (const auto &[key, value] : *map)
6086 {
6087 (*keys)[i] = key;
6088 ++i;
6089 }
6090
6091 return keys;
6092}
6093
6094static C4ValueArray *FnGetValues(C4AulContext *ctx, C4ValueHash *map)
6095{
6096 if (!map) throw C4AulExecError(ctx->Obj, "GetValues(): map expected, got 0");
6097
6098 C4ValueArray *keys = new C4ValueArray(map->size());
6099
6100 size_t i = 0;
6101 for (const auto &[key, value] : *map)
6102 {
6103 (*keys)[i] = value;
6104 ++i;
6105 }
6106
6107 return keys;
6108}
6109
6110static void FnSetRestoreInfos(C4AulContext *ctx, C4ValueInt what)
6111{
6112 Game.RestartRestoreInfos.What = static_cast<std::underlying_type_t<C4NetworkRestartInfos::RestoreInfo>>(what);
6113}
6114
6115template<std::size_t ParCount>
6116class C4AulEngineFuncHelper : public C4AulFunc
6117{
6118 const std::array<C4V_Type, ParCount> parTypes;
6119 const bool pub;
6120
6121public:
6122 template<typename... ParTypes>
6123 C4AulEngineFuncHelper(C4AulScript *owner, const char *name, bool pub, ParTypes... parTypes) : C4AulFunc{owner, name}, parTypes{parTypes...}, pub{pub} {}
6124 virtual const C4V_Type *GetParType() noexcept override { return parTypes.data(); }
6125 virtual int GetParCount() noexcept override { return ParCount; }
6126 virtual bool GetPublic() noexcept override { return pub; }
6127};
6128
6129template<typename Ret, typename... Pars>
6130class C4AulEngineFunc : public C4AulEngineFuncHelper<sizeof...(Pars)>
6131{
6132 constexpr static auto ParCount = sizeof...(Pars);
6133 using Func = Ret(&)(C4AulContext *context, Pars...);
6134 constexpr auto static isVoid = std::is_same_v<Ret, void>;
6135 Func func;
6136
6137public:
6138 C4AulEngineFunc(C4AulScript *owner, const char *name, Func func, bool pub) : C4AulEngineFuncHelper<ParCount>{owner, name, pub, C4ValueConv<Pars>::Type()...}, func{func} {}
6139
6140 virtual C4V_Type GetRetType() noexcept override
6141 {
6142 if constexpr (isVoid) { return C4V_Any; }
6143 else return C4ValueConv<Ret>::Type();
6144 }
6145
6146 C4Value Exec(C4AulContext *context, const C4Value pars[], bool = false) override
6147 {
6148 constexpr auto callHelper = [](C4AulEngineFunc *that, C4AulContext *context, const C4Value pars[])
6149 {
6150 return that->ExecHelper(context, pars, std::make_index_sequence<ParCount>());
6151 };
6152 if constexpr (isVoid)
6153 {
6154 callHelper(this, context, pars);
6155 return C4VNull;
6156 }
6157 else
6158 {
6159 return C4ValueConv<Ret>::ToC4V(callHelper(this, context, pars));
6160 }
6161 }
6162
6163private:
6164 template<std::size_t... indices>
6165 auto ExecHelper(C4AulContext *context, const C4Value pars[], std::index_sequence<indices...>) const
6166 {
6167 return func(context, C4ValueConv<Pars>::_FromC4V(pars[indices])...);
6168 }
6169};
6170
6171template <typename Ret, typename... Pars>
6172static void AddFunc(C4AulScript *owner, const char *name, Ret (&func)(C4AulContext *context, Pars...), bool pub = true)
6173{
6174 new C4AulEngineFunc<Ret, Pars...>{owner, name, func, pub};
6175}
6176
6177template<C4V_Type fromType, C4V_Type toType>
6178class C4AulDefCastFunc : public C4AulEngineFuncHelper<1>
6179{
6180public:
6181 C4AulDefCastFunc(C4AulScript *owner, const char *name) :
6182 C4AulEngineFuncHelper<1>(owner, name, false, fromType) {}
6183
6184 C4Value Exec(C4AulContext *, const C4Value pars[], bool = false) override
6185 {
6186 return C4Value{pars->GetData(), toType};
6187 }
6188
6189 C4V_Type GetRetType() noexcept override { return toType; }
6190};
6191
6192template<std::size_t ParCount, C4V_Type RetType>
6193class C4AulEngineFuncParArray : public C4AulEngineFuncHelper<ParCount>
6194{
6195 using Func = C4Value(&)(C4AulContext *context, const C4Value *pars);
6196 Func func;
6197
6198public:
6199 template<typename... ParTypes>
6200 C4AulEngineFuncParArray(C4AulScript *owner, const char *name, Func func, ParTypes... parTypes) : C4AulEngineFuncHelper<ParCount>{owner, name, true, parTypes...}, func{func} {}
6201 C4V_Type GetRetType() noexcept override { return RetType; }
6202 C4Value Exec(C4AulContext *context, const C4Value pars[], bool = false) override
6203 {
6204 return func(context, pars);
6205 }
6206};
6207
6208static constexpr C4ScriptConstDef C4ScriptConstMap[] =
6209{
6210 { .Identifier: "C4D_All", .ValType: C4V_Int, .Data: C4D_All },
6211 { .Identifier: "C4D_StaticBack", .ValType: C4V_Int, .Data: C4D_StaticBack },
6212 { .Identifier: "C4D_Structure", .ValType: C4V_Int, .Data: C4D_Structure },
6213 { .Identifier: "C4D_Vehicle", .ValType: C4V_Int, .Data: C4D_Vehicle },
6214 { .Identifier: "C4D_Living", .ValType: C4V_Int, .Data: C4D_Living },
6215 { .Identifier: "C4D_Object", .ValType: C4V_Int, .Data: C4D_Object },
6216 { .Identifier: "C4D_Goal", .ValType: C4V_Int, .Data: C4D_Goal },
6217 { .Identifier: "C4D_Environment", .ValType: C4V_Int, .Data: C4D_Environment },
6218 { .Identifier: "C4D_Knowledge", .ValType: C4V_Int, .Data: C4D_SelectKnowledge },
6219 { .Identifier: "C4D_Magic", .ValType: C4V_Int, .Data: C4D_Magic },
6220 { .Identifier: "C4D_Rule", .ValType: C4V_Int, .Data: C4D_Rule },
6221 { .Identifier: "C4D_Background", .ValType: C4V_Int, .Data: C4D_Background },
6222 { .Identifier: "C4D_Parallax", .ValType: C4V_Int, .Data: C4D_Parallax },
6223 { .Identifier: "C4D_MouseSelect", .ValType: C4V_Int, .Data: C4D_MouseSelect },
6224 { .Identifier: "C4D_Foreground", .ValType: C4V_Int, .Data: C4D_Foreground },
6225 { .Identifier: "C4D_MouseIgnore", .ValType: C4V_Int, .Data: C4D_MouseIgnore },
6226 { .Identifier: "C4D_IgnoreFoW", .ValType: C4V_Int, .Data: C4D_IgnoreFoW },
6227
6228 { .Identifier: "C4D_GrabGet", .ValType: C4V_Int, .Data: C4D_Grab_Get },
6229 { .Identifier: "C4D_GrabPut", .ValType: C4V_Int, .Data: C4D_Grab_Put },
6230
6231 { .Identifier: "C4D_LinePower", .ValType: C4V_Int, .Data: C4D_Line_Power },
6232 { .Identifier: "C4D_LineSource", .ValType: C4V_Int, .Data: C4D_Line_Source },
6233 { .Identifier: "C4D_LineDrain", .ValType: C4V_Int, .Data: C4D_Line_Drain },
6234 { .Identifier: "C4D_LineLightning", .ValType: C4V_Int, .Data: C4D_Line_Lightning },
6235 { .Identifier: "C4D_LineVolcano", .ValType: C4V_Int, .Data: C4D_Line_Volcano },
6236 { .Identifier: "C4D_LineRope", .ValType: C4V_Int, .Data: C4D_Line_Rope },
6237 { .Identifier: "C4D_LineColored", .ValType: C4V_Int, .Data: C4D_Line_Colored },
6238 { .Identifier: "C4D_LineVertex", .ValType: C4V_Int, .Data: C4D_Line_Vertex },
6239
6240 { .Identifier: "C4D_PowerInput", .ValType: C4V_Int, .Data: C4D_Power_Input },
6241 { .Identifier: "C4D_PowerOutput", .ValType: C4V_Int, .Data: C4D_Power_Output },
6242 { .Identifier: "C4D_LiquidInput", .ValType: C4V_Int, .Data: C4D_Liquid_Input },
6243 { .Identifier: "C4D_LiquidOutput", .ValType: C4V_Int, .Data: C4D_Liquid_Output },
6244 { .Identifier: "C4D_PowerGenerator", .ValType: C4V_Int, .Data: C4D_Power_Generator },
6245 { .Identifier: "C4D_PowerConsumer", .ValType: C4V_Int, .Data: C4D_Power_Consumer },
6246 { .Identifier: "C4D_LiquidPump", .ValType: C4V_Int, .Data: C4D_Liquid_Pump },
6247 { .Identifier: "C4D_EnergyHolder", .ValType: C4V_Int, .Data: C4D_EnergyHolder },
6248
6249 { .Identifier: "C4V_Any", .ValType: C4V_Int, .Data: C4V_Any },
6250 { .Identifier: "C4V_Int", .ValType: C4V_Int, .Data: C4V_Int },
6251 { .Identifier: "C4V_Bool", .ValType: C4V_Int, .Data: C4V_Bool },
6252 { .Identifier: "C4V_C4ID", .ValType: C4V_Int, .Data: C4V_C4ID },
6253 { .Identifier: "C4V_C4Object", .ValType: C4V_Int, .Data: C4V_C4Object },
6254 { .Identifier: "C4V_String", .ValType: C4V_Int, .Data: C4V_String },
6255 { .Identifier: "C4V_Array", .ValType: C4V_Int, .Data: C4V_Array },
6256 { .Identifier: "C4V_Map", .ValType: C4V_Int, .Data: C4V_Map },
6257
6258 { .Identifier: "COMD_None", .ValType: C4V_Int, COMD_None },
6259 { .Identifier: "COMD_Stop", .ValType: C4V_Int, COMD_Stop },
6260 { .Identifier: "COMD_Up", .ValType: C4V_Int, COMD_Up },
6261 { .Identifier: "COMD_UpRight", .ValType: C4V_Int, COMD_UpRight },
6262 { .Identifier: "COMD_Right", .ValType: C4V_Int, COMD_Right },
6263 { .Identifier: "COMD_DownRight", .ValType: C4V_Int, COMD_DownRight },
6264 { .Identifier: "COMD_Down", .ValType: C4V_Int, COMD_Down },
6265 { .Identifier: "COMD_DownLeft", .ValType: C4V_Int, COMD_DownLeft },
6266 { .Identifier: "COMD_Left", .ValType: C4V_Int, COMD_Left },
6267 { .Identifier: "COMD_UpLeft", .ValType: C4V_Int, COMD_UpLeft },
6268
6269 { .Identifier: "DIR_Left", .ValType: C4V_Int, DIR_Left },
6270 { .Identifier: "DIR_Right", .ValType: C4V_Int, DIR_Right },
6271
6272 { .Identifier: "CON_CursorLeft", .ValType: C4V_Int, .Data: CON_CursorLeft },
6273 { .Identifier: "CON_CursorToggle", .ValType: C4V_Int, .Data: CON_CursorToggle },
6274 { .Identifier: "CON_CursorRight", .ValType: C4V_Int, .Data: CON_CursorRight },
6275 { .Identifier: "CON_Throw", .ValType: C4V_Int, .Data: CON_Throw },
6276 { .Identifier: "CON_Up", .ValType: C4V_Int, .Data: CON_Up },
6277 { .Identifier: "CON_Dig", .ValType: C4V_Int, .Data: CON_Dig },
6278 { .Identifier: "CON_Left", .ValType: C4V_Int, .Data: CON_Left },
6279 { .Identifier: "CON_Down", .ValType: C4V_Int, .Data: CON_Down },
6280 { .Identifier: "CON_Right", .ValType: C4V_Int, .Data: CON_Right },
6281 { .Identifier: "CON_Menu", .ValType: C4V_Int, .Data: CON_Menu },
6282 { .Identifier: "CON_Special", .ValType: C4V_Int, .Data: CON_Special },
6283 { .Identifier: "CON_Special2", .ValType: C4V_Int, .Data: CON_Special2 },
6284
6285 { .Identifier: "OCF_Construct", .ValType: C4V_Int, .Data: OCF_Construct },
6286 { .Identifier: "OCF_Grab", .ValType: C4V_Int, .Data: OCF_Grab },
6287 { .Identifier: "OCF_Collectible", .ValType: C4V_Int, .Data: OCF_Carryable },
6288 { .Identifier: "OCF_OnFire", .ValType: C4V_Int, .Data: OCF_OnFire },
6289 { .Identifier: "OCF_HitSpeed1", .ValType: C4V_Int, .Data: OCF_HitSpeed1 },
6290 { .Identifier: "OCF_Fullcon", .ValType: C4V_Int, .Data: OCF_FullCon },
6291 { .Identifier: "OCF_Inflammable", .ValType: C4V_Int, .Data: OCF_Inflammable },
6292 { .Identifier: "OCF_Chop", .ValType: C4V_Int, .Data: OCF_Chop },
6293 { .Identifier: "OCF_Rotate", .ValType: C4V_Int, .Data: OCF_Rotate },
6294 { .Identifier: "OCF_Exclusive", .ValType: C4V_Int, .Data: OCF_Exclusive },
6295 { .Identifier: "OCF_Entrance", .ValType: C4V_Int, .Data: OCF_Entrance },
6296 { .Identifier: "OCF_HitSpeed2", .ValType: C4V_Int, .Data: OCF_HitSpeed2 },
6297 { .Identifier: "OCF_HitSpeed3", .ValType: C4V_Int, .Data: OCF_HitSpeed3 },
6298 { .Identifier: "OCF_Collection", .ValType: C4V_Int, .Data: OCF_Collection },
6299 { .Identifier: "OCF_Living", .ValType: C4V_Int, .Data: OCF_Living },
6300 { .Identifier: "OCF_HitSpeed4", .ValType: C4V_Int, .Data: OCF_HitSpeed4 },
6301 { .Identifier: "OCF_FightReady", .ValType: C4V_Int, .Data: OCF_FightReady },
6302 { .Identifier: "OCF_LineConstruct", .ValType: C4V_Int, .Data: OCF_LineConstruct },
6303 { .Identifier: "OCF_Prey", .ValType: C4V_Int, .Data: OCF_Prey },
6304 { .Identifier: "OCF_AttractLightning", .ValType: C4V_Int, .Data: OCF_AttractLightning },
6305 { .Identifier: "OCF_NotContained", .ValType: C4V_Int, .Data: OCF_NotContained },
6306 { .Identifier: "OCF_CrewMember", .ValType: C4V_Int, .Data: OCF_CrewMember },
6307 { .Identifier: "OCF_Edible", .ValType: C4V_Int, .Data: OCF_Edible },
6308 { .Identifier: "OCF_InLiquid", .ValType: C4V_Int, .Data: OCF_InLiquid },
6309 { .Identifier: "OCF_InSolid", .ValType: C4V_Int, .Data: OCF_InSolid },
6310 { .Identifier: "OCF_InFree", .ValType: C4V_Int, .Data: OCF_InFree },
6311 { .Identifier: "OCF_Available", .ValType: C4V_Int, .Data: OCF_Available },
6312 { .Identifier: "OCF_PowerConsumer", .ValType: C4V_Int, .Data: OCF_PowerConsumer },
6313 { .Identifier: "OCF_PowerSupply", .ValType: C4V_Int, .Data: OCF_PowerSupply },
6314 { .Identifier: "OCF_Container", .ValType: C4V_Int, .Data: OCF_Container },
6315 { .Identifier: "OCF_Alive", .ValType: C4V_Int, .Data: static_cast<C4ValueInt>(OCF_Alive) },
6316
6317 { .Identifier: "VIS_All", .ValType: C4V_Int, VIS_All },
6318 { .Identifier: "VIS_None", .ValType: C4V_Int, VIS_None },
6319 { .Identifier: "VIS_Owner", .ValType: C4V_Int, VIS_Owner },
6320 { .Identifier: "VIS_Allies", .ValType: C4V_Int, VIS_Allies },
6321 { .Identifier: "VIS_Enemies", .ValType: C4V_Int, VIS_Enemies },
6322 { .Identifier: "VIS_Local", .ValType: C4V_Int, VIS_Local },
6323 { .Identifier: "VIS_God", .ValType: C4V_Int, VIS_God },
6324 { .Identifier: "VIS_LayerToggle", .ValType: C4V_Int, VIS_LayerToggle },
6325 { .Identifier: "VIS_OverlayOnly", .ValType: C4V_Int, VIS_OverlayOnly },
6326
6327 { .Identifier: "C4X_Ver1", .ValType: C4V_Int, C4XVER1 },
6328 { .Identifier: "C4X_Ver2", .ValType: C4V_Int, C4XVER2 },
6329 { .Identifier: "C4X_Ver3", .ValType: C4V_Int, C4XVER3 },
6330 { .Identifier: "C4X_Ver4", .ValType: C4V_Int, C4XVER4 },
6331 { .Identifier: "C4X_VerBuild", .ValType: C4V_Int, C4XVERBUILD },
6332
6333 { .Identifier: "SkyPar_Keep", .ValType: C4V_Int, SkyPar_KEEP },
6334
6335 { .Identifier: "C4MN_Style_Normal", .ValType: C4V_Int, .Data: C4MN_Style_Normal },
6336 { .Identifier: "C4MN_Style_Context", .ValType: C4V_Int, .Data: C4MN_Style_Context },
6337 { .Identifier: "C4MN_Style_Info", .ValType: C4V_Int, .Data: C4MN_Style_Info },
6338 { .Identifier: "C4MN_Style_Dialog", .ValType: C4V_Int, .Data: C4MN_Style_Dialog },
6339 { .Identifier: "C4MN_Style_EqualItemHeight", .ValType: C4V_Int, .Data: C4MN_Style_EqualItemHeight },
6340
6341 { .Identifier: "C4MN_Extra_None", .ValType: C4V_Int, .Data: C4MN_Extra_None },
6342 { .Identifier: "C4MN_Extra_Components", .ValType: C4V_Int, .Data: C4MN_Extra_Components },
6343 { .Identifier: "C4MN_Extra_Value", .ValType: C4V_Int, .Data: C4MN_Extra_Value },
6344 { .Identifier: "C4MN_Extra_MagicValue", .ValType: C4V_Int, .Data: C4MN_Extra_MagicValue },
6345 { .Identifier: "C4MN_Extra_Info", .ValType: C4V_Int, .Data: C4MN_Extra_Info },
6346 { .Identifier: "C4MN_Extra_ComponentsMagic", .ValType: C4V_Int, .Data: C4MN_Extra_ComponentsMagic },
6347 { .Identifier: "C4MN_Extra_LiveMagicValue", .ValType: C4V_Int, .Data: C4MN_Extra_LiveMagicValue },
6348 { .Identifier: "C4MN_Extra_ComponentsLiveMagic", .ValType: C4V_Int, .Data: C4MN_Extra_ComponentsLiveMagic },
6349
6350 { .Identifier: "C4MN_Add_ImgRank", .ValType: C4V_Int, .Data: C4MN_Add_ImgRank },
6351 { .Identifier: "C4MN_Add_ImgIndexed", .ValType: C4V_Int, .Data: C4MN_Add_ImgIndexed },
6352 { .Identifier: "C4MN_Add_ImgObjRank", .ValType: C4V_Int, .Data: C4MN_Add_ImgObjRank },
6353 { .Identifier: "C4MN_Add_ImgObject", .ValType: C4V_Int, .Data: C4MN_Add_ImgObject },
6354 { .Identifier: "C4MN_Add_ImgTextSpec", .ValType: C4V_Int, .Data: C4MN_Add_ImgTextSpec },
6355 { .Identifier: "C4MN_Add_ImgColor", .ValType: C4V_Int, .Data: C4MN_Add_ImgColor },
6356 { .Identifier: "C4MN_Add_ImgIndexedColor", .ValType: C4V_Int, .Data: C4MN_Add_ImgIndexedColor },
6357 { .Identifier: "C4MN_Add_PassValue", .ValType: C4V_Int, .Data: C4MN_Add_PassValue },
6358 { .Identifier: "C4MN_Add_ForceCount", .ValType: C4V_Int, .Data: C4MN_Add_ForceCount },
6359 { .Identifier: "C4MN_Add_ForceNoDesc", .ValType: C4V_Int, .Data: C4MN_Add_ForceNoDesc },
6360
6361 { .Identifier: "FX_OK", .ValType: C4V_Int, C4Fx_OK }, // generic standard behaviour for all effect callbacks
6362 { .Identifier: "FX_Effect_Deny", .ValType: C4V_Int, C4Fx_Effect_Deny }, // delete effect
6363 { .Identifier: "FX_Effect_Annul", .ValType: C4V_Int, C4Fx_Effect_Annul }, // delete effect, because it has annulled a countereffect
6364 { .Identifier: "FX_Effect_AnnulDoCalls", .ValType: C4V_Int, C4Fx_Effect_AnnulCalls }, // delete effect, because it has annulled a countereffect; temp readd countereffect
6365 { .Identifier: "FX_Execute_Kill", .ValType: C4V_Int, C4Fx_Execute_Kill }, // execute callback: Remove effect now
6366 { .Identifier: "FX_Stop_Deny", .ValType: C4V_Int, C4Fx_Stop_Deny }, // deny effect removal
6367 { .Identifier: "FX_Start_Deny", .ValType: C4V_Int, C4Fx_Start_Deny }, // deny effect start
6368
6369 { .Identifier: "FX_Call_Normal", .ValType: C4V_Int, C4FxCall_Normal }, // normal call; effect is being added or removed
6370 { .Identifier: "FX_Call_Temp", .ValType: C4V_Int, C4FxCall_Temp }, // temp call; effect is being added or removed in responce to a lower-level effect change
6371 { .Identifier: "FX_Call_TempAddForRemoval", .ValType: C4V_Int, C4FxCall_TempAddForRemoval }, // temp call; effect is being added because it had been temp removed and is now removed forever
6372 { .Identifier: "FX_Call_RemoveClear", .ValType: C4V_Int, C4FxCall_RemoveClear }, // effect is being removed because object is being removed
6373 { .Identifier: "FX_Call_RemoveDeath", .ValType: C4V_Int, C4FxCall_RemoveDeath }, // effect is being removed because object died - return -1 to avoid removal
6374 { .Identifier: "FX_Call_DmgScript", .ValType: C4V_Int, C4FxCall_DmgScript }, // damage through script call
6375 { .Identifier: "FX_Call_DmgBlast", .ValType: C4V_Int, C4FxCall_DmgBlast }, // damage through blast
6376 { .Identifier: "FX_Call_DmgFire", .ValType: C4V_Int, C4FxCall_DmgFire }, // damage through fire
6377 { .Identifier: "FX_Call_DmgChop", .ValType: C4V_Int, C4FxCall_DmgChop }, // damage through chopping
6378 { .Identifier: "FX_Call_Energy", .ValType: C4V_Int, .Data: 32 }, // bitmask for generic energy loss
6379 { .Identifier: "FX_Call_EngScript", .ValType: C4V_Int, C4FxCall_EngScript }, // energy loss through script call
6380 { .Identifier: "FX_Call_EngBlast", .ValType: C4V_Int, C4FxCall_EngBlast }, // energy loss through blast
6381 { .Identifier: "FX_Call_EngObjHit", .ValType: C4V_Int, C4FxCall_EngObjHit }, // energy loss through object hitting the living
6382 { .Identifier: "FX_Call_EngFire", .ValType: C4V_Int, C4FxCall_EngFire }, // energy loss through fire
6383 { .Identifier: "FX_Call_EngBaseRefresh", .ValType: C4V_Int, C4FxCall_EngBaseRefresh }, // energy reload in base (also by base object, but that's normally not called)
6384 { .Identifier: "FX_Call_EngAsphyxiation", .ValType: C4V_Int, C4FxCall_EngAsphyxiation }, // energy loss through asphyxiaction
6385 { .Identifier: "FX_Call_EngCorrosion", .ValType: C4V_Int, C4FxCall_EngCorrosion }, // energy loss through corrosion (acid)
6386 { .Identifier: "FX_Call_EngStruct", .ValType: C4V_Int, C4FxCall_EngStruct }, // regular structure energy loss (normally not called)
6387 { .Identifier: "FX_Call_EngGetPunched", .ValType: C4V_Int, C4FxCall_EngGetPunched }, // energy loss during fighting
6388
6389 { .Identifier: "GFXOV_MODE_None", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_None }, // gfx overlay modes
6390 { .Identifier: "GFXOV_MODE_Base", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_Base },
6391 { .Identifier: "GFXOV_MODE_Action", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_Action },
6392 { .Identifier: "GFXOV_MODE_Picture", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_Picture },
6393 { .Identifier: "GFXOV_MODE_IngamePicture", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_IngamePicture },
6394 { .Identifier: "GFXOV_MODE_Object", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_Object },
6395 { .Identifier: "GFXOV_MODE_ExtraGraphics", .ValType: C4V_Int, .Data: C4GraphicsOverlay::MODE_ExtraGraphics },
6396 { .Identifier: "GFX_Overlay", .ValType: C4V_Int, .Data: 1 }, // default overlay index
6397 { .Identifier: "GFXOV_Clothing", .ValType: C4V_Int, .Data: 1000 }, // overlay indices for clothes on Clonks, etc.
6398 { .Identifier: "GFXOV_Tools", .ValType: C4V_Int, .Data: 2000 }, // overlay indices for tools, weapons, etc.
6399 { .Identifier: "GFXOV_ProcessTarget", .ValType: C4V_Int, .Data: 3000 }, // overlay indices for objects processed by a Clonk
6400 { .Identifier: "GFXOV_Misc", .ValType: C4V_Int, .Data: 5000 }, // overlay indices for other stuff
6401 { .Identifier: "GFXOV_UI", .ValType: C4V_Int, .Data: 6000 }, // overlay indices for user interface
6402 { .Identifier: "GFX_BLIT_Additive", .ValType: C4V_Int, C4GFXBLIT_ADDITIVE }, // blit modes
6403 { .Identifier: "GFX_BLIT_Mod2", .ValType: C4V_Int, C4GFXBLIT_MOD2 },
6404 { .Identifier: "GFX_BLIT_ClrSfc_OwnClr", .ValType: C4V_Int, C4GFXBLIT_CLRSFC_OWNCLR },
6405 { .Identifier: "GFX_BLIT_ClrSfc_Mod2", .ValType: C4V_Int, C4GFXBLIT_CLRSFC_MOD2 },
6406 { .Identifier: "GFX_BLIT_Custom", .ValType: C4V_Int, C4GFXBLIT_CUSTOM },
6407 { .Identifier: "GFX_BLIT_Parent", .ValType: C4V_Int, C4GFXBLIT_PARENT },
6408
6409 { .Identifier: "NO_OWNER", .ValType: C4V_Int, .Data: NO_OWNER }, // invalid player number
6410
6411 // contact attachment
6412 { .Identifier: "CNAT_None", .ValType: C4V_Int, .Data: CNAT_None },
6413 { .Identifier: "CNAT_Left", .ValType: C4V_Int, .Data: CNAT_Left },
6414 { .Identifier: "CNAT_Right", .ValType: C4V_Int, .Data: CNAT_Right },
6415 { .Identifier: "CNAT_Top", .ValType: C4V_Int, .Data: CNAT_Top },
6416 { .Identifier: "CNAT_Bottom", .ValType: C4V_Int, .Data: CNAT_Bottom },
6417 { .Identifier: "CNAT_Center", .ValType: C4V_Int, .Data: CNAT_Center },
6418 { .Identifier: "CNAT_MultiAttach", .ValType: C4V_Int, .Data: CNAT_MultiAttach },
6419 { .Identifier: "CNAT_NoCollision", .ValType: C4V_Int, .Data: CNAT_NoCollision },
6420
6421 // vertex data
6422 { .Identifier: "VTX_X", .ValType: C4V_Int, .Data: VTX_X },
6423 { .Identifier: "VTX_Y", .ValType: C4V_Int, .Data: VTX_Y },
6424 { .Identifier: "VTX_CNAT", .ValType: C4V_Int, .Data: VTX_CNAT },
6425 { .Identifier: "VTX_Friction", .ValType: C4V_Int, .Data: VTX_Friction },
6426
6427 // vertex set mode
6428 { .Identifier: "VTX_SetPermanent", .ValType: C4V_Int, .Data: VTX_SetPermanent },
6429 { .Identifier: "VTX_SetPermanentUpd", .ValType: C4V_Int, .Data: VTX_SetPermanentUpd },
6430
6431 // material density
6432 { .Identifier: "C4M_Vehicle", .ValType: C4V_Int, .Data: C4M_Vehicle },
6433 { .Identifier: "C4M_Solid", .ValType: C4V_Int, .Data: C4M_Solid },
6434 { .Identifier: "C4M_SemiSolid", .ValType: C4V_Int, .Data: C4M_SemiSolid },
6435 { .Identifier: "C4M_Liquid", .ValType: C4V_Int, .Data: C4M_Liquid },
6436 { .Identifier: "C4M_Background", .ValType: C4V_Int, .Data: C4M_Background },
6437
6438 // scoreboard
6439 { .Identifier: "SBRD_Caption", .ValType: C4V_Int, .Data: C4Scoreboard::TitleKey }, // used to set row/coloumn headers
6440
6441 // teams - constants for GetTeamConfig
6442 { .Identifier: "TEAM_Custom", .ValType: C4V_Int, .Data: C4TeamList::TEAM_Custom },
6443 { .Identifier: "TEAM_Active", .ValType: C4V_Int, .Data: C4TeamList::TEAM_Active },
6444 { .Identifier: "TEAM_AllowHostilityChange", .ValType: C4V_Int, .Data: C4TeamList::TEAM_AllowHostilityChange },
6445 { .Identifier: "TEAM_Dist", .ValType: C4V_Int, .Data: C4TeamList::TEAM_Dist },
6446 { .Identifier: "TEAM_AllowTeamSwitch", .ValType: C4V_Int, .Data: C4TeamList::TEAM_AllowTeamSwitch },
6447 { .Identifier: "TEAM_AutoGenerateTeams", .ValType: C4V_Int, .Data: C4TeamList::TEAM_AutoGenerateTeams },
6448 { .Identifier: "TEAM_TeamColors", .ValType: C4V_Int, .Data: C4TeamList::TEAM_TeamColors },
6449
6450 { .Identifier: "C4OS_DELETED", .ValType: C4V_Int, C4OS_DELETED },
6451 { .Identifier: "C4OS_NORMAL", .ValType: C4V_Int, C4OS_NORMAL },
6452 { .Identifier: "C4OS_INACTIVE", .ValType: C4V_Int, C4OS_INACTIVE },
6453
6454 { .Identifier: "C4MSGCMDR_Escaped", .ValType: C4V_Int, .Data: C4MessageBoardCommand::C4MSGCMDR_Escaped },
6455 { .Identifier: "C4MSGCMDR_Plain", .ValType: C4V_Int, .Data: C4MessageBoardCommand::C4MSGCMDR_Plain },
6456 { .Identifier: "C4MSGCMDR_Identifier", .ValType: C4V_Int, .Data: C4MessageBoardCommand::C4MSGCMDR_Identifier },
6457
6458 { .Identifier: "BASEFUNC_Default", .ValType: C4V_Int, .Data: BASEFUNC_Default },
6459 { .Identifier: "BASEFUNC_AutoSellContents", .ValType: C4V_Int, .Data: BASEFUNC_AutoSellContents },
6460 { .Identifier: "BASEFUNC_RegenerateEnergy", .ValType: C4V_Int, .Data: BASEFUNC_RegenerateEnergy },
6461 { .Identifier: "BASEFUNC_Buy", .ValType: C4V_Int, .Data: BASEFUNC_Buy },
6462 { .Identifier: "BASEFUNC_Sell", .ValType: C4V_Int, .Data: BASEFUNC_Sell },
6463 { .Identifier: "BASEFUNC_RejectEntrance", .ValType: C4V_Int, .Data: BASEFUNC_RejectEntrance },
6464 { .Identifier: "BASEFUNC_Extinguish", .ValType: C4V_Int, .Data: BASEFUNC_Extinguish },
6465
6466 { .Identifier: "C4FO_Not", .ValType: C4V_Int, .Data: C4FO_Not },
6467 { .Identifier: "C4FO_And", .ValType: C4V_Int, .Data: C4FO_And },
6468 { .Identifier: "C4FO_Or", .ValType: C4V_Int, .Data: C4FO_Or },
6469 { .Identifier: "C4FO_Exclude", .ValType: C4V_Int, .Data: C4FO_Exclude },
6470 { .Identifier: "C4FO_InRect", .ValType: C4V_Int, .Data: C4FO_InRect },
6471 { .Identifier: "C4FO_AtPoint", .ValType: C4V_Int, .Data: C4FO_AtPoint },
6472 { .Identifier: "C4FO_AtRect", .ValType: C4V_Int, .Data: C4FO_AtRect },
6473 { .Identifier: "C4FO_OnLine", .ValType: C4V_Int, .Data: C4FO_OnLine },
6474 { .Identifier: "C4FO_Distance", .ValType: C4V_Int, .Data: C4FO_Distance },
6475 { .Identifier: "C4FO_ID", .ValType: C4V_Int, .Data: C4FO_ID },
6476 { .Identifier: "C4FO_OCF", .ValType: C4V_Int, .Data: C4FO_OCF },
6477 { .Identifier: "C4FO_Category", .ValType: C4V_Int, .Data: C4FO_Category },
6478 { .Identifier: "C4FO_Action", .ValType: C4V_Int, .Data: C4FO_Action },
6479 { .Identifier: "C4FO_ActionTarget", .ValType: C4V_Int, .Data: C4FO_ActionTarget },
6480 { .Identifier: "C4FO_Container", .ValType: C4V_Int, .Data: C4FO_Container },
6481 { .Identifier: "C4FO_AnyContainer", .ValType: C4V_Int, .Data: C4FO_AnyContainer },
6482 { .Identifier: "C4FO_Owner", .ValType: C4V_Int, .Data: C4FO_Owner },
6483 { .Identifier: "C4FO_Controller", .ValType: C4V_Int, .Data: C4FO_Controller },
6484 { .Identifier: "C4FO_Func", .ValType: C4V_Int, .Data: C4FO_Func },
6485 { .Identifier: "C4FO_Layer", .ValType: C4V_Int, .Data: C4FO_Layer },
6486
6487 { .Identifier: "C4SO_Reverse", .ValType: C4V_Int, .Data: C4SO_Reverse },
6488 { .Identifier: "C4SO_Multiple", .ValType: C4V_Int, .Data: C4SO_Multiple },
6489 { .Identifier: "C4SO_Distance", .ValType: C4V_Int, .Data: C4SO_Distance },
6490 { .Identifier: "C4SO_Random", .ValType: C4V_Int, .Data: C4SO_Random },
6491 { .Identifier: "C4SO_Speed", .ValType: C4V_Int, .Data: C4SO_Speed },
6492 { .Identifier: "C4SO_Mass", .ValType: C4V_Int, .Data: C4SO_Mass },
6493 { .Identifier: "C4SO_Value", .ValType: C4V_Int, .Data: C4SO_Value },
6494 { .Identifier: "C4SO_Func", .ValType: C4V_Int, .Data: C4SO_Func },
6495
6496 { .Identifier: "PHYS_Current", .ValType: C4V_Int, .Data: PHYS_Current },
6497 { .Identifier: "PHYS_Permanent", .ValType: C4V_Int, .Data: PHYS_Permanent },
6498 { .Identifier: "PHYS_Temporary", .ValType: C4V_Int, .Data: PHYS_Temporary },
6499 { .Identifier: "PHYS_StackTemporary", .ValType: C4V_Int, .Data: PHYS_StackTemporary },
6500
6501 { .Identifier: "C4CMD_Base", .ValType: C4V_Int, .Data: C4CMD_Mode_Base },
6502 { .Identifier: "C4CMD_SilentBase", .ValType: C4V_Int, .Data: C4CMD_Mode_SilentBase },
6503 { .Identifier: "C4CMD_Sub", .ValType: C4V_Int, .Data: C4CMD_Mode_Sub },
6504 { .Identifier: "C4CMD_SilentSub", .ValType: C4V_Int, .Data: C4CMD_Mode_SilentSub },
6505
6506 { .Identifier: "C4CMD_MoveTo_NoPosAdjust", .ValType: C4V_Int, .Data: C4CMD_MoveTo_NoPosAdjust },
6507 { .Identifier: "C4CMD_MoveTo_PushTarget", .ValType: C4V_Int, .Data: C4CMD_MoveTo_PushTarget },
6508 { .Identifier: "C4CMD_Enter_PushTarget", .ValType: C4V_Int, .Data: C4CMD_Enter_PushTarget },
6509
6510 { .Identifier: "C4SECT_SaveLandscape", .ValType: C4V_Int, C4S_SAVE_LANDSCAPE },
6511 { .Identifier: "C4SECT_SaveObjects", .ValType: C4V_Int, C4S_SAVE_OBJECTS },
6512 { .Identifier: "C4SECT_KeepEffects", .ValType: C4V_Int, C4S_KEEP_EFFECTS },
6513
6514 { .Identifier: "TEAMID_New", .ValType: C4V_Int, .Data: TEAMID_New },
6515
6516 { .Identifier: "MSG_NoLinebreak", .ValType: C4V_Int, .Data: C4GM_NoBreak },
6517 { .Identifier: "MSG_Bottom", .ValType: C4V_Int, .Data: C4GM_Bottom },
6518 { .Identifier: "MSG_Multiple", .ValType: C4V_Int, .Data: C4GM_Multiple },
6519 { .Identifier: "MSG_Top", .ValType: C4V_Int, .Data: C4GM_Top },
6520 { .Identifier: "MSG_Left", .ValType: C4V_Int, .Data: C4GM_Left },
6521 { .Identifier: "MSG_Right", .ValType: C4V_Int, .Data: C4GM_Right },
6522 { .Identifier: "MSG_HCenter", .ValType: C4V_Int, .Data: C4GM_HCenter },
6523 { .Identifier: "MSG_VCenter", .ValType: C4V_Int, .Data: C4GM_VCenter },
6524 { .Identifier: "MSG_DropSpeech", .ValType: C4V_Int, .Data: C4GM_DropSpeech },
6525 { .Identifier: "MSG_WidthRel", .ValType: C4V_Int, .Data: C4GM_WidthRel },
6526 { .Identifier: "MSG_XRel", .ValType: C4V_Int, .Data: C4GM_XRel },
6527 { .Identifier: "MSG_YRel", .ValType: C4V_Int, .Data: C4GM_YRel },
6528 { .Identifier: "MSG_ALeft", .ValType: C4V_Int, .Data: C4GM_ALeft },
6529 { .Identifier: "MSG_ACenter", .ValType: C4V_Int, .Data: C4GM_ACenter },
6530 { .Identifier: "MSG_ARight", .ValType: C4V_Int, .Data: C4GM_ARight },
6531
6532 { .Identifier: "C4PT_User", .ValType: C4V_Int, .Data: C4PT_User },
6533 { .Identifier: "C4PT_Script", .ValType: C4V_Int, .Data: C4PT_Script },
6534
6535 { .Identifier: "CSPF_FixedAttributes", .ValType: C4V_Int, .Data: CSPF_FixedAttributes },
6536 { .Identifier: "CSPF_NoScenarioInit", .ValType: C4V_Int, .Data: CSPF_NoScenarioInit },
6537 { .Identifier: "CSPF_NoEliminationCheck", .ValType: C4V_Int, .Data: CSPF_NoEliminationCheck },
6538 { .Identifier: "CSPF_Invisible", .ValType: C4V_Int, .Data: CSPF_Invisible },
6539
6540 { .Identifier: "RESTORE_None", .ValType: C4V_Int, .Data: C4NetworkRestartInfos::None },
6541 { .Identifier: "RESTORE_ScriptPlayers", .ValType: C4V_Int, .Data: C4NetworkRestartInfos::ScriptPlayers },
6542 { .Identifier: "RESTORE_PlayerTeams", .ValType: C4V_Int, .Data: C4NetworkRestartInfos::PlayerTeams },
6543
6544 { .Identifier: "C4PVM_Cursor", .ValType: C4V_Int, .Data: C4PVM_Cursor },
6545 { .Identifier: "C4PVM_Target", .ValType: C4V_Int, .Data: C4PVM_Target },
6546 { .Identifier: "C4PVM_Scrolling", .ValType: C4V_Int, .Data: C4PVM_Scrolling },
6547};
6548
6549template <> struct C4ValueConv<C4Value>
6550{
6551 inline static C4V_Type Type() { return C4V_Any; }
6552 inline static C4Value FromC4V(C4Value &v) { return v; }
6553 inline static C4Value _FromC4V(const C4Value &v) { return v; }
6554 inline static C4Value ToC4V(C4Value v) { return v; }
6555};
6556
6557template <typename T> struct C4ValueConv<std::optional<T>>
6558{
6559 inline static C4V_Type Type() { return C4ValueConv<T>::Type(); }
6560 inline static std::optional<T> FromC4V(C4Value &v)
6561 {
6562 if (v.GetType() != C4V_Any) return {C4ValueConv<T>::FromC4V(v)};
6563 return {};
6564 }
6565 inline static std::optional<T> _FromC4V(const C4Value &v)
6566 {
6567 if (v.GetType() != C4V_Any) return {C4ValueConv<T>::_FromC4V(v)};
6568 return {};
6569 }
6570 inline static C4Value ToC4V(const std::optional<T>& v)
6571 {
6572 if (v) return C4ValueConv<T>::ToC4V(*v);
6573 return C4VNull;
6574 }
6575};
6576
6577void InitFunctionMap(C4AulScriptEngine *pEngine)
6578{
6579 // add all def constants (all Int)
6580 for (const auto &def : C4ScriptConstMap)
6581 Game.ScriptEngine.RegisterGlobalConstant(szName: def.Identifier, rValue: C4Value(def.Data, def.ValType));
6582
6583 AddFunc(owner: pEngine, name: "goto", func&: Fn_goto);
6584 AddFunc(owner: pEngine, name: "this", func&: Fn_this);
6585 AddFunc(owner: pEngine, name: "Equal", func&: FnEqual);
6586 AddFunc(owner: pEngine, name: "Var", func&: FnVar);
6587 AddFunc(owner: pEngine, name: "AssignVar", func&: FnSetVar, pub: false);
6588 AddFunc(owner: pEngine, name: "SetVar", func&: FnSetVar);
6589 AddFunc(owner: pEngine, name: "IncVar", func&: FnIncVar);
6590 AddFunc(owner: pEngine, name: "DecVar", func&: FnDecVar);
6591 AddFunc(owner: pEngine, name: "SetGlobal", func&: FnSetGlobal);
6592 AddFunc(owner: pEngine, name: "Global", func&: FnGlobal);
6593 AddFunc(owner: pEngine, name: "SetLocal", func&: FnSetLocal);
6594 AddFunc(owner: pEngine, name: "Local", func&: FnLocal);
6595 AddFunc(owner: pEngine, name: "Explode", func&: FnExplode);
6596 AddFunc(owner: pEngine, name: "Incinerate", func&: FnIncinerate);
6597 AddFunc(owner: pEngine, name: "IncinerateLandscape", func&: FnIncinerateLandscape);
6598 AddFunc(owner: pEngine, name: "Extinguish", func&: FnExtinguish);
6599 AddFunc(owner: pEngine, name: "GrabContents", func&: FnGrabContents);
6600 AddFunc(owner: pEngine, name: "Punch", func&: FnPunch);
6601 AddFunc(owner: pEngine, name: "Kill", func&: FnKill);
6602 AddFunc(owner: pEngine, name: "Fling", func&: FnFling);
6603 AddFunc(owner: pEngine, name: "Jump", func&: FnJump);
6604 AddFunc(owner: pEngine, name: "ChangeDef", func&: FnChangeDef);
6605 AddFunc(owner: pEngine, name: "Exit", func&: FnExit);
6606 AddFunc(owner: pEngine, name: "Enter", func&: FnEnter);
6607 AddFunc(owner: pEngine, name: "Collect", func&: FnCollect);
6608 AddFunc(owner: pEngine, name: "Split2Components", func&: FnSplit2Components);
6609 AddFunc(owner: pEngine, name: "PlayerObjectCommand", func&: FnPlayerObjectCommand);
6610 AddFunc(owner: pEngine, name: "SetCommand", func&: FnSetCommand);
6611 AddFunc(owner: pEngine, name: "AddCommand", func&: FnAddCommand);
6612 AddFunc(owner: pEngine, name: "AppendCommand", func&: FnAppendCommand);
6613 AddFunc(owner: pEngine, name: "GetCommand", func&: FnGetCommand);
6614 AddFunc(owner: pEngine, name: "DeathAnnounce", func&: FnDeathAnnounce);
6615 AddFunc(owner: pEngine, name: "FindObject", func&: FnFindObject);
6616 AddFunc(owner: pEngine, name: "ObjectCount", func&: FnObjectCount);
6617 AddFunc(owner: pEngine, name: "ObjectCall", func&: FnObjectCall);
6618 AddFunc(owner: pEngine, name: "ProtectedCall", func&: FnProtectedCall);
6619 AddFunc(owner: pEngine, name: "PrivateCall", func&: FnPrivateCall);
6620 AddFunc(owner: pEngine, name: "GameCall", func&: FnGameCall);
6621 AddFunc(owner: pEngine, name: "GameCallEx", func&: FnGameCallEx);
6622 AddFunc(owner: pEngine, name: "DefinitionCall", func&: FnDefinitionCall);
6623 AddFunc(owner: pEngine, name: "Call", func&: FnCall, pub: false);
6624 AddFunc(owner: pEngine, name: "GetPlrKnowledge", func&: FnGetPlrKnowledge);
6625 AddFunc(owner: pEngine, name: "GetPlrMagic", func&: FnGetPlrMagic);
6626 AddFunc(owner: pEngine, name: "GetComponent", func&: FnGetComponent);
6627 AddFunc(owner: pEngine, name: "PlayerMessage", func&: FnPlayerMessage);
6628 AddFunc(owner: pEngine, name: "Message", func&: FnMessage);
6629 AddFunc(owner: pEngine, name: "AddMessage", func&: FnAddMessage);
6630 AddFunc(owner: pEngine, name: "PlrMessage", func&: FnPlrMessage);
6631 AddFunc(owner: pEngine, name: "Log", func&: FnLog);
6632 AddFunc(owner: pEngine, name: "DebugLog", func&: FnDebugLog);
6633 AddFunc(owner: pEngine, name: "Format", func&: FnFormat);
6634 AddFunc(owner: pEngine, name: "EditCursor", func&: FnEditCursor);
6635 AddFunc(owner: pEngine, name: "AddMenuItem", func&: FnAddMenuItem);
6636 AddFunc(owner: pEngine, name: "SetSolidMask", func&: FnSetSolidMask);
6637 AddFunc(owner: pEngine, name: "SetGravity", func&: FnSetGravity);
6638 AddFunc(owner: pEngine, name: "GetGravity", func&: FnGetGravity);
6639 AddFunc(owner: pEngine, name: "GetHomebaseMaterial", func&: FnGetHomebaseMaterial);
6640 AddFunc(owner: pEngine, name: "GetHomebaseProduction", func&: FnGetHomebaseProduction);
6641 AddFunc(owner: pEngine, name: "IsRef", func&: FnIsRef);
6642 AddFunc(owner: pEngine, name: "GetType", func&: FnGetType);
6643 AddFunc(owner: pEngine, name: "CreateArray", func&: FnCreateArray);
6644 AddFunc(owner: pEngine, name: "GetLength", func&: FnGetLength);
6645 AddFunc(owner: pEngine, name: "GetIndexOf", func&: FnGetIndexOf);
6646 AddFunc(owner: pEngine, name: "GetDefCoreVal", func&: FnGetDefCoreVal);
6647 AddFunc(owner: pEngine, name: "GetActMapVal", func&: FnGetActMapVal);
6648 AddFunc(owner: pEngine, name: "GetObjectVal", func&: FnGetObjectVal);
6649 AddFunc(owner: pEngine, name: "GetObjectInfoCoreVal", func&: FnGetObjectInfoCoreVal);
6650 AddFunc(owner: pEngine, name: "GetScenarioVal", func&: FnGetScenarioVal);
6651 AddFunc(owner: pEngine, name: "GetPlayerVal", func&: FnGetPlayerVal);
6652 AddFunc(owner: pEngine, name: "GetPlayerInfoCoreVal", func&: FnGetPlayerInfoCoreVal);
6653 AddFunc(owner: pEngine, name: "GetMaterialVal", func&: FnGetMaterialVal);
6654 AddFunc(owner: pEngine, name: "SetPlrExtraData", func&: FnSetPlrExtraData);
6655 AddFunc(owner: pEngine, name: "GetPlrExtraData", func&: FnGetPlrExtraData);
6656 AddFunc(owner: pEngine, name: "SetCrewExtraData", func&: FnSetCrewExtraData);
6657 AddFunc(owner: pEngine, name: "GetCrewExtraData", func&: FnGetCrewExtraData);
6658 AddFunc(owner: pEngine, name: "GetPortrait", func&: FnGetPortrait);
6659 AddFunc(owner: pEngine, name: "AddEffect", func&: FnAddEffect);
6660 AddFunc(owner: pEngine, name: "GetEffect", func&: FnGetEffect);
6661 AddFunc(owner: pEngine, name: "CheckEffect", func&: FnCheckEffect);
6662 AddFunc(owner: pEngine, name: "EffectCall", func&: FnEffectCall);
6663 AddFunc(owner: pEngine, name: "eval", func&: FnEval);
6664 AddFunc(owner: pEngine, name: "VarN", func&: FnVarN);
6665 AddFunc(owner: pEngine, name: "LocalN", func&: FnLocalN);
6666 AddFunc(owner: pEngine, name: "GlobalN", func&: FnGlobalN);
6667 AddFunc(owner: pEngine, name: "Set", func&: FnSet);
6668 AddFunc(owner: pEngine, name: "Inc", func&: FnInc);
6669 AddFunc(owner: pEngine, name: "Dec", func&: FnDec);
6670 AddFunc(owner: pEngine, name: "SetLength", func&: FnSetLength);
6671 AddFunc(owner: pEngine, name: "SimFlight", func&: FnSimFlight);
6672 AddFunc(owner: pEngine, name: "EffectVar", func&: FnEffectVar);
6673 AddFunc(owner: pEngine, name: "Or", func&: FnOr, pub: false);
6674 AddFunc(owner: pEngine, name: "Not", func&: FnNot, pub: false);
6675 AddFunc(owner: pEngine, name: "And", func&: FnAnd, pub: false);
6676 AddFunc(owner: pEngine, name: "BitAnd", func&: FnBitAnd, pub: false);
6677 AddFunc(owner: pEngine, name: "Sum", func&: FnSum, pub: false);
6678 AddFunc(owner: pEngine, name: "Sub", func&: FnSub, pub: false);
6679 AddFunc(owner: pEngine, name: "Abs", func&: FnAbs);
6680 AddFunc(owner: pEngine, name: "Min", func&: FnMin);
6681 AddFunc(owner: pEngine, name: "Max", func&: FnMax);
6682 AddFunc(owner: pEngine, name: "Mul", func&: FnMul, pub: false);
6683 AddFunc(owner: pEngine, name: "Div", func&: FnDiv, pub: false);
6684 AddFunc(owner: pEngine, name: "Mod", func&: FnMod, pub: false);
6685 AddFunc(owner: pEngine, name: "Pow", func&: FnPow, pub: false);
6686 AddFunc(owner: pEngine, name: "Sin", func&: FnSin);
6687 AddFunc(owner: pEngine, name: "Cos", func&: FnCos);
6688 AddFunc(owner: pEngine, name: "Sqrt", func&: FnSqrt);
6689 AddFunc(owner: pEngine, name: "ArcSin", func&: FnArcSin);
6690 AddFunc(owner: pEngine, name: "ArcCos", func&: FnArcCos);
6691 AddFunc(owner: pEngine, name: "LessThan", func&: FnLessThan, pub: false);
6692 AddFunc(owner: pEngine, name: "GreaterThan", func&: FnGreaterThan, pub: false);
6693 AddFunc(owner: pEngine, name: "BoundBy", func&: FnBoundBy);
6694 AddFunc(owner: pEngine, name: "Inside", func&: FnInside);
6695 AddFunc(owner: pEngine, name: "SEqual", func&: FnSEqual, pub: false);
6696 AddFunc(owner: pEngine, name: "Random", func&: FnRandom);
6697 AddFunc(owner: pEngine, name: "AsyncRandom", func&: FnAsyncRandom);
6698 AddFunc(owner: pEngine, name: "DoCon", func&: FnDoCon);
6699 AddFunc(owner: pEngine, name: "GetCon", func&: FnGetCon);
6700 AddFunc(owner: pEngine, name: "DoDamage", func&: FnDoDamage);
6701 AddFunc(owner: pEngine, name: "DoEnergy", func&: FnDoEnergy);
6702 AddFunc(owner: pEngine, name: "DoBreath", func&: FnDoBreath);
6703 AddFunc(owner: pEngine, name: "DoMagicEnergy", func&: FnDoMagicEnergy);
6704 AddFunc(owner: pEngine, name: "GetMagicEnergy", func&: FnGetMagicEnergy);
6705 AddFunc(owner: pEngine, name: "EnergyCheck", func&: FnEnergyCheck);
6706 AddFunc(owner: pEngine, name: "CheckEnergyNeedChain", func&: FnCheckEnergyNeedChain);
6707 AddFunc(owner: pEngine, name: "GetEnergy", func&: FnGetEnergy);
6708 AddFunc(owner: pEngine, name: "OnFire", func&: FnOnFire);
6709 AddFunc(owner: pEngine, name: "Smoke", func&: FnSmoke);
6710 AddFunc(owner: pEngine, name: "Stuck", func&: FnStuck);
6711 AddFunc(owner: pEngine, name: "InLiquid", func&: FnInLiquid);
6712 AddFunc(owner: pEngine, name: "Bubble", func&: FnBubble);
6713 AddFunc(owner: pEngine, name: "SetAction", func&: FnSetAction);
6714 AddFunc(owner: pEngine, name: "SetActionData", func&: FnSetActionData);
6715 AddFunc(owner: pEngine, name: "SetBridgeActionData", func&: FnSetBridgeActionData);
6716 AddFunc(owner: pEngine, name: "GetAction", func&: FnGetAction);
6717 AddFunc(owner: pEngine, name: "GetActTime", func&: FnGetActTime);
6718 AddFunc(owner: pEngine, name: "GetOwner", func&: FnGetOwner);
6719 AddFunc(owner: pEngine, name: "GetMass", func&: FnGetMass);
6720 AddFunc(owner: pEngine, name: "GetBreath", func&: FnGetBreath);
6721 AddFunc(owner: pEngine, name: "GetX", func&: FnGetX);
6722 AddFunc(owner: pEngine, name: "GetY", func&: FnGetY);
6723 AddFunc(owner: pEngine, name: "GetBase", func&: FnGetBase);
6724 AddFunc(owner: pEngine, name: "GetMenu", func&: FnGetMenu);
6725 AddFunc(owner: pEngine, name: "GetVertexNum", func&: FnGetVertexNum);
6726 AddFunc(owner: pEngine, name: "GetVertex", func&: FnGetVertex);
6727 AddFunc(owner: pEngine, name: "SetVertex", func&: FnSetVertex);
6728 AddFunc(owner: pEngine, name: "AddVertex", func&: FnAddVertex);
6729 AddFunc(owner: pEngine, name: "RemoveVertex", func&: FnRemoveVertex);
6730 AddFunc(owner: pEngine, name: "SetContactDensity", func&: FnSetContactDensity, pub: false);
6731 AddFunc(owner: pEngine, name: "AnyContainer", func&: FnAnyContainer);
6732 AddFunc(owner: pEngine, name: "NoContainer", func&: FnNoContainer);
6733 AddFunc(owner: pEngine, name: "GetController", func&: FnGetController);
6734 AddFunc(owner: pEngine, name: "SetController", func&: FnSetController);
6735 AddFunc(owner: pEngine, name: "GetKiller", func&: FnGetKiller);
6736 AddFunc(owner: pEngine, name: "SetKiller", func&: FnSetKiller);
6737 AddFunc(owner: pEngine, name: "GetPhase", func&: FnGetPhase);
6738 AddFunc(owner: pEngine, name: "SetPhase", func&: FnSetPhase);
6739 AddFunc(owner: pEngine, name: "GetCategory", func&: FnGetCategory);
6740 AddFunc(owner: pEngine, name: "GetOCF", func&: FnGetOCF);
6741 AddFunc(owner: pEngine, name: "SetAlive", func&: FnSetAlive);
6742 AddFunc(owner: pEngine, name: "GetAlive", func&: FnGetAlive);
6743 AddFunc(owner: pEngine, name: "GetDamage", func&: FnGetDamage);
6744 AddFunc(owner: pEngine, name: "CrewMember", func&: FnCrewMember);
6745 AddFunc(owner: pEngine, name: "ObjectSetAction", func&: FnObjectSetAction, pub: false);
6746 AddFunc(owner: pEngine, name: "ComponentAll", func&: FnComponentAll);
6747 AddFunc(owner: pEngine, name: "SetComDir", func&: FnSetComDir);
6748 AddFunc(owner: pEngine, name: "GetComDir", func&: FnGetComDir);
6749 AddFunc(owner: pEngine, name: "SetDir", func&: FnSetDir);
6750 AddFunc(owner: pEngine, name: "GetDir", func&: FnGetDir);
6751 AddFunc(owner: pEngine, name: "SetEntrance", func&: FnSetEntrance);
6752 AddFunc(owner: pEngine, name: "GetEntrance", func&: FnGetEntrance);
6753 AddFunc(owner: pEngine, name: "SetCategory", func&: FnSetCategory);
6754 AddFunc(owner: pEngine, name: "FinishCommand", func&: FnFinishCommand);
6755 AddFunc(owner: pEngine, name: "GetDefinition", func&: FnGetDefinition);
6756 AddFunc(owner: pEngine, name: "ActIdle", func&: FnActIdle);
6757 AddFunc(owner: pEngine, name: "SetRDir", func&: FnSetRDir);
6758 AddFunc(owner: pEngine, name: "GetRDir", func&: FnGetRDir);
6759 AddFunc(owner: pEngine, name: "GetXDir", func&: FnGetXDir);
6760 AddFunc(owner: pEngine, name: "GetYDir", func&: FnGetYDir);
6761 AddFunc(owner: pEngine, name: "GetR", func&: FnGetR);
6762 AddFunc(owner: pEngine, name: "GetName", func&: FnGetName);
6763 AddFunc(owner: pEngine, name: "SetName", func&: FnSetName);
6764 AddFunc(owner: pEngine, name: "GetDesc", func&: FnGetDesc);
6765 AddFunc(owner: pEngine, name: "GetPlayerName", func&: FnGetPlayerName);
6766 AddFunc(owner: pEngine, name: "GetTaggedPlayerName", func&: FnGetTaggedPlayerName);
6767 AddFunc(owner: pEngine, name: "GetPlayerType", func&: FnGetPlayerType);
6768 AddFunc(owner: pEngine, name: "SetXDir", func&: FnSetXDir);
6769 AddFunc(owner: pEngine, name: "SetYDir", func&: FnSetYDir);
6770 AddFunc(owner: pEngine, name: "SetR", func&: FnSetR);
6771 AddFunc(owner: pEngine, name: "SetOwner", func&: FnSetOwner);
6772 AddFunc(owner: pEngine, name: "CreateObject", func&: FnCreateObject);
6773 AddFunc(owner: pEngine, name: "MakeCrewMember", func&: FnMakeCrewMember);
6774 AddFunc(owner: pEngine, name: "GrabObjectInfo", func&: FnGrabObjectInfo);
6775 AddFunc(owner: pEngine, name: "CreateContents", func&: FnCreateContents);
6776 AddFunc(owner: pEngine, name: "ShiftContents", func&: FnShiftContents);
6777 AddFunc(owner: pEngine, name: "ComposeContents", func&: FnComposeContents);
6778 AddFunc(owner: pEngine, name: "CreateConstruction", func&: FnCreateConstruction);
6779 AddFunc(owner: pEngine, name: "GetID", func&: FnGetID);
6780 AddFunc(owner: pEngine, name: "Contents", func&: FnContents);
6781 AddFunc(owner: pEngine, name: "ScrollContents", func&: FnScrollContents);
6782 AddFunc(owner: pEngine, name: "Contained", func&: FnContained);
6783 AddFunc(owner: pEngine, name: "ContentsCount", func&: FnContentsCount);
6784 AddFunc(owner: pEngine, name: "FindContents", func&: FnFindContents);
6785 AddFunc(owner: pEngine, name: "FindConstructionSite", func&: FnFindConstructionSite);
6786 AddFunc(owner: pEngine, name: "FindOtherContents", func&: FnFindOtherContents);
6787 AddFunc(owner: pEngine, name: "FindBase", func&: FnFindBase);
6788 AddFunc(owner: pEngine, name: "Sound", func&: FnSound);
6789 AddFunc(owner: pEngine, name: "Music", func&: FnMusic);
6790 AddFunc(owner: pEngine, name: "MusicLevel", func&: FnMusicLevel);
6791 AddFunc(owner: pEngine, name: "SetPlayList", func&: FnSetPlayList);
6792 AddFunc(owner: pEngine, name: "SoundLevel", func&: FnSoundLevel, pub: false);
6793 AddFunc(owner: pEngine, name: "FindObjectOwner", func&: FnFindObjectOwner);
6794 AddFunc(owner: pEngine, name: "RemoveObject", func&: FnRemoveObject);
6795 AddFunc(owner: pEngine, name: "GetActionTarget", func&: FnGetActionTarget);
6796 AddFunc(owner: pEngine, name: "SetActionTargets", func&: FnSetActionTargets);
6797 AddFunc(owner: pEngine, name: "SetPlrView", func&: FnSetPlrView);
6798 AddFunc(owner: pEngine, name: "SetPlrKnowledge", func&: FnSetPlrKnowledge);
6799 AddFunc(owner: pEngine, name: "SetPlrMagic", func&: FnSetPlrMagic);
6800 AddFunc(owner: pEngine, name: "GetPlrDownDouble", func&: FnGetPlrDownDouble);
6801 AddFunc(owner: pEngine, name: "ClearLastPlrCom", func&: FnClearLastPlrCom);
6802 AddFunc(owner: pEngine, name: "GetPlrViewMode", func&: FnGetPlrViewMode);
6803 AddFunc(owner: pEngine, name: "GetPlrView", func&: FnGetPlrView);
6804 AddFunc(owner: pEngine, name: "GetWealth", func&: FnGetWealth);
6805 AddFunc(owner: pEngine, name: "SetWealth", func&: FnSetWealth);
6806 AddFunc(owner: pEngine, name: "SetComponent", func&: FnSetComponent);
6807 AddFunc(owner: pEngine, name: "DoScore", func&: FnDoScore);
6808 AddFunc(owner: pEngine, name: "GetScore", func&: FnGetScore);
6809 AddFunc(owner: pEngine, name: "GetPlrValue", func&: FnGetPlrValue);
6810 AddFunc(owner: pEngine, name: "GetPlrValueGain", func&: FnGetPlrValueGain);
6811 AddFunc(owner: pEngine, name: "SetPlrShowControl", func&: FnSetPlrShowControl);
6812 AddFunc(owner: pEngine, name: "SetPlrShowControlPos", func&: FnSetPlrShowControlPos);
6813 AddFunc(owner: pEngine, name: "GetPlrControlName", func&: FnGetPlrControlName);
6814 AddFunc(owner: pEngine, name: "GetPlrJumpAndRunControl", func&: FnGetPlrJumpAndRunControl);
6815 AddFunc(owner: pEngine, name: "SetPlrShowCommand", func&: FnSetPlrShowCommand);
6816 AddFunc(owner: pEngine, name: "GetWind", func&: FnGetWind);
6817 AddFunc(owner: pEngine, name: "SetWind", func&: FnSetWind);
6818 AddFunc(owner: pEngine, name: "SetSkyFade", func&: FnSetSkyFade);
6819 AddFunc(owner: pEngine, name: "SetSkyColor", func&: FnSetSkyColor);
6820 AddFunc(owner: pEngine, name: "GetSkyColor", func&: FnGetSkyColor);
6821 AddFunc(owner: pEngine, name: "GetTemperature", func&: FnGetTemperature);
6822 AddFunc(owner: pEngine, name: "SetTemperature", func&: FnSetTemperature);
6823 AddFunc(owner: pEngine, name: "LaunchLightning", func&: FnLaunchLightning);
6824 AddFunc(owner: pEngine, name: "LaunchVolcano", func&: FnLaunchVolcano);
6825 AddFunc(owner: pEngine, name: "LaunchEarthquake", func&: FnLaunchEarthquake);
6826 AddFunc(owner: pEngine, name: "ShakeFree", func&: FnShakeFree);
6827 AddFunc(owner: pEngine, name: "ShakeObjects", func&: FnShakeObjects);
6828 AddFunc(owner: pEngine, name: "DigFree", func&: FnDigFree);
6829 AddFunc(owner: pEngine, name: "FreeRect", func&: FnFreeRect);
6830 AddFunc(owner: pEngine, name: "DigFreeRect", func&: FnDigFreeRect);
6831 AddFunc(owner: pEngine, name: "CastPXS", func&: FnCastPXS);
6832 AddFunc(owner: pEngine, name: "CastObjects", func&: FnCastObjects);
6833 AddFunc(owner: pEngine, name: "Hostile", func&: FnHostile);
6834 AddFunc(owner: pEngine, name: "SetHostility", func&: FnSetHostility);
6835 AddFunc(owner: pEngine, name: "PlaceVegetation", func&: FnPlaceVegetation);
6836 AddFunc(owner: pEngine, name: "PlaceAnimal", func&: FnPlaceAnimal);
6837 AddFunc(owner: pEngine, name: "GameOver", func&: FnGameOver);
6838 AddFunc(owner: pEngine, name: "C4Id", func&: FnC4Id);
6839 AddFunc(owner: pEngine, name: "ScriptGo", func&: FnScriptGo);
6840 AddFunc(owner: pEngine, name: "GetHiRank", func&: FnGetHiRank);
6841 AddFunc(owner: pEngine, name: "GetCrew", func&: FnGetCrew);
6842 AddFunc(owner: pEngine, name: "GetCrewCount", func&: FnGetCrewCount);
6843 AddFunc(owner: pEngine, name: "GetPlayerCount", func&: FnGetPlayerCount);
6844 AddFunc(owner: pEngine, name: "GetPlayerByIndex", func&: FnGetPlayerByIndex);
6845 AddFunc(owner: pEngine, name: "EliminatePlayer", func&: FnEliminatePlayer);
6846 AddFunc(owner: pEngine, name: "SurrenderPlayer", func&: FnSurrenderPlayer);
6847 AddFunc(owner: pEngine, name: "SetLeaguePerformance", func&: FnSetLeaguePerformance);
6848 AddFunc(owner: pEngine, name: "SetLeagueProgressData", func&: FnSetLeagueProgressData);
6849 AddFunc(owner: pEngine, name: "GetLeagueProgressData", func&: FnGetLeagueProgressData);
6850 AddFunc(owner: pEngine, name: "CreateScriptPlayer", func&: FnCreateScriptPlayer);
6851 AddFunc(owner: pEngine, name: "GetCursor", func&: FnGetCursor);
6852 AddFunc(owner: pEngine, name: "GetViewCursor", func&: FnGetViewCursor);
6853 AddFunc(owner: pEngine, name: "GetCaptain", func&: FnGetCaptain);
6854 AddFunc(owner: pEngine, name: "SetCursor", func&: FnSetCursor);
6855 AddFunc(owner: pEngine, name: "SetViewCursor", func&: FnSetViewCursor);
6856 AddFunc(owner: pEngine, name: "SelectCrew", func&: FnSelectCrew);
6857 AddFunc(owner: pEngine, name: "GetSelectCount", func&: FnGetSelectCount);
6858 AddFunc(owner: pEngine, name: "SetCrewStatus", func&: FnSetCrewStatus, pub: false);
6859 AddFunc(owner: pEngine, name: "SetPosition", func&: FnSetPosition);
6860 AddFunc(owner: pEngine, name: "ExtractLiquid", func&: FnExtractLiquid);
6861 AddFunc(owner: pEngine, name: "GetMaterial", func&: FnGetMaterial);
6862 AddFunc(owner: pEngine, name: "GetTexture", func&: FnGetTexture);
6863 AddFunc(owner: pEngine, name: "GetMaterialCount", func&: FnGetMaterialCount);
6864 AddFunc(owner: pEngine, name: "GBackSolid", func&: FnGBackSolid);
6865 AddFunc(owner: pEngine, name: "GBackSemiSolid", func&: FnGBackSemiSolid);
6866 AddFunc(owner: pEngine, name: "GBackLiquid", func&: FnGBackLiquid);
6867 AddFunc(owner: pEngine, name: "GBackSky", func&: FnGBackSky);
6868 AddFunc(owner: pEngine, name: "Material", func&: FnMaterial);
6869 AddFunc(owner: pEngine, name: "BlastObjects", func&: FnBlastObjects);
6870 AddFunc(owner: pEngine, name: "BlastObject", func&: FnBlastObject);
6871 AddFunc(owner: pEngine, name: "BlastFree", func&: FnBlastFree);
6872 AddFunc(owner: pEngine, name: "InsertMaterial", func&: FnInsertMaterial);
6873 AddFunc(owner: pEngine, name: "DrawVolcanoBranch", func&: FnDrawVolcanoBranch, pub: false);
6874 AddFunc(owner: pEngine, name: "FlameConsumeMaterial", func&: FnFlameConsumeMaterial, pub: false);
6875 AddFunc(owner: pEngine, name: "LandscapeWidth", func&: FnLandscapeWidth);
6876 AddFunc(owner: pEngine, name: "LandscapeHeight", func&: FnLandscapeHeight);
6877 AddFunc(owner: pEngine, name: "Resort", func&: FnResort);
6878 AddFunc(owner: pEngine, name: "CreateMenu", func&: FnCreateMenu);
6879 AddFunc(owner: pEngine, name: "SelectMenuItem", func&: FnSelectMenuItem);
6880 AddFunc(owner: pEngine, name: "SetMenuDecoration", func&: FnSetMenuDecoration);
6881 AddFunc(owner: pEngine, name: "SetMenuTextProgress", func&: FnSetMenuTextProgress);
6882 AddFunc(owner: pEngine, name: "SetSeason", func&: FnSetSeason);
6883 AddFunc(owner: pEngine, name: "GetSeason", func&: FnGetSeason);
6884 AddFunc(owner: pEngine, name: "SetClimate", func&: FnSetClimate);
6885 AddFunc(owner: pEngine, name: "GetClimate", func&: FnGetClimate);
6886 AddFunc(owner: pEngine, name: "Distance", func&: FnDistance);
6887 AddFunc(owner: pEngine, name: "ObjectDistance", func&: FnObjectDistance);
6888 AddFunc(owner: pEngine, name: "GetValue", func&: FnGetValue);
6889 AddFunc(owner: pEngine, name: "GetRank", func&: FnGetRank);
6890 AddFunc(owner: pEngine, name: "Value", func&: FnValue);
6891 AddFunc(owner: pEngine, name: "Angle", func&: FnAngle);
6892 AddFunc(owner: pEngine, name: "DoHomebaseMaterial", func&: FnDoHomebaseMaterial);
6893 AddFunc(owner: pEngine, name: "DoHomebaseProduction", func&: FnDoHomebaseProduction);
6894 AddFunc(owner: pEngine, name: "GainMissionAccess", func&: FnGainMissionAccess);
6895 AddFunc(owner: pEngine, name: "SetPhysical", func&: FnSetPhysical);
6896 AddFunc(owner: pEngine, name: "TrainPhysical", func&: FnTrainPhysical);
6897 AddFunc(owner: pEngine, name: "GetPhysical", func&: FnGetPhysical);
6898 AddFunc(owner: pEngine, name: "ResetPhysical", func&: FnResetPhysical);
6899 AddFunc(owner: pEngine, name: "SetTransferZone", func&: FnSetTransferZone);
6900 AddFunc(owner: pEngine, name: "IsNetwork", func&: FnIsNetwork);
6901 AddFunc(owner: pEngine, name: "GetLeague", func&: FnGetLeague);
6902 AddFunc(owner: pEngine, name: "TestMessageBoard", func&: FnTestMessageBoard, pub: false);
6903 AddFunc(owner: pEngine, name: "CallMessageBoard", func&: FnCallMessageBoard, pub: false);
6904 AddFunc(owner: pEngine, name: "AbortMessageBoard", func&: FnAbortMessageBoard, pub: false);
6905 AddFunc(owner: pEngine, name: "OnMessageBoardAnswer", func&: FnOnMessageBoardAnswer, pub: false);
6906 AddFunc(owner: pEngine, name: "ScriptCounter", func&: FnScriptCounter);
6907 AddFunc(owner: pEngine, name: "SetMass", func&: FnSetMass);
6908 AddFunc(owner: pEngine, name: "GetColor", func&: FnGetColor);
6909 AddFunc(owner: pEngine, name: "SetColor", func&: FnSetColor);
6910 AddFunc(owner: pEngine, name: "SetFoW", func&: FnSetFoW);
6911 AddFunc(owner: pEngine, name: "SetPlrViewRange", func&: FnSetPlrViewRange);
6912 AddFunc(owner: pEngine, name: "GetMaxPlayer", func&: FnGetMaxPlayer);
6913 AddFunc(owner: pEngine, name: "SetMaxPlayer", func&: FnSetMaxPlayer);
6914 AddFunc(owner: pEngine, name: "SetPicture", func&: FnSetPicture);
6915 AddFunc(owner: pEngine, name: "Buy", func&: FnBuy);
6916 AddFunc(owner: pEngine, name: "Sell", func&: FnSell);
6917 AddFunc(owner: pEngine, name: "GetProcedure", func&: FnGetProcedure);
6918 AddFunc(owner: pEngine, name: "GetChar", func&: FnGetChar);
6919 AddFunc(owner: pEngine, name: "ActivateGameGoalMenu", func&: FnActivateGameGoalMenu);
6920 AddFunc(owner: pEngine, name: "SetGraphics", func&: FnSetGraphics);
6921 AddFunc(owner: pEngine, name: "Object", func&: FnObject);
6922 AddFunc(owner: pEngine, name: "ObjectNumber", func&: FnObjectNumber);
6923 AddFunc(owner: pEngine, name: "ShowInfo", func&: FnShowInfo);
6924 AddFunc(owner: pEngine, name: "GetTime", func&: FnGetTime);
6925 AddFunc(owner: pEngine, name: "GetSystemTime", func&: FnGetSystemTime, pub: false);
6926 AddFunc(owner: pEngine, name: "SetVisibility", func&: FnSetVisibility);
6927 AddFunc(owner: pEngine, name: "GetVisibility", func&: FnGetVisibility);
6928 AddFunc(owner: pEngine, name: "SetClrModulation", func&: FnSetClrModulation);
6929 AddFunc(owner: pEngine, name: "GetClrModulation", func&: FnGetClrModulation);
6930 AddFunc(owner: pEngine, name: "GetMissionAccess", func&: FnGetMissionAccess);
6931 AddFunc(owner: pEngine, name: "CloseMenu", func&: FnCloseMenu);
6932 AddFunc(owner: pEngine, name: "GetMenuSelection", func&: FnGetMenuSelection);
6933 AddFunc(owner: pEngine, name: "ResortObjects", func&: FnResortObjects);
6934 AddFunc(owner: pEngine, name: "ResortObject", func&: FnResortObject);
6935 AddFunc(owner: pEngine, name: "GetDefBottom", func&: FnGetDefBottom);
6936 AddFunc(owner: pEngine, name: "SetMaterialColor", func&: FnSetMaterialColor);
6937 AddFunc(owner: pEngine, name: "GetMaterialColor", func&: FnGetMaterialColor);
6938 AddFunc(owner: pEngine, name: "MaterialName", func&: FnMaterialName);
6939 AddFunc(owner: pEngine, name: "SetMenuSize", func&: FnSetMenuSize);
6940 AddFunc(owner: pEngine, name: "GetNeededMatStr", func&: FnGetNeededMatStr);
6941 AddFunc(owner: pEngine, name: "GetCrewEnabled", func&: FnGetCrewEnabled);
6942 AddFunc(owner: pEngine, name: "SetCrewEnabled", func&: FnSetCrewEnabled);
6943 AddFunc(owner: pEngine, name: "UnselectCrew", func&: FnUnselectCrew);
6944 AddFunc(owner: pEngine, name: "DrawMap", func&: FnDrawMap);
6945 AddFunc(owner: pEngine, name: "DrawDefMap", func&: FnDrawDefMap);
6946 AddFunc(owner: pEngine, name: "CreateParticle", func&: FnCreateParticle);
6947 AddFunc(owner: pEngine, name: "CastParticles", func&: FnCastParticles);
6948 AddFunc(owner: pEngine, name: "CastBackParticles", func&: FnCastBackParticles);
6949 AddFunc(owner: pEngine, name: "PushParticles", func&: FnPushParticles);
6950 AddFunc(owner: pEngine, name: "ClearParticles", func&: FnClearParticles);
6951 AddFunc(owner: pEngine, name: "IsNewgfx", func&: FnIsNewgfx, pub: false);
6952 AddFunc(owner: pEngine, name: "SetSkyAdjust", func&: FnSetSkyAdjust);
6953 AddFunc(owner: pEngine, name: "SetMatAdjust", func&: FnSetMatAdjust);
6954 AddFunc(owner: pEngine, name: "GetSkyAdjust", func&: FnGetSkyAdjust);
6955 AddFunc(owner: pEngine, name: "GetMatAdjust", func&: FnGetMatAdjust);
6956 AddFunc(owner: pEngine, name: "SetSkyParallax", func&: FnSetSkyParallax);
6957 AddFunc(owner: pEngine, name: "DoCrewExp", func&: FnDoCrewExp);
6958 AddFunc(owner: pEngine, name: "ReloadDef", func&: FnReloadDef);
6959 AddFunc(owner: pEngine, name: "ReloadParticle", func&: FnReloadParticle);
6960 AddFunc(owner: pEngine, name: "SetGamma", func&: FnSetGamma);
6961 AddFunc(owner: pEngine, name: "ResetGamma", func&: FnResetGamma);
6962 AddFunc(owner: pEngine, name: "FrameCounter", func&: FnFrameCounter);
6963 AddFunc(owner: pEngine, name: "SetLandscapePixel", func&: FnSetLandscapePixel);
6964 AddFunc(owner: pEngine, name: "SetObjectOrder", func&: FnSetObjectOrder);
6965 AddFunc(owner: pEngine, name: "SetColorDw", func&: FnSetColorDw);
6966 AddFunc(owner: pEngine, name: "GetColorDw", func&: FnGetColorDw);
6967 AddFunc(owner: pEngine, name: "GetPlrColorDw", func&: FnGetPlrColorDw);
6968 AddFunc(owner: pEngine, name: "DrawMaterialQuad", func&: FnDrawMaterialQuad);
6969 AddFunc(owner: pEngine, name: "FightWith", func&: FnFightWith);
6970 AddFunc(owner: pEngine, name: "SetFilmView", func&: FnSetFilmView);
6971 AddFunc(owner: pEngine, name: "ClearMenuItems", func&: FnClearMenuItems);
6972 AddFunc(owner: pEngine, name: "GetObjectLayer", func&: FnGetObjectLayer, pub: false);
6973 AddFunc(owner: pEngine, name: "SetObjectLayer", func&: FnSetObjectLayer, pub: false);
6974 AddFunc(owner: pEngine, name: "SetShape", func&: FnSetShape);
6975 AddFunc(owner: pEngine, name: "AddMsgBoardCmd", func&: FnAddMsgBoardCmd);
6976 AddFunc(owner: pEngine, name: "SetGameSpeed", func&: FnSetGameSpeed, pub: false);
6977 AddFunc(owner: pEngine, name: "DrawMatChunks", func&: FnDrawMatChunks, pub: false);
6978 AddFunc(owner: pEngine, name: "GetPath", func&: FnGetPath);
6979 AddFunc(owner: pEngine, name: "SetTextureIndex", func&: FnSetTextureIndex, pub: false);
6980 AddFunc(owner: pEngine, name: "RemoveUnusedTexMapEntries", func&: FnRemoveUnusedTexMapEntries, pub: false);
6981 AddFunc(owner: pEngine, name: "SetObjDrawTransform", func&: FnSetObjDrawTransform);
6982 AddFunc(owner: pEngine, name: "SetObjDrawTransform2", func&: FnSetObjDrawTransform2, pub: false);
6983 AddFunc(owner: pEngine, name: "SetPortrait", func&: FnSetPortrait);
6984 AddFunc(owner: pEngine, name: "LoadScenarioSection", func&: FnLoadScenarioSection, pub: false);
6985 AddFunc(owner: pEngine, name: "SetObjectStatus", func&: FnSetObjectStatus, pub: false);
6986 AddFunc(owner: pEngine, name: "GetObjectStatus", func&: FnGetObjectStatus, pub: false);
6987 AddFunc(owner: pEngine, name: "AdjustWalkRotation", func&: FnAdjustWalkRotation, pub: false);
6988 AddFunc(owner: pEngine, name: "FxFireStart", func&: FnFxFireStart, pub: false);
6989 AddFunc(owner: pEngine, name: "FxFireTimer", func&: FnFxFireTimer, pub: false);
6990 AddFunc(owner: pEngine, name: "FxFireStop", func&: FnFxFireStop, pub: false);
6991 AddFunc(owner: pEngine, name: "FxFireInfo", func&: FnFxFireInfo, pub: false);
6992 AddFunc(owner: pEngine, name: "RemoveEffect", func&: FnRemoveEffect);
6993 AddFunc(owner: pEngine, name: "ChangeEffect", func&: FnChangeEffect);
6994 AddFunc(owner: pEngine, name: "ModulateColor", func&: FnModulateColor);
6995 AddFunc(owner: pEngine, name: "WildcardMatch", func&: FnWildcardMatch);
6996 AddFunc(owner: pEngine, name: "GetContact", func&: FnGetContact);
6997 AddFunc(owner: pEngine, name: "SetObjectBlitMode", func&: FnSetObjectBlitMode);
6998 AddFunc(owner: pEngine, name: "GetObjectBlitMode", func&: FnGetObjectBlitMode);
6999 AddFunc(owner: pEngine, name: "SetViewOffset", func&: FnSetViewOffset);
7000 AddFunc(owner: pEngine, name: "SetPreSend", func&: FnSetPreSend, pub: false);
7001 AddFunc(owner: pEngine, name: "GetPlayerID", func&: FnGetPlayerID, pub: false);
7002 AddFunc(owner: pEngine, name: "GetPlayerTeam", func&: FnGetPlayerTeam);
7003 AddFunc(owner: pEngine, name: "SetPlayerTeam", func&: FnSetPlayerTeam);
7004 AddFunc(owner: pEngine, name: "GetTeamConfig", func&: FnGetTeamConfig);
7005 AddFunc(owner: pEngine, name: "GetTeamName", func&: FnGetTeamName);
7006 AddFunc(owner: pEngine, name: "GetTeamColor", func&: FnGetTeamColor);
7007 AddFunc(owner: pEngine, name: "GetTeamByIndex", func&: FnGetTeamByIndex);
7008 AddFunc(owner: pEngine, name: "GetTeamCount", func&: FnGetTeamCount);
7009 AddFunc(owner: pEngine, name: "InitScenarioPlayer", func&: FnInitScenarioPlayer, pub: false);
7010 AddFunc(owner: pEngine, PSF_OnOwnerRemoved, func&: FnOnOwnerRemoved, pub: false);
7011 AddFunc(owner: pEngine, name: "SetScoreboardData", func&: FnSetScoreboardData, pub: false);
7012 AddFunc(owner: pEngine, name: "GetScoreboardString", func&: FnGetScoreboardString, pub: false);
7013 AddFunc(owner: pEngine, name: "GetScoreboardData", func&: FnGetScoreboardData, pub: false);
7014 AddFunc(owner: pEngine, name: "DoScoreboardShow", func&: FnDoScoreboardShow, pub: false);
7015 AddFunc(owner: pEngine, name: "SortScoreboard", func&: FnSortScoreboard, pub: false);
7016 AddFunc(owner: pEngine, name: "AddEvaluationData", func&: FnAddEvaluationData, pub: false);
7017 AddFunc(owner: pEngine, name: "GetLeagueScore", func&: FnGetLeagueScore, pub: false);
7018 AddFunc(owner: pEngine, name: "HideSettlementScoreInEvaluation", func&: FnHideSettlementScoreInEvaluation, pub: false);
7019 AddFunc(owner: pEngine, name: "GetUnusedOverlayID", func&: FnGetUnusedOverlayID, pub: false);
7020 AddFunc(owner: pEngine, name: "FatalError", func&: FnFatalError, pub: false);
7021 AddFunc(owner: pEngine, name: "ExtractMaterialAmount", func&: FnExtractMaterialAmount);
7022 AddFunc(owner: pEngine, name: "GetEffectCount", func&: FnGetEffectCount);
7023 AddFunc(owner: pEngine, name: "StartCallTrace", func&: FnStartCallTrace);
7024 AddFunc(owner: pEngine, name: "StartScriptProfiler", func&: FnStartScriptProfiler);
7025 AddFunc(owner: pEngine, name: "StopScriptProfiler", func&: FnStopScriptProfiler);
7026 AddFunc(owner: pEngine, name: "CustomMessage", func&: FnCustomMessage);
7027 AddFunc(owner: pEngine, name: "PauseGame", func&: FnPauseGame);
7028 AddFunc(owner: pEngine, name: "ExecuteCommand", func&: FnExecuteCommand);
7029 AddFunc(owner: pEngine, name: "LocateFunc", func&: FnLocateFunc);
7030 AddFunc(owner: pEngine, name: "PathFree", func&: FnPathFree);
7031 AddFunc(owner: pEngine, name: "PathFree2", func&: FnPathFree2);
7032 AddFunc(owner: pEngine, name: "SetNextMission", func&: FnSetNextMission);
7033 AddFunc(owner: pEngine, name: "GetKeys", func&: FnGetKeys);
7034 AddFunc(owner: pEngine, name: "GetValues", func&: FnGetValues);
7035 AddFunc(owner: pEngine, name: "SetRestoreInfos", func&: FnSetRestoreInfos);
7036 new C4AulDefCastFunc<C4V_C4ID, C4V_Int>{pEngine, "ScoreboardCol"};
7037 new C4AulDefCastFunc<C4V_Any, C4V_Int>{pEngine, "CastInt"};
7038 new C4AulDefCastFunc<C4V_Any, C4V_Bool>{pEngine, "CastBool"};
7039 new C4AulDefCastFunc<C4V_Any, C4V_C4ID>{pEngine, "CastC4ID"};
7040 new C4AulDefCastFunc<C4V_Any, C4V_Any>{pEngine, "CastAny"};
7041
7042 new C4AulEngineFuncParArray<10, C4V_C4Object>{pEngine, "FindObject2", FnFindObject2, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array};
7043 new C4AulEngineFuncParArray<10, C4V_Array> {pEngine, "FindObjects", FnFindObjects, C4V_Array, C4V_Any, C4V_Any, C4V_Any, C4V_Any, C4V_Any, C4V_Any, C4V_Any, C4V_Any, C4V_Any};
7044 new C4AulEngineFuncParArray<10, C4V_Int> {pEngine, "ObjectCount2", FnObjectCount2, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array, C4V_Array};
7045}
7046