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