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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25
26 #include "common/system.h"
27 #include "common/file.h"
28 #include "common/textconsole.h"
29
30 #include "sword2/sword2.h"
31 #include "sword2/defs.h"
32 #include "sword2/header.h"
33 #include "sword2/screen.h"
34 #include "sword2/console.h"
35 #include "sword2/interpreter.h"
36 #include "sword2/logic.h"
37 #include "sword2/maketext.h"
38 #include "sword2/mouse.h"
39 #include "sword2/resman.h"
40 #include "sword2/router.h"
41 #include "sword2/sound.h"
42 #include "sword2/animation.h"
43
44 namespace Sword2 {
45
fnTestFunction(int32 * params)46 int32 Logic::fnTestFunction(int32 *params) {
47 // params: 0 address of a flag
48 return IR_CONT;
49 }
50
fnTestFlags(int32 * params)51 int32 Logic::fnTestFlags(int32 *params) {
52 // params: 0 value of flag
53 return IR_CONT;
54 }
55
fnRegisterStartPoint(int32 * params)56 int32 Logic::fnRegisterStartPoint(int32 *params) {
57 // params: 0 id of startup script to call - key
58 // 1 pointer to ascii message
59
60 int32 key = params[0];
61 char *name = (char *)decodePtr(params[1]);
62
63 _vm->registerStartPoint(key, name);
64 return IR_CONT;
65 }
66
fnInitBackground(int32 * params)67 int32 Logic::fnInitBackground(int32 *params) {
68 // this screen defines the size of the back buffer
69
70 // params: 0 res id of normal background layer - cannot be 0
71 // 1 1 yes 0 no for a new palette
72
73 if (Sword2Engine::isPsx())
74 _vm->_screen->initPsxBackground(params[0], params[1]);
75 else
76 _vm->_screen->initBackground(params[0], params[1]);
77 return IR_CONT;
78 }
79
80 /**
81 * This function is used by start scripts.
82 */
83
fnSetSession(int32 * params)84 int32 Logic::fnSetSession(int32 *params) {
85 // params: 0 id of new run list
86
87 expressChangeSession(params[0]);
88 return IR_CONT;
89 }
90
fnBackSprite(int32 * params)91 int32 Logic::fnBackSprite(int32 *params) {
92 // params: 0 pointer to object's graphic structure
93 _router->setSpriteStatus(decodePtr(params[0]), BACK_SPRITE);
94 return IR_CONT;
95 }
96
fnSortSprite(int32 * params)97 int32 Logic::fnSortSprite(int32 *params) {
98 // params: 0 pointer to object's graphic structure
99 _router->setSpriteStatus(decodePtr(params[0]), SORT_SPRITE);
100 return IR_CONT;
101 }
102
fnForeSprite(int32 * params)103 int32 Logic::fnForeSprite(int32 *params) {
104 // params: 0 pointer to object's graphic structure
105 _router->setSpriteStatus(decodePtr(params[0]), FORE_SPRITE);
106 return IR_CONT;
107 }
108
fnRegisterMouse(int32 * params)109 int32 Logic::fnRegisterMouse(int32 *params) {
110 // this call would be made from an objects service script 0
111 // the object would be one with no graphic but with a mouse - i.e. a
112 // floor or one whose mouse area is manually defined rather than
113 // intended to fit sprite shape
114
115 // params: 0 pointer to ObjectMouse or 0 for no write to mouse
116 // list
117
118 _vm->_mouse->registerMouse(decodePtr(params[0]), NULL);
119 return IR_CONT;
120 }
121
fnAnim(int32 * params)122 int32 Logic::fnAnim(int32 *params) {
123 // params: 0 pointer to object's logic structure
124 // 1 pointer to object's graphic structure
125 // 2 resource id of animation file
126
127 // Normal forward animation
128 return _router->doAnimate(
129 decodePtr(params[0]),
130 decodePtr(params[1]),
131 params[2], false);
132 }
133
fnRandom(int32 * params)134 int32 Logic::fnRandom(int32 *params) {
135 // params: 0 min
136 // 1 max
137
138 writeVar(RESULT, _vm->_rnd.getRandomNumberRng(params[0], params[1]));
139 return IR_CONT;
140 }
141
fnPreLoad(int32 * params)142 int32 Logic::fnPreLoad(int32 *params) {
143 // Forces a resource into memory before it's "officially" opened for
144 // use. eg. if an anim needs to run on smoothly from another,
145 // "preloading" gets it into memory in advance to avoid the cacheing
146 // delay that normally occurs before the first frame.
147
148 // params: 0 resource to preload
149
150 _vm->_resman->openResource(params[0]);
151 _vm->_resman->closeResource(params[0]);
152 return IR_CONT;
153 }
154
fnAddSubject(int32 * params)155 int32 Logic::fnAddSubject(int32 *params) {
156 // params: 0 id
157 // 1 daves reference number
158 _vm->_mouse->addSubject(params[0], params[1]);
159 return IR_CONT;
160 }
161
fnInteract(int32 * params)162 int32 Logic::fnInteract(int32 *params) {
163 // Run targets action on a subroutine. Called by player on his base
164 // level 0 idle, for example.
165
166 // params: 0 id of target from which we derive action script
167 // reference
168
169 writeVar(PLAYER_ACTION, 0); // must clear this
170 logicUp((params[0] << 16) | 2); // 3rd script of clicked on id
171
172 // Out, up and around again - pc is saved for current level to be
173 // returned to.
174 return IR_GOSUB;
175 }
176
fnChoose(int32 * params)177 int32 Logic::fnChoose(int32 *params) {
178 // params: none
179
180 // This opcode is used to open the conversation menu. The human is
181 // switched off so there will be no normal mouse engine.
182
183 // The player's choice is piggy-backed on the standard opcode return
184 // values, to be used with the CP_JUMP_ON_RETURNED opcode. As far as I
185 // can tell, this is the only function that uses that feature.
186
187 uint32 response = _vm->_mouse->chooseMouse();
188
189 if (response == (uint32)-1)
190 return IR_REPEAT;
191
192 return IR_CONT | (response << 3);
193 }
194
195 /**
196 * Walk mega to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set
197 * RESULT to 1.
198 */
199
fnWalk(int32 * params)200 int32 Logic::fnWalk(int32 *params) {
201 // params: 0 pointer to object's logic structure
202 // 1 pointer to object's graphic structure
203 // 2 pointer to object's mega structure
204 // 3 pointer to object's walkdata structure
205 // 4 target x-coord
206 // 5 target y-coord
207 // 6 target direction (8 means end walk on ANY direction)
208
209 return _router->doWalk(
210 decodePtr(params[0]),
211 decodePtr(params[1]),
212 decodePtr(params[2]),
213 decodePtr(params[3]),
214 params[4], params[5], params[6]);
215 }
216
217 /**
218 * Walk mega to start position of anim
219 */
220
fnWalkToAnim(int32 * params)221 int32 Logic::fnWalkToAnim(int32 *params) {
222 // params: 0 pointer to object's logic structure
223 // 1 pointer to object's graphic structure
224 // 2 pointer to object's mega structure
225 // 3 pointer to object's walkdata structure
226 // 4 anim resource id
227
228 return _router->walkToAnim(
229 decodePtr(params[0]),
230 decodePtr(params[1]),
231 decodePtr(params[2]),
232 decodePtr(params[3]),
233 params[4]);
234 }
235
236 /**
237 * Turn mega to the specified direction.
238 */
239
fnTurn(int32 * params)240 int32 Logic::fnTurn(int32 *params) {
241 // params: 0 pointer to object's logic structure
242 // 1 pointer to object's graphic structure
243 // 2 pointer to object's mega structure
244 // 3 pointer to object's walkdata structure
245 // 4 target direction
246
247 return _router->doFace(
248 decodePtr(params[0]),
249 decodePtr(params[1]),
250 decodePtr(params[2]),
251 decodePtr(params[3]),
252 params[4]);
253 }
254
255 /**
256 * Stand mega at (x,y,dir)
257 * Sets up the graphic object, but also needs to set the new 'current_dir' in
258 * the mega object, so the router knows in future
259 */
260
fnStandAt(int32 * params)261 int32 Logic::fnStandAt(int32 *params) {
262 // params: 0 pointer to object's graphic structure
263 // 1 pointer to object's mega structure
264 // 2 target x-coord
265 // 3 target y-coord
266 // 4 target direction
267
268 _router->standAt(
269 decodePtr(params[0]),
270 decodePtr(params[1]),
271 params[2], params[3], params[4]);
272 return IR_CONT;
273 }
274
275 /**
276 * Stand mega into the specified direction at current feet coords.
277 * Just needs to call standAt() with current feet coords.
278 */
279
fnStand(int32 * params)280 int32 Logic::fnStand(int32 *params) {
281 // params: 0 pointer to object's graphic structure
282 // 1 pointer to object's mega structure
283 // 2 target direction
284 byte *ob_mega = decodePtr(params[1]);
285
286 ObjectMega obMega(ob_mega);
287
288 _router->standAt(
289 decodePtr(params[0]),
290 ob_mega, obMega.getFeetX(), obMega.getFeetY(), params[2]);
291 return IR_CONT;
292 }
293
294 /**
295 * stand mega at end position of anim
296 */
297
fnStandAfterAnim(int32 * params)298 int32 Logic::fnStandAfterAnim(int32 *params) {
299 // params: 0 pointer to object's graphic structure
300 // 1 pointer to object's mega structure
301 // 2 anim resource id
302
303 _router->standAfterAnim(
304 decodePtr(params[0]),
305 decodePtr(params[1]),
306 params[2]);
307 return IR_CONT;
308 }
309
fnPause(int32 * params)310 int32 Logic::fnPause(int32 *params) {
311 // params: 0 pointer to object's logic structure
312 // 1 number of game-cycles to pause
313
314 // NB. Pause-value of 0 causes script to continue, 1 causes a 1-cycle
315 // quit, 2 gives 2 cycles, etc.
316
317 ObjectLogic obLogic(decodePtr(params[0]));
318
319 if (obLogic.getLooping() == 0) {
320 obLogic.setLooping(1);
321 obLogic.setPause(params[1]);
322 }
323
324 if (obLogic.getPause()) {
325 obLogic.setPause(obLogic.getPause() - 1);
326 return IR_REPEAT;
327 }
328
329 obLogic.setLooping(0);
330 return IR_CONT;
331 }
332
fnMegaTableAnim(int32 * params)333 int32 Logic::fnMegaTableAnim(int32 *params) {
334 // params: 0 pointer to object's logic structure
335 // 1 pointer to object's graphic structure
336 // 2 pointer to object's mega structure
337 // 3 pointer to animation table
338
339 // Normal forward anim
340 return _router->megaTableAnimate(
341 decodePtr(params[0]),
342 decodePtr(params[1]),
343 decodePtr(params[2]),
344 decodePtr(params[3]),
345 false);
346 }
347
fnAddMenuObject(int32 * params)348 int32 Logic::fnAddMenuObject(int32 *params) {
349 // params: 0 pointer to a MenuObject structure to copy down
350
351 _vm->_mouse->addMenuObject(decodePtr(params[0]));
352 return IR_CONT;
353 }
354
355 /**
356 * Start a conversation.
357 *
358 * Note that fnStartConversation() might accidentally be called every time the
359 * script loops back for another chooser, but we only want to reset the chooser
360 * count flag the first time this function is called, i.e. when the talk flag
361 * is zero.
362 */
363
fnStartConversation(int32 * params)364 int32 Logic::fnStartConversation(int32 *params) {
365 // params: none
366
367 _vm->_mouse->startConversation();
368 return IR_CONT;
369 }
370
371 /**
372 * End a conversation.
373 */
374
fnEndConversation(int32 * params)375 int32 Logic::fnEndConversation(int32 *params) {
376 // params: none
377
378 _vm->_mouse->endConversation();
379 return IR_CONT;
380 }
381
fnSetFrame(int32 * params)382 int32 Logic::fnSetFrame(int32 *params) {
383 // params: 0 pointer to object's graphic structure
384 // 1 resource id of animation file
385 // 2 frame flag (0=first 1=last)
386
387 int32 res = params[1];
388 assert(res);
389
390 // open the resource (& check it's valid)
391 byte *anim_file = _vm->_resman->openResource(res);
392
393 assert(_vm->_resman->fetchType(res) == ANIMATION_FILE);
394
395 // set up pointer to the animation header
396 AnimHeader anim_head;
397
398 anim_head.read(_vm->fetchAnimHeader(anim_file));
399
400 // set up anim resource in graphic object
401 ObjectGraphic obGraph(decodePtr(params[0]));
402
403 obGraph.setAnimResource(res);
404 obGraph.setAnimPc(params[2] ? anim_head.noAnimFrames - 1 : 0);
405
406 // Close the anim file and drop out of script
407 _vm->_resman->closeResource(obGraph.getAnimResource());
408 return IR_CONT;
409 }
410
fnRandomPause(int32 * params)411 int32 Logic::fnRandomPause(int32 *params) {
412 // params: 0 pointer to object's logic structure
413 // 1 minimum number of game-cycles to pause
414 // 2 maximum number of game-cycles to pause
415
416 ObjectLogic obLogic(decodePtr(params[0]));
417 int32 pars[2];
418
419 if (obLogic.getLooping() == 0) {
420 pars[0] = params[1];
421 pars[1] = params[2];
422 fnRandom(pars);
423 pars[1] = readVar(RESULT);
424 }
425
426 pars[0] = params[0];
427 return fnPause(pars);
428 }
429
fnRegisterFrame(int32 * params)430 int32 Logic::fnRegisterFrame(int32 *params) {
431 // this call would be made from an objects service script 0
432
433 // params: 0 pointer to mouse structure or NULL for no write to
434 // mouse list (non-zero means write sprite-shape to
435 // mouse list)
436 // 1 pointer to graphic structure
437 // 2 pointer to mega structure or NULL if not a mega
438
439 _vm->_screen->registerFrame(
440 decodePtr(params[0]),
441 decodePtr(params[1]),
442 decodePtr(params[2]));
443 return IR_CONT;
444 }
445
fnNoSprite(int32 * params)446 int32 Logic::fnNoSprite(int32 *params) {
447 // params: 0 pointer to object's graphic structure
448 _router->setSpriteStatus(decodePtr(params[0]), NO_SPRITE);
449 return IR_CONT;
450 }
451
fnSendSync(int32 * params)452 int32 Logic::fnSendSync(int32 *params) {
453 // params: 0 sync's recipient
454 // 1 sync value
455
456 sendSync(params[0], params[1]);
457 return IR_CONT;
458 }
459
fnUpdatePlayerStats(int32 * params)460 int32 Logic::fnUpdatePlayerStats(int32 *params) {
461 // engine needs to know certain info about the player
462
463 // params: 0 pointer to mega structure
464
465 ObjectMega obMega(decodePtr(params[0]));
466
467 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
468
469 screenInfo->player_feet_x = obMega.getFeetX();
470 screenInfo->player_feet_y = obMega.getFeetY();
471
472 // for the script
473 writeVar(PLAYER_FEET_X, obMega.getFeetX());
474 writeVar(PLAYER_FEET_Y, obMega.getFeetY());
475 writeVar(PLAYER_CUR_DIR, obMega.getCurDir());
476 writeVar(SCROLL_OFFSET_X, screenInfo->scroll_offset_x);
477
478 debug(5, "fnUpdatePlayerStats: %d %d",
479 obMega.getFeetX(), obMega.getFeetY());
480
481 return IR_CONT;
482 }
483
fnPassGraph(int32 * params)484 int32 Logic::fnPassGraph(int32 *params) {
485 // makes an engine local copy of passed ObjectGraphic - run script 4
486 // of an object to request this used by fnTurnTo(id) etc
487 //
488 // remember, we cannot simply read a compact any longer but instead
489 // must request it from the object itself
490
491 // params: 0 pointer to an ObjectGraphic structure
492
493 warning("fnPassGraph() is a no-op now");
494 return IR_CONT;
495 }
496
fnInitFloorMouse(int32 * params)497 int32 Logic::fnInitFloorMouse(int32 *params) {
498 // params: 0 pointer to object's mouse structure
499
500 byte *ob_mouse = decodePtr(params[0]);
501 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
502
503 // floor is always lowest priority
504
505 ObjectMouse mouse;
506
507 mouse.x1 = 0;
508 mouse.y1 = 0;
509 mouse.x2 = screenInfo->screen_wide - 1;
510 mouse.y2 = screenInfo->screen_deep - 1;
511 mouse.priority = 9;
512 mouse.pointer = NORMAL_MOUSE_ID;
513
514 mouse.write(ob_mouse);
515 return IR_CONT;
516 }
517
fnPassMega(int32 * params)518 int32 Logic::fnPassMega(int32 *params) {
519 // makes an engine local copy of passed mega_structure - run script 4
520 // of an object to request this used by fnTurnTo(id) etc
521 //
522 // remember, we cannot simply read a compact any longer but instead
523 // must request it from the object itself
524
525 // params: 0 pointer to a mega structure
526
527 memcpy(_engineMega, decodePtr(params[0]), ObjectMega::size());
528 return IR_CONT;
529 }
530
531 /**
532 * Turn mega to face point (x,y) on the floor
533 */
534
fnFaceXY(int32 * params)535 int32 Logic::fnFaceXY(int32 *params) {
536 // params: 0 pointer to object's logic structure
537 // 1 pointer to object's graphic structure
538 // 2 pointer to object's mega structure
539 // 3 pointer to object's walkdata structure
540 // 4 target x-coord
541 // 5 target y-coord
542
543 return _router->faceXY(
544 decodePtr(params[0]),
545 decodePtr(params[1]),
546 decodePtr(params[2]),
547 decodePtr(params[3]),
548 params[4], params[5]);
549 }
550
551 /**
552 * Causes no more objects in this logic loop to be processed. The logic engine
553 * will restart at the beginning of the new list. The current screen will not
554 * be drawn!
555 */
556
fnEndSession(int32 * params)557 int32 Logic::fnEndSession(int32 *params) {
558 // params: 0 id of new run-list
559
560 // terminate current and change to next run-list
561 expressChangeSession(params[0]);
562
563 // stop the script - logic engine will now go around and the new
564 // screen will begin
565 return IR_STOP;
566 }
567
fnNoHuman(int32 * params)568 int32 Logic::fnNoHuman(int32 *params) {
569 // params: none
570
571 _vm->_mouse->noHuman();
572 return IR_CONT;
573 }
574
fnAddHuman(int32 * params)575 int32 Logic::fnAddHuman(int32 *params) {
576 // params: none
577
578 _vm->_mouse->addHuman();
579 return IR_CONT;
580 }
581
582 /**
583 * Wait for a target to become waiting, i.e. not busy.
584 */
585
fnWeWait(int32 * params)586 int32 Logic::fnWeWait(int32 *params) {
587 // params: 0 target
588
589 assert(_vm->_resman->fetchType(params[0]) == GAME_OBJECT);
590
591 // Run the target's get-speech-state script
592 runResScript(params[0], 5);
593
594 if (readVar(RESULT) == 0) {
595 // The target is busy. Try again.
596 _vm->_debugger->_speechScriptWaiting = params[0];
597 return IR_REPEAT;
598 }
599
600 // The target is waiting, i.e. not busy.
601
602 _vm->_debugger->_speechScriptWaiting = 0;
603 return IR_CONT;
604 }
605
606 /**
607 * Wait for a target to become waiting, i.e. not busy, send a command to it,
608 * then wait for it to finish.
609 */
610
fnTheyDoWeWait(int32 * params)611 int32 Logic::fnTheyDoWeWait(int32 *params) {
612 // params: 0 pointer to ob_logic
613 // 1 target
614 // 2 command
615 // 3 ins1
616 // 4 ins2
617 // 5 ins3
618 // 6 ins4
619 // 7 ins5
620
621 assert(_vm->_resman->fetchType(params[1]) == GAME_OBJECT);
622
623 // Run the target's get-speech-state script
624 runResScript(params[1], 5);
625
626 ObjectLogic obLogic(decodePtr(params[0]));
627
628 if (readVar(RESULT) == 1 && readVar(INS_COMMAND) == 0 && obLogic.getLooping() == 0) {
629 // The target is waiting, i.e. not busy, and there is no other
630 // command queued. We haven't sent the command yet, so do it.
631
632 debug(5, "fnTheyDoWeWait: sending command to %d", params[1]);
633
634 _vm->_debugger->_speechScriptWaiting = params[1];
635 obLogic.setLooping(1);
636
637 writeVar(SPEECH_ID, params[1]);
638 writeVar(INS_COMMAND, params[2]);
639 writeVar(INS1, params[3]);
640 writeVar(INS2, params[4]);
641 writeVar(INS3, params[5]);
642 writeVar(INS4, params[6]);
643 writeVar(INS5, params[7]);
644
645 return IR_REPEAT;
646 }
647
648 if (obLogic.getLooping() == 0) {
649 // The command has not been sent yet. Keep waiting.
650 _vm->_debugger->_speechScriptWaiting = params[1];
651 return IR_REPEAT;
652 }
653
654 if (readVar(RESULT) == 0) {
655 // The command has been sent, and the target is busy doing it.
656 // Wait for it to finish.
657
658 debug(5, "fnTheyDoWeWait: Waiting for %d to finish", params[1]);
659
660 _vm->_debugger->_speechScriptWaiting = params[1];
661 return IR_REPEAT;
662 }
663
664 debug(5, "fnTheyDoWeWait: %d finished", params[1]);
665
666 obLogic.setLooping(0);
667 _vm->_debugger->_speechScriptWaiting = 0;
668 return IR_CONT;
669 }
670
671 /**
672 * Wait for a target to become waiting, i.e. not busy, then send a command to
673 * it.
674 */
675
fnTheyDo(int32 * params)676 int32 Logic::fnTheyDo(int32 *params) {
677 // params: 0 target
678 // 1 command
679 // 2 ins1
680 // 3 ins2
681 // 4 ins3
682 // 5 ins4
683 // 6 ins5
684
685 assert(_vm->_resman->fetchType(params[0]) == GAME_OBJECT);
686
687 // Run the target's get-speech-state script
688 runResScript(params[0], 5);
689
690 if (readVar(RESULT) == 1 && !readVar(INS_COMMAND)) {
691 // The target is waiting, i.e. not busy, and there is no other
692 // command queued. Send the command.
693
694 debug(5, "fnTheyDo: sending command to %d", params[0]);
695
696 _vm->_debugger->_speechScriptWaiting = 0;
697
698 writeVar(SPEECH_ID, params[0]);
699 writeVar(INS_COMMAND, params[1]);
700 writeVar(INS1, params[2]);
701 writeVar(INS2, params[3]);
702 writeVar(INS3, params[4]);
703 writeVar(INS4, params[5]);
704 writeVar(INS5, params[6]);
705
706 return IR_CONT;
707 }
708
709 // The target is busy. Come back again next cycle.
710
711 _vm->_debugger->_speechScriptWaiting = params[0];
712 return IR_REPEAT;
713 }
714
715 /**
716 * Route to the left or right hand side of target id, if possible.
717 */
718
fnWalkToTalkToMega(int32 * params)719 int32 Logic::fnWalkToTalkToMega(int32 *params) {
720 // params: 0 pointer to object's logic structure
721 // 1 pointer to object's graphic structure
722 // 2 pointer to object's mega structure
723 // 3 pointer to object's walkdata structure
724 // 4 id of target mega to face
725 // 5 separation
726
727 return _router->walkToTalkToMega(
728 decodePtr(params[0]),
729 decodePtr(params[1]),
730 decodePtr(params[2]),
731 decodePtr(params[3]),
732 params[4], params[5]);
733 }
734
fnFadeDown(int32 * params)735 int32 Logic::fnFadeDown(int32 *params) {
736 // NONE means up! can only be called when screen is fully faded up -
737 // multiple calls wont have strange effects
738
739 // params: none
740
741 if (_vm->_screen->getFadeStatus() == RDFADE_NONE)
742 _vm->_screen->fadeDown();
743
744 return IR_CONT;
745 }
746
747 enum {
748 S_OB_GRAPHIC = 0,
749 S_OB_SPEECH = 1,
750 S_OB_LOGIC = 2,
751 S_OB_MEGA = 3,
752
753 S_TEXT = 4,
754 S_WAV = 5,
755 S_ANIM = 6,
756 S_DIR_TABLE = 7,
757 S_ANIM_MODE = 8
758 };
759
760 /**
761 * It's the super versatile fnSpeak. Text and wavs can be selected in any
762 * combination.
763 *
764 * @note We can assume no human - there should be no human, at least!
765 */
766
fnISpeak(int32 * params)767 int32 Logic::fnISpeak(int32 *params) {
768 // params: 0 pointer to ob_graphic
769 // 1 pointer to ob_speech
770 // 2 pointer to ob_logic
771 // 3 pointer to ob_mega
772 // 4 encoded text number
773 // 5 wav res id
774 // 6 anim res id
775 // 7 anim table res id
776 // 8 animation mode 0 lip synced,
777 // 1 just straight animation
778
779 // Set up the pointers which we know we'll always need
780
781 ObjectLogic obLogic(decodePtr(params[S_OB_LOGIC]));
782 ObjectGraphic obGraph(decodePtr(params[S_OB_GRAPHIC]));
783
784 // FIRST TIME ONLY: create the text, load the wav, set up the anim,
785 // etc.
786
787 if (obLogic.getLooping() == 0) {
788 // New fudge to wait for smacker samples to finish
789 // since they can over-run into the game
790
791 if (_vm->_sound->getSpeechStatus() != RDSE_SAMPLEFINISHED)
792 return IR_REPEAT;
793
794 // New fudge for 'fx' subtitles: If subtitles switched off, and
795 // we don't want to use a wav for this line either, then just
796 // quit back to script right now!
797
798 if (!_vm->getSubtitles() && !wantSpeechForLine(params[S_WAV]))
799 return IR_CONT;
800
801 // Drop out for 1st cycle to allow walks/anims to end and
802 // display last frame before system locks while speech loaded
803
804 if (!_cycleSkip) {
805 _cycleSkip = true;
806 return IR_REPEAT;
807 }
808
809 _cycleSkip = false;
810
811 _vm->_debugger->_textNumber = params[S_TEXT];
812
813 // Pull out the text line to get the official text number
814 // (for wav id). Once the wav id's go into all script text
815 // commands, we'll only need this for debugging.
816
817 uint32 text_res = params[S_TEXT] / SIZE;
818 uint32 local_text = params[S_TEXT] & 0xffff;
819
820 // For testing all text & speech!
821 //
822 // A script loop can send any text number to fnISpeak and it
823 // will only run the valid ones or return with 'result' equal
824 // to '1' or '2' to mean 'invalid text resource' and 'text
825 // number out of range' respectively
826 //
827 // See 'testing_routines' object in George's Player Character
828 // section of linc
829
830 if (readVar(SYSTEM_TESTING_TEXT)) {
831 if (!_vm->_resman->checkValid(text_res)) {
832 // Not a valid resource number - invalid (null
833 // resource)
834 writeVar(RESULT, 1);
835 return IR_CONT;
836 }
837
838 if (_vm->_resman->fetchType(text_res) != TEXT_FILE) {
839 // Invalid - not a text resource
840 _vm->_resman->closeResource(text_res);
841 writeVar(RESULT, 1);
842 return IR_CONT;
843 }
844
845 if (!_vm->checkTextLine(_vm->_resman->openResource(text_res), local_text)) {
846 // Line number out of range
847 _vm->_resman->closeResource(text_res);
848 writeVar(RESULT, 2);
849 return IR_CONT;
850 }
851
852 _vm->_resman->closeResource(text_res);
853 writeVar(RESULT, 0);
854 }
855
856 byte *text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
857 _officialTextNumber = READ_LE_UINT16(text);
858 _vm->_resman->closeResource(text_res);
859
860 // Prevent dud lines from appearing while testing text & speech
861 // since these will not occur in the game anyway
862
863 if (readVar(SYSTEM_TESTING_TEXT)) {
864 // If actor number is 0 and text line is just a 'dash'
865 // character
866 if (_officialTextNumber == 0 && text[2] == '-' && text[3] == 0) {
867 writeVar(RESULT, 3);
868 return IR_CONT;
869 }
870 }
871
872 // Set the 'looping_flag' and the text-click-delays. We can
873 // left-click past the text after half a second, and
874 // right-click past it after a quarter of a second.
875
876 obLogic.setLooping(1);
877 _leftClickDelay = 6;
878 _rightClickDelay = 3;
879
880 if (readVar(PLAYER_ID) != CUR_PLAYER_ID)
881 debug(5, "(%d) Nico: %s", _officialTextNumber, text + 2);
882 else {
883 debug(5, "(%d) %s: %s", _officialTextNumber, _vm->_resman->fetchName(readVar(ID)), text + 2);
884 }
885
886 // Set up the speech animation
887
888 if (params[S_ANIM]) {
889 // Just a straight anim.
890 _animId = params[6];
891 } else if (params[S_DIR_TABLE]) {
892 // Use this direction table to derive the anim
893 // NB. ASSUMES WE HAVE A MEGA OBJECT!!
894
895 ObjectMega obMega(decodePtr(params[S_OB_MEGA]));
896 byte *anim_table = decodePtr(params[S_DIR_TABLE]);
897
898 _animId = READ_LE_UINT32(anim_table + 4 * obMega.getCurDir());
899 } else {
900 // No animation choosen
901 _animId = 0;
902 }
903
904 if (_animId) {
905 // Set the talker's graphic to the first frame of this
906 // speech anim for now.
907
908 _speechAnimType = readVar(SPEECHANIMFLAG);
909 obGraph.setAnimResource(_animId);
910 obGraph.setAnimPc(0);
911 }
912
913 // Default back to looped lip synced anims.
914 writeVar(SPEECHANIMFLAG, 0);
915
916 // Set up _textX and _textY for speech panning and/or text
917 // sprite position.
918
919 locateTalker(params);
920
921 // If the speech is associated with a specific animation, and
922 // not just a voice-over, set the focus area to the calculated
923 // position.
924
925 if (_animId) {
926 _vm->_system->setFocusRectangle(Common::Rect::center(_textX, _textY, 192, 128));
927 }
928
929 // Is it to be speech or subtitles or both?
930
931 // Assume not running until know otherwise
932 _speechRunning = false;
933
934 // New fudge for 'fx' subtitles: If speech is selected, and
935 // this line is allowed speech (not if it's an fx subtitle!)
936
937 if (!_vm->_sound->isSpeechMute() && wantSpeechForLine(_officialTextNumber)) {
938 // If the wavId parameter is zero because not yet
939 // compiled into speech command, we can still get it
940 // from the 1st 2 chars of the text line.
941
942 if (!params[S_WAV])
943 params[S_WAV] = (int32)_officialTextNumber;
944
945 // Panning goes from -16 (left) to 16 (right)
946 int8 speech_pan = ((_textX - 320) * 16) / 320;
947
948 if (speech_pan < -16)
949 speech_pan = -16;
950 else if (speech_pan > 16)
951 speech_pan = 16;
952
953 uint32 rv = _vm->_sound->playCompSpeech(params[S_WAV], 16, speech_pan);
954
955 if (rv == RD_OK) {
956 // Ok, we've got something to play. Set it
957 // playing now. (We might want to do this the
958 // next cycle, don't know yet.)
959
960 _speechRunning = true;
961 _vm->_sound->unpauseSpeech();
962 } else {
963 debug(5, "ERROR: PlayCompSpeech(wav=%d (res=%d pos=%d)) returned %.8x", params[S_WAV], text_res, local_text, rv);
964 }
965 }
966
967 if (_vm->getSubtitles() || !_speechRunning) {
968 // We want subtitles, or the speech failed to load.
969 // Either way, we're going to show the text so create
970 // the text sprite.
971
972 formText(params);
973 }
974 }
975
976 // EVERY TIME: run a cycle of animation, if there is one
977
978 if (_animId) {
979 // There is an animation - Increment the anim frame number.
980 obGraph.setAnimPc(obGraph.getAnimPc() + 1);
981
982 byte *anim_file = _vm->_resman->openResource(obGraph.getAnimResource());
983 AnimHeader anim_head;
984
985 anim_head.read(_vm->fetchAnimHeader(anim_file));
986
987 if (!_speechAnimType) {
988 // ANIM IS TO BE LIP-SYNC'ED & REPEATING
989
990 if (obGraph.getAnimPc() == (int32)anim_head.noAnimFrames) {
991 // End of animation - restart from frame 0
992 obGraph.setAnimPc(0);
993 } else if (_speechRunning && _vm->_sound->amISpeaking() == RDSE_QUIET) {
994 // The speech is running, but we're at a quiet
995 // bit. Restart from frame 0 (closed mouth).
996 obGraph.setAnimPc(0);
997 }
998 } else {
999 // ANIM IS TO PLAY ONCE ONLY
1000 if (obGraph.getAnimPc() == (int32)anim_head.noAnimFrames - 1) {
1001 // Reached the last frame of the anim. Hold
1002 // anim on this last frame
1003 _animId = 0;
1004 }
1005 }
1006
1007 _vm->_resman->closeResource(obGraph.getAnimResource());
1008 } else if (_speechAnimType) {
1009 // Placed here so we actually display the last frame of the
1010 // anim.
1011 _speechAnimType = 0;
1012 }
1013
1014 // EVERY TIME: FIND OUT IF WE NEED TO STOP THE SPEECH NOW...
1015
1016 // If there is a wav then we're using that to end the speech naturally
1017
1018 bool speechFinished = false;
1019
1020 // If playing a sample
1021
1022 if (_speechRunning) {
1023 // Has it finished?
1024 if (_vm->_sound->getSpeechStatus() == RDSE_SAMPLEFINISHED)
1025 speechFinished = true;
1026 } else if (!_speechRunning && _speechTime) {
1027 // Counting down text time because there is no sample - this
1028 // ends the speech
1029
1030 // if no sample then we're using _speechTime to end speech
1031 // naturally
1032
1033 _speechTime--;
1034 if (!_speechTime)
1035 speechFinished = true;
1036 }
1037
1038 // Ok, all is running along smoothly - but a click means stop
1039 // unnaturally
1040
1041 int mouseX, mouseY;
1042
1043 _vm->_mouse->getPos(mouseX, mouseY);
1044
1045 // So that we can go to the options panel while text & speech is
1046 // being tested
1047 if (readVar(SYSTEM_TESTING_TEXT) == 0 || mouseY > 0) {
1048 MouseEvent *me = _vm->mouseEvent();
1049
1050 // Note that we now have TWO click-delays - one for LEFT
1051 // button, one for RIGHT BUTTON
1052
1053 if ((!_leftClickDelay && me && (me->buttons & RD_LEFTBUTTONDOWN)) ||
1054 (!_rightClickDelay && me && (me->buttons & RD_RIGHTBUTTONDOWN))) {
1055 // Mouse click, after click_delay has expired -> end
1056 // the speech.
1057
1058 // if testing text & speech
1059 if (readVar(SYSTEM_TESTING_TEXT)) {
1060 // and RB used to click past text
1061 if (me->buttons & RD_RIGHTBUTTONDOWN) {
1062 // then we want the previous line again
1063 writeVar(SYSTEM_WANT_PREVIOUS_LINE, 1);
1064 } else {
1065 // LB just want next line again
1066 writeVar(SYSTEM_WANT_PREVIOUS_LINE, 0);
1067 }
1068 }
1069
1070 speechFinished = true;
1071
1072 // if speech sample playing, halt it prematurely
1073 if (_speechRunning)
1074 _vm->_sound->stopSpeech();
1075 }
1076 }
1077
1078 // If we are finishing the speech this cycle, do the business
1079
1080 // !speechAnimType, as we want an anim which is playing once to have
1081 // finished.
1082
1083 if (speechFinished && !_speechAnimType) {
1084 _vm->_system->clearFocusRectangle();
1085
1086 // If there is text, kill it
1087 if (_speechTextBlocNo) {
1088 _vm->_fontRenderer->killTextBloc(_speechTextBlocNo);
1089 _speechTextBlocNo = 0;
1090 }
1091
1092 // if there is a speech anim, end it on closed mouth frame
1093 if (_animId) {
1094 _animId = 0;
1095 obGraph.setAnimPc(0);
1096 }
1097
1098 _speechRunning = false;
1099
1100 // no longer in a script function loop
1101 obLogic.setLooping(0);
1102
1103 _vm->_debugger->_textNumber = 0;
1104
1105 // reset to zero, in case text line not even extracted (since
1106 // this number comes from the text line)
1107 _officialTextNumber = 0;
1108
1109 writeVar(RESULT, 0);
1110 return IR_CONT;
1111 }
1112
1113 // Speech still going, so decrement the click_delay if it's still
1114 // active
1115
1116 if (_leftClickDelay)
1117 _leftClickDelay--;
1118
1119 if (_rightClickDelay)
1120 _rightClickDelay--;
1121
1122 return IR_REPEAT;
1123 }
1124
1125 /**
1126 * Reset the object and restart script 1 on level 0
1127 */
1128
fnTotalRestart(int32 * params)1129 int32 Logic::fnTotalRestart(int32 *params) {
1130 // mega runs this to restart its base logic again - like being cached
1131 // in again
1132
1133 // params: none
1134
1135 _curObjectHub.setLogicLevel(0);
1136 _curObjectHub.setScriptPc(0, 1);
1137
1138 return IR_TERMINATE;
1139 }
1140
fnSetWalkGrid(int32 * params)1141 int32 Logic::fnSetWalkGrid(int32 *params) {
1142 // params: none
1143
1144 warning("fnSetWalkGrid() is no longer a valid opcode");
1145 return IR_CONT;
1146 }
1147
1148 /**
1149 * Receive and sequence the commands sent from the conversation script. We have
1150 * to do this in a slightly tweeky manner as we can no longer have generic
1151 * scripts.
1152 */
1153
1154 enum {
1155 INS_talk = 1,
1156 INS_anim = 2,
1157 INS_reverse_anim = 3,
1158 INS_walk = 4,
1159 INS_turn = 5,
1160 INS_face = 6,
1161 INS_trace = 7,
1162 INS_no_sprite = 8,
1163 INS_sort = 9,
1164 INS_foreground = 10,
1165 INS_background = 11,
1166 INS_table_anim = 12,
1167 INS_reverse_table_anim = 13,
1168 INS_walk_to_anim = 14,
1169 INS_set_frame = 15,
1170 INS_stand_after_anim = 16,
1171 INS_quit = 42
1172 };
1173
fnSpeechProcess(int32 * params)1174 int32 Logic::fnSpeechProcess(int32 *params) {
1175 // params: 0 pointer to ob_graphic
1176 // 1 pointer to ob_speech
1177 // 2 pointer to ob_logic
1178 // 3 pointer to ob_mega
1179 // 4 pointer to ob_walkdata
1180
1181 ObjectSpeech obSpeech(decodePtr(params[1]));
1182
1183 while (1) {
1184 int32 pars[9];
1185
1186 // Check which command we're waiting for, and call the
1187 // appropriate function. Once we're done, clear the command
1188 // and set wait_state to 1.
1189 //
1190 // Note: we could save a var and ditch wait_state and check
1191 // 'command' for non zero means busy
1192 //
1193 // Note: I can't see that we ever check the value of wait_state
1194 // but perhaps it accesses that memory location directly?
1195
1196 switch (obSpeech.getCommand()) {
1197 case 0:
1198 break;
1199 case INS_talk:
1200 pars[0] = params[0]; // ob_graphic
1201 pars[1] = params[1]; // ob_speech
1202 pars[2] = params[2]; // ob_logic
1203 pars[3] = params[3]; // ob_mega
1204 pars[4] = obSpeech.getIns1(); // encoded text number
1205 pars[5] = obSpeech.getIns2(); // wav res id
1206 pars[6] = obSpeech.getIns3(); // anim res id
1207 pars[7] = obSpeech.getIns4(); // anim table res id
1208 pars[8] = obSpeech.getIns5(); // animation mode - 0 lip synced, 1 just straight animation
1209
1210 if (fnISpeak(pars) != IR_REPEAT) {
1211 obSpeech.setCommand(0);
1212 obSpeech.setWaitState(1);
1213 }
1214
1215 return IR_REPEAT;
1216 case INS_turn:
1217 pars[0] = params[2]; // ob_logic
1218 pars[1] = params[0]; // ob_graphic
1219 pars[2] = params[3]; // ob_mega
1220 pars[3] = params[4]; // ob_walkdata
1221 pars[4] = obSpeech.getIns1(); // direction to turn to
1222
1223 if (fnTurn(pars) != IR_REPEAT) {
1224 obSpeech.setCommand(0);
1225 obSpeech.setWaitState(1);
1226 }
1227
1228 return IR_REPEAT;
1229 case INS_face:
1230 pars[0] = params[2]; // ob_logic
1231 pars[1] = params[0]; // ob_graphic
1232 pars[2] = params[3]; // ob_mega
1233 pars[3] = params[4]; // ob_walkdata
1234 pars[4] = obSpeech.getIns1(); // target
1235
1236 if (fnFaceMega(pars) != IR_REPEAT) {
1237 obSpeech.setCommand(0);
1238 obSpeech.setWaitState(1);
1239 }
1240
1241 return IR_REPEAT;
1242 case INS_anim:
1243 pars[0] = params[2]; // ob_logic
1244 pars[1] = params[0]; // ob_graphic
1245 pars[2] = obSpeech.getIns1(); // anim res
1246
1247 if (fnAnim(pars) != IR_REPEAT) {
1248 obSpeech.setCommand(0);
1249 obSpeech.setWaitState(1);
1250 }
1251
1252 return IR_REPEAT;
1253 case INS_reverse_anim:
1254 pars[0] = params[2]; // ob_logic
1255 pars[1] = params[0]; // ob_graphic
1256 pars[2] = obSpeech.getIns1(); // anim res
1257
1258 if (fnReverseAnim(pars) != IR_REPEAT) {
1259 obSpeech.setCommand(0);
1260 obSpeech.setWaitState(1);
1261 }
1262
1263 return IR_REPEAT;
1264 case INS_table_anim:
1265 pars[0] = params[2]; // ob_logic
1266 pars[1] = params[0]; // ob_graphic
1267 pars[2] = params[3]; // ob_mega
1268 pars[3] = obSpeech.getIns1(); // pointer to anim table
1269
1270 if (fnMegaTableAnim(pars) != IR_REPEAT) {
1271 obSpeech.setCommand(0);
1272 obSpeech.setWaitState(1);
1273 }
1274
1275 return IR_REPEAT;
1276 case INS_reverse_table_anim:
1277 pars[0] = params[2]; // ob_logic
1278 pars[1] = params[0]; // ob_graphic
1279 pars[2] = params[3]; // ob_mega
1280 pars[3] = obSpeech.getIns1(); // pointer to anim table
1281
1282 if (fnReverseMegaTableAnim(pars) != IR_REPEAT) {
1283 obSpeech.setCommand(0);
1284 obSpeech.setWaitState(1);
1285 }
1286
1287 return IR_REPEAT;
1288 case INS_no_sprite:
1289 fnNoSprite(params); // ob_graphic
1290
1291 obSpeech.setCommand(0);
1292 obSpeech.setWaitState(1);
1293 return IR_REPEAT;
1294 case INS_sort:
1295 fnSortSprite(params); // ob_graphic
1296
1297 obSpeech.setCommand(0);
1298 obSpeech.setWaitState(1);
1299 return IR_REPEAT;
1300 case INS_foreground:
1301 fnForeSprite(params); // ob_graphic
1302
1303 obSpeech.setCommand(0);
1304 obSpeech.setWaitState(1);
1305 return IR_REPEAT;
1306 case INS_background:
1307 fnBackSprite(params); // ob_graphic
1308
1309 obSpeech.setCommand(0);
1310 obSpeech.setWaitState(1);
1311 return IR_REPEAT;
1312 case INS_walk:
1313 pars[0] = params[2]; // ob_logic
1314 pars[1] = params[0]; // ob_graphic
1315 pars[2] = params[3]; // ob_mega
1316 pars[3] = params[4]; // ob_walkdata
1317 pars[4] = obSpeech.getIns1(); // target x
1318 pars[5] = obSpeech.getIns2(); // target y
1319 pars[6] = obSpeech.getIns3(); // target direction
1320
1321 if (fnWalk(pars) != IR_REPEAT) {
1322 obSpeech.setCommand(0);
1323 obSpeech.setWaitState(1);
1324 }
1325
1326 return IR_REPEAT;
1327 case INS_walk_to_anim:
1328 pars[0] = params[2]; // ob_logic
1329 pars[1] = params[0]; // ob_graphic
1330 pars[2] = params[3]; // ob_mega
1331 pars[3] = params[4]; // ob_walkdata
1332 pars[4] = obSpeech.getIns1(); // anim resource
1333
1334 if (fnWalkToAnim(pars) != IR_REPEAT) {
1335 obSpeech.setCommand(0);
1336 obSpeech.setWaitState(1);
1337 }
1338
1339 return IR_REPEAT;
1340 case INS_stand_after_anim:
1341 pars[0] = params[0]; // ob_graphic
1342 pars[1] = params[3]; // ob_mega
1343 pars[2] = obSpeech.getIns1(); // anim resource
1344
1345 fnStandAfterAnim(pars);
1346
1347 obSpeech.setCommand(0);
1348 obSpeech.setWaitState(1);
1349 return IR_REPEAT;
1350 case INS_set_frame:
1351 pars[0] = params[0]; // ob_graphic
1352 pars[1] = obSpeech.getIns1(); // anim_resource
1353 pars[2] = obSpeech.getIns2(); // FIRST_FRAME or LAST_FRAME
1354 fnSetFrame(pars);
1355
1356 obSpeech.setCommand(0);
1357 obSpeech.setWaitState(1);
1358 return IR_REPEAT;
1359 case INS_quit:
1360 // That's it - we're finished with this
1361 obSpeech.setCommand(0);
1362 // obSpeech.setWaitState(0);
1363 return IR_CONT;
1364 default:
1365 // Unimplemented command - just cancel
1366 obSpeech.setCommand(0);
1367 obSpeech.setWaitState(1);
1368 break;
1369 }
1370
1371 if (readVar(SPEECH_ID) == readVar(ID)) {
1372 // There's a new command for us! Grab the command -
1373 // potentially we only have this cycle to do this - and
1374 // set things up so that the command will be picked up
1375 // on the next iteration of the while loop.
1376
1377 debug(5, "fnSpeechProcess: Received new command %d", readVar(INS_COMMAND));
1378
1379 writeVar(SPEECH_ID, 0);
1380
1381 obSpeech.setCommand(readVar(INS_COMMAND));
1382 obSpeech.setIns1(readVar(INS1));
1383 obSpeech.setIns2(readVar(INS2));
1384 obSpeech.setIns3(readVar(INS3));
1385 obSpeech.setIns4(readVar(INS4));
1386 obSpeech.setIns5(readVar(INS5));
1387 obSpeech.setWaitState(0);
1388
1389 writeVar(INS_COMMAND, 0);
1390 } else {
1391 // No new command. We could run a blink anim (or
1392 // something) here.
1393
1394 obSpeech.setWaitState(1);
1395 return IR_REPEAT;
1396 }
1397 }
1398 }
1399
fnSetScaling(int32 * params)1400 int32 Logic::fnSetScaling(int32 *params) {
1401 // params: 0 pointer to object's mega structure
1402 // 1 scale constant A
1403 // 2 scale constant B
1404
1405 // 256 * s = A * y + B
1406
1407 // Where s is system scale, which itself is (256 * actual_scale) ie.
1408 // s == 128 is half size
1409
1410 ObjectMega obMega(decodePtr(params[0]));
1411
1412 obMega.setScaleA(params[1]);
1413 obMega.setScaleB(params[2]);
1414
1415 return IR_CONT;
1416 }
1417
fnStartEvent(int32 * params)1418 int32 Logic::fnStartEvent(int32 *params) {
1419 // params: none
1420
1421 startEvent();
1422 return IR_TERMINATE;
1423 }
1424
fnCheckEventWaiting(int32 * params)1425 int32 Logic::fnCheckEventWaiting(int32 *params) {
1426 // params: none
1427
1428 writeVar(RESULT, checkEventWaiting());
1429 return IR_CONT;
1430 }
1431
fnRequestSpeech(int32 * params)1432 int32 Logic::fnRequestSpeech(int32 *params) {
1433 // change current script - must be followed by a TERMINATE script
1434 // directive
1435
1436 // params: 0 id of target to catch the event and startup speech
1437 // servicing
1438
1439 // Full script id to interact with - megas run their own 7th script
1440 sendEvent(params[0], (params[0] << 16) | 6);
1441 return IR_CONT;
1442 }
1443
fnGosub(int32 * params)1444 int32 Logic::fnGosub(int32 *params) {
1445 // params: 0 id of script
1446
1447 // Hurray, script subroutines. Logic goes up - pc is saved for current
1448 // level.
1449 logicUp(params[0]);
1450 return IR_GOSUB;
1451 }
1452
1453 /**
1454 * Wait for a target to become waiting, i.e. not busy, or until we time out.
1455 * This is useful when clicking on a target to talk to it, and it doesn't
1456 * reply. This way, we won't lock up.
1457 *
1458 * If the target becomes waiting, RESULT is set to 0. If we time out, RESULT is
1459 * set to 1.
1460 */
1461
fnTimedWait(int32 * params)1462 int32 Logic::fnTimedWait(int32 *params) {
1463 // params: 0 ob_logic
1464 // 1 target
1465 // 2 number of cycles before give up
1466
1467 assert(_vm->_resman->fetchType(params[1]) == GAME_OBJECT);
1468
1469 ObjectLogic obLogic(decodePtr(params[0]));
1470
1471 if (obLogic.getLooping() == 0) {
1472 // This is the first time, so set up the time-out.
1473 obLogic.setLooping(params[2]);
1474 }
1475
1476 // Run the target's get-speech-state script
1477 runResScript(params[1], 5);
1478
1479 if (readVar(RESULT) == 1) {
1480 // The target is waiting, i.e. not busy
1481
1482 _vm->_debugger->_speechScriptWaiting = 0;
1483
1484 obLogic.setLooping(0);
1485 writeVar(RESULT, 0);
1486 return IR_CONT;
1487 }
1488
1489 obLogic.setLooping(obLogic.getLooping() - 1);
1490
1491 if (obLogic.getLooping() == 0) {
1492 // Time's up.
1493
1494 debug(5, "fnTimedWait: Timed out waiting for %d", params[1]);
1495 _vm->_debugger->_speechScriptWaiting = 0;
1496
1497 // Clear the event that hasn't been picked up - in theory,
1498 // none of this should ever happen.
1499
1500 killAllIdsEvents(params[1]);
1501 writeVar(RESULT, 1);
1502 return IR_CONT;
1503 }
1504
1505 // Target is busy. Keep trying.
1506
1507 _vm->_debugger->_speechScriptWaiting = params[1];
1508 return IR_REPEAT;
1509 }
1510
fnPlayFx(int32 * params)1511 int32 Logic::fnPlayFx(int32 *params) {
1512 // params: 0 sample resource id
1513 // 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
1514 // 2 delay (0..65535)
1515 // 3 volume (0..16)
1516 // 4 pan (-16..16)
1517
1518 // example script:
1519 // fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
1520 // // fx_water is just a local script flag
1521 // fx_water = result;
1522 // .
1523 // .
1524 // .
1525 // fnStopFx (fx_water);
1526
1527 int32 res = params[0];
1528 int32 type = params[1];
1529 int32 delay = params[2];
1530 int32 volume = params[3];
1531 int32 pan = params[4];
1532
1533 _vm->_sound->queueFx(res, type, delay, volume, pan);
1534 return IR_CONT;
1535 }
1536
fnStopFx(int32 * params)1537 int32 Logic::fnStopFx(int32 *params) {
1538 // params: 0 position in queue
1539 if (_vm->_sound->stopFx(params[0]) != RD_OK)
1540 debug(5, "SFX ERROR: Trying to stop an inactive sound slot");
1541
1542 return IR_CONT;
1543 }
1544
1545 /**
1546 * Start a tune playing, to play once or to loop until stopped or next one
1547 * played.
1548 */
1549
fnPlayMusic(int32 * params)1550 int32 Logic::fnPlayMusic(int32 *params) {
1551 // params: 0 tune id
1552 // 1 loop flag (0 or 1)
1553
1554 char filename[128];
1555 bool loopFlag;
1556 uint32 rv;
1557
1558 loopFlag = (params[1] == FX_LOOP);
1559
1560 rv = _vm->_sound->streamCompMusic(params[0], loopFlag);
1561
1562 if (rv)
1563 debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
1564
1565 return IR_CONT;
1566 }
1567
fnStopMusic(int32 * params)1568 int32 Logic::fnStopMusic(int32 *params) {
1569 // params: none
1570
1571 _vm->_sound->stopMusic(false);
1572 return IR_CONT;
1573 }
1574
fnSetValue(int32 * params)1575 int32 Logic::fnSetValue(int32 *params) {
1576 // temp. function!
1577
1578 // used for setting far-referenced megaset resource field in mega
1579 // object, from start script
1580
1581 // params: 0 pointer to object's mega structure
1582 // 1 value to set it to
1583
1584 ObjectMega obMega(decodePtr(params[0]));
1585
1586 obMega.setMegasetRes(params[1]);
1587 return IR_CONT;
1588 }
1589
fnNewScript(int32 * params)1590 int32 Logic::fnNewScript(int32 *params) {
1591 // change current script - must be followed by a TERMINATE script
1592 // directive
1593
1594 // params: 0 id of script
1595
1596 writeVar(PLAYER_ACTION, 0); // must clear this
1597 logicReplace(params[0]);
1598 return IR_TERMINATE;
1599 }
1600
1601 /**
1602 * Like getSync(), but called from scripts. Sets the RESULT variable to
1603 * the sync value, or 0 if none is found.
1604 */
1605
fnGetSync(int32 * params)1606 int32 Logic::fnGetSync(int32 *params) {
1607 // params: none
1608
1609 int slot = getSync();
1610
1611 writeVar(RESULT, (slot != -1) ? _syncList[slot].sync : 0);
1612 return IR_CONT;
1613 }
1614
1615 /**
1616 * Wait for sync to happen. Sets the RESULT variable to the sync value, once
1617 * it has been found.
1618 */
1619
fnWaitSync(int32 * params)1620 int32 Logic::fnWaitSync(int32 *params) {
1621 // params: none
1622
1623 debug(6, "fnWaitSync: %d waits", readVar(ID));
1624
1625 int slot = getSync();
1626
1627 if (slot == -1)
1628 return IR_REPEAT;
1629
1630 debug(5, "fnWaitSync: %d got sync %d", readVar(ID), _syncList[slot].sync);
1631 writeVar(RESULT, _syncList[slot].sync);
1632 return IR_CONT;
1633 }
1634
fnRegisterWalkGrid(int32 * params)1635 int32 Logic::fnRegisterWalkGrid(int32 *params) {
1636 // params: none
1637
1638 warning("fnRegisterWalkGrid() is no longer a valid opcode");
1639 return IR_CONT;
1640 }
1641
fnReverseMegaTableAnim(int32 * params)1642 int32 Logic::fnReverseMegaTableAnim(int32 *params) {
1643 // params: 0 pointer to object's logic structure
1644 // 1 pointer to object's graphic structure
1645 // 2 pointer to object's mega structure
1646 // 3 pointer to animation table
1647
1648 // Reverse anim
1649 return _router->megaTableAnimate(
1650 decodePtr(params[0]),
1651 decodePtr(params[1]),
1652 decodePtr(params[2]),
1653 decodePtr(params[3]),
1654 true);
1655 }
1656
fnReverseAnim(int32 * params)1657 int32 Logic::fnReverseAnim(int32 *params) {
1658 // params: 0 pointer to object's logic structure
1659 // 1 pointer to object's graphic structure
1660 // 2 resource id of animation file
1661
1662 // Reverse anim
1663 return _router->doAnimate(
1664 decodePtr(params[0]),
1665 decodePtr(params[1]),
1666 params[2], true);
1667 }
1668
1669 /**
1670 * Mark this object for killing - to be killed when player leaves this screen.
1671 * Object reloads and script restarts upon re-entry to screen, which causes
1672 * this object's startup logic to be re-run every time we enter the screen.
1673 * "Which is nice."
1674 *
1675 * @note Call ONCE from object's logic script, i.e. in startup code, so not
1676 * re-called every time script frops off and restarts!
1677 */
1678
fnAddToKillList(int32 * params)1679 int32 Logic::fnAddToKillList(int32 *params) {
1680 // params: none
1681 uint32 id = readVar(ID);
1682
1683 // DON'T EVER KILL GEORGE!
1684 if (id == CUR_PLAYER_ID)
1685 return IR_CONT;
1686
1687 // Scan the list to see if it's already included
1688
1689 for (uint32 i = 0; i < _kills; i++) {
1690 if (_objectKillList[i] == id)
1691 return IR_CONT;
1692 }
1693
1694 assert(_kills < OBJECT_KILL_LIST_SIZE); // no room at the inn
1695
1696 _objectKillList[_kills++] = id;
1697
1698 // "another one bites the dust"
1699
1700 // When we leave the screen, all these object resources are to be
1701 // cleaned out of memory and the kill list emptied by doing
1702 // '_kills = 0', ensuring that all resources are in fact still in
1703 // memory and, more importantly, closed before killing!
1704
1705 return IR_CONT;
1706 }
1707
1708 /**
1709 * Set the standby walk coords to be used by fnWalkToAnim() and
1710 * fnStandAfterAnim() when the anim header's start/end coords are zero.
1711 * Useful during development; can stay in final game anyway.
1712 */
1713
fnSetStandbyCoords(int32 * params)1714 int32 Logic::fnSetStandbyCoords(int32 *params) {
1715 // params: 0 x-coord
1716 // 1 y-coord
1717 // 2 direction (0..7)
1718
1719 _router->setStandbyCoords(params[0], params[1], params[2]);
1720 return IR_CONT;
1721 }
1722
fnBackPar0Sprite(int32 * params)1723 int32 Logic::fnBackPar0Sprite(int32 *params) {
1724 // params: 0 pointer to object's graphic structure
1725 _router->setSpriteStatus(decodePtr(params[0]), BGP0_SPRITE);
1726 return IR_CONT;
1727 }
1728
fnBackPar1Sprite(int32 * params)1729 int32 Logic::fnBackPar1Sprite(int32 *params) {
1730 // params: 0 pointer to object's graphic structure
1731 _router->setSpriteStatus(decodePtr(params[0]), BGP1_SPRITE);
1732 return IR_CONT;
1733 }
1734
fnForePar0Sprite(int32 * params)1735 int32 Logic::fnForePar0Sprite(int32 *params) {
1736 // params: 0 pointer to object's graphic structure
1737 _router->setSpriteStatus(decodePtr(params[0]), FGP0_SPRITE);
1738 return IR_CONT;
1739 }
1740
fnForePar1Sprite(int32 * params)1741 int32 Logic::fnForePar1Sprite(int32 *params) {
1742 // params: 0 pointer to object's graphic structure
1743 _router->setSpriteStatus(decodePtr(params[0]), FGP1_SPRITE);
1744 return IR_CONT;
1745 }
1746
fnSetPlayerActionEvent(int32 * params)1747 int32 Logic::fnSetPlayerActionEvent(int32 *params) {
1748 // we want to intercept the player character and have him interact
1749 // with an object - from script this code is the same as the mouse
1750 // engine calls when you click on an object - here, a third party
1751 // does the clicking IYSWIM
1752
1753 // note - this routine used CUR_PLAYER_ID as the target
1754
1755 // params: 0 id to interact with
1756
1757 setPlayerActionEvent(CUR_PLAYER_ID, params[0]);
1758 return IR_CONT;
1759 }
1760
1761 /**
1762 * Set the special scroll offset variables
1763 *
1764 * Call when starting screens and to change the camera within screens
1765 *
1766 * call AFTER fnInitBackground() to override the defaults
1767 */
1768
fnSetScrollCoordinate(int32 * params)1769 int32 Logic::fnSetScrollCoordinate(int32 *params) {
1770 // params: 0 feet_x value
1771 // 1 feet_y value
1772
1773 // Called feet_x and feet_y to retain intellectual compatibility with
1774 // Sword1!
1775 //
1776 // feet_x & feet_y refer to the physical screen coords where the
1777 // system will try to maintain George's feet
1778
1779 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
1780
1781 screenInfo->feet_x = params[0];
1782 screenInfo->feet_y = params[1];
1783 return IR_CONT;
1784 }
1785
1786 /**
1787 * Stand mega at start position of anim
1788 */
1789
fnStandAtAnim(int32 * params)1790 int32 Logic::fnStandAtAnim(int32 *params) {
1791 // params: 0 pointer to object's graphic structure
1792 // 1 pointer to object's mega structure
1793 // 2 anim resource id
1794
1795 _router->standAtAnim(
1796 decodePtr(params[0]),
1797 decodePtr(params[1]),
1798 params[2]);
1799 return IR_CONT;
1800 }
1801
1802 #define SCROLL_MOUSE_WIDTH 20
1803
fnSetScrollLeftMouse(int32 * params)1804 int32 Logic::fnSetScrollLeftMouse(int32 *params) {
1805 // params: 0 pointer to object's mouse structure
1806
1807 byte *ob_mouse = decodePtr(params[0]);
1808 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
1809
1810 // Highest priority
1811
1812 ObjectMouse mouse;
1813
1814 mouse.x1 = 0;
1815 mouse.y1 = 0;
1816 mouse.x2 = screenInfo->scroll_offset_x + SCROLL_MOUSE_WIDTH;
1817 mouse.y2 = screenInfo->screen_deep - 1;
1818 mouse.priority = 0;
1819
1820 if (screenInfo->scroll_offset_x > 0) {
1821 // not fully scrolled to the left
1822 mouse.pointer = SCROLL_LEFT_MOUSE_ID;
1823 } else {
1824 // so the mouse area doesn't get registered
1825 mouse.pointer = 0;
1826 }
1827
1828 mouse.write(ob_mouse);
1829 return IR_CONT;
1830 }
1831
fnSetScrollRightMouse(int32 * params)1832 int32 Logic::fnSetScrollRightMouse(int32 *params) {
1833 // params: 0 pointer to object's mouse structure
1834
1835 byte *ob_mouse = decodePtr(params[0]);
1836 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
1837
1838 // Highest priority
1839
1840 ObjectMouse mouse;
1841
1842 mouse.x1 = screenInfo->scroll_offset_x + _vm->_screen->getScreenWide() - SCROLL_MOUSE_WIDTH;
1843 mouse.y1 = 0;
1844 mouse.x2 = screenInfo->screen_wide - 1;
1845 mouse.y2 = screenInfo->screen_deep - 1;
1846 mouse.priority = 0;
1847
1848 if (screenInfo->scroll_offset_x < screenInfo->max_scroll_offset_x) {
1849 // not fully scrolled to the right
1850 mouse.pointer = SCROLL_RIGHT_MOUSE_ID;
1851 } else {
1852 // so the mouse area doesn't get registered
1853 mouse.pointer = 0;
1854 }
1855
1856 mouse.write(ob_mouse);
1857 return IR_CONT;
1858 }
1859
fnColor(int32 * params)1860 int32 Logic::fnColor(int32 *params) {
1861 // set border color - useful during script development
1862 // eg. set to color during a timer situation, then black when timed
1863 // out
1864
1865 // params 0: color (see defines above)
1866
1867 #ifdef SWORD2_DEBUG
1868 // what color?
1869 switch (params[0]) {
1870 case BLACK:
1871 _vm->_screen->setPalette(0, 1, black, RDPAL_INSTANT);
1872 break;
1873 case WHITE:
1874 _vm->_screen->setPalette(0, 1, white, RDPAL_INSTANT);
1875 break;
1876 case RED:
1877 _vm->_screen->setPalette(0, 1, red, RDPAL_INSTANT);
1878 break;
1879 case GREEN:
1880 _vm->_screen->setPalette(0, 1, green, RDPAL_INSTANT);
1881 break;
1882 case BLUE:
1883 _vm->_screen->setPalette(0, 1, blue, RDPAL_INSTANT);
1884 break;
1885 }
1886 #endif
1887
1888 return IR_CONT;
1889 }
1890
1891 #ifdef SWORD2_DEBUG
1892 #define BLACK 0
1893 #define WHITE 1
1894 #define RED 2
1895 #define GREEN 3
1896 #define BLUE 4
1897
1898 static const uint8 black[3] = { 0, 0, 0 };
1899 static const uint8 white[3] = { 255, 255, 255 };
1900 static const uint8 red[3] = { 255, 0, 0 };
1901 static const uint8 green[3] = { 0, 255, 0 };
1902 static const uint8 blue[3] = { 0, 0, 255 };
1903 #endif
1904
fnFlash(int32 * params)1905 int32 Logic::fnFlash(int32 *params) {
1906 // flash color 0 (ie. border) - useful during script development
1907 // eg. fnFlash(BLUE) where a text line is missed; RED when some code
1908 // missing, etc
1909
1910 // params: 0 color to flash
1911
1912 #ifdef SWORD2_DEBUG
1913 // what color?
1914 switch (params[0]) {
1915 case WHITE:
1916 _vm->_screen->setPalette(0, 1, white, RDPAL_INSTANT);
1917 break;
1918 case RED:
1919 _vm->_screen->setPalette(0, 1, red, RDPAL_INSTANT);
1920 break;
1921 case GREEN:
1922 _vm->_screen->setPalette(0, 1, green, RDPAL_INSTANT);
1923 break;
1924 case BLUE:
1925 _vm->_screen->setPalette(0, 1, blue, RDPAL_INSTANT);
1926 break;
1927 }
1928
1929 // There used to be a busy-wait loop here, so I don't know how long
1930 // the delay was meant to be. Probably doesn't matter much.
1931
1932 _vm->_screen->updateDisplay();
1933 _vm->_system->delayMillis(250);
1934 _vm->_screen->setPalette(0, 1, black, RDPAL_INSTANT);
1935 #endif
1936
1937 return IR_CONT;
1938 }
1939
fnPreFetch(int32 * params)1940 int32 Logic::fnPreFetch(int32 *params) {
1941 // Go fetch resource in the background.
1942
1943 // params: 0 resource to fetch [guess]
1944
1945 return IR_CONT;
1946 }
1947
1948 /**
1949 * Reverse of fnPassPlayerSaveData() - run script 8 of player object.
1950 */
1951
fnGetPlayerSaveData(int32 * params)1952 int32 Logic::fnGetPlayerSaveData(int32 *params) {
1953 // params: 0 pointer to object's logic structure
1954 // 1 pointer to object's graphic structure
1955 // 2 pointer to object's mega structure
1956
1957 byte *ob_logic = decodePtr(params[0]);
1958 byte *ob_graph = decodePtr(params[1]);
1959 byte *ob_mega = decodePtr(params[2]);
1960
1961 // Copy from savegame buffers to player object
1962
1963 memcpy(ob_logic, _saveLogic, ObjectLogic::size());
1964 memcpy(ob_graph, _saveGraphic, ObjectGraphic::size());
1965 memcpy(ob_mega, _saveMega, ObjectMega::size());
1966
1967 // Any walk-data must be cleared - the player will be set to stand if
1968 // he was walking when saved.
1969
1970 ObjectMega obMega(ob_mega);
1971
1972 if (obMega.getIsWalking()) {
1973 ObjectLogic obLogic(ob_logic);
1974
1975 obMega.setIsWalking(0);
1976
1977 int32 pars[3];
1978
1979 pars[0] = params[1]; // ob_graphic;
1980 pars[1] = params[2]; // ob_mega
1981 pars[2] = obMega.getCurDir();
1982
1983 fnStand(pars);
1984
1985 // Reset looping flag (which would have been 1 during fnWalk)
1986 obLogic.setLooping(0);
1987 }
1988
1989 return IR_CONT;
1990 }
1991
1992 /**
1993 * Copies the 4 essential player structures into the savegame header - run
1994 * script 7 of player object to request this.
1995 *
1996 * Remember, we cannot simply read a compact any longer but instead must
1997 * request it from the object itself.
1998 */
1999
fnPassPlayerSaveData(int32 * params)2000 int32 Logic::fnPassPlayerSaveData(int32 *params) {
2001 // params: 0 pointer to object's logic structure
2002 // 1 pointer to object's graphic structure
2003 // 2 pointer to object's mega structure
2004
2005 // Copy from player object to savegame buffers
2006
2007 memcpy(_saveLogic, decodePtr(params[0]), ObjectLogic::size());
2008 memcpy(_saveGraphic, decodePtr(params[1]), ObjectGraphic::size());
2009 memcpy(_saveMega, decodePtr(params[2]), ObjectMega::size());
2010
2011 return IR_CONT;
2012 }
2013
fnSendEvent(int32 * params)2014 int32 Logic::fnSendEvent(int32 *params) {
2015 // we want to intercept the player character and have him interact
2016 // with an object - from script
2017
2018 // params: 0 id to receive event
2019 // 1 script to run
2020
2021 sendEvent(params[0], params[1]);
2022 return IR_CONT;
2023 }
2024
2025 /**
2026 * Add this walkgrid resource to the list of those used for routing in this
2027 * location. Note that this is ignored if the resource is already in the list.
2028 */
2029
fnAddWalkGrid(int32 * params)2030 int32 Logic::fnAddWalkGrid(int32 *params) {
2031 // params: 0 id of walkgrid resource
2032
2033 // All objects that add walkgrids must be restarted whenever we
2034 // re-enter a location.
2035
2036 // DON'T EVER KILL GEORGE!
2037 if (readVar(ID) != CUR_PLAYER_ID) {
2038 // Need to call this in case it wasn't called in script!
2039 fnAddToKillList(NULL);
2040 }
2041
2042 _router->addWalkGrid(params[0]);
2043 fnPreLoad(params);
2044 return IR_CONT;
2045 }
2046
2047 /**
2048 * Remove this walkgrid resource from the list of those used for routing in
2049 * this location. Note that this is ignored if the resource isn't actually
2050 * in the list.
2051 */
2052
fnRemoveWalkGrid(int32 * params)2053 int32 Logic::fnRemoveWalkGrid(int32 *params) {
2054 // params: 0 id of walkgrid resource
2055
2056 _router->removeWalkGrid(params[0]);
2057 return IR_CONT;
2058 }
2059
2060 // like fnCheckEventWaiting, but starts the event rather than setting RESULT
2061 // to 1
2062
fnCheckForEvent(int32 * params)2063 int32 Logic::fnCheckForEvent(int32 *params) {
2064 // params: none
2065
2066 if (checkEventWaiting()) {
2067 startEvent();
2068 return IR_TERMINATE;
2069 }
2070
2071 return IR_CONT;
2072 }
2073
2074 // combination of fnPause and fnCheckForEvent
2075 // - ie. does a pause, but also checks for event each cycle
2076
fnPauseForEvent(int32 * params)2077 int32 Logic::fnPauseForEvent(int32 *params) {
2078 // params: 0 pointer to object's logic structure
2079 // 1 number of game-cycles to pause
2080
2081 ObjectLogic obLogic(decodePtr(params[0]));
2082
2083 if (checkEventWaiting()) {
2084 obLogic.setLooping(0);
2085 startEvent();
2086 return IR_TERMINATE;
2087 }
2088
2089 return fnPause(params);
2090 }
2091
fnClearEvent(int32 * params)2092 int32 Logic::fnClearEvent(int32 *params) {
2093 // params: none
2094
2095 clearEvent(readVar(ID));
2096 return IR_CONT;
2097 }
2098
fnFaceMega(int32 * params)2099 int32 Logic::fnFaceMega(int32 *params) {
2100 // params: 0 pointer to object's logic structure
2101 // 1 pointer to object's graphic structure
2102 // 2 pointer to object's mega structure
2103 // 3 pointer to object's walkdata structure
2104 // 4 id of target mega to face
2105
2106 return _router->faceMega(
2107 decodePtr(params[0]),
2108 decodePtr(params[1]),
2109 decodePtr(params[2]),
2110 decodePtr(params[3]),
2111 params[4]);
2112 }
2113
fnPlaySequence(int32 * params)2114 int32 Logic::fnPlaySequence(int32 *params) {
2115 // params: 0 pointer to null-terminated ascii filename
2116 // 1 number of frames in the sequence, used for PSX.
2117
2118 char filename[30];
2119
2120 // The original code had some #ifdef blocks for skipping or muting the
2121 // cutscenes - fondly described as "the biggest fudge in the history
2122 // of computer games" - but at the very least we want to show the
2123 // cutscene subtitles, so I removed them.
2124
2125 debug(5, "fnPlaySequence(\"%s\");", (const char *)decodePtr(params[0]));
2126
2127 // add the appropriate file extension & play it
2128
2129 strcpy(filename, (const char *)decodePtr(params[0]));
2130
2131 // Write to walkthrough file (zebug0.txt)
2132 debug(5, "PLAYING SEQUENCE \"%s\"", filename);
2133
2134 // don't want to carry on streaming game music when cutscene starts!
2135 fnStopMusic(NULL);
2136
2137 // pause sfx during sequence
2138 _vm->_sound->pauseFx();
2139
2140 uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0;
2141
2142 _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_system, frameCount);
2143
2144 if (_moviePlayer && _moviePlayer->load(filename)) {
2145 _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut);
2146 }
2147
2148 _sequenceTextLines = 0;
2149
2150 delete _moviePlayer;
2151 _moviePlayer = NULL;
2152
2153 // unpause sound fx again, in case we're staying in same location
2154 _vm->_sound->unpauseFx();
2155
2156 _smackerLeadIn = 0;
2157 _smackerLeadOut = 0;
2158
2159 // now clear the screen in case the Sequence was quitted (using ESC)
2160 // rather than fading down to black
2161
2162 _vm->_screen->clearScene();
2163
2164 // zero the entire palette in case we're about to fade up!
2165
2166 byte pal[3 * 256];
2167
2168 memset(pal, 0, sizeof(pal));
2169 _vm->_screen->setPalette(0, 256, pal, RDPAL_INSTANT);
2170
2171 debug(5, "fnPlaySequence FINISHED");
2172 return IR_CONT;
2173 }
2174
fnShadedSprite(int32 * params)2175 int32 Logic::fnShadedSprite(int32 *params) {
2176 // params: 0 pointer to object's graphic structure
2177 _router->setSpriteShading(decodePtr(params[0]), SHADED_SPRITE);
2178 return IR_CONT;
2179 }
2180
fnUnshadedSprite(int32 * params)2181 int32 Logic::fnUnshadedSprite(int32 *params) {
2182 // params: 0 pointer to object's graphic structure
2183 _router->setSpriteShading(decodePtr(params[0]), UNSHADED_SPRITE);
2184 return IR_CONT;
2185 }
2186
fnFadeUp(int32 * params)2187 int32 Logic::fnFadeUp(int32 *params) {
2188 // params: none
2189
2190 _vm->_screen->waitForFade();
2191
2192 if (_vm->_screen->getFadeStatus() == RDFADE_BLACK)
2193 _vm->_screen->fadeUp();
2194
2195 return IR_CONT;
2196 }
2197
fnDisplayMsg(int32 * params)2198 int32 Logic::fnDisplayMsg(int32 *params) {
2199 // Display a message to the user on the screen.
2200
2201 // params: 0 Text number of message to be displayed.
2202
2203 uint32 local_text = params[0] & 0xffff;
2204 uint32 text_res = params[0] / SIZE;
2205
2206 // Display message for three seconds.
2207
2208 // +2 to skip the encoded text number in the first 2 chars; 3 is
2209 // duration in seconds
2210
2211 _vm->_screen->displayMsg(_vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text) + 2, 3);
2212 _vm->_resman->closeResource(text_res);
2213
2214 return IR_CONT;
2215 }
2216
fnSetObjectHeld(int32 * params)2217 int32 Logic::fnSetObjectHeld(int32 *params) {
2218 // params: 0 luggage icon to set
2219 uint32 res = (uint32)params[0];
2220
2221 _vm->_mouse->setObjectHeld(res);
2222 return IR_CONT;
2223 }
2224
fnAddSequenceText(int32 * params)2225 int32 Logic::fnAddSequenceText(int32 *params) {
2226 // params: 0 text number
2227 // 1 frame number to start the text displaying
2228 // 2 frame number to stop the text dispalying
2229
2230 if (!readVar(DEMO)) {
2231 assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
2232
2233 _sequenceTextList[_sequenceTextLines].reset();
2234 _sequenceTextList[_sequenceTextLines]._textNumber = params[0];
2235 _sequenceTextList[_sequenceTextLines]._startFrame = params[1];
2236 _sequenceTextList[_sequenceTextLines]._endFrame = params[2];
2237 _sequenceTextLines++;
2238 }
2239
2240 return IR_CONT;
2241 }
2242
fnResetGlobals(int32 * params)2243 int32 Logic::fnResetGlobals(int32 *params) {
2244 // fnResetGlobals is used by the demo - so it can loop back & restart
2245 // itself
2246
2247 // params: none
2248
2249 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
2250
2251 byte *globals = _vm->_resman->openResource(1) + ResHeader::size();
2252 int32 size = _vm->_resman->fetchLen(1) - ResHeader::size();
2253
2254 debug(5, "globals size: %d", size);
2255
2256 // blank each global variable
2257 memset(globals, 0, size);
2258
2259 _vm->_resman->closeResource(1);
2260
2261 // all objects but george
2262 _vm->_resman->killAllObjects(false);
2263
2264 // FOR THE DEMO - FORCE THE SCROLLING TO BE RESET!
2265 // - this is taken from fnInitBackground
2266
2267 // switch on scrolling (2 means first time on screen)
2268 screenInfo->scroll_flag = 2;
2269
2270 // Used to be IR_CONT, but that's a bad idea. We may just have killed
2271 // our own script resource -- continuing will cause a bad memory read
2272 // access.
2273 return IR_STOP;
2274 }
2275
fnSetPalette(int32 * params)2276 int32 Logic::fnSetPalette(int32 *params) {
2277 // params: 0 resource number of palette file, or 0 if it's to be
2278 // the palette from the current screen
2279
2280 _vm->_screen->setFullPalette(params[0]);
2281 return IR_CONT;
2282 }
2283
2284 // use this in the object's service script prior to registering the mouse area
2285 // ie. before fnRegisterMouse or fnRegisterFrame
2286 // - best if kept at very top of service script
2287
fnRegisterPointerText(int32 * params)2288 int32 Logic::fnRegisterPointerText(int32 *params) {
2289 // params: 0 local id of text line to use as pointer text
2290
2291 _vm->_mouse->registerPointerText(params[0]);
2292 return IR_CONT;
2293 }
2294
fnFetchWait(int32 * params)2295 int32 Logic::fnFetchWait(int32 *params) {
2296 // Fetches a resource in the background but prevents the script from
2297 // continuing until the resource is in memory.
2298
2299 // params: 0 resource to fetch [guess]
2300
2301 return IR_CONT;
2302 }
2303
fnRelease(int32 * params)2304 int32 Logic::fnRelease(int32 *params) {
2305 // Releases a resource from memory. Used for freeing memory for
2306 // sprites that have just been used and will not be used again.
2307 // Sometimes it is better to kick out a sprite straight away so that
2308 // the memory can be used for more frequent animations.
2309
2310 // params: 0 resource to release [guess]
2311
2312 return IR_CONT;
2313 }
2314
fnPrepareMusic(int32 * params)2315 int32 Logic::fnPrepareMusic(int32 *params) {
2316 // params: 1 id of music to prepare [guess]
2317 return IR_CONT;
2318 }
2319
fnSoundFetch(int32 * params)2320 int32 Logic::fnSoundFetch(int32 *params) {
2321 // params: 0 id of sound to fetch [guess]
2322 return IR_CONT;
2323 }
2324
fnSmackerLeadIn(int32 * params)2325 int32 Logic::fnSmackerLeadIn(int32 *params) {
2326 // params: 0 id of lead-in music
2327
2328 // ready for use in fnPlaySequence
2329 _smackerLeadIn = params[0];
2330 return IR_CONT;
2331 }
2332
fnSmackerLeadOut(int32 * params)2333 int32 Logic::fnSmackerLeadOut(int32 *params) {
2334 // params: 0 id of lead-out music
2335
2336 // ready for use in fnPlaySequence
2337 _smackerLeadOut = params[0];
2338 return IR_CONT;
2339 }
2340
2341 /**
2342 * Stops all FX and clears the entire FX queue.
2343 */
2344
fnStopAllFx(int32 * params)2345 int32 Logic::fnStopAllFx(int32 *params) {
2346 // params: none
2347
2348 _vm->_sound->clearFxQueue(false);
2349 return IR_CONT;
2350 }
2351
fnCheckPlayerActivity(int32 * params)2352 int32 Logic::fnCheckPlayerActivity(int32 *params) {
2353 // Used to decide when to trigger music cues described as "no player
2354 // activity for a while"
2355
2356 // params: 0 threshold delay in seconds, ie. what we want to
2357 // check the actual delay against
2358
2359 uint32 seconds = (uint32)params[0];
2360
2361 _vm->_mouse->checkPlayerActivity(seconds);
2362 return IR_CONT;
2363 }
2364
fnResetPlayerActivityDelay(int32 * params)2365 int32 Logic::fnResetPlayerActivityDelay(int32 *params) {
2366 // Use if you want to deliberately reset the "no player activity"
2367 // counter for any reason
2368
2369 // params: none
2370
2371 _vm->_mouse->resetPlayerActivityDelay();
2372 return IR_CONT;
2373 }
2374
fnCheckMusicPlaying(int32 * params)2375 int32 Logic::fnCheckMusicPlaying(int32 *params) {
2376 // params: none
2377
2378 // sets result to no. of seconds of current tune remaining
2379 // or 0 if no music playing
2380
2381 // in seconds, rounded up to the nearest second
2382 writeVar(RESULT, _vm->_sound->musicTimeRemaining());
2383 return IR_CONT;
2384 }
2385
fnPlayCredits(int32 * params)2386 int32 Logic::fnPlayCredits(int32 *params) {
2387 // This function just quits the game if this is the playable demo, ie.
2388 // credits are NOT played in the demo any more!
2389
2390 // params: none
2391
2392 if (readVar(DEMO)) {
2393 _vm->quitGame();
2394 return IR_STOP;
2395 }
2396
2397 _vm->_screen->rollCredits();
2398 return IR_CONT;
2399 }
2400
fnSetScrollSpeedNormal(int32 * params)2401 int32 Logic::fnSetScrollSpeedNormal(int32 *params) {
2402 // params: none
2403
2404 _vm->_screen->setScrollFraction(16);
2405 return IR_CONT;
2406 }
2407
fnSetScrollSpeedSlow(int32 * params)2408 int32 Logic::fnSetScrollSpeedSlow(int32 *params) {
2409 // params: none
2410
2411 _vm->_screen->setScrollFraction(32);
2412 return IR_CONT;
2413 }
2414
2415 // Called from speech scripts to remove the chooser bar when it's not
2416 // appropriate to keep it displayed
2417
fnRemoveChooser(int32 * params)2418 int32 Logic::fnRemoveChooser(int32 *params) {
2419 // params: none
2420
2421 _vm->_mouse->hideMenu(RDMENU_BOTTOM);
2422 return IR_CONT;
2423 }
2424
2425 /**
2426 * Alter the volume and pan of a currently playing FX
2427 */
2428
fnSetFxVolAndPan(int32 * params)2429 int32 Logic::fnSetFxVolAndPan(int32 *params) {
2430 // params: 0 id of fx (ie. the id returned in 'result' from
2431 // fnPlayFx
2432 // 1 new volume (0..16)
2433 // 2 new pan (-16..16)
2434
2435 debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
2436
2437 _vm->_sound->setFxIdVolumePan(params[0], params[1], params[2]);
2438 return IR_CONT;
2439 }
2440
2441 /**
2442 * Alter the volume of a currently playing FX
2443 */
2444
fnSetFxVol(int32 * params)2445 int32 Logic::fnSetFxVol(int32 *params) {
2446 // params: 0 id of fx (ie. the id returned in 'result' from
2447 // fnPlayFx
2448 // 1 new volume (0..16)
2449
2450 _vm->_sound->setFxIdVolumePan(params[0], params[1]);
2451 return IR_CONT;
2452 }
2453
fnRestoreGame(int32 * params)2454 int32 Logic::fnRestoreGame(int32 *params) {
2455 // params: none
2456 return IR_CONT;
2457 }
2458
fnRefreshInventory(int32 * params)2459 int32 Logic::fnRefreshInventory(int32 *params) {
2460 // Called from 'menu_look_or_combine' script in 'menu_master' object
2461 // to update the menu to display a combined object while George runs
2462 // voice-over. Note that 'object_held' must be set to the graphic of
2463 // the combined object
2464
2465 // params: none
2466
2467 _vm->_mouse->refreshInventory();
2468 return IR_CONT;
2469 }
2470
fnChangeShadows(int32 * params)2471 int32 Logic::fnChangeShadows(int32 *params) {
2472 // params: none
2473 ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
2474
2475 // if last screen was using a shading mask (see below)
2476 if (screenInfo->mask_flag) {
2477 uint32 rv = _vm->_screen->closeLightMask();
2478 if (rv)
2479 error("Driver Error %.8x", rv);
2480 screenInfo->mask_flag = false;
2481 }
2482
2483 return IR_CONT;
2484 }
2485
2486 } // End of namespace Sword2
2487