1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/config-manager.h"
24 #include "common/random.h"
25 #include "common/savefile.h"
26 #include "common/system.h"
27
28 #include "sludge/builtin.h"
29 #include "sludge/cursors.h"
30 #include "sludge/event.h"
31 #include "sludge/floor.h"
32 #include "sludge/fonttext.h"
33 #include "sludge/function.h"
34 #include "sludge/graphics.h"
35 #include "sludge/moreio.h"
36 #include "sludge/movie.h"
37 #include "sludge/newfatal.h"
38 #include "sludge/objtypes.h"
39 #include "sludge/people.h"
40 #include "sludge/region.h"
41 #include "sludge/savedata.h"
42 #include "sludge/sludger.h"
43 #include "sludge/sound.h"
44 #include "sludge/speech.h"
45 #include "sludge/statusba.h"
46 #include "sludge/sludge.h"
47 #include "sludge/timing.h"
48
49 namespace Sludge {
50
51 Variable *launchResult = NULL;
52
53 extern bool allowAnyFilename;
54 extern VariableStack *noStack;
55 extern int numBIFNames, numUserFunc;
56
57 extern Common::String *allUserFunc;
58 extern Common::String *allBIFNames;
59
failSecurityCheck(const Common::String & fn)60 bool failSecurityCheck(const Common::String &fn) {
61 if (fn.empty())
62 return true;
63
64 for (uint i = 0; i < fn.size(); ++i) {
65 switch (fn[i]) {
66 case ':':
67 case '\\':
68 case '/':
69 case '*':
70 case '?':
71 case '"':
72 case '<':
73 case '>':
74 case '|':
75 fatal("Filenames may not contain the following characters: \n\n\\ / : \" < > | ? *\n\nConsequently, the following filename is not allowed:", fn);
76 return true;
77 default:
78 break;
79 }
80 }
81 return false;
82 }
83
84 extern LoadedFunction *saverFunc;
85
86 typedef BuiltReturn (*builtInSludgeFunc)(int numParams, LoadedFunction *fun);
87 struct builtInFunctionData {
88 const char *name;
89 builtInSludgeFunc func;
90 int paramNum;
91 };
92
93 #define builtIn(a) static BuiltReturn builtIn_ ## a (int numParams, LoadedFunction *fun)
94 #define UNUSEDALL (void) (0 && sizeof(numParams) && sizeof (fun));
95
96
sayCore(int numParams,LoadedFunction * fun,bool sayIt)97 static BuiltReturn sayCore(int numParams, LoadedFunction *fun, bool sayIt) {
98 int fileNum = -1;
99 Common::String newText;
100 int objT, p;
101 killSpeechTimers();
102
103 switch (numParams) {
104 case 3:
105 if (!fun->stack->thisVar.getValueType(fileNum, SVT_FILE))
106 return BR_ERROR;
107 trimStack(fun->stack);
108 // fall through
109
110 case 2:
111 newText = fun->stack->thisVar.getTextFromAnyVar();
112 trimStack(fun->stack);
113 if (!fun->stack->thisVar.getValueType(objT, SVT_OBJTYPE))
114 return BR_ERROR;
115 trimStack(fun->stack);
116 p = g_sludge->_speechMan->wrapSpeech(newText, objT, fileNum, sayIt);
117 fun->timeLeft = p;
118 //debugOut ("BUILTIN: sayCore: %s (%i)\n", newText, p);
119 fun->isSpeech = true;
120 return BR_KEEP_AND_PAUSE;
121
122 default:
123 break;
124 }
125
126 fatal("Function should have either 2 or 3 parameters");
127 return BR_ERROR;
128 }
129
130 #pragma mark -
131 #pragma mark Built in functions
132
builtIn(say)133 builtIn(say) {
134 UNUSEDALL
135 return sayCore(numParams, fun, true);
136 }
137
builtIn(think)138 builtIn(think) {
139 UNUSEDALL
140 return sayCore(numParams, fun, false);
141 }
142
builtIn(freeze)143 builtIn(freeze) {
144 UNUSEDALL
145 g_sludge->_gfxMan->freeze();
146 freezeSubs();
147 fun->freezerLevel = 0;
148 return BR_CONTINUE;
149 }
150
builtIn(unfreeze)151 builtIn(unfreeze) {
152 UNUSEDALL
153 g_sludge->_gfxMan->unfreeze();
154 unfreezeSubs();
155 return BR_CONTINUE;
156 }
157
builtIn(howFrozen)158 builtIn(howFrozen) {
159 UNUSEDALL
160 fun->reg.setVariable(SVT_INT, g_sludge->_gfxMan->howFrozen());
161 return BR_CONTINUE;
162 }
163
builtIn(setCursor)164 builtIn(setCursor) {
165 UNUSEDALL
166 PersonaAnimation *aa = fun->stack->thisVar.getAnimationFromVar();
167 g_sludge->_cursorMan->pickAnimCursor(aa);
168 trimStack(fun->stack);
169 return BR_CONTINUE;
170 }
171
builtIn(getMouseX)172 builtIn(getMouseX) {
173 UNUSEDALL
174 fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseX() + g_sludge->_gfxMan->getCamX());
175 return BR_CONTINUE;
176 }
177
builtIn(getMouseY)178 builtIn(getMouseY) {
179 UNUSEDALL
180 fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseY() + g_sludge->_gfxMan->getCamY());
181 return BR_CONTINUE;
182 }
183
builtIn(getMouseScreenX)184 builtIn(getMouseScreenX) {
185 UNUSEDALL
186 fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseX() * g_sludge->_gfxMan->getCamZoom());
187 return BR_CONTINUE;
188 }
189
builtIn(getMouseScreenY)190 builtIn(getMouseScreenY) {
191 UNUSEDALL
192 fun->reg.setVariable(SVT_INT, g_sludge->_evtMan->mouseY() * g_sludge->_gfxMan->getCamZoom());
193 return BR_CONTINUE;
194 }
195
builtIn(getStatusText)196 builtIn(getStatusText) {
197 UNUSEDALL
198 fun->reg.makeTextVar(g_sludge->_statusBar->statusBarText());
199 return BR_CONTINUE;
200 }
201
builtIn(getMatchingFiles)202 builtIn(getMatchingFiles) {
203 UNUSEDALL
204 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
205 trimStack(fun->stack);
206 fun->reg.unlinkVar();
207
208 // Return value
209 fun->reg.varType = SVT_STACK;
210 fun->reg.varData.theStack = new StackHandler;
211 if (!checkNew(fun->reg.varData.theStack))
212 return BR_ERROR;
213 fun->reg.varData.theStack->first = NULL;
214 fun->reg.varData.theStack->last = NULL;
215 fun->reg.varData.theStack->timesUsed = 1;
216 if (!fun->reg.varData.theStack->getSavedGamesStack(newText))
217 return BR_ERROR;
218 return BR_CONTINUE;
219 }
220
builtIn(saveGame)221 builtIn(saveGame) {
222 UNUSEDALL
223
224 if (g_sludge->_gfxMan->isFrozen()) {
225 fatal("Can't save game state while the engine is frozen");
226 }
227
228 g_sludge->loadNow = fun->stack->thisVar.getTextFromAnyVar();
229 trimStack(fun->stack);
230
231 Common::String aaaaa = encodeFilename(g_sludge->loadNow);
232 g_sludge->loadNow.clear();
233 if (failSecurityCheck(aaaaa))
234 return BR_ERROR; // Won't fail if encoded, how cool is that? OK, not very.
235
236 g_sludge->loadNow = ":" + aaaaa;
237
238 fun->reg.setVariable(SVT_INT, 0);
239 saverFunc = fun;
240 return BR_KEEP_AND_PAUSE;
241 }
242
builtIn(fileExists)243 builtIn(fileExists) {
244 UNUSEDALL
245 g_sludge->loadNow = fun->stack->thisVar.getTextFromAnyVar();
246 trimStack(fun->stack);
247 Common::String aaaaa = encodeFilename(g_sludge->loadNow);
248 g_sludge->loadNow.clear();
249
250 if (failSecurityCheck(aaaaa))
251 return BR_ERROR;
252
253 bool exist = false;
254
255 Common::File fd;
256 if (fd.open(aaaaa)) {
257 exist = true;
258 fd.close();
259 } else {
260 Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(aaaaa);
261 if (fp) {
262 exist = true;
263 delete fp;
264 }
265 }
266
267 // Return value
268 fun->reg.setVariable(SVT_INT, exist);
269 return BR_CONTINUE;
270 }
271
builtIn(loadGame)272 builtIn(loadGame) {
273 UNUSEDALL
274 Common::String aaaaa = fun->stack->thisVar.getTextFromAnyVar();
275 trimStack(fun->stack);
276 g_sludge->loadNow.clear();
277 g_sludge->loadNow = encodeFilename(aaaaa);
278
279 if (g_sludge->_gfxMan->isFrozen()) {
280 fatal("Can't load a saved game while the engine is frozen");
281 }
282 if (failSecurityCheck(g_sludge->loadNow))
283 return BR_ERROR;
284 Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(g_sludge->loadNow);
285 if (fp) {
286 delete fp;
287 return BR_KEEP_AND_PAUSE;
288 }
289 debug("not find sav file");
290
291 g_sludge->loadNow.clear();
292 return BR_CONTINUE;
293 }
294
295 //--------------------------------------
296 #pragma mark -
297 #pragma mark Background image - Painting
298
builtIn(blankScreen)299 builtIn(blankScreen) {
300 UNUSEDALL
301 g_sludge->_gfxMan->blankAllScreen();
302 return BR_CONTINUE;
303 }
304
builtIn(blankArea)305 builtIn(blankArea) {
306 UNUSEDALL
307 int x1, y1, x2, y2;
308 if (!fun->stack->thisVar.getValueType(y2, SVT_INT))
309 return BR_ERROR;
310 trimStack(fun->stack);
311 if (!fun->stack->thisVar.getValueType(x2, SVT_INT))
312 return BR_ERROR;
313 trimStack(fun->stack);
314 if (!fun->stack->thisVar.getValueType(y1, SVT_INT))
315 return BR_ERROR;
316 trimStack(fun->stack);
317 if (!fun->stack->thisVar.getValueType(x1, SVT_INT))
318 return BR_ERROR;
319 trimStack(fun->stack);
320 g_sludge->_gfxMan->blankScreen(x1, y1, x2, y2);
321 return BR_CONTINUE;
322 }
323
builtIn(darkBackground)324 builtIn(darkBackground) {
325 UNUSEDALL
326 g_sludge->_gfxMan->darkScreen();
327 return BR_CONTINUE;
328 }
329
builtIn(addOverlay)330 builtIn(addOverlay) {
331 UNUSEDALL
332 int fileNumber, xPos, yPos;
333 if (!fun->stack->thisVar.getValueType(yPos, SVT_INT))
334 return BR_ERROR;
335 trimStack(fun->stack);
336 if (!fun->stack->thisVar.getValueType(xPos, SVT_INT))
337 return BR_ERROR;
338 trimStack(fun->stack);
339 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
340 return BR_ERROR;
341 trimStack(fun->stack);
342 g_sludge->_gfxMan->loadBackDrop(fileNumber, xPos, yPos);
343 return BR_CONTINUE;
344 }
345
builtIn(mixOverlay)346 builtIn(mixOverlay) {
347 UNUSEDALL
348 int fileNumber, xPos, yPos;
349 if (!fun->stack->thisVar.getValueType(yPos, SVT_INT))
350 return BR_ERROR;
351 trimStack(fun->stack);
352 if (!fun->stack->thisVar.getValueType(xPos, SVT_INT))
353 return BR_ERROR;
354 trimStack(fun->stack);
355 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
356 return BR_ERROR;
357 trimStack(fun->stack);
358 g_sludge->_gfxMan->mixBackDrop(fileNumber, xPos, yPos);
359 return BR_CONTINUE;
360 }
361
builtIn(pasteImage)362 builtIn(pasteImage) {
363 UNUSEDALL
364 int x, y;
365 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
366 return BR_ERROR;
367 trimStack(fun->stack);
368 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
369 return BR_ERROR;
370 trimStack(fun->stack);
371 PersonaAnimation *pp = fun->stack->thisVar.getAnimationFromVar();
372 trimStack(fun->stack);
373 if (pp == NULL)
374 return BR_CONTINUE;
375
376 g_sludge->_cursorMan->pasteCursor(x, y, pp);
377
378 delete pp;
379
380 return BR_CONTINUE;
381 }
382
383 #pragma mark -
384 #pragma mark Background Image - Scrolling
385
builtIn(setSceneDimensions)386 builtIn(setSceneDimensions) {
387 UNUSEDALL
388 int x, y;
389 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
390 return BR_ERROR;
391 trimStack(fun->stack);
392 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
393 return BR_ERROR;
394 trimStack(fun->stack);
395 if (g_sludge->_gfxMan->killResizeBackdrop(x, y)) {
396 g_sludge->_gfxMan->blankScreen(0, 0, x, y);
397 return BR_CONTINUE;
398 }
399 fatal("Out of memory creating new backdrop.");
400 return BR_ERROR;
401 }
402
builtIn(aimCamera)403 builtIn(aimCamera) {
404 UNUSEDALL
405 int cameraX, cameraY;
406 if (!fun->stack->thisVar.getValueType(cameraY, SVT_INT))
407 return BR_ERROR;
408 trimStack(fun->stack);
409 if (!fun->stack->thisVar.getValueType(cameraX, SVT_INT))
410 return BR_ERROR;
411 trimStack(fun->stack);
412
413 g_sludge->_gfxMan->aimCamera(cameraX, cameraY);
414
415 return BR_CONTINUE;
416 }
417
builtIn(zoomCamera)418 builtIn(zoomCamera) {
419 UNUSEDALL
420 int z;
421 if (!fun->stack->thisVar.getValueType(z, SVT_INT))
422 return BR_ERROR;
423 trimStack(fun->stack);
424
425 g_sludge->_gfxMan->zoomCamera(z);
426
427 return BR_CONTINUE;
428 }
429
430 #pragma mark -
431 #pragma mark Variables
432
builtIn(pickOne)433 builtIn(pickOne) {
434 UNUSEDALL
435 if (!numParams) {
436 fatal("Built-in function should have at least 1 parameter");
437 return BR_ERROR;
438 }
439
440 int i;
441
442 i = g_sludge->getRandomSource()->getRandomNumber(numParams - 1);
443
444 // Return value
445 while (numParams--) {
446 if (i == numParams)
447 fun->reg.copyFrom(fun->stack->thisVar);
448 trimStack(fun->stack);
449 }
450 return BR_CONTINUE;
451 }
452
builtIn(substring)453 builtIn(substring) {
454 UNUSEDALL
455 Common::String wholeString;
456 int start, length;
457
458 //debugOut ("BUILTIN: substring\n");
459
460 if (!fun->stack->thisVar.getValueType(length, SVT_INT))
461 return BR_ERROR;
462 trimStack(fun->stack);
463 if (!fun->stack->thisVar.getValueType(start, SVT_INT))
464 return BR_ERROR;
465 trimStack(fun->stack);
466 wholeString = fun->stack->thisVar.getTextFromAnyVar();
467 trimStack(fun->stack);
468
469 Common::U32String str32 = wholeString.decode(Common::kUtf8);
470
471 if ((int)str32.size() < start + length) {
472 length = str32.size() - start;
473 if ((int)str32.size() < start) {
474 start = 0;
475 }
476 }
477 if (length < 0) {
478 length = 0;
479 }
480
481 Common::String newString = str32.substr(start, length).encode(Common::kUtf8);
482
483 fun->reg.makeTextVar(newString);
484 return BR_CONTINUE;
485 }
486
builtIn(stringLength)487 builtIn(stringLength) {
488 UNUSEDALL
489 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
490 trimStack(fun->stack);
491 fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->stringLength(newText));
492 return BR_CONTINUE;
493 }
494
builtIn(newStack)495 builtIn(newStack) {
496 UNUSEDALL
497 fun->reg.unlinkVar();
498
499 // Return value
500 fun->reg.varType = SVT_STACK;
501 fun->reg.varData.theStack = new StackHandler;
502 if (!checkNew(fun->reg.varData.theStack))
503 return BR_ERROR;
504 fun->reg.varData.theStack->first = NULL;
505 fun->reg.varData.theStack->last = NULL;
506 fun->reg.varData.theStack->timesUsed = 1;
507 while (numParams--) {
508 if (!addVarToStack(fun->stack->thisVar, fun->reg.varData.theStack->first))
509 return BR_ERROR;
510 if (fun->reg.varData.theStack->last == NULL) {
511 fun->reg.varData.theStack->last = fun->reg.varData.theStack->first;
512 }
513 trimStack(fun->stack);
514 }
515 return BR_CONTINUE;
516 }
517
518 // wait is exactly the same function, but limited to 2 parameters
519 #define builtIn_wait builtIn_newStack
520
builtIn(stackSize)521 builtIn(stackSize) {
522 UNUSEDALL
523 switch (fun->stack->thisVar.varType) {
524 case SVT_STACK:
525 // Return value
526 fun->reg.setVariable(SVT_INT, fun->stack->thisVar.varData.theStack->getStackSize());
527 trimStack(fun->stack);
528 return BR_CONTINUE;
529
530 case SVT_FASTARRAY:
531 // Return value
532 fun->reg.setVariable(SVT_INT, fun->stack->thisVar.varData.fastArray->size);
533 trimStack(fun->stack);
534 return BR_CONTINUE;
535
536 default:
537 break;
538 }
539 fatal("Parameter isn't a stack or a fast array.");
540 return BR_ERROR;
541 }
542
builtIn(copyStack)543 builtIn(copyStack) {
544 UNUSEDALL
545 if (fun->stack->thisVar.varType != SVT_STACK) {
546 fatal("Parameter isn't a stack.");
547 return BR_ERROR;
548 }
549 // Return value
550 if (!fun->reg.copyStack(fun->stack->thisVar))
551 return BR_ERROR;
552 trimStack(fun->stack);
553 return BR_CONTINUE;
554 }
555
builtIn(pushToStack)556 builtIn(pushToStack) {
557 UNUSEDALL
558 if (fun->stack->next->thisVar.varType != SVT_STACK) {
559 fatal("Parameter isn't a stack");
560 return BR_ERROR;
561 }
562
563 if (!addVarToStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first))
564 return BR_ERROR;
565
566 if (fun->stack->next->thisVar.varData.theStack->first->next == NULL)
567 fun->stack->next->thisVar.varData.theStack->last = fun->stack->next->thisVar.varData.theStack->first;
568
569 trimStack(fun->stack);
570 trimStack(fun->stack);
571 return BR_CONTINUE;
572 }
573
builtIn(enqueue)574 builtIn(enqueue) {
575 UNUSEDALL
576 if (fun->stack->next->thisVar.varType != SVT_STACK) {
577 fatal("Parameter isn't a stack");
578 return BR_ERROR;
579 }
580
581 if (fun->stack->next->thisVar.varData.theStack->first == NULL) {
582 if (!addVarToStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first))
583 return BR_ERROR;
584
585 fun->stack->next->thisVar.varData.theStack->last = fun->stack->next->thisVar.varData.theStack->first;
586 } else {
587 if (!addVarToStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->last->next))
588 return BR_ERROR;
589 fun->stack->next->thisVar.varData.theStack->last = fun->stack->next->thisVar.varData.theStack->last->next;
590 }
591
592 trimStack(fun->stack);
593 trimStack(fun->stack);
594 return BR_CONTINUE;
595 }
596
builtIn(deleteFromStack)597 builtIn(deleteFromStack) {
598 UNUSEDALL
599 if (fun->stack->next->thisVar.varType != SVT_STACK) {
600 fatal("Parameter isn't a stack.");
601 return BR_ERROR;
602 }
603
604 // Return value
605 fun->reg.setVariable(SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, false));
606
607 // Horrible hacking because 'last' value might now be wrong!
608 VariableStack *nextFirstStack = fun->stack->next->thisVar.varData.theStack->first;
609 fun->stack->next->thisVar.varData.theStack->last = (nextFirstStack == NULL) ? NULL : nextFirstStack->stackFindLast();
610
611 trimStack(fun->stack);
612 trimStack(fun->stack);
613 return BR_CONTINUE;
614 }
615
builtIn(deleteAllFromStack)616 builtIn(deleteAllFromStack) {
617 UNUSEDALL
618 if (fun->stack->next->thisVar.varType != SVT_STACK) {
619 fatal("Parameter isn't a stack.");
620 return BR_ERROR;
621 }
622
623 // Return value
624 fun->reg.setVariable(SVT_INT, deleteVarFromStack(fun->stack->thisVar, fun->stack->next->thisVar.varData.theStack->first, true));
625
626 // Horrible hacking because 'last' value might now be wrong!
627 VariableStack *nextFirstStack = fun->stack->next->thisVar.varData.theStack->first;
628 fun->stack->next->thisVar.varData.theStack->last = (nextFirstStack == NULL) ? NULL : nextFirstStack->stackFindLast();
629
630 trimStack(fun->stack);
631 trimStack(fun->stack);
632 return BR_CONTINUE;
633 }
634
builtIn(popFromStack)635 builtIn(popFromStack) {
636 UNUSEDALL
637 if (fun->stack->thisVar.varType != SVT_STACK) {
638 fatal("Parameter isn't a stack.");
639 return BR_ERROR;
640 }
641 if (fun->stack->thisVar.varData.theStack->first == NULL) {
642 fatal("The stack's empty.");
643 return BR_ERROR;
644 }
645
646 // Return value
647 fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->first->thisVar);
648 trimStack(fun->stack->thisVar.varData.theStack->first);
649 trimStack(fun->stack);
650 return BR_CONTINUE;
651 }
652
builtIn(peekStart)653 builtIn(peekStart) {
654 UNUSEDALL
655 if (fun->stack->thisVar.varType != SVT_STACK) {
656 fatal("Parameter isn't a stack.");
657 return BR_ERROR;
658 }
659 if (fun->stack->thisVar.varData.theStack->first == NULL) {
660 fatal("The stack's empty.");
661 return BR_ERROR;
662 }
663
664 // Return value
665 fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->first->thisVar);
666 trimStack(fun->stack);
667 return BR_CONTINUE;
668 }
669
builtIn(peekEnd)670 builtIn(peekEnd) {
671 UNUSEDALL
672 if (fun->stack->thisVar.varType != SVT_STACK) {
673 fatal("Parameter isn't a stack.");
674 return BR_ERROR;
675 }
676 if (fun->stack->thisVar.varData.theStack->first == NULL) {
677 fatal("The stack's empty.");
678 return BR_ERROR;
679 }
680
681 // Return value
682 fun->reg.copyFrom(fun->stack->thisVar.varData.theStack->last->thisVar);
683 trimStack(fun->stack);
684 return BR_CONTINUE;
685 }
686
builtIn(random)687 builtIn(random) {
688 UNUSEDALL
689 int num;
690
691 if (!fun->stack->thisVar.getValueType(num, SVT_INT))
692 return BR_ERROR;
693
694 trimStack(fun->stack);
695 if (num <= 0)
696 num = 1;
697 fun->reg.setVariable(SVT_INT, g_sludge->getRandomSource()->getRandomNumber(num - 1));
698 return BR_CONTINUE;
699 }
700
getRGBParams(int & red,int & green,int & blue,LoadedFunction * fun)701 static bool getRGBParams(int &red, int &green, int &blue, LoadedFunction *fun) {
702 if (!fun->stack->thisVar.getValueType(blue, SVT_INT))
703 return false;
704 trimStack(fun->stack);
705 if (!fun->stack->thisVar.getValueType(green, SVT_INT))
706 return false;
707 trimStack(fun->stack);
708 if (!fun->stack->thisVar.getValueType(red, SVT_INT))
709 return false;
710 trimStack(fun->stack);
711 return true;
712 }
713
builtIn(setStatusColour)714 builtIn(setStatusColour) {
715 UNUSEDALL
716 int red, green, blue;
717
718 if (!getRGBParams(red, green, blue, fun))
719 return BR_ERROR;
720
721 g_sludge->_statusBar->statusBarColour((byte)red, (byte)green, (byte)blue);
722 return BR_CONTINUE;
723 }
724
builtIn(setLitStatusColour)725 builtIn(setLitStatusColour) {
726 UNUSEDALL
727 int red, green, blue;
728
729 if (!getRGBParams(red, green, blue, fun))
730 return BR_ERROR;
731
732 g_sludge->_statusBar->statusBarLitColour((byte)red, (byte)green, (byte)blue);
733 return BR_CONTINUE;
734 }
735
builtIn(setPasteColour)736 builtIn(setPasteColour) {
737 UNUSEDALL
738 int red, green, blue;
739
740 if (!getRGBParams(red, green, blue, fun))
741 return BR_ERROR;
742
743 g_sludge->_txtMan->setPasterColor((byte)red, (byte)green, (byte)blue);
744 return BR_CONTINUE;
745 }
746
builtIn(setBlankColour)747 builtIn(setBlankColour) {
748 UNUSEDALL
749 int red, green, blue;
750
751 if (!getRGBParams(red, green, blue, fun))
752 return BR_ERROR;
753
754 g_sludge->_gfxMan->setBlankColor(red, green, blue);
755 fun->reg.setVariable(SVT_INT, 1);
756 return BR_CONTINUE;
757 }
758
builtIn(setBurnColour)759 builtIn(setBurnColour) {
760 UNUSEDALL
761 int red, green, blue;
762
763 if (!getRGBParams(red, green, blue, fun))
764 return BR_ERROR;
765
766 g_sludge->_gfxMan->setBurnColor(red, green, blue);
767 fun->reg.setVariable(SVT_INT, 1);
768 return BR_CONTINUE;
769 }
770
builtIn(setFont)771 builtIn(setFont) {
772 UNUSEDALL
773 int fileNumber, newHeight;
774 if (!fun->stack->thisVar.getValueType(newHeight, SVT_INT))
775 return BR_ERROR;
776 // newDebug (" Height:", newHeight);
777 trimStack(fun->stack);
778 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
779 // newDebug (" Character supported:", newText);
780 trimStack(fun->stack);
781 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
782 return BR_ERROR;
783 // newDebug (" File:", fileNumber);
784 trimStack(fun->stack);
785 if (!g_sludge->_txtMan->loadFont(fileNumber, newText, newHeight))
786 return BR_ERROR;
787 // newDebug (" Done!");
788 return BR_CONTINUE;
789 }
790
builtIn(inFont)791 builtIn(inFont) {
792 UNUSEDALL
793 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
794 trimStack(fun->stack);
795
796 // Return value
797 fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->isInFont(newText));
798 return BR_CONTINUE;
799 }
800
builtIn(pasteString)801 builtIn(pasteString) {
802 UNUSEDALL
803 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
804 trimStack(fun->stack);
805 int y, x;
806 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
807 return BR_ERROR;
808 trimStack(fun->stack);
809 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
810 return BR_ERROR;
811 trimStack(fun->stack);
812 if (x == IN_THE_CENTRE)
813 x = g_sludge->_gfxMan->getCenterX(g_sludge->_txtMan->stringWidth(newText));
814 g_sludge->_txtMan->pasteStringToBackdrop(newText, x, y);
815 return BR_CONTINUE;
816 }
817
builtIn(anim)818 builtIn(anim) {
819 UNUSEDALL
820 if (numParams < 2) {
821 fatal("Built-in function anim() must have at least 2 parameters.");
822 return BR_ERROR;
823 }
824
825 // First store the frame numbers and take 'em off the stack
826 PersonaAnimation *ba = new PersonaAnimation(numParams - 1, fun->stack);
827
828 // Only remaining paramter is the file number
829 int fileNumber;
830 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
831 return BR_ERROR;
832 trimStack(fun->stack);
833
834 // Load the required sprite bank
835 LoadedSpriteBank *sprBanky = g_sludge->_gfxMan->loadBankForAnim(fileNumber);
836 if (!sprBanky) {
837 delete ba;
838 return BR_ERROR; // File not found, fatal done already
839 }
840 ba->theSprites = sprBanky;
841
842 // Return value
843 fun->reg.makeAnimationVariable(ba);
844
845 return BR_CONTINUE;
846 }
847
builtIn(costume)848 builtIn(costume) {
849 UNUSEDALL
850 Persona *newPersona = new Persona;
851 if (!checkNew(newPersona))
852 return BR_ERROR;
853 newPersona->numDirections = numParams / 3;
854 if (numParams == 0 || newPersona->numDirections * 3 != numParams) {
855 fatal("Illegal number of parameters (should be greater than 0 and divisible by 3)");
856 return BR_ERROR;
857 }
858 int iii;
859 newPersona->animation = new PersonaAnimation *[numParams];
860 if (!checkNew(newPersona->animation))
861 return BR_ERROR;
862 for (iii = numParams - 1; iii >= 0; iii--) {
863 newPersona->animation[iii] = fun->stack->thisVar.getAnimationFromVar();
864 trimStack(fun->stack);
865 }
866
867 // Return value
868 fun->reg.makeCostumeVariable(newPersona);
869 return BR_CONTINUE;
870 }
871
builtIn(launch)872 builtIn(launch) {
873 UNUSEDALL
874 Common::String newTextA = fun->stack->thisVar.getTextFromAnyVar();
875
876 Common::String newText = encodeFilename(newTextA);
877
878 trimStack(fun->stack);
879 if (newTextA[0] == 'h' && newTextA[1] == 't' && newTextA[2] == 't' && newTextA[3] == 'p' &&
880 (newTextA[4] == ':' || (newTextA[4] == 's' && newTextA[5] == ':'))) {
881
882 // IT'S A WEBSITE!
883 g_sludge->launchMe.clear();
884 g_sludge->launchMe = newTextA;
885 } else {
886 Common::String gameDir = g_sludge->gamePath;
887 gameDir += "/";
888 g_sludge->launchMe.clear();
889 g_sludge->launchMe = gameDir + newText;
890 if (g_sludge->launchMe.empty())
891 return BR_ERROR;
892 }
893 fun->reg.setVariable(SVT_INT, 1);
894 launchResult = &fun->reg;
895
896 return BR_KEEP_AND_PAUSE;
897 }
898
builtIn(pause)899 builtIn(pause) {
900 UNUSEDALL
901 int theTime;
902 if (!fun->stack->thisVar.getValueType(theTime, SVT_INT))
903 return BR_ERROR;
904 trimStack(fun->stack);
905 if (theTime > 0) {
906 fun->timeLeft = theTime - 1;
907 fun->isSpeech = false;
908 return BR_KEEP_AND_PAUSE;
909 }
910 return BR_CONTINUE;
911 }
912
builtIn(completeTimers)913 builtIn(completeTimers) {
914 UNUSEDALL
915 completeTimers();
916 return BR_CONTINUE;
917 }
918
builtIn(callEvent)919 builtIn(callEvent) {
920 UNUSEDALL
921 int obj1, obj2;
922 if (!fun->stack->thisVar.getValueType(obj2, SVT_OBJTYPE))
923 return BR_ERROR;
924 trimStack(fun->stack);
925 if (!fun->stack->thisVar.getValueType(obj1, SVT_OBJTYPE))
926 return BR_ERROR;
927 trimStack(fun->stack);
928
929 int fNum = g_sludge->_objMan->getCombinationFunction(obj1, obj2);
930
931 // Return value
932 if (fNum) {
933 fun->reg.setVariable(SVT_FUNC, fNum);
934 return BR_CALLAFUNC;
935 }
936 fun->reg.setVariable(SVT_INT, 0);
937 return BR_CONTINUE;
938 }
939
builtIn(quitGame)940 builtIn(quitGame) {
941 UNUSEDALL
942 g_sludge->_evtMan->quitGame();
943 return BR_CONTINUE;
944 }
945
946 #pragma mark -
947 #pragma mark Movie functions
948
949 // The old movie functions are deprecated and does nothing.
builtIn(_rem_movieStart)950 builtIn(_rem_movieStart) {
951 UNUSEDALL
952 trimStack(fun->stack);
953 return BR_CONTINUE;
954 }
955
builtIn(_rem_movieAbort)956 builtIn(_rem_movieAbort) {
957 UNUSEDALL
958 fun->reg.setVariable(SVT_INT, 0);
959 return BR_CONTINUE;
960 }
961
builtIn(_rem_moviePlaying)962 builtIn(_rem_moviePlaying) {
963 UNUSEDALL
964 fun->reg.setVariable(SVT_INT, 0);
965 return BR_CONTINUE;
966 }
967
builtIn(playMovie)968 builtIn(playMovie) {
969 UNUSEDALL
970 int fileNumber, r;
971
972 if (movieIsPlaying)
973 return BR_PAUSE;
974
975 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
976 return BR_ERROR;
977 trimStack(fun->stack);
978
979 r = playMovie(fileNumber);
980
981 fun->reg.setVariable(SVT_INT, r);
982
983 if (r && (!fun->next)) {
984 restartFunction(fun);
985 return BR_ALREADY_GONE;
986 }
987 return BR_CONTINUE;
988 }
989
builtIn(stopMovie)990 builtIn(stopMovie) {
991 UNUSEDALL
992
993 stopMovie();
994
995 fun->reg.setVariable(SVT_INT, 0);
996 return BR_CONTINUE;
997 }
998
builtIn(pauseMovie)999 builtIn(pauseMovie) {
1000 UNUSEDALL
1001
1002 pauseMovie();
1003
1004 fun->reg.setVariable(SVT_INT, 0);
1005 return BR_CONTINUE;
1006 }
1007
1008 #pragma mark -
1009 #pragma mark Audio functions
1010
builtIn(startMusic)1011 builtIn(startMusic) {
1012 UNUSEDALL
1013 int fromTrack, musChan, fileNumber;
1014 if (!fun->stack->thisVar.getValueType(fromTrack, SVT_INT))
1015 return BR_ERROR;
1016 trimStack(fun->stack);
1017 if (!fun->stack->thisVar.getValueType(musChan, SVT_INT))
1018 return BR_ERROR;
1019 trimStack(fun->stack);
1020 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
1021 return BR_ERROR;
1022 trimStack(fun->stack);
1023 if (!g_sludge->_soundMan->playMOD(fileNumber, musChan, fromTrack))
1024 return BR_CONTINUE; //BR_ERROR;
1025 return BR_CONTINUE;
1026 }
1027
builtIn(stopMusic)1028 builtIn(stopMusic) {
1029 UNUSEDALL
1030 int v;
1031 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1032 return BR_ERROR;
1033 trimStack(fun->stack);
1034 g_sludge->_soundMan->stopMOD(v);
1035 return BR_CONTINUE;
1036 }
1037
builtIn(setMusicVolume)1038 builtIn(setMusicVolume) {
1039 UNUSEDALL
1040 int musChan, v;
1041 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1042 return BR_ERROR;
1043 trimStack(fun->stack);
1044 if (!fun->stack->thisVar.getValueType(musChan, SVT_INT))
1045 return BR_ERROR;
1046 trimStack(fun->stack);
1047 g_sludge->_soundMan->setMusicVolume(musChan, v);
1048 return BR_CONTINUE;
1049 }
1050
builtIn(setDefaultMusicVolume)1051 builtIn(setDefaultMusicVolume) {
1052 UNUSEDALL
1053 int v;
1054 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1055 return BR_ERROR;
1056 trimStack(fun->stack);
1057 g_sludge->_soundMan->setDefaultMusicVolume(v);
1058 return BR_CONTINUE;
1059 }
1060
builtIn(playSound)1061 builtIn(playSound) {
1062 UNUSEDALL
1063 int fileNumber;
1064 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
1065 return BR_ERROR;
1066 trimStack(fun->stack);
1067 if (!g_sludge->_soundMan->startSound(fileNumber, false))
1068 return BR_CONTINUE; // Was BR_ERROR
1069 return BR_CONTINUE;
1070 }
builtIn(loopSound)1071 builtIn(loopSound) {
1072 UNUSEDALL
1073 int fileNumber;
1074
1075 if (numParams < 1) {
1076 fatal("Built-in function loopSound() must have at least 1 parameter.");
1077 return BR_ERROR;
1078 } else if (numParams < 2) {
1079
1080 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
1081 return BR_ERROR;
1082 trimStack(fun->stack);
1083 if (!g_sludge->_soundMan->startSound(fileNumber, true))
1084 return BR_CONTINUE; // Was BR_ERROR
1085 return BR_CONTINUE;
1086 } else {
1087 // We have more than one sound to play!
1088
1089 int doLoop = 2;
1090 SoundList*s = NULL;
1091 SoundList*old = NULL;
1092
1093 // Should we loop?
1094 if (fun->stack->thisVar.varType != SVT_FILE) {
1095 fun->stack->thisVar.getValueType(doLoop, SVT_INT);
1096 trimStack(fun->stack);
1097 numParams--;
1098 }
1099 while (numParams) {
1100 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE)) {
1101 fatal("Illegal parameter given built-in function loopSound().");
1102 return BR_ERROR;
1103 }
1104 s = new SoundList;
1105 if (!checkNew(s))
1106 return BR_ERROR;
1107
1108 s->next = old;
1109 s->prev = NULL;
1110 s->sound = fileNumber;
1111 old = s;
1112
1113 trimStack(fun->stack);
1114 numParams--;
1115 }
1116 while (s->next)
1117 s = s->next;
1118 if (doLoop > 1) {
1119 s->next = old;
1120 old->prev = s;
1121 } else if (doLoop) {
1122 s->next = s;
1123 }
1124 old->vol = -1;
1125 g_sludge->_soundMan->playSoundList(old);
1126 return BR_CONTINUE;
1127 }
1128 }
1129
builtIn(stopSound)1130 builtIn(stopSound) {
1131 UNUSEDALL
1132 int v;
1133 if (!fun->stack->thisVar.getValueType(v, SVT_FILE))
1134 return BR_ERROR;
1135 trimStack(fun->stack);
1136 g_sludge->_soundMan->huntKillSound(v);
1137 return BR_CONTINUE;
1138 }
1139
builtIn(setDefaultSoundVolume)1140 builtIn(setDefaultSoundVolume) {
1141 UNUSEDALL
1142 int v;
1143 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1144 return BR_ERROR;
1145 trimStack(fun->stack);
1146 g_sludge->_soundMan->setDefaultSoundVolume(v);
1147 return BR_CONTINUE;
1148 }
1149
builtIn(setSoundVolume)1150 builtIn(setSoundVolume) {
1151 UNUSEDALL
1152 int musChan, v;
1153 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1154 return BR_ERROR;
1155 trimStack(fun->stack);
1156 if (!fun->stack->thisVar.getValueType(musChan, SVT_FILE))
1157 return BR_ERROR;
1158 trimStack(fun->stack);
1159 g_sludge->_soundMan->setSoundVolume(musChan, v);
1160 return BR_CONTINUE;
1161 }
1162
builtIn(setSoundLoopPoints)1163 builtIn(setSoundLoopPoints) {
1164 UNUSEDALL
1165 int musChan, theEnd, theStart;
1166 if (!fun->stack->thisVar.getValueType(theEnd, SVT_INT))
1167 return BR_ERROR;
1168 trimStack(fun->stack);
1169 if (!fun->stack->thisVar.getValueType(theStart, SVT_INT))
1170 return BR_ERROR;
1171 trimStack(fun->stack);
1172 if (!fun->stack->thisVar.getValueType(musChan, SVT_FILE))
1173 return BR_ERROR;
1174 trimStack(fun->stack);
1175 g_sludge->_soundMan->setSoundLoop(musChan, theStart, theEnd);
1176 return BR_CONTINUE;
1177 }
1178
1179 #pragma mark -
1180 #pragma mark Extra room bits
1181
builtIn(setFloor)1182 builtIn(setFloor) {
1183 UNUSEDALL
1184 if (fun->stack->thisVar.varType == SVT_FILE) {
1185 int v;
1186 fun->stack->thisVar.getValueType(v, SVT_FILE);
1187 trimStack(fun->stack);
1188 if (!g_sludge->_floorMan->setFloor(v))
1189 return BR_ERROR;
1190 } else {
1191 trimStack(fun->stack);
1192 g_sludge->_floorMan->setFloorNull();
1193 }
1194 return BR_CONTINUE;
1195 }
1196
builtIn(showFloor)1197 builtIn(showFloor) {
1198 UNUSEDALL
1199 g_sludge->_floorMan->drawFloor();
1200 return BR_CONTINUE;
1201 }
1202
builtIn(setZBuffer)1203 builtIn(setZBuffer) {
1204 UNUSEDALL
1205 if (fun->stack->thisVar.varType == SVT_FILE) {
1206 int v;
1207 fun->stack->thisVar.getValueType(v, SVT_FILE);
1208 trimStack(fun->stack);
1209 if (!g_sludge->_gfxMan->setZBuffer(v))
1210 return BR_ERROR;
1211 } else {
1212 trimStack(fun->stack);
1213 g_sludge->_gfxMan->killZBuffer();
1214 }
1215 return BR_CONTINUE;
1216 }
1217
builtIn(setLightMap)1218 builtIn(setLightMap) {
1219 UNUSEDALL
1220 switch (numParams) {
1221 case 2:
1222 if (!fun->stack->thisVar.getValueType(g_sludge->_gfxMan->_lightMapMode, SVT_INT))
1223 return BR_ERROR;
1224 trimStack(fun->stack);
1225 g_sludge->_gfxMan->_lightMapMode %= LIGHTMAPMODE_NUM;
1226 // fall through
1227
1228 case 1:
1229 if (fun->stack->thisVar.varType == SVT_FILE) {
1230 int v;
1231 fun->stack->thisVar.getValueType(v, SVT_FILE);
1232 trimStack(fun->stack);
1233 if (!g_sludge->_gfxMan->loadLightMap(v))
1234 return BR_ERROR;
1235 fun->reg.setVariable(SVT_INT, 1);
1236 } else {
1237 trimStack(fun->stack);
1238 g_sludge->_gfxMan->killLightMap();
1239 fun->reg.setVariable(SVT_INT, 0);
1240 }
1241 break;
1242
1243 default:
1244 fatal("Function should have either 2 or 3 parameters");
1245 return BR_ERROR;
1246 }
1247 return BR_CONTINUE;
1248 }
1249
1250 #pragma mark -
1251 #pragma mark Objects
1252
builtIn(setSpeechMode)1253 builtIn(setSpeechMode) {
1254 UNUSEDALL
1255 int speechMode;
1256 if (!fun->stack->thisVar.getValueType(speechMode, SVT_INT))
1257 return BR_ERROR;
1258 trimStack(fun->stack);
1259 if (speechMode < 0 || speechMode > 2) {
1260 fatal("Valid parameters are be SPEECHANDTEXT, SPEECHONLY or TEXTONLY");
1261 return BR_ERROR;
1262 }
1263 g_sludge->_speechMan->setSpeechMode(speechMode);
1264 return BR_CONTINUE;
1265 }
1266
builtIn(somethingSpeaking)1267 builtIn(somethingSpeaking) {
1268 UNUSEDALL
1269 int i = g_sludge->_speechMan->isThereAnySpeechGoingOn();
1270 if (i == -1) {
1271 fun->reg.setVariable(SVT_INT, 0);
1272 } else {
1273 fun->reg.setVariable(SVT_OBJTYPE, i);
1274 }
1275 return BR_CONTINUE;
1276 }
1277
builtIn(skipSpeech)1278 builtIn(skipSpeech) {
1279 UNUSEDALL
1280 killSpeechTimers();
1281 return BR_CONTINUE;
1282 }
1283
builtIn(getOverObject)1284 builtIn(getOverObject) {
1285 UNUSEDALL
1286 if (g_sludge->_regionMan->getOverRegion())
1287 // Return value
1288 fun->reg.setVariable(SVT_OBJTYPE, g_sludge->_regionMan->getOverRegion()->thisType->objectNum);
1289 else
1290 // Return value
1291 fun->reg.setVariable(SVT_INT, 0);
1292 return BR_CONTINUE;
1293 }
1294
builtIn(rename)1295 builtIn(rename) {
1296 UNUSEDALL
1297 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
1298 int objT;
1299 trimStack(fun->stack);
1300 if (!fun->stack->thisVar.getValueType(objT, SVT_OBJTYPE))
1301 return BR_ERROR;
1302 trimStack(fun->stack);
1303 ObjectType *o = g_sludge->_objMan->findObjectType(objT);
1304 o->screenName.clear();
1305 o->screenName = newText;
1306 return BR_CONTINUE;
1307 }
1308
builtIn(getObjectX)1309 builtIn(getObjectX) {
1310 UNUSEDALL
1311 int objectNumber;
1312 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1313 return BR_ERROR;
1314 trimStack(fun->stack);
1315
1316 OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber);
1317 if (pers) {
1318 fun->reg.setVariable(SVT_INT, pers->x);
1319 } else {
1320 ScreenRegion *la = g_sludge->_regionMan->getRegionForObject(objectNumber);
1321 if (la) {
1322 fun->reg.setVariable(SVT_INT, la->sX);
1323 } else {
1324 fun->reg.setVariable(SVT_INT, 0);
1325 }
1326 }
1327 return BR_CONTINUE;
1328 }
1329
builtIn(getObjectY)1330 builtIn(getObjectY) {
1331 UNUSEDALL
1332 int objectNumber;
1333 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1334 return BR_ERROR;
1335 trimStack(fun->stack);
1336
1337 OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber);
1338 if (pers) {
1339 fun->reg.setVariable(SVT_INT, pers->y);
1340 } else {
1341 ScreenRegion *la = g_sludge->_regionMan->getRegionForObject(objectNumber);
1342 if (la) {
1343 fun->reg.setVariable(SVT_INT, la->sY);
1344 } else {
1345 fun->reg.setVariable(SVT_INT, 0);
1346 }
1347 }
1348 return BR_CONTINUE;
1349 }
1350
builtIn(addScreenRegion)1351 builtIn(addScreenRegion) {
1352 UNUSEDALL
1353 int sX, sY, x1, y1, x2, y2, di, objectNumber;
1354 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1355 return BR_ERROR;
1356 trimStack(fun->stack);
1357 if (!fun->stack->thisVar.getValueType(sY, SVT_INT))
1358 return BR_ERROR;
1359 trimStack(fun->stack);
1360 if (!fun->stack->thisVar.getValueType(sX, SVT_INT))
1361 return BR_ERROR;
1362 trimStack(fun->stack);
1363 if (!fun->stack->thisVar.getValueType(y2, SVT_INT))
1364 return BR_ERROR;
1365 trimStack(fun->stack);
1366 if (!fun->stack->thisVar.getValueType(x2, SVT_INT))
1367 return BR_ERROR;
1368 trimStack(fun->stack);
1369 if (!fun->stack->thisVar.getValueType(y1, SVT_INT))
1370 return BR_ERROR;
1371 trimStack(fun->stack);
1372 if (!fun->stack->thisVar.getValueType(x1, SVT_INT))
1373 return BR_ERROR;
1374 trimStack(fun->stack);
1375 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1376 return BR_ERROR;
1377 trimStack(fun->stack);
1378 if (g_sludge->_regionMan->addScreenRegion(x1, y1, x2, y2, sX, sY, di, objectNumber))
1379 return BR_CONTINUE;
1380 return BR_ERROR;
1381
1382 }
1383
builtIn(removeScreenRegion)1384 builtIn(removeScreenRegion) {
1385 UNUSEDALL
1386 int objectNumber;
1387 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1388 return BR_ERROR;
1389 trimStack(fun->stack);
1390 g_sludge->_regionMan->removeScreenRegion(objectNumber);
1391 return BR_CONTINUE;
1392 }
1393
builtIn(showBoxes)1394 builtIn(showBoxes) {
1395 UNUSEDALL
1396 g_sludge->_regionMan->showBoxes();
1397 return BR_CONTINUE;
1398 }
1399
builtIn(removeAllScreenRegions)1400 builtIn(removeAllScreenRegions) {
1401 UNUSEDALL
1402 g_sludge->_regionMan->kill();
1403 return BR_CONTINUE;
1404 }
1405
builtIn(addCharacter)1406 builtIn(addCharacter) {
1407 UNUSEDALL
1408 Persona *p;
1409 int x, y, objectNumber;
1410
1411 p = fun->stack->thisVar.getCostumeFromVar();
1412 if (p == NULL)
1413 return BR_ERROR;
1414
1415 trimStack(fun->stack);
1416 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
1417 return BR_ERROR;
1418 trimStack(fun->stack);
1419 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
1420 return BR_ERROR;
1421 trimStack(fun->stack);
1422 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1423 return BR_ERROR;
1424 trimStack(fun->stack);
1425 if (g_sludge->_peopleMan->addPerson(x, y, objectNumber, p))
1426 return BR_CONTINUE;
1427 return BR_ERROR;
1428 }
1429
builtIn(hideCharacter)1430 builtIn(hideCharacter) {
1431 UNUSEDALL
1432 int objectNumber;
1433 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1434 return BR_ERROR;
1435 trimStack(fun->stack);
1436 g_sludge->_peopleMan->setShown(false, objectNumber);
1437 return BR_CONTINUE;
1438 }
1439
builtIn(showCharacter)1440 builtIn(showCharacter) {
1441 UNUSEDALL
1442 int objectNumber;
1443 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1444 return BR_ERROR;
1445 trimStack(fun->stack);
1446 g_sludge->_peopleMan->setShown(true, objectNumber);
1447 return BR_CONTINUE;
1448 }
1449
builtIn(removeAllCharacters)1450 builtIn(removeAllCharacters) {
1451 UNUSEDALL
1452 killSpeechTimers();
1453 g_sludge->_peopleMan->killMostPeople();
1454 return BR_CONTINUE;
1455 }
1456
builtIn(setCharacterDrawMode)1457 builtIn(setCharacterDrawMode) {
1458 UNUSEDALL
1459 int obj, di;
1460 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1461 return BR_ERROR;
1462 trimStack(fun->stack);
1463 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1464 return BR_ERROR;
1465 trimStack(fun->stack);
1466 g_sludge->_peopleMan->setDrawMode(di, obj);
1467 return BR_CONTINUE;
1468 }
builtIn(setCharacterTransparency)1469 builtIn(setCharacterTransparency) {
1470 UNUSEDALL
1471 int obj, x;
1472 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
1473 return BR_ERROR;
1474 trimStack(fun->stack);
1475 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1476 return BR_ERROR;
1477 trimStack(fun->stack);
1478 g_sludge->_peopleMan->setPersonTransparency(obj, x);
1479 return BR_CONTINUE;
1480 }
builtIn(setCharacterColourise)1481 builtIn(setCharacterColourise) {
1482 UNUSEDALL
1483 int obj, r, g, b, mix;
1484 if (!fun->stack->thisVar.getValueType(mix, SVT_INT))
1485 return BR_ERROR;
1486 trimStack(fun->stack);
1487 if (!fun->stack->thisVar.getValueType(b, SVT_INT))
1488 return BR_ERROR;
1489 trimStack(fun->stack);
1490 if (!fun->stack->thisVar.getValueType(g, SVT_INT))
1491 return BR_ERROR;
1492 trimStack(fun->stack);
1493 if (!fun->stack->thisVar.getValueType(r, SVT_INT))
1494 return BR_ERROR;
1495 trimStack(fun->stack);
1496 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1497 return BR_ERROR;
1498 trimStack(fun->stack);
1499 g_sludge->_peopleMan->setPersonColourise(obj, r, g, b, mix);
1500 return BR_CONTINUE;
1501 }
1502
builtIn(setScale)1503 builtIn(setScale) {
1504 UNUSEDALL
1505 int val1, val2;
1506 if (!fun->stack->thisVar.getValueType(val2, SVT_INT))
1507 return BR_ERROR;
1508 trimStack(fun->stack);
1509 if (!fun->stack->thisVar.getValueType(val1, SVT_INT))
1510 return BR_ERROR;
1511 trimStack(fun->stack);
1512 g_sludge->_peopleMan->setScale((int16)val1, (int16)val2);
1513 return BR_CONTINUE;
1514 }
1515
builtIn(stopCharacter)1516 builtIn(stopCharacter) {
1517 UNUSEDALL
1518 int obj;
1519 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1520 return BR_ERROR;
1521 trimStack(fun->stack);
1522
1523 // Return value
1524 fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->stopPerson(obj));
1525 return BR_CONTINUE;
1526 }
1527
builtIn(pasteCharacter)1528 builtIn(pasteCharacter) {
1529 UNUSEDALL
1530 int obj;
1531 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1532 return BR_ERROR;
1533 trimStack(fun->stack);
1534
1535 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(obj);
1536 if (thisPerson) {
1537 PersonaAnimation *myAnim;
1538 myAnim = thisPerson->myAnim;
1539 if (myAnim != thisPerson->lastUsedAnim) {
1540 thisPerson->lastUsedAnim = myAnim;
1541 thisPerson->frameNum = 0;
1542 thisPerson->frameTick = myAnim->frames[0].howMany;
1543 }
1544
1545 int fNum = myAnim->frames[thisPerson->frameNum].frameNum;
1546 g_sludge->_gfxMan->fixScaleSprite(thisPerson->x, thisPerson->y, myAnim->theSprites->bank.sprites[ABS(fNum)], myAnim->theSprites->bank.myPalette, thisPerson, 0, 0, fNum < 0);
1547 fun->reg.setVariable(SVT_INT, 1);
1548 } else {
1549 fun->reg.setVariable(SVT_INT, 0);
1550 }
1551 return BR_CONTINUE;
1552 }
1553
builtIn(animate)1554 builtIn(animate) {
1555 UNUSEDALL
1556 int obj;
1557 PersonaAnimation *pp = fun->stack->thisVar.getAnimationFromVar();
1558 if (pp == NULL)
1559 return BR_ERROR;
1560 trimStack(fun->stack);
1561 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1562 return BR_ERROR;
1563 trimStack(fun->stack);
1564 g_sludge->_peopleMan->animatePerson(obj, pp);
1565 fun->reg.setVariable(SVT_INT, pp->getTotalTime());
1566 return BR_CONTINUE;
1567 }
1568
builtIn(setCostume)1569 builtIn(setCostume) {
1570 UNUSEDALL
1571 int obj;
1572 Persona *pp = fun->stack->thisVar.getCostumeFromVar();
1573 if (pp == NULL)
1574 return BR_ERROR;
1575 trimStack(fun->stack);
1576 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1577 return BR_ERROR;
1578 trimStack(fun->stack);
1579 g_sludge->_peopleMan->animatePerson(obj, pp);
1580 return BR_CONTINUE;
1581 }
1582
builtIn(floatCharacter)1583 builtIn(floatCharacter) {
1584 UNUSEDALL
1585 int obj, di;
1586 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1587 return BR_ERROR;
1588 trimStack(fun->stack);
1589 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1590 return BR_ERROR;
1591 trimStack(fun->stack);
1592 fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->floatCharacter(di, obj));
1593 return BR_CONTINUE;
1594 }
1595
builtIn(setCharacterWalkSpeed)1596 builtIn(setCharacterWalkSpeed) {
1597 UNUSEDALL
1598 int obj, di;
1599 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1600 return BR_ERROR;
1601 trimStack(fun->stack);
1602 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1603 return BR_ERROR;
1604 trimStack(fun->stack);
1605 fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->setCharacterWalkSpeed(di, obj));
1606 return BR_CONTINUE;
1607 }
1608
builtIn(turnCharacter)1609 builtIn(turnCharacter) {
1610 UNUSEDALL
1611 int obj, di;
1612 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1613 return BR_ERROR;
1614 trimStack(fun->stack);
1615 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1616 return BR_ERROR;
1617 trimStack(fun->stack);
1618 fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->turnPersonToFace(obj, di));
1619 return BR_CONTINUE;
1620 }
1621
builtIn(setCharacterExtra)1622 builtIn(setCharacterExtra) {
1623 UNUSEDALL
1624 int obj, di;
1625 if (!fun->stack->thisVar.getValueType(di, SVT_INT))
1626 return BR_ERROR;
1627 trimStack(fun->stack);
1628 if (!fun->stack->thisVar.getValueType(obj, SVT_OBJTYPE))
1629 return BR_ERROR;
1630 trimStack(fun->stack);
1631 fun->reg.setVariable(SVT_INT, g_sludge->_peopleMan->setPersonExtra(obj, di));
1632 return BR_CONTINUE;
1633 }
1634
builtIn(removeCharacter)1635 builtIn(removeCharacter) {
1636 UNUSEDALL
1637 int objectNumber;
1638 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1639 return BR_ERROR;
1640 trimStack(fun->stack);
1641 g_sludge->_peopleMan->removeOneCharacter(objectNumber);
1642 return BR_CONTINUE;
1643 }
1644
moveChr(int numParams,LoadedFunction * fun,bool force,bool immediate)1645 static BuiltReturn moveChr(int numParams, LoadedFunction *fun, bool force, bool immediate) {
1646 switch (numParams) {
1647 case 3: {
1648 int x, y, objectNumber;
1649
1650 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
1651 return BR_ERROR;
1652 trimStack(fun->stack);
1653 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
1654 return BR_ERROR;
1655 trimStack(fun->stack);
1656 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1657 return BR_ERROR;
1658 trimStack(fun->stack);
1659
1660 if (force) {
1661 if (g_sludge->_peopleMan->forceWalkingPerson(x, y, objectNumber, fun, -1))
1662 return BR_PAUSE;
1663 } else if (immediate) {
1664 g_sludge->_peopleMan->jumpPerson(x, y, objectNumber);
1665 } else {
1666 if (g_sludge->_peopleMan->makeWalkingPerson(x, y, objectNumber, fun, -1))
1667 return BR_PAUSE;
1668 }
1669 return BR_CONTINUE;
1670 }
1671
1672 case 2: {
1673 int toObj, objectNumber;
1674 ScreenRegion*reggie;
1675
1676 if (!fun->stack->thisVar.getValueType(toObj, SVT_OBJTYPE))
1677 return BR_ERROR;
1678 trimStack(fun->stack);
1679 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1680 return BR_ERROR;
1681 trimStack(fun->stack);
1682 reggie = g_sludge->_regionMan->getRegionForObject(toObj);
1683 if (reggie == NULL)
1684 return BR_CONTINUE;
1685
1686 if (force) {
1687 if (g_sludge->_peopleMan->forceWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di))
1688 return BR_PAUSE;
1689 } else if (immediate) {
1690 g_sludge->_peopleMan->jumpPerson(reggie->sX, reggie->sY, objectNumber);
1691 } else {
1692 if (g_sludge->_peopleMan->makeWalkingPerson(reggie->sX, reggie->sY, objectNumber, fun, reggie->di))
1693 return BR_PAUSE;
1694 }
1695 return BR_CONTINUE;
1696 }
1697
1698 default:
1699 fatal("Built-in function must have either 2 or 3 parameters.");
1700 return BR_ERROR;
1701 }
1702 }
1703
builtIn(moveCharacter)1704 builtIn(moveCharacter) {
1705 UNUSEDALL
1706 return moveChr(numParams, fun, false, false);
1707 }
1708
builtIn(forceCharacter)1709 builtIn(forceCharacter) {
1710 UNUSEDALL
1711 return moveChr(numParams, fun, true, false);
1712 }
1713
builtIn(jumpCharacter)1714 builtIn(jumpCharacter) {
1715 UNUSEDALL
1716 return moveChr(numParams, fun, false, true);
1717 }
1718
builtIn(clearStatus)1719 builtIn(clearStatus) {
1720 UNUSEDALL
1721 g_sludge->_statusBar->clear();
1722 return BR_CONTINUE;
1723 }
1724
builtIn(removeLastStatus)1725 builtIn(removeLastStatus) {
1726 UNUSEDALL
1727 g_sludge->_statusBar->killLastStatus();
1728 return BR_CONTINUE;
1729 }
1730
builtIn(addStatus)1731 builtIn(addStatus) {
1732 UNUSEDALL
1733 g_sludge->_statusBar->addStatusBar();
1734 return BR_CONTINUE;
1735 }
1736
builtIn(statusText)1737 builtIn(statusText) {
1738 UNUSEDALL
1739 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
1740 trimStack(fun->stack);
1741 g_sludge->_statusBar->set(newText);
1742 return BR_CONTINUE;
1743 }
1744
builtIn(lightStatus)1745 builtIn(lightStatus) {
1746 UNUSEDALL
1747 int val;
1748 if (!fun->stack->thisVar.getValueType(val, SVT_INT))
1749 return BR_ERROR;
1750 trimStack(fun->stack);
1751 g_sludge->_statusBar->setLitStatus(val);
1752 return BR_CONTINUE;
1753 }
1754
builtIn(positionStatus)1755 builtIn(positionStatus) {
1756 UNUSEDALL
1757 int x, y;
1758 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
1759 return BR_ERROR;
1760 trimStack(fun->stack);
1761 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
1762 return BR_ERROR;
1763 trimStack(fun->stack);
1764 g_sludge->_statusBar->positionStatus(x, y);
1765 return BR_CONTINUE;
1766 }
1767
builtIn(alignStatus)1768 builtIn(alignStatus) {
1769 UNUSEDALL
1770 int val;
1771 if (!fun->stack->thisVar.getValueType(val, SVT_INT))
1772 return BR_ERROR;
1773 trimStack(fun->stack);
1774 g_sludge->_statusBar->setAlignStatus(val);
1775 return BR_CONTINUE;
1776 }
1777
getFuncNumForCallback(int numParams,LoadedFunction * fun,int & functionNum)1778 static bool getFuncNumForCallback(int numParams, LoadedFunction *fun, int &functionNum) {
1779 switch (numParams) {
1780 case 0:
1781 functionNum = 0;
1782 break;
1783
1784 case 1:
1785 if (!fun->stack->thisVar.getValueType(functionNum, SVT_FUNC))
1786 return false;
1787 trimStack(fun->stack);
1788 break;
1789
1790 default:
1791 fatal("Too many parameters.");
1792 return false;
1793 }
1794 return true;
1795 }
1796
builtIn(onLeftMouse)1797 builtIn(onLeftMouse) {
1798 UNUSEDALL
1799 int functionNum;
1800 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1801 g_sludge->_evtMan->setEventFunction(kLeftMouse, functionNum);
1802 return BR_CONTINUE;
1803 }
1804 return BR_ERROR;
1805 }
1806
builtIn(onLeftMouseUp)1807 builtIn(onLeftMouseUp) {
1808 UNUSEDALL
1809 int functionNum;
1810 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1811 g_sludge->_evtMan->setEventFunction(kLeftMouseUp, functionNum);
1812 return BR_CONTINUE;
1813 }
1814 return BR_ERROR;
1815 }
1816
builtIn(onRightMouse)1817 builtIn(onRightMouse) {
1818 UNUSEDALL
1819 int functionNum;
1820 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1821 g_sludge->_evtMan->setEventFunction(kRightMouse, functionNum);
1822 return BR_CONTINUE;
1823 }
1824 return BR_ERROR;
1825 }
1826
builtIn(onRightMouseUp)1827 builtIn(onRightMouseUp) {
1828 UNUSEDALL
1829 int functionNum;
1830 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1831 g_sludge->_evtMan->setEventFunction(kRightMouseUp, functionNum);
1832 return BR_CONTINUE;
1833 }
1834 return BR_ERROR;
1835 }
1836
builtIn(onFocusChange)1837 builtIn(onFocusChange) {
1838 UNUSEDALL
1839 int functionNum;
1840 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1841 g_sludge->_evtMan->setEventFunction(kFocus, functionNum);
1842 return BR_CONTINUE;
1843 }
1844 return BR_ERROR;
1845 }
1846
builtIn(onMoveMouse)1847 builtIn(onMoveMouse) {
1848 UNUSEDALL
1849 int functionNum;
1850 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1851 g_sludge->_evtMan->setEventFunction(kMoveMouse, functionNum);
1852 return BR_CONTINUE;
1853 }
1854 return BR_ERROR;
1855 }
1856
builtIn(onKeyboard)1857 builtIn(onKeyboard) {
1858 UNUSEDALL
1859 int functionNum;
1860 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1861 g_sludge->_evtMan->setEventFunction(kSpace, functionNum);
1862 return BR_CONTINUE;
1863 }
1864 return BR_ERROR;
1865 }
1866
builtIn(spawnSub)1867 builtIn(spawnSub) {
1868 UNUSEDALL
1869 int functionNum;
1870 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1871 if (!startNewFunctionNum(functionNum, 0, NULL, noStack))
1872 return BR_ERROR;
1873 return BR_CONTINUE;
1874 }
1875 return BR_ERROR;
1876 }
1877
builtIn(cancelSub)1878 builtIn(cancelSub) {
1879 UNUSEDALL
1880 int functionNum;
1881 if (getFuncNumForCallback(numParams, fun, functionNum)) {
1882 bool killedMyself;
1883 cancelAFunction(functionNum, fun, killedMyself);
1884 if (killedMyself) {
1885 abortFunction(fun);
1886 return BR_ALREADY_GONE;
1887 }
1888 return BR_CONTINUE;
1889 }
1890 return BR_ERROR;
1891 }
1892
builtIn(stringWidth)1893 builtIn(stringWidth) {
1894 UNUSEDALL
1895 Common::String theText = fun->stack->thisVar.getTextFromAnyVar();
1896 trimStack(fun->stack);
1897
1898 // Return value
1899 fun->reg.setVariable(SVT_INT, g_sludge->_txtMan->stringWidth(theText));
1900 return BR_CONTINUE;
1901 }
1902
builtIn(hardScroll)1903 builtIn(hardScroll) {
1904 UNUSEDALL
1905 int v;
1906 if (!fun->stack->thisVar.getValueType(v, SVT_INT))
1907 return BR_ERROR;
1908 trimStack(fun->stack);
1909 g_sludge->_gfxMan->hardScroll(v);
1910 return BR_CONTINUE;
1911 }
1912
builtIn(isScreenRegion)1913 builtIn(isScreenRegion) {
1914 UNUSEDALL
1915 int objectNumber;
1916 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1917 return BR_ERROR;
1918 trimStack(fun->stack);
1919 fun->reg.setVariable(SVT_INT, g_sludge->_regionMan->getRegionForObject(objectNumber) != NULL);
1920 return BR_CONTINUE;
1921 }
1922
builtIn(setSpeechSpeed)1923 builtIn(setSpeechSpeed) {
1924 UNUSEDALL
1925 int number;
1926 if (!fun->stack->thisVar.getValueType(number, SVT_INT))
1927 return BR_ERROR;
1928 trimStack(fun->stack);
1929 g_sludge->_speechMan->setSpeechSpeed(number * 0.01);
1930 fun->reg.setVariable(SVT_INT, 1);
1931 return BR_CONTINUE;
1932 }
1933
builtIn(setFontSpacing)1934 builtIn(setFontSpacing) {
1935 UNUSEDALL
1936 int fontSpaceI;
1937 if (!fun->stack->thisVar.getValueType(fontSpaceI, SVT_INT))
1938 return BR_ERROR;
1939 g_sludge->_txtMan->setFontSpace(fontSpaceI);
1940 trimStack(fun->stack);
1941 fun->reg.setVariable(SVT_INT, 1);
1942 return BR_CONTINUE;
1943 }
1944
builtIn(transitionLevel)1945 builtIn(transitionLevel) {
1946 UNUSEDALL
1947 int brightnessLevel;
1948 if (!fun->stack->thisVar.getValueType(brightnessLevel, SVT_INT))
1949 return BR_ERROR;
1950 trimStack(fun->stack);
1951
1952 g_sludge->_gfxMan->setBrightnessLevel(brightnessLevel);
1953
1954 fun->reg.setVariable(SVT_INT, 1);
1955 return BR_CONTINUE;
1956 }
1957
builtIn(captureAllKeys)1958 builtIn(captureAllKeys) {
1959 UNUSEDALL
1960 // This built-in function doesn't have any effect any more, we capture all keys by default
1961 bool captureAllKeysDeprecated = fun->stack->thisVar.getBoolean();
1962 trimStack(fun->stack);
1963 fun->reg.setVariable(SVT_INT, captureAllKeysDeprecated);
1964 return BR_CONTINUE;
1965 }
1966
builtIn(spinCharacter)1967 builtIn(spinCharacter) {
1968 UNUSEDALL
1969 int number, objectNumber;
1970 if (!fun->stack->thisVar.getValueType(number, SVT_INT))
1971 return BR_ERROR;
1972 trimStack(fun->stack);
1973 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1974 return BR_ERROR;
1975 trimStack(fun->stack);
1976
1977 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber);
1978 if (thisPerson) {
1979 thisPerson->wantAngle = number;
1980 thisPerson->spinning = true;
1981 thisPerson->continueAfterWalking = fun;
1982 fun->reg.setVariable(SVT_INT, 1);
1983 return BR_PAUSE;
1984 } else {
1985 fun->reg.setVariable(SVT_INT, 0);
1986 return BR_CONTINUE;
1987 }
1988 }
1989
builtIn(getCharacterDirection)1990 builtIn(getCharacterDirection) {
1991 UNUSEDALL
1992 int objectNumber;
1993 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
1994 return BR_ERROR;
1995 trimStack(fun->stack);
1996 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber);
1997 if (thisPerson) {
1998 fun->reg.setVariable(SVT_INT, thisPerson->direction);
1999 } else {
2000 fun->reg.setVariable(SVT_INT, 0);
2001 }
2002 return BR_CONTINUE;
2003 }
2004
builtIn(isCharacter)2005 builtIn(isCharacter) {
2006 UNUSEDALL
2007 int objectNumber;
2008 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
2009 return BR_ERROR;
2010 trimStack(fun->stack);
2011 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber);
2012 fun->reg.setVariable(SVT_INT, thisPerson != NULL);
2013 return BR_CONTINUE;
2014 }
2015
builtIn(normalCharacter)2016 builtIn(normalCharacter) {
2017 UNUSEDALL
2018 int objectNumber;
2019 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
2020 return BR_ERROR;
2021 trimStack(fun->stack);
2022 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber);
2023 if (thisPerson) {
2024 thisPerson->myAnim = thisPerson->myPersona->animation[thisPerson->direction];
2025 fun->reg.setVariable(SVT_INT, 1);
2026 } else {
2027 fun->reg.setVariable(SVT_INT, 0);
2028 }
2029 return BR_CONTINUE;
2030 }
2031
builtIn(isMoving)2032 builtIn(isMoving) {
2033 UNUSEDALL
2034 int objectNumber;
2035 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
2036 return BR_ERROR;
2037 trimStack(fun->stack);
2038 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objectNumber);
2039 if (thisPerson) {
2040 fun->reg.setVariable(SVT_INT, thisPerson->walking);
2041 } else {
2042 fun->reg.setVariable(SVT_INT, 0);
2043 }
2044 return BR_CONTINUE;
2045 }
2046
builtIn(fetchEvent)2047 builtIn(fetchEvent) {
2048 UNUSEDALL
2049 int obj1, obj2;
2050 if (!fun->stack->thisVar.getValueType(obj2, SVT_OBJTYPE))
2051 return BR_ERROR;
2052 trimStack(fun->stack);
2053 if (!fun->stack->thisVar.getValueType(obj1, SVT_OBJTYPE))
2054 return BR_ERROR;
2055 trimStack(fun->stack);
2056
2057 int fNum = g_sludge->_objMan->getCombinationFunction(obj1, obj2);
2058
2059 // Return value
2060 if (fNum) {
2061 fun->reg.setVariable(SVT_FUNC, fNum);
2062 } else {
2063 fun->reg.setVariable(SVT_INT, 0);
2064 }
2065 return BR_CONTINUE;
2066 }
2067
builtIn(deleteFile)2068 builtIn(deleteFile) {
2069 UNUSEDALL
2070
2071 Common::String namNormal = fun->stack->thisVar.getTextFromAnyVar();
2072 trimStack(fun->stack);
2073 Common::String nam = encodeFilename(namNormal);
2074 namNormal.clear();
2075 if (failSecurityCheck(nam))
2076 return BR_ERROR;
2077
2078 fun->reg.setVariable(SVT_INT, !g_system->getSavefileManager()->removeSavefile(nam));
2079
2080 return BR_CONTINUE;
2081 }
2082
builtIn(renameFile)2083 builtIn(renameFile) {
2084 UNUSEDALL
2085 Common::String temp;
2086
2087 temp.clear();
2088 temp = fun->stack->thisVar.getTextFromAnyVar();
2089 Common::String newnam = encodeFilename(temp);
2090 trimStack(fun->stack);
2091 if (failSecurityCheck(newnam))
2092 return BR_ERROR;
2093 temp.clear();
2094
2095 temp = fun->stack->thisVar.getTextFromAnyVar();
2096 Common::String nam = encodeFilename(temp);
2097 trimStack(fun->stack);
2098 if (failSecurityCheck(nam))
2099 return BR_ERROR;
2100
2101 fun->reg.setVariable(SVT_INT, !g_system->getSavefileManager()->renameSavefile(nam, newnam));
2102
2103 return BR_CONTINUE;
2104 }
2105
builtIn(cacheSound)2106 builtIn(cacheSound) {
2107 UNUSEDALL
2108 int fileNumber;
2109 if (!fun->stack->thisVar.getValueType(fileNumber, SVT_FILE))
2110 return BR_ERROR;
2111 trimStack(fun->stack);
2112 if (g_sludge->_soundMan->cacheSound(fileNumber) == -1)
2113 return BR_ERROR;
2114 return BR_CONTINUE;
2115 }
2116
builtIn(burnString)2117 builtIn(burnString) {
2118 UNUSEDALL
2119 Common::String newText = fun->stack->thisVar.getTextFromAnyVar();
2120 trimStack(fun->stack);
2121 int y, x;
2122 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
2123 return BR_ERROR;
2124 trimStack(fun->stack);
2125 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
2126 return BR_ERROR;
2127 trimStack(fun->stack);
2128 if (x == IN_THE_CENTRE)
2129 x = g_sludge->_gfxMan->getCenterX(g_sludge->_txtMan->stringWidth(newText));
2130 g_sludge->_txtMan->burnStringToBackdrop(newText, x, y);
2131 return BR_CONTINUE;
2132 }
2133
builtIn(setCharacterSpinSpeed)2134 builtIn(setCharacterSpinSpeed) {
2135 UNUSEDALL
2136 int speed, who;
2137 if (!fun->stack->thisVar.getValueType(speed, SVT_INT))
2138 return BR_ERROR;
2139 trimStack(fun->stack);
2140 if (!fun->stack->thisVar.getValueType(who, SVT_OBJTYPE))
2141 return BR_ERROR;
2142 trimStack(fun->stack);
2143
2144 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(who);
2145
2146 if (thisPerson) {
2147 thisPerson->spinSpeed = speed;
2148 fun->reg.setVariable(SVT_INT, 1);
2149 } else {
2150 fun->reg.setVariable(SVT_INT, 0);
2151 }
2152 return BR_CONTINUE;
2153 }
2154
builtIn(setCharacterAngleOffset)2155 builtIn(setCharacterAngleOffset) {
2156 UNUSEDALL
2157 int angle, who;
2158 if (!fun->stack->thisVar.getValueType(angle, SVT_INT))
2159 return BR_ERROR;
2160 trimStack(fun->stack);
2161 if (!fun->stack->thisVar.getValueType(who, SVT_OBJTYPE))
2162 return BR_ERROR;
2163 trimStack(fun->stack);
2164
2165 OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(who);
2166
2167 if (thisPerson) {
2168 thisPerson->angleOffset = angle;
2169 fun->reg.setVariable(SVT_INT, 1);
2170 } else {
2171 fun->reg.setVariable(SVT_INT, 0);
2172 }
2173 return BR_CONTINUE;
2174 }
2175
builtIn(transitionMode)2176 builtIn(transitionMode) {
2177 UNUSEDALL
2178 int n;
2179 if (!fun->stack->thisVar.getValueType(n, SVT_INT))
2180 return BR_ERROR;
2181 g_sludge->_gfxMan->setFadeMode(n);
2182 trimStack(fun->stack);
2183 fun->reg.setVariable(SVT_INT, 1);
2184 return BR_CONTINUE;
2185 }
2186
2187 // Removed function - does nothing
builtIn(_rem_updateDisplay)2188 builtIn(_rem_updateDisplay) {
2189 UNUSEDALL
2190 trimStack(fun->stack);
2191 fun->reg.setVariable(SVT_INT, true);
2192 return BR_CONTINUE;
2193 }
2194
builtIn(getSoundCache)2195 builtIn(getSoundCache) {
2196 UNUSEDALL
2197 fun->reg.varType = SVT_STACK;
2198 fun->reg.varData.theStack = new StackHandler;
2199 if (!checkNew(fun->reg.varData.theStack))
2200 return BR_ERROR;
2201 fun->reg.varData.theStack->first = NULL;
2202 fun->reg.varData.theStack->last = NULL;
2203 fun->reg.varData.theStack->timesUsed = 1;
2204 if (!g_sludge->_soundMan->getSoundCacheStack(fun->reg.varData.theStack))
2205 return BR_ERROR;
2206 return BR_CONTINUE;
2207 }
2208
builtIn(saveCustomData)2209 builtIn(saveCustomData) {
2210 UNUSEDALL
2211 // saveCustomData (thisStack, fileName);
2212 Common::String fileNameB = fun->stack->thisVar.getTextFromAnyVar();
2213
2214 Common::String fileName = encodeFilename(fileNameB);
2215
2216 if (failSecurityCheck(fileName))
2217 return BR_ERROR;
2218 trimStack(fun->stack);
2219
2220 if (fun->stack->thisVar.varType != SVT_STACK) {
2221 fatal("First parameter isn't a stack");
2222 return BR_ERROR;
2223 }
2224 if (!CustomSaveHelper::stackToFile(fileName, fun->stack->thisVar))
2225 return BR_ERROR;
2226 trimStack(fun->stack);
2227 return BR_CONTINUE;
2228 }
2229
builtIn(loadCustomData)2230 builtIn(loadCustomData) {
2231 UNUSEDALL
2232
2233 Common::String newTextA = fun->stack->thisVar.getTextFromAnyVar();
2234
2235 Common::String newText = encodeFilename(newTextA);
2236
2237 if (failSecurityCheck(newText))
2238 return BR_ERROR;
2239 trimStack(fun->stack);
2240
2241 fun->reg.unlinkVar();
2242 fun->reg.varType = SVT_STACK;
2243 fun->reg.varData.theStack = new StackHandler;
2244 if (!checkNew(fun->reg.varData.theStack))
2245 return BR_ERROR;
2246 fun->reg.varData.theStack->first = NULL;
2247 fun->reg.varData.theStack->last = NULL;
2248 fun->reg.varData.theStack->timesUsed = 1;
2249 if (!CustomSaveHelper::fileToStack(newText, fun->reg.varData.theStack))
2250 return BR_ERROR;
2251 return BR_CONTINUE;
2252 }
2253
builtIn(setCustomEncoding)2254 builtIn(setCustomEncoding) {
2255 UNUSEDALL
2256 int n;
2257 if (!fun->stack->thisVar.getValueType(n, SVT_INT))
2258 return BR_ERROR;
2259 CustomSaveHelper::_saveEncoding = n;
2260 trimStack(fun->stack);
2261 fun->reg.setVariable(SVT_INT, 1);
2262 return BR_CONTINUE;
2263 }
2264
builtIn(freeSound)2265 builtIn(freeSound) {
2266 UNUSEDALL
2267 int v;
2268 if (!fun->stack->thisVar.getValueType(v, SVT_FILE))
2269 return BR_ERROR;
2270 trimStack(fun->stack);
2271 g_sludge->_soundMan->huntKillFreeSound(v);
2272 return BR_CONTINUE;
2273 }
2274
builtIn(parallaxAdd)2275 builtIn(parallaxAdd) {
2276 UNUSEDALL
2277 if (g_sludge->_gfxMan->isFrozen()) {
2278 fatal("Can't set background parallax image while frozen");
2279 return BR_ERROR;
2280 } else {
2281 int wrapX, wrapY, v;
2282 if (!fun->stack->thisVar.getValueType(wrapY, SVT_INT))
2283 return BR_ERROR;
2284 trimStack(fun->stack);
2285 if (!fun->stack->thisVar.getValueType(wrapX, SVT_INT))
2286 return BR_ERROR;
2287 trimStack(fun->stack);
2288 if (!fun->stack->thisVar.getValueType(v, SVT_FILE))
2289 return BR_ERROR;
2290 trimStack(fun->stack);
2291
2292 if (!g_sludge->_gfxMan->loadParallax(v, wrapX, wrapY))
2293 return BR_ERROR;
2294 fun->reg.setVariable(SVT_INT, 1);
2295 }
2296 return BR_CONTINUE;
2297 }
2298
builtIn(parallaxClear)2299 builtIn(parallaxClear) {
2300 UNUSEDALL
2301 g_sludge->_gfxMan->killParallax();
2302 fun->reg.setVariable(SVT_INT, 1);
2303 return BR_CONTINUE;
2304 }
2305
builtIn(getPixelColour)2306 builtIn(getPixelColour) {
2307 UNUSEDALL
2308 int x, y;
2309 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
2310 return BR_ERROR;
2311 trimStack(fun->stack);
2312 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
2313 return BR_ERROR;
2314 trimStack(fun->stack);
2315
2316 fun->reg.unlinkVar();
2317 fun->reg.varType = SVT_STACK;
2318 fun->reg.varData.theStack = new StackHandler;
2319 if (!checkNew(fun->reg.varData.theStack))
2320 return BR_ERROR;
2321 fun->reg.varData.theStack->first = NULL;
2322 fun->reg.varData.theStack->last = NULL;
2323 fun->reg.varData.theStack->timesUsed = 1;
2324 if (!g_sludge->_gfxMan->getRGBIntoStack(x, y, fun->reg.varData.theStack))
2325 return BR_ERROR;
2326
2327 return BR_CONTINUE;
2328 }
2329
builtIn(makeFastArray)2330 builtIn(makeFastArray) {
2331 UNUSEDALL
2332 switch (fun->stack->thisVar.varType) {
2333 case SVT_STACK: {
2334 bool success = fun->reg.makeFastArrayFromStack(fun->stack->thisVar.varData.theStack);
2335 trimStack(fun->stack);
2336 return success ? BR_CONTINUE : BR_ERROR;
2337 }
2338 break;
2339
2340 case SVT_INT: {
2341 int i = fun->stack->thisVar.varData.intValue;
2342 trimStack(fun->stack);
2343 return fun->reg.makeFastArraySize(i) ? BR_CONTINUE : BR_ERROR;
2344 }
2345 break;
2346
2347 default:
2348 break;
2349 }
2350 fatal("Parameter must be a number or a stack.");
2351 return BR_ERROR;
2352 }
2353
builtIn(getCharacterScale)2354 builtIn(getCharacterScale) {
2355 UNUSEDALL
2356 int objectNumber;
2357 if (!fun->stack->thisVar.getValueType(objectNumber, SVT_OBJTYPE))
2358 return BR_ERROR;
2359 trimStack(fun->stack);
2360
2361 OnScreenPerson *pers = g_sludge->_peopleMan->findPerson(objectNumber);
2362 if (pers) {
2363 fun->reg.setVariable(SVT_INT, pers->scale * 100);
2364 } else {
2365 fun->reg.setVariable(SVT_INT, 0);
2366 }
2367 return BR_CONTINUE;
2368 }
2369
builtIn(getLanguageID)2370 builtIn(getLanguageID) {
2371 UNUSEDALL
2372 fun->reg.setVariable(SVT_INT, g_sludge->getLanguageID());
2373 return BR_CONTINUE;
2374 }
2375
2376 // Removed function
builtIn(_rem_launchWith)2377 builtIn(_rem_launchWith) {
2378 UNUSEDALL
2379
2380 trimStack(fun->stack);
2381
2382 // To support some windows only games
2383 Common::String filename = fun->stack->thisVar.getTextFromAnyVar();
2384 trimStack(fun->stack);
2385
2386 if (filename.hasSuffix(".exe")) {
2387 const Common::FSNode gameDataDir(ConfMan.get("path"));
2388 Common::FSList files;
2389 gameDataDir.getChildren(files, Common::FSNode::kListFilesOnly);
2390
2391 if (!files.empty()) {
2392 for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) {
2393 Common::String fileName = file->getName();
2394 fileName.toLowercase();
2395 if (fileName.hasSuffix(".dat") || fileName == "data") {
2396 g_sludge->launchNext = file->getName();
2397 return BR_CONTINUE;
2398 }
2399 }
2400 }
2401 }
2402
2403 g_sludge->launchNext.clear();
2404 fun->reg.setVariable(SVT_INT, false);
2405 return BR_CONTINUE;
2406 }
2407
builtIn(getFramesPerSecond)2408 builtIn(getFramesPerSecond) {
2409 UNUSEDALL
2410 fun->reg.setVariable(SVT_INT, g_sludge->_timer->getLastFps());
2411 return BR_CONTINUE;
2412 }
2413
builtIn(showThumbnail)2414 builtIn(showThumbnail) {
2415 UNUSEDALL
2416 int x, y;
2417 if (!fun->stack->thisVar.getValueType(y, SVT_INT))
2418 return BR_ERROR;
2419 trimStack(fun->stack);
2420 if (!fun->stack->thisVar.getValueType(x, SVT_INT))
2421 return BR_ERROR;
2422 trimStack(fun->stack);
2423
2424 // Encode the name!Encode the name!
2425 Common::String aaaaa = fun->stack->thisVar.getTextFromAnyVar();
2426 trimStack(fun->stack);
2427 Common::String file = encodeFilename(aaaaa);
2428 g_sludge->_gfxMan->showThumbnail(file, x, y);
2429 return BR_CONTINUE;
2430 }
2431
builtIn(setThumbnailSize)2432 builtIn(setThumbnailSize) {
2433 UNUSEDALL
2434 int thumbHeight, thumbWidth;
2435 if (!fun->stack->thisVar.getValueType(thumbHeight, SVT_INT))
2436 return BR_ERROR;
2437 trimStack(fun->stack);
2438 if (!fun->stack->thisVar.getValueType(thumbWidth, SVT_INT))
2439 return BR_ERROR;
2440 trimStack(fun->stack);
2441 if (!g_sludge->_gfxMan->setThumbnailSize(thumbWidth, thumbHeight)) {
2442 Common::String buff = Common::String::format("%i x %i", thumbWidth, thumbWidth);
2443 fatal("Invalid thumbnail size", buff);
2444 return BR_ERROR;
2445 }
2446 return BR_CONTINUE;
2447 }
2448
builtIn(hasFlag)2449 builtIn(hasFlag) {
2450 UNUSEDALL
2451 int objNum, flagIndex;
2452 if (!fun->stack->thisVar.getValueType(flagIndex, SVT_INT))
2453 return BR_ERROR;
2454 trimStack(fun->stack);
2455 if (!fun->stack->thisVar.getValueType(objNum, SVT_OBJTYPE))
2456 return BR_ERROR;
2457 trimStack(fun->stack);
2458 ObjectType *objT = g_sludge->_objMan->findObjectType(objNum);
2459 if (!objT)
2460 return BR_ERROR;
2461 fun->reg.setVariable(SVT_INT, objT->flags & (1 << flagIndex));
2462 return BR_CONTINUE;
2463 }
2464
builtIn(snapshotGrab)2465 builtIn(snapshotGrab) {
2466 UNUSEDALL
2467 return g_sludge->_gfxMan->snapshot() ? BR_CONTINUE : BR_ERROR;
2468 }
2469
builtIn(snapshotClear)2470 builtIn(snapshotClear) {
2471 UNUSEDALL
2472 g_sludge->_gfxMan->nosnapshot();
2473 return BR_CONTINUE;
2474 }
2475
builtIn(bodgeFilenames)2476 builtIn(bodgeFilenames) {
2477 UNUSEDALL
2478 bool lastValue = allowAnyFilename;
2479 allowAnyFilename = fun->stack->thisVar.getBoolean();
2480 trimStack(fun->stack);
2481 fun->reg.setVariable(SVT_INT, lastValue);
2482 return BR_CONTINUE;
2483 }
2484
2485 // Deprecated - does nothing.
builtIn(_rem_registryGetString)2486 builtIn(_rem_registryGetString) {
2487 UNUSEDALL
2488 trimStack(fun->stack);
2489 trimStack(fun->stack);
2490 fun->reg.setVariable(SVT_INT, 0);
2491
2492 return BR_CONTINUE;
2493 }
2494
builtIn(quitWithFatalError)2495 builtIn(quitWithFatalError) {
2496 UNUSEDALL
2497 Common::String mess = fun->stack->thisVar.getTextFromAnyVar();
2498 trimStack(fun->stack);
2499 fatal(mess);
2500 return BR_ERROR;
2501 }
2502
builtIn(_rem_setCharacterAA)2503 builtIn(_rem_setCharacterAA) {
2504 UNUSEDALL
2505
2506 trimStack(fun->stack);
2507 trimStack(fun->stack);
2508 trimStack(fun->stack);
2509 trimStack(fun->stack);
2510
2511 return BR_CONTINUE;
2512 }
2513
builtIn(_rem_setMaximumAA)2514 builtIn(_rem_setMaximumAA) {
2515 UNUSEDALL
2516
2517 trimStack(fun->stack);
2518 trimStack(fun->stack);
2519 trimStack(fun->stack);
2520
2521 return BR_CONTINUE;
2522
2523 }
2524
builtIn(setBackgroundEffect)2525 builtIn(setBackgroundEffect) {
2526 UNUSEDALL
2527 bool done = g_sludge->_gfxMan->blur_createSettings(numParams, fun->stack);
2528 fun->reg.setVariable(SVT_INT, done ? 1 : 0);
2529 return BR_CONTINUE;
2530 }
2531
builtIn(doBackgroundEffect)2532 builtIn(doBackgroundEffect) {
2533 UNUSEDALL
2534 bool done = g_sludge->_gfxMan->blurScreen();
2535 fun->reg.setVariable(SVT_INT, done ? 1 : 0);
2536 return BR_CONTINUE;
2537 }
2538
2539 #pragma mark -
2540 #pragma mark Other functions
2541
2542 //-------------------------------------
2543
2544 } // End of namespace Sludge
2545
2546 #include "functionlist.h"
2547
2548 namespace Sludge {
2549
callBuiltIn(int whichFunc,int numParams,LoadedFunction * fun)2550 BuiltReturn callBuiltIn(int whichFunc, int numParams, LoadedFunction *fun) {
2551 if (numBIFNames) {
2552 setFatalInfo((fun->originalNumber < numUserFunc) ? allUserFunc[fun->originalNumber] : "Unknown user function",
2553 (whichFunc < numBIFNames) ? allBIFNames[whichFunc] : "Unknown built-in function");
2554 }
2555
2556 if (whichFunc < NUM_FUNCS) {
2557 if (builtInFunctionArray[whichFunc].paramNum != -1) {
2558 if (builtInFunctionArray[whichFunc].paramNum != numParams) {
2559 Common::String buff = Common::String::format("Built in function must have %i parameter%s", builtInFunctionArray[whichFunc].paramNum, (builtInFunctionArray[whichFunc].paramNum == 1) ? "" : "s");
2560 Common::String msg = buff;
2561 fatal(msg);
2562 return BR_ERROR;
2563 }
2564 }
2565
2566 if (builtInFunctionArray[whichFunc].func) {
2567 debugC(3, kSludgeDebugBuiltin,
2568 "Run built-in function %i : %s",
2569 whichFunc, (whichFunc < numBIFNames) ? allBIFNames[whichFunc].c_str() : "Unknown");
2570 return builtInFunctionArray[whichFunc].func(numParams, fun);
2571 }
2572 }
2573
2574 fatal("Unknown / unimplemented built-in function.");
2575 return BR_ERROR;
2576 }
2577
getBuiltInName(int num)2578 const char *getBuiltInName(int num) {
2579 if (num >= NUM_FUNCS)
2580 error("getBuiltInName: incorrect builtin number. %d > %d", num, NUM_FUNCS);
2581
2582 return builtInFunctionArray[num].name;
2583 }
2584
2585 } // End of namespace Sludge
2586