1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/endian.h"
24 #include "common/rect.h"
25 #include "common/textconsole.h"
26 
27 #include "sky/autoroute.h"
28 #include "sky/compact.h"
29 #include "sky/control.h"
30 #include "sky/debug.h"
31 #include "sky/disk.h"
32 #include "sky/grid.h"
33 #include "sky/logic.h"
34 #include "sky/mouse.h"
35 #include "sky/music/musicbase.h"
36 #include "sky/text.h"
37 #include "sky/screen.h"
38 #include "sky/sky.h"
39 #include "sky/sound.h"
40 #include "sky/struc.h"
41 
42 namespace Sky {
43 
44 uint32 Logic::_scriptVariables[NUM_SKY_SCRIPTVARS];
45 
setupLogicTable()46 void Logic::setupLogicTable() {
47 	static const LogicTable logicTable[] = {
48 		&Logic::nop,
49 		&Logic::logicScript,	 // 1  script processor
50 		&Logic::autoRoute,	 // 2  Make a route
51 		&Logic::arAnim,	 // 3  Follow a route
52 		&Logic::arTurn,	 // 4  Mega turns araound
53 		&Logic::alt,		 // 5  Set up new get-to script
54 		&Logic::anim,	 // 6  Follow a sequence
55 		&Logic::turn,	 // 7  Mega turning
56 		&Logic::cursor,	 // 8  id tracks the pointer
57 		&Logic::talk,	 // 9  count down and animate
58 		&Logic::listen,	 // 10 player waits for talking id
59 		&Logic::stopped,	 // 11 wait for id to move
60 		&Logic::choose,	 // 12 wait for player to click
61 		&Logic::frames,	 // 13 animate just frames
62 		&Logic::pause,	 // 14 Count down to 0 and go
63 		&Logic::waitSync,	 // 15 Set to l_script when sync!=0
64 		&Logic::simpleAnim,	 // 16 Module anim without x,y's
65 	};
66 
67 	_logicTable = logicTable;
68 }
69 
Logic(SkyCompact * skyCompact,Screen * skyScreen,Disk * skyDisk,Text * skyText,MusicBase * skyMusic,Mouse * skyMouse,Sound * skySound)70 Logic::Logic(SkyCompact *skyCompact, Screen *skyScreen, Disk *skyDisk, Text *skyText, MusicBase *skyMusic, Mouse *skyMouse, Sound *skySound)
71 	: _rnd("sky") {
72 
73 	_skyCompact = skyCompact;
74 	_skyScreen = skyScreen;
75 	_skyDisk = skyDisk;
76 	_skyText = skyText;
77 	_skyMusic = skyMusic;
78 	_skySound = skySound;
79 	_skyMouse = skyMouse;
80 	_skyGrid = new Grid(_skyDisk, _skyCompact);
81 	_skyAutoRoute = new AutoRoute(_skyGrid, _skyCompact);
82 
83 	setupLogicTable();
84 	setupMcodeTable();
85 
86 	memset(_objectList, 0, 30 * sizeof(uint32));
87 
88 	for (int i = 0; i < ARRAYSIZE(_moduleList); i++)
89 		_moduleList[i] = 0;
90 	_stackPtr = 0;
91 
92 	_currentSection = 0xFF; //force music & sound reload
93 	initScriptVariables();
94 }
95 
~Logic()96 Logic::~Logic() {
97 	delete _skyGrid;
98 	delete _skyAutoRoute;
99 
100 	for (int i = 0; i < ARRAYSIZE(_moduleList); i++)
101 		if (_moduleList[i])
102 			free(_moduleList[i]);
103 }
104 
initScreen0()105 void Logic::initScreen0() {
106 	fnEnterSection(0, 0, 0);
107 	_skyMusic->startMusic(2);
108 	SkyEngine::_systemVars.currentMusic = 2;
109 }
110 
parseSaveData(uint32 * data)111 void Logic::parseSaveData(uint32 *data) {
112 	if (!SkyEngine::isDemo())
113 		fnLeaveSection(_scriptVariables[CUR_SECTION], 0, 0);
114 	for (uint16 cnt = 0; cnt < NUM_SKY_SCRIPTVARS; cnt++)
115 		_scriptVariables[cnt] = READ_LE_UINT32(data++);
116 	fnEnterSection(_scriptVariables[CUR_SECTION], 0, 0);
117 }
118 
checkProtection()119 bool Logic::checkProtection() {
120 	if (_scriptVariables[ENTER_DIGITS]) {
121 		if (_scriptVariables[CONSOLE_TYPE] == 5) // reactor code
122 			_scriptVariables[FS_COMMAND] = 240;
123 		else									 // copy protection
124 			_scriptVariables[FS_COMMAND] = 337;
125 		_scriptVariables[ENTER_DIGITS] = 0;
126 		return true;
127 	} else
128 		return false;
129 }
130 
engine()131 void Logic::engine() {
132 	do {
133 		uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]);
134 
135 		while (uint16 id = *logicList++) { // 0 means end of list
136 			if (id == 0xffff) {
137 				// Change logic data address
138 				logicList = (uint16 *)_skyCompact->fetchCpt(*logicList);
139 				continue;
140 			}
141 
142 			_scriptVariables[CUR_ID] = id;
143 			_compact = _skyCompact->fetchCpt(id);
144 
145 			// check the id actually wishes to be processed
146 			if (!(_compact->status & (1 << 6)))
147 				continue;
148 
149 			// ok, here we process the logic bit system
150 
151 			if (_compact->status & (1 << 7))
152 				_skyGrid->removeObjectFromWalk(_compact);
153 
154 			Debug::logic(_compact->logic);
155 			(this->*_logicTable[_compact->logic]) ();
156 
157 			if (_compact->status & (1 << 7))
158 				_skyGrid->objectToWalk(_compact);
159 
160 			// a sync sent to the compact is available for one cycle
161 			// only. that cycle has just ended so remove the sync.
162 			// presumably the mega has just reacted to it.
163 			_compact->sync = 0;
164 		}
165 		// usually this loop is run only once, it'll only be run a second time if the game
166 		// script just asked the user to enter a copy protection code.
167 		// this is done to prevent the copy protection screen from flashing up.
168 		// (otherwise it would be visible for 1/50 second)
169 	} while (checkProtection());
170 }
171 
nop()172 void Logic::nop() {}
173 
174 /**
175  * This function is basicly a wrapper around the real script engine. It runs
176  * the script engine until a script has finished.
177  * @see script()
178  */
logicScript()179 void Logic::logicScript() {
180 	/// Process the current mega's script
181 	/// If the script finishes then drop back a level
182 
183 	for (;;) {
184 		uint16 mode = _compact->mode; // get pointer to current script
185 		uint16 scriptNo = SkyCompact::getSub(_compact, mode);
186 		uint16 offset   = SkyCompact::getSub(_compact, mode + 2);
187 
188 		offset = script(scriptNo, offset);
189 		SkyCompact::setSub(_compact, mode + 2, offset);
190 
191 		if (!offset) // script finished
192 			_compact->mode -= 4;
193 		else if (_compact->mode == mode)
194 			return;
195 	}
196 }
197 
autoRoute()198 void Logic::autoRoute() {
199 
200 	_compact->downFlag = _skyAutoRoute->autoRoute(_compact);
201 	if ((_compact->downFlag == 2) && _skyCompact->cptIsId(_compact, CPT_JOEY) &&
202 	   (_compact->mode == 0) && (_compact->baseSub == JOEY_OUT_OF_LIFT)) {
203 		   // workaround for script bug #1064113. Details unclear...
204 		   _compact->downFlag = 0;
205 	}
206 	if (_compact->downFlag != 1) { // route ok
207 		_compact->grafixProgId = _compact->animScratchId;
208 		_compact->grafixProgPos = 0;
209 	}
210 
211 	_compact->logic = L_SCRIPT; // continue the script
212 
213 	logicScript();
214 	return;
215 }
216 
arAnim()217 void Logic::arAnim() {
218 	/// Follow a route
219 	/// Mega should be in getToMode
220 
221 	// only check collisions on character boundaries
222 	if ((_compact->xcood & 7) || (_compact->ycood & 7)) {
223 		mainAnim();
224 		return;
225 	}
226 
227 	// On character boundary. Have we been told to wait?
228 	// if not - are WE colliding?
229 
230 	if (_compact->waitingFor == 0xffff) { // 1st cycle of re-route does not require collision checks
231 		mainAnim();
232 		return;
233 	}
234 
235 	if (_compact->waitingFor) {
236 		// ok, we've been told we've hit someone
237 		// we will wait until we are no longer colliding
238 		// with them. here we check to see if we are (still) colliding.
239 		// if we are then run the stop script. if not clear the flag
240 		// and continue.
241 
242 		// remember - this could be the first ar cycle for some time,
243 		// we might have been told to wait months ago. if we are
244 		// waiting for one person then another hits us then
245 		// c_waiting_for will be replaced by the new mega - this is
246 		// fine because the later collision will almost certainly
247 		// take longer to clear than the earlier one.
248 
249 		if (isCollision(_skyCompact->fetchCpt(_compact->waitingFor))) {
250 			stopAndWait();
251 			return;
252 		}
253 
254 		// we are not in fact hitting this person so clr & continue
255 		// it must have registered some time ago
256 
257 		_compact->waitingFor = 0; // clear id flag
258 	}
259 
260 	// ok, our turn to check for collisions
261 
262 	uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]);
263 	Compact *cpt = 0;
264 
265 	while (uint16 id = *logicList++) { // get an id
266 
267 		if (id == 0xffff) { // address change?
268 			logicList = (uint16 *)_skyCompact->fetchCpt(*logicList); // get new logic list
269 			continue;
270 		}
271 
272 		if (id == (uint16)(_scriptVariables[CUR_ID] & 0xffff)) // is it us?
273 			continue;
274 
275 		_scriptVariables[HIT_ID] = id; // save target id for any possible c_mini_bump
276 		cpt = _skyCompact->fetchCpt(id); // let's have a closer look
277 
278 		if (!(cpt->status & (1 << ST_COLLISION_BIT))) // can it collide?
279 			continue;
280 
281 		if (cpt->screen != _compact->screen) // is it on our screen?
282 			continue;
283 
284 		if (isCollision(cpt)) { // check for a hit
285 			// ok, we've hit a mega
286 			// is it moving... or something else?
287 
288 			if (cpt->logic != L_AR_ANIM) { // check for following route
289 				// it is doing something else
290 				// we restart our get-to script
291 				// first tell it to wait for us - in case it starts moving
292 				// ( *it may have already hit us and stopped to wait )
293 
294 				_compact->waitingFor = 0xffff; // effect 1 cycle collision skip
295 				// tell it it is waiting for us
296 				cpt->waitingFor = (uint16)(_scriptVariables[CUR_ID] & 0xffff);
297 				// restart current script
298 				SkyCompact::setSub(_compact, _compact->mode + 2, 0);
299 				_compact->logic = L_SCRIPT;
300 				logicScript();
301 				return;
302 			}
303 
304 			script(_compact->miniBump, 0);
305 			return;
306 		}
307 	}
308 
309 	// ok, there was no collisions
310 	// now check for interaction request
311 	// *note: the interaction is always set up as an action script
312 
313 	if (_compact->request) {
314 		_compact->mode = C_ACTION_MODE; // put into action mode
315 		_compact->actionSub = _compact->request;
316 		_compact->actionSub_off = 0;
317 		_compact->request = 0; // trash request
318 		_compact->logic = L_SCRIPT;
319 		logicScript();
320 		return;
321 	}
322 
323 	// any flag? - or any change?
324 	// if change then re-run the current script, which must be
325 	// a position independent get-to		 ----
326 
327 	if (!_compact->atWatch) { // any flag set?
328 		mainAnim();
329 		return;
330 	}
331 
332 	// ok, there is an at watch - see if it's changed
333 
334 	if (_compact->atWas == _scriptVariables[_compact->atWatch/4]) { // still the same?
335 		mainAnim();
336 		return;
337 	}
338 
339 	// changed so restart the current script
340 	// *not suitable for base initiated ARing
341 	SkyCompact::setSub(_compact, _compact->mode + 2, 0);
342 
343 	_compact->logic = L_SCRIPT;
344 	logicScript();
345 }
346 
mainAnim()347 void Logic::mainAnim() {
348 	/// Extension of arAnim()
349 	_compact->waitingFor = 0; // clear possible zero-zero skip
350 
351 	uint16 *sequence = _skyCompact->getGrafixPtr(_compact);
352 	if (!*sequence) {
353 		// ok, move to new anim segment
354 		sequence += 2;
355 		_compact->grafixProgPos += 2;
356 		if (!*sequence) { // end of route?
357 			// ok, sequence has finished
358 
359 			// will start afresh if new sequence continues in last direction
360 			_compact->arAnimIndex = 0;
361 
362 			_compact->downFlag = 0; // pass back ok to script
363 			_compact->logic = L_SCRIPT;
364 			logicScript();
365 			return;
366 		}
367 
368 		_compact->arAnimIndex = 0; // reset position
369 	}
370 
371 	uint16 dir;
372 	while ((dir = _compact->dir) != *(sequence + 1)) {
373 		// ok, setup turning
374 		_compact->dir = *(sequence + 1);
375 
376 		uint16 *tt = _skyCompact->getTurnTable(_compact, dir);
377 		if (tt[_compact->dir]) {
378 			_compact->turnProgId = tt[_compact->dir];
379 			_compact->turnProgPos = 0;
380 			_compact->logic = L_AR_TURNING;
381 			arTurn();
382 			return;
383 		}
384 	};
385 
386 	uint16 animId = *(uint16 *)_skyCompact->getCompactElem(_compact, C_ANIM_UP + _compact->megaSet + dir * 4);
387 	uint16 *animList = (uint16 *)_skyCompact->fetchCpt(animId);
388 
389 	uint16 arAnimIndex = _compact->arAnimIndex;
390 	if (!animList[arAnimIndex / 2]) {
391 		 arAnimIndex = 0;
392 		_compact->arAnimIndex = 0; // reset
393 	}
394 
395 	_compact->arAnimIndex += S_LENGTH;
396 
397 	*sequence       -= animList[(S_COUNT + arAnimIndex)/2]; // reduce the distance to travel
398 	_compact->frame  = animList[(S_FRAME + arAnimIndex)/2]; // new graphic frame
399 	_compact->xcood += animList[(S_AR_X  + arAnimIndex)/2]; // update x coordinate
400 	_compact->ycood += animList[(S_AR_Y  + arAnimIndex)/2]; // update y coordinate
401 }
402 
arTurn()403 void Logic::arTurn() {
404 	uint16 *turnData = (uint16 *)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos;
405 	_compact->frame = *turnData++;
406 	_compact->turnProgPos++;
407 
408 	if (!*turnData) { // turn done?
409 		// Back to ar mode
410 		_compact->arAnimIndex = 0;
411 		_compact->logic = L_AR_ANIM;
412 	}
413 }
414 
alt()415 void Logic::alt() {
416 	/// change the current script
417 	_compact->logic = L_SCRIPT;
418 	SkyCompact::setSub(_compact, _compact->mode, _compact->alt);
419 	SkyCompact::setSub(_compact, _compact->mode + 2, 0);
420 	logicScript();
421 }
422 
anim()423 void Logic::anim() {
424 	/// Follow an animation sequence
425 	uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact);
426 
427 	while (*grafixProg) {
428 		_compact->grafixProgPos += 3; // all types are 3 words.
429 		if (*grafixProg == LF_START_FX) { // do fx
430 			grafixProg++;
431 			uint16 sound = *grafixProg++;
432 			uint16 volume = *grafixProg++;
433 
434 			// channel 0
435 			fnStartFx(sound, 0, volume);
436 		} else if (*grafixProg >= LF_START_FX) { // do sync
437 			grafixProg++;
438 
439 			Compact *cpt = _skyCompact->fetchCpt(*grafixProg++);
440 
441 			cpt->sync = *grafixProg++;
442 		} else { // put coordinates and frame in
443 			_compact->xcood = *grafixProg++;
444 			_compact->ycood = *grafixProg++;
445 
446 			_compact->frame = *grafixProg++ | _compact->offset;
447 			return;
448 		}
449 	}
450 
451 	_compact->downFlag = 0;
452 	_compact->logic = L_SCRIPT;
453 	logicScript();
454 }
455 
turn()456 void Logic::turn() {
457 	uint16 *turnData = (uint16 *)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos;
458 	if (*turnData) {
459 		_compact->frame = *turnData;
460 		_compact->turnProgPos++;
461 		return;
462 	}
463 
464 	// turn_to_script:
465 	_compact->arAnimIndex = 0;
466 	_compact->logic = L_SCRIPT;
467 
468 	logicScript();
469 }
470 
cursor()471 void Logic::cursor() {
472 	_skyText->logicCursor(_compact, _skyMouse->giveMouseX(), _skyMouse->giveMouseY());
473 }
474 
475 static uint16 clickTable[46] = {
476 	ID_FOSTER,
477 	ID_JOEY,
478 	ID_JOBS,
479 	ID_LAMB,
480 	ID_ANITA,
481 	ID_SON,
482 	ID_DAD,
483 	ID_MONITOR,
484 	ID_SHADES,
485 	MINI_SS,
486 	FULL_SS,
487 	ID_FOREMAN,
488 	ID_RADMAN,
489 	ID_GALLAGER_BEL,
490 	ID_BURKE,
491 	ID_BODY,
492 	ID_HOLO,
493 	ID_TREVOR,
494 	ID_ANCHOR,
495 	ID_WRECK_GUARD,
496 	ID_SKORL_GUARD,
497 
498 	// BASE LEVEL
499 	ID_SC30_HENRI,
500 	ID_SC31_GUARD,
501 	ID_SC32_VINCENT,
502 	ID_SC32_GARDENER,
503 	ID_SC32_BUZZER,
504 	ID_SC36_BABS,
505 	ID_SC36_BARMAN,
506 	ID_SC36_COLSTON,
507 	ID_SC36_GALLAGHER,
508 	ID_SC36_JUKEBOX,
509 	ID_DANIELLE,
510 	ID_SC42_JUDGE,
511 	ID_SC42_CLERK,
512 	ID_SC42_PROSECUTION,
513 	ID_SC42_JOBSWORTH,
514 
515 	// UNDERWORLD
516 	ID_MEDI,
517 	ID_WITNESS,
518 	ID_GALLAGHER,
519 	ID_KEN,
520 	ID_SC76_ANDROID_2,
521 	ID_SC76_ANDROID_3,
522 	ID_SC81_FATHER,
523 	ID_SC82_JOBSWORTH,
524 
525 	// LINC WORLD
526 	ID_HOLOGRAM_B,
527 	12289
528 };
529 
talk()530 void Logic::talk() {
531 	// first count through the frames
532 	// just frames - nothing tweeky
533 	// the speech finishes when the timer runs out &
534 	// not when the animation finishes
535 	// this routine is very task specific
536 
537 	// TODO: Check for mouse clicking
538 
539 	// Are we allowed to click
540 
541 	if (_skyMouse->wasClicked())
542 		for (int i = 0; i < ARRAYSIZE(clickTable); i++)
543 			if (clickTable[i] == (uint16)_scriptVariables[CUR_ID]) {
544 				if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_SPEECH) && (!_skySound->speechFinished()))
545 					_skySound->stopSpeech();
546 				if ((_compact->spTextId > 0) &&
547 					(_compact->spTextId < 0xFFFF)) {
548 
549 					_skyCompact->fetchCpt(_compact->spTextId)->status = 0;
550 				}
551 				if (_skyCompact->getGrafixPtr(_compact)) {
552 					_compact->frame = _compact->getToFlag; // set character to stand
553 					_compact->grafixProgId = 0;
554 				}
555 
556 				_compact->logic = L_SCRIPT;
557 				logicScript();
558 				return;
559 			}
560 
561 	// If speech is allowed then check for it to finish before finishing animations
562 
563 	if ((_compact->spTextId == 0xFFFF) && // is this a voc file?
564 		(_skySound->speechFinished())) { // finished?
565 
566 		_compact->logic = L_SCRIPT; // restart character control
567 
568 		if (_skyCompact->getGrafixPtr(_compact)) {
569 			_compact->frame = _compact->getToFlag; // set character to stand
570 			_compact->grafixProgId = 0;
571 		}
572 
573 		logicScript();
574 		return;
575 	}
576 
577 	uint16 *graphixProg = _skyCompact->getGrafixPtr(_compact);
578 	if (graphixProg) {
579 		if ((*graphixProg) && ((_compact->spTime != 3) || (!_skySound->speechFinished()))) {
580 			// we will force the animation to finish 3 game cycles
581 			// before the speech actually finishes - because it looks good.
582 
583 			_compact->frame = *(graphixProg + 2) + _compact->offset;
584 			graphixProg += 3;
585 			_compact->grafixProgPos += 3;
586 		} else {
587 			// we ran out of frames or finished speech, let actor stand still.
588 			_compact->frame = _compact->getToFlag;
589 			_compact->grafixProgId = 0;
590 		}
591 	}
592 
593 	if (_skySound->speechFinished()) _compact->spTime--;
594 
595 	if (_compact->spTime == 0) {
596 
597 		// ok, speech has finished
598 
599 		if (_compact->spTextId) {
600 			Compact *cpt = _skyCompact->fetchCpt(_compact->spTextId); // get text id to kill
601 			cpt->status = 0; // kill the text
602 		}
603 
604 		_compact->logic = L_SCRIPT;
605 		logicScript();
606 	}
607 }
608 
listen()609 void Logic::listen() {
610 	/// Stay in this mode until id in getToFlag leaves L_TALK mode
611 
612 	Compact *cpt = _skyCompact->fetchCpt(_compact->flag);
613 
614 	if (cpt->logic == L_TALK)
615 		return;
616 
617 	_compact->logic = L_SCRIPT;
618 	logicScript();
619 }
620 
stopped()621 void Logic::stopped() {
622 	/// waiting for another mega to move or give-up trying
623 	///
624 	/// this mode will always be set up from a special script
625 	/// that will be one level higher than the script we
626 	/// would wish to restart from
627 
628 	Compact *cpt = _skyCompact->fetchCpt(_compact->waitingFor);
629 
630 	if (cpt)
631 		if (!cpt->mood && isCollision(cpt))
632 			return;
633 
634 	// we are free, continue processing the script
635 
636 	// restart script one level below
637 	SkyCompact::setSub(_compact, _compact->mode - 2, 0);
638 	_compact->waitingFor = 0xffff;
639 
640 	_compact->logic = L_SCRIPT;
641 	logicScript();
642 }
643 
choose()644 void Logic::choose() {
645 	// Remain in this mode until player selects some text
646 	if (!_scriptVariables[THE_CHOSEN_ONE])
647 		return;
648 
649 	fnNoHuman(0, 0, 0); // kill mouse again
650 
651 	SkyEngine::_systemVars.systemFlags &= ~SF_CHOOSING; // restore save/restore
652 
653 	_compact->logic = L_SCRIPT; // and continue script
654 	logicScript();
655 }
656 
frames()657 void Logic::frames() {
658 	if (!_compact->sync)
659 		simpleAnim();
660 	else {
661 		_compact->downFlag = 0; // return 'ok' to script
662 		_compact->logic = L_SCRIPT;
663 		logicScript();
664 	}
665 }
666 
pause()667 void Logic::pause() {
668 	if (--_compact->flag)
669 		return;
670 
671 	_compact->logic = L_SCRIPT;
672 	logicScript();
673 	return;
674 }
675 
waitSync()676 void Logic::waitSync() {
677 	/// checks c_sync, when its non 0
678 	/// the id is put back into script mode
679 	// use this instead of loops in the script
680 
681 	if (!_compact->sync)
682 		return;
683 
684 	_compact->logic = L_SCRIPT;
685 	logicScript();
686 }
687 
simpleAnim()688 void Logic::simpleAnim() {
689 	/// follow an animation sequence module whilst ignoring the coordinate data
690 
691 	uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact);
692 
693 	// *grafix_prog: command
694 	while (*grafixProg) {
695 		_compact->grafixProgPos += 3;
696 		if (*grafixProg != SEND_SYNC) {
697 			grafixProg++;
698 			grafixProg++; // skip coordinates
699 
700 			// *grafix_prog: frame
701 			if (*grafixProg >= 64)
702 				_compact->frame = *grafixProg;
703 			else
704 				_compact->frame = *grafixProg + _compact->offset;
705 
706 			return;
707 		}
708 
709 		grafixProg++;
710 		// *grafix_prog: id to sync
711 		Compact *compact2 = _skyCompact->fetchCpt(*grafixProg);
712 		grafixProg++;
713 
714 		// *grafix_prog: sync
715 		compact2->sync = *grafixProg;
716 		grafixProg++;
717 	}
718 
719 	_compact->downFlag = 0; // return 'ok' to script
720 	_compact->logic = L_SCRIPT;
721 	logicScript();
722 }
723 
724 /** Checks if the currently processed object in _compact collides
725 	with the one given as parameter */
isCollision(Compact * other)726 bool Logic::isCollision(Compact *other) {
727 	MegaSet *thisMegaSet = SkyCompact::getMegaSet(_compact);
728 	MegaSet *otherMegaSet = SkyCompact::getMegaSet(other);
729 
730 	// target's base coordinates
731 	uint16 otherX = other->xcood & ~7;
732 	uint16 otherY = other->ycood & ~7;
733 	if ((_compact->dir == UPY) || (_compact->dir == DOWNY)) { // If we're looking up or down...
734 		otherX -= thisMegaSet->colOffset; // ...then compensate inner otherX offsets
735 		otherX += otherMegaSet->colOffset;
736 	}
737 
738 	if ((_compact->dir == UPY) || (_compact->dir == DOWNY)) {
739 		// Check X coordinate, same for facing up or down
740 		if (otherX + otherMegaSet->colWidth < _compact->xcood) // their rightmost
741 			return false; // other is left of us
742 
743 		if (otherX - thisMegaSet->colWidth >= _compact->xcood) // our left, their right
744 			return false; // other is right of us
745 		// Check Y coordinate according to actual direction
746 		if (_compact->dir == UPY) {
747 			if (otherY + 8 == _compact->ycood)
748 				return true;
749 			if (otherY + 16 == _compact->ycood)
750 				return true;
751 		} else {
752 			if (otherY - 8 == _compact->ycood)
753 				return true;
754 			if (otherY - 16 == _compact->ycood)
755 				return true;
756 		}
757 	} else {
758 		// Facing left, right (or talking, which probably never happens)
759 		if (otherY != _compact->ycood)
760 			return false;
761 		if (_compact->dir == LEFTY) { // looking left
762 			if (otherX + otherMegaSet->lastChr == _compact->xcood)
763 				return true;
764 			if (otherX + otherMegaSet->lastChr - 8 == _compact->xcood)
765 				return true;
766 		} else {
767 			if (otherX - thisMegaSet->lastChr == _compact->xcood)
768 				return true;
769 			if (otherX - thisMegaSet->lastChr - 8 == _compact->xcood)
770 				return true;
771 		}
772 	}
773 	return false;
774 }
775 
runGetOff()776 void Logic::runGetOff() {
777 	uint32 getOff = _scriptVariables[GET_OFF];
778 	_scriptVariables[GET_OFF] = 0;
779 	if (getOff)
780 		script((uint16)(getOff & 0xffff), (uint16)(getOff >> 16));
781 }
782 
stopAndWait()783 void Logic::stopAndWait() {
784 	_compact->mode += 4;
785 
786 	SkyCompact::setSub(_compact, _compact->mode, _compact->stopScript);
787 	SkyCompact::setSub(_compact, _compact->mode + 2, 0);
788 
789 	_compact->logic = L_SCRIPT;
790 	logicScript();
791 }
792 
checkModuleLoaded(uint16 moduleNo)793 void Logic::checkModuleLoaded(uint16 moduleNo) {
794 	if (!_moduleList[moduleNo])
795 		_moduleList[moduleNo] = (uint16 *)_skyDisk->loadFile((uint16)moduleNo + F_MODULE_0);
796 }
797 
push(uint32 a)798 void Logic::push(uint32 a) {
799 	if (_stackPtr > ARRAYSIZE(_stack) - 2)
800 		error("Stack overflow");
801 	_stack[_stackPtr++] = a;
802 }
803 
pop()804 uint32 Logic::pop() {
805 	if (_stackPtr < 1 || _stackPtr > ARRAYSIZE(_stack) - 1)
806 		error("No items on Stack to pop");
807 	return _stack[--_stackPtr];
808 }
809 
setupMcodeTable()810 void Logic::setupMcodeTable() {
811 	static const McodeTable mcodeTable[] = {
812 		&Logic::fnCacheChip,
813 		&Logic::fnCacheFast,
814 		&Logic::fnDrawScreen,
815 		&Logic::fnAr,
816 		&Logic::fnArAnimate,
817 		&Logic::fnIdle,
818 		&Logic::fnInteract,
819 		&Logic::fnStartSub,
820 		&Logic::fnTheyStartSub,
821 		&Logic::fnAssignBase,
822 		&Logic::fnDiskMouse,
823 		&Logic::fnNormalMouse,
824 		&Logic::fnBlankMouse,
825 		&Logic::fnCrossMouse,
826 		&Logic::fnCursorRight,
827 		&Logic::fnCursorLeft,
828 		&Logic::fnCursorDown,
829 		&Logic::fnOpenHand,
830 		&Logic::fnCloseHand,
831 		&Logic::fnGetTo,
832 		&Logic::fnSetToStand,
833 		&Logic::fnTurnTo,
834 		&Logic::fnArrived,
835 		&Logic::fnLeaving,
836 		&Logic::fnSetAlternate,
837 		&Logic::fnAltSetAlternate,
838 		&Logic::fnKillId,
839 		&Logic::fnNoHuman,
840 		&Logic::fnAddHuman,
841 		&Logic::fnAddButtons,
842 		&Logic::fnNoButtons,
843 		&Logic::fnSetStop,
844 		&Logic::fnClearStop,
845 		&Logic::fnPointerText,
846 		&Logic::fnQuit,
847 		&Logic::fnSpeakMe,
848 		&Logic::fnSpeakMeDir,
849 		&Logic::fnSpeakWait,
850 		&Logic::fnSpeakWaitDir,
851 		&Logic::fnChooser,
852 		&Logic::fnHighlight,
853 		&Logic::fnTextKill,
854 		&Logic::fnStopMode,
855 		&Logic::fnWeWait,
856 		&Logic::fnSendSync,
857 		&Logic::fnSendFastSync,
858 		&Logic::fnSendRequest,
859 		&Logic::fnClearRequest,
860 		&Logic::fnCheckRequest,
861 		&Logic::fnStartMenu,
862 		&Logic::fnUnhighlight,
863 		&Logic::fnFaceId,
864 		&Logic::fnForeground,
865 		&Logic::fnBackground,
866 		&Logic::fnNewBackground,
867 		&Logic::fnSort,
868 		&Logic::fnNoSpriteEngine,
869 		&Logic::fnNoSpritesA6,
870 		&Logic::fnResetId,
871 		&Logic::fnToggleGrid,
872 		&Logic::fnPause,
873 		&Logic::fnRunAnimMod,
874 		&Logic::fnSimpleMod,
875 		&Logic::fnRunFrames,
876 		&Logic::fnAwaitSync,
877 		&Logic::fnIncMegaSet,
878 		&Logic::fnDecMegaSet,
879 		&Logic::fnSetMegaSet,
880 		&Logic::fnMoveItems,
881 		&Logic::fnNewList,
882 		&Logic::fnAskThis,
883 		&Logic::fnRandom,
884 		&Logic::fnPersonHere,
885 		&Logic::fnToggleMouse,
886 		&Logic::fnMouseOn,
887 		&Logic::fnMouseOff,
888 		&Logic::fnFetchX,
889 		&Logic::fnFetchY,
890 		&Logic::fnTestList,
891 		&Logic::fnFetchPlace,
892 		&Logic::fnCustomJoey,
893 		&Logic::fnSetPalette,
894 		&Logic::fnTextModule,
895 		&Logic::fnChangeName,
896 		&Logic::fnMiniLoad,
897 		&Logic::fnFlushBuffers,
898 		&Logic::fnFlushChip,
899 		&Logic::fnSaveCoods,
900 		&Logic::fnPlotGrid,
901 		&Logic::fnRemoveGrid,
902 		&Logic::fnEyeball,
903 		&Logic::fnCursorUp,
904 		&Logic::fnLeaveSection,
905 		&Logic::fnEnterSection,
906 		&Logic::fnRestoreGame,
907 		&Logic::fnRestartGame,
908 		&Logic::fnNewSwingSeq,
909 		&Logic::fnWaitSwingEnd,
910 		&Logic::fnSkipIntroCode,
911 		&Logic::fnBlankScreen,
912 		&Logic::fnPrintCredit,
913 		&Logic::fnLookAt,
914 		&Logic::fnLincTextModule,
915 		&Logic::fnTextKill2,
916 		&Logic::fnSetFont,
917 		&Logic::fnStartFx,
918 		&Logic::fnStopFx,
919 		&Logic::fnStartMusic,
920 		&Logic::fnStopMusic,
921 		&Logic::fnFadeDown,
922 		&Logic::fnFadeUp,
923 		&Logic::fnQuitToDos,
924 		&Logic::fnPauseFx,
925 		&Logic::fnUnPauseFx,
926 		&Logic::fnPrintf
927 	};
928 
929 	_mcodeTable = mcodeTable;
930 }
931 
932 static const uint32 forwardList1b[] = {
933 	JOBS_SPEECH,
934 	JOBS_S4,
935 	JOBS_ALARMED,
936 	JOEY_RECYCLE,
937 	SHOUT_SSS,
938 	JOEY_MISSION,
939 	TRANS_MISSION,
940 	SLOT_MISSION,
941 	CORNER_MISSION,
942 	JOEY_LOGIC,
943 	GORDON_SPEECH,
944 	JOEY_BUTTON_MISSION,
945 	LOB_DAD_SPEECH,
946 	LOB_SON_SPEECH,
947 	GUARD_SPEECH,
948 	MANTRACH_SPEECH,
949 	WRECK_SPEECH,
950 	ANITA_SPEECH,
951 	LAMB_FACTORY,
952 	FORE_SPEECH,
953 	JOEY_42_MISS,
954 	JOEY_JUNCTION_MISS,
955 	WELDER_MISSION,
956 	JOEY_WELD_MISSION,
957 	RADMAN_SPEECH,
958 	LINK_7_29,
959 	LINK_29_7,
960 	LAMB_TO_3,
961 	LAMB_TO_2,
962 	BURKE_SPEECH,
963 	BURKE_1,
964 	BURKE_2,
965 	DR_BURKE_1,
966 	JASON_SPEECH,
967 	JOEY_BELLEVUE,
968 	ANCHOR_SPEECH,
969 	ANCHOR_MISSION,
970 	JOEY_PC_MISSION,
971 	HOOK_MISSION,
972 	TREVOR_SPEECH,
973 	JOEY_FACTORY,
974 	HELGA_SPEECH,
975 	JOEY_HELGA_MISSION,
976 	GALL_BELLEVUE,
977 	GLASS_MISSION,
978 	LAMB_FACT_RETURN,
979 	LAMB_LEAVE_GARDEN,
980 	LAMB_START_29,
981 	LAMB_BELLEVUE,
982 	CABLE_MISSION,
983 	FOSTER_TOUR,
984 	LAMB_TOUR,
985 	FOREMAN_LOGIC,
986 	LAMB_LEAVE_FACTORY,
987 	LAMB_BELL_LOGIC,
988 	LAMB_FACT_2,
989 	START90,
990 	0,
991 	0,
992 	LINK_28_31,
993 	LINK_31_28,
994 	EXIT_LINC,
995 	DEATH_SCRIPT
996 };
997 
998 static uint32 forwardList1b288[] = {
999 	JOBS_SPEECH,
1000 	JOBS_S4,
1001 	JOBS_ALARMED,
1002 	JOEY_RECYCLE,
1003 	SHOUT_SSS,
1004 	JOEY_MISSION,
1005 	TRANS_MISSION,
1006 	SLOT_MISSION,
1007 	CORNER_MISSION,
1008 	JOEY_LOGIC,
1009 	GORDON_SPEECH,
1010 	JOEY_BUTTON_MISSION,
1011 	LOB_DAD_SPEECH,
1012 	LOB_SON_SPEECH,
1013 	GUARD_SPEECH,
1014 	0x68,
1015 	WRECK_SPEECH,
1016 	ANITA_SPEECH,
1017 	LAMB_FACTORY,
1018 	FORE_SPEECH,
1019 	JOEY_42_MISS,
1020 	JOEY_JUNCTION_MISS,
1021 	WELDER_MISSION,
1022 	JOEY_WELD_MISSION,
1023 	RADMAN_SPEECH,
1024 	LINK_7_29,
1025 	LINK_29_7,
1026 	LAMB_TO_3,
1027 	LAMB_TO_2,
1028 	0x3147,
1029 	0x3100,
1030 	0x3101,
1031 	0x3102,
1032 	0x3148,
1033 	0x3149,
1034 	0x314A,
1035 	0x30C5,
1036 	0x30C6,
1037 	0x30CB,
1038 	0x314B,
1039 	JOEY_FACTORY,
1040 	0x314C,
1041 	0x30E2,
1042 	0x314D,
1043 	0x310C,
1044 	LAMB_FACT_RETURN,
1045 	0x3139,
1046 	0x313A,
1047 	0x004F,
1048 	CABLE_MISSION,
1049 	FOSTER_TOUR,
1050 	LAMB_TOUR,
1051 	FOREMAN_LOGIC,
1052 	LAMB_LEAVE_FACTORY,
1053 	0x3138,
1054 	LAMB_FACT_2,
1055 	0x004D,
1056 	0,
1057 	0,
1058 	LINK_28_31,
1059 	LINK_31_28,
1060 	0x004E,
1061 	DEATH_SCRIPT
1062 };
1063 
1064 static const uint32 forwardList2b[] = {
1065 	STD_ON,
1066 	STD_EXIT_LEFT_ON,
1067 	STD_EXIT_RIGHT_ON,
1068 	ADVISOR_188,
1069 	SHOUT_ACTION,
1070 	MEGA_CLICK,
1071 	MEGA_ACTION
1072 };
1073 
1074 static const uint32 forwardList3b[] = {
1075 	DANI_SPEECH,
1076 	DANIELLE_GO_HOME,
1077 	SPUNKY_GO_HOME,
1078 	HENRI_SPEECH,
1079 	BUZZER_SPEECH,
1080 	FOSTER_VISIT_DANI,
1081 	DANIELLE_LOGIC,
1082 	JUKEBOX_SPEECH,
1083 	VINCENT_SPEECH,
1084 	EDDIE_SPEECH,
1085 	BLUNT_SPEECH,
1086 	DANI_ANSWER_PHONE,
1087 	SPUNKY_SEE_VIDEO,
1088 	SPUNKY_BARK_AT_FOSTER,
1089 	SPUNKY_SMELLS_FOOD,
1090 	BARRY_SPEECH,
1091 	COLSTON_SPEECH,
1092 	GALL_SPEECH,
1093 	BABS_SPEECH,
1094 	CHUTNEY_SPEECH,
1095 	FOSTER_ENTER_COURT
1096 };
1097 
1098 static const uint32 forwardList4b[] = {
1099 	WALTER_SPEECH,
1100 	JOEY_MEDIC,
1101 	JOEY_MED_LOGIC,
1102 	JOEY_MED_MISSION72,
1103 	KEN_LOGIC,
1104 	KEN_SPEECH,
1105 	KEN_MISSION_HAND,
1106 	SC70_IRIS_OPENED,
1107 	SC70_IRIS_CLOSED,
1108 	FOSTER_ENTER_BOARDROOM,
1109 	BORED_ROOM,
1110 	FOSTER_ENTER_NEW_BOARDROOM,
1111 	HOBS_END,
1112 	SC82_JOBS_SSS
1113 };
1114 
1115 static const uint32 forwardList5b[] = {
1116 	SET_UP_INFO_WINDOW,
1117 	SLAB_ON,
1118 	UP_MOUSE,
1119 	DOWN_MOUSE,
1120 	LEFT_MOUSE,
1121 	RIGHT_MOUSE,
1122 	DISCONNECT_FOSTER
1123 };
1124 
fnExec(uint16 num,uint32 a,uint32 b,uint32 c)1125 void Logic::fnExec(uint16 num, uint32 a, uint32 b, uint32 c) {
1126 	(this->*_mcodeTable[num])(a, b, c);
1127 }
1128 
initScriptVariables()1129 void Logic::initScriptVariables() {
1130 	for (int i = 0; i < ARRAYSIZE(_scriptVariables); i++)
1131 		_scriptVariables[i] = 0;
1132 
1133 	_scriptVariables[LOGIC_LIST_NO] = 141;
1134 	_scriptVariables[LAMB_GREET] = 62;
1135 	_scriptVariables[JOEY_SECTION] = 1;
1136 	_scriptVariables[LAMB_SECTION] = 2;
1137 	_scriptVariables[S15_FLOOR] = 8371;
1138 	_scriptVariables[GUARDIAN_THERE] = 1;
1139 	_scriptVariables[DOOR_67_68_FLAG] = 1;
1140 	_scriptVariables[SC70_IRIS_FLAG] = 3;
1141 	_scriptVariables[DOOR_73_75_FLAG] = 1;
1142 	_scriptVariables[SC76_CABINET1_FLAG] = 1;
1143 	_scriptVariables[SC76_CABINET2_FLAG] = 1;
1144 	_scriptVariables[SC76_CABINET3_FLAG] = 1;
1145 	_scriptVariables[DOOR_77_78_FLAG] = 1;
1146 	_scriptVariables[SC80_EXIT_FLAG] = 1;
1147 	_scriptVariables[SC31_LIFT_FLAG] = 1;
1148 	_scriptVariables[SC32_LIFT_FLAG] = 1;
1149 	_scriptVariables[SC33_SHED_DOOR_FLAG] = 1;
1150 	_scriptVariables[BAND_PLAYING] = 1;
1151 	_scriptVariables[COLSTON_AT_TABLE] = 1;
1152 	_scriptVariables[SC36_NEXT_DEALER] = 16731;
1153 	_scriptVariables[SC36_DOOR_FLAG] = 1;
1154 	_scriptVariables[SC37_DOOR_FLAG] = 2;
1155 	_scriptVariables[SC40_LOCKER_1_FLAG] = 1;
1156 	_scriptVariables[SC40_LOCKER_2_FLAG] = 1;
1157 	_scriptVariables[SC40_LOCKER_3_FLAG] = 1;
1158 	_scriptVariables[SC40_LOCKER_4_FLAG] = 1;
1159 	_scriptVariables[SC40_LOCKER_5_FLAG] = 1;
1160 
1161 	if (SkyEngine::_systemVars.gameVersion == 288)
1162 		memcpy(_scriptVariables + 352, forwardList1b288, sizeof(forwardList1b288));
1163 	else
1164 		memcpy(_scriptVariables + 352, forwardList1b, sizeof(forwardList1b));
1165 
1166 	memcpy(_scriptVariables + 656, forwardList2b, sizeof(forwardList2b));
1167 	memcpy(_scriptVariables + 721, forwardList3b, sizeof(forwardList3b));
1168 	memcpy(_scriptVariables + 663, forwardList4b, sizeof(forwardList4b));
1169 	memcpy(_scriptVariables + 505, forwardList5b, sizeof(forwardList5b));
1170 }
1171 
mouseScript(uint32 scrNum,Compact * scriptComp)1172 uint16 Logic::mouseScript(uint32 scrNum, Compact *scriptComp) {
1173 	Compact *tmpComp = _compact;
1174 	_compact = scriptComp;
1175 	uint16 retVal = script((uint16)(scrNum & 0xFFFF), (uint16)(scrNum >> 16));
1176 	_compact = tmpComp;
1177 
1178 	if (scrNum == MENU_SELECT || (scrNum >= LINC_MENU_SELECT && scrNum <= DOC_MENU_SELECT)) {
1179 		// HACK: See patch #1689516 for details. The short story:
1180 		// The user has clicked on an inventory item.  We update the
1181 		// mouse cursor instead of waiting for the script to update it.
1182 		// In the original game the cursor is just updated when the mouse
1183 		// moves away the item, but it's unintuitive.
1184 		fnCrossMouse(0, 0, 0);
1185 	}
1186 
1187 	return retVal;
1188 }
1189 
1190 /**
1191  * This is the actual script engine.  It interprets script \a scriptNo starting at \a offset
1192  *
1193  * @param scriptNo The script to interpret.
1194  *	 @arg Bits 0-11 - Script number
1195  *	 @arg Bits 12-15 - Module number
1196  * @param offset At which offset to start interpreting the script.
1197  *
1198  * @return 0 if script finished. Else offset where to continue.
1199  */
script(uint16 scriptNo,uint16 offset)1200 uint16 Logic::script(uint16 scriptNo, uint16 offset) {
1201 	do {
1202 		bool restartScript = false;
1203 
1204 		/// process a script
1205 		/// low level interface to interpreter
1206 
1207 		uint16 moduleNo = scriptNo >> 12;
1208 		uint16 *scriptData = _moduleList[moduleNo]; // get module address
1209 
1210 		if (!scriptData) { // We need to load the script module
1211 			_moduleList[moduleNo] = _skyDisk->loadScriptFile(moduleNo + F_MODULE_0);
1212 			 scriptData = _moduleList[moduleNo]; // module has been loaded
1213 		}
1214 
1215 		uint16 *moduleStart = scriptData;
1216 
1217 		debug(3, "Doing Script: %d:%d:%x", moduleNo, scriptNo & 0xFFF, offset ? (offset - moduleStart[scriptNo & 0xFFF]) : 0);
1218 
1219 		// WORKAROUND for bug #3149412: "Invalid Mode when giving shades to travel agent"
1220 		// Using the dark glasses on Trevor (travel agent) multiple times in succession would
1221 		// wreck the trevor compact's mode, as the script in question doesn't account for using
1222 		// this item at this point in the game (you will only have it here if you play the game
1223 		// in an unusual way) and thus would loop indefinitely / never drop out.
1224 		// To prevent this, we trigger the generic response by pretending we're using an item
1225 		// which the script /does/ handle.
1226 		if (scriptNo == TREVOR_SPEECH && _scriptVariables[OBJECT_HELD] == IDO_SHADES)
1227 			_scriptVariables[OBJECT_HELD] = IDO_GLASS;
1228 
1229 
1230 		// Check whether we have an offset or what
1231 		if (offset)
1232 			scriptData = moduleStart + offset;
1233 		else
1234 			scriptData += scriptData[scriptNo & 0x0FFF];
1235 
1236 		uint32 a = 0, b = 0, c = 0;
1237 		uint16 command, s;
1238 
1239 		while(!restartScript) {
1240 			command = *scriptData++; // get a command
1241 			Debug::script(command, scriptData);
1242 
1243 			switch (command) {
1244 			case 0: // push_variable
1245 				push( _scriptVariables[*scriptData++ / 4] );
1246 				break;
1247 			case 1: // less_than
1248 				a = pop();
1249 				b = pop();
1250 				if (a > b)
1251 					push(1);
1252 				else
1253 					push(0);
1254 				break;
1255 			case 2: // push_number
1256 				push(*scriptData++);
1257 				break;
1258 			case 3: // not_equal
1259 				a = pop();
1260 				b = pop();
1261 				if (a != b)
1262 					push(1);
1263 				else
1264 					push(0);
1265 				break;
1266 			case 4: // if_and
1267 				a = pop();
1268 				b = pop();
1269 				if (a && b)
1270 					push(1);
1271 				else
1272 					push(0);
1273 				break;
1274 			case 5: // skip_zero
1275 				s = *scriptData++;
1276 
1277 				a = pop();
1278 				if (!a)
1279 					scriptData += s / 2;
1280 				break;
1281 			case 6: // pop_var
1282 				b = _scriptVariables[*scriptData++ / 4] = pop();
1283 				break;
1284 			case 7: // minus
1285 				a = pop();
1286 				b = pop();
1287 				push(b-a);
1288 				break;
1289 			case 8: // plus
1290 				a = pop();
1291 				b = pop();
1292 				push(b+a);
1293 				break;
1294 			case 9: // skip_always
1295 				s = *scriptData++;
1296 				scriptData += s / 2;
1297 				break;
1298 			case 10: // if_or
1299 				a = pop();
1300 				b = pop();
1301 				if (a || b)
1302 					push(1);
1303 				else
1304 					push(0);
1305 				break;
1306 			case 11: // call_mcode
1307 				{
1308 					a = *scriptData++;
1309 					assert(a <= 3);
1310 					// No, I did not forget the "break"s
1311 					switch (a) {
1312 					case 3:
1313 						c = pop();
1314 						// fall through
1315 					case 2:
1316 						b = pop();
1317 						// fall through
1318 					case 1:
1319 						a = pop();
1320 						// fall through
1321 					}
1322 
1323 					uint16 mcode = *scriptData++ / 4; // get mcode number
1324 					Debug::mcode(mcode, a, b, c);
1325 
1326 					Compact *saveCpt = _compact;
1327 					bool ret = (this->*_mcodeTable[mcode]) (a, b, c);
1328 					_compact = saveCpt;
1329 
1330 					if (!ret)
1331 						return (scriptData - moduleStart);
1332 				}
1333 				break;
1334 			case 12: // more_than
1335 				a = pop();
1336 				b = pop();
1337 				if (a < b)
1338 					push(1);
1339 				else
1340 					push(0);
1341 				break;
1342 			case 14: // switch
1343 				c = s = *scriptData++; // get number of cases
1344 
1345 				a = pop(); // and value to switch on
1346 
1347 				do {
1348 					if (a == *scriptData) {
1349 						scriptData += scriptData[1] / 2;
1350 						scriptData++;
1351 						break;
1352 					}
1353 					scriptData += 2;
1354 				} while (--s);
1355 
1356 				if (s == 0)
1357 					scriptData += *scriptData / 2; // use the default
1358 				break;
1359 			case 15: // push_offset
1360 				push( *(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) );
1361 				break;
1362 			case 16: // pop_offset
1363 				// pop a value into a compact
1364 				*(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) = (uint16)pop();
1365 				break;
1366 			case 17: // is_equal
1367 				a = pop();
1368 				b = pop();
1369 				if (a == b)
1370 					push(1);
1371 				else
1372 					push(0);
1373 				break;
1374 			case 18: { // skip_nz
1375 					int16 t = *scriptData++;
1376 					a = pop();
1377 					if (a)
1378 						scriptData += t / 2;
1379 					break;
1380 				}
1381 			case 13:
1382 			case 19: // script_exit
1383 				return 0;
1384 			case 20: // restart_script
1385 				offset = 0;
1386 				restartScript = true;
1387 				break;
1388 			default:
1389 				error("Unknown script command: %d", command);
1390 			}
1391 		}
1392 	} while (true);
1393 }
1394 
fnCacheChip(uint32 a,uint32 b,uint32 c)1395 bool Logic::fnCacheChip(uint32 a, uint32 b, uint32 c) {
1396 	_skySound->fnStopFx();
1397 	_skyDisk->fnCacheChip((uint16 *)_skyCompact->fetchCpt((uint16)a));
1398 	return true;
1399 }
1400 
fnCacheFast(uint32 a,uint32 b,uint32 c)1401 bool Logic::fnCacheFast(uint32 a, uint32 b, uint32 c) {
1402 	_skyDisk->fnCacheFast((uint16 *)_skyCompact->fetchCpt((uint16)a));
1403 	return true;
1404 }
1405 
fnDrawScreen(uint32 a,uint32 b,uint32 c)1406 bool Logic::fnDrawScreen(uint32 a, uint32 b, uint32 c) {
1407 	debug(5, "Call: fnDrawScreen(%X, %X)",a,b);
1408 	SkyEngine::_systemVars.currentPalette = a;
1409 	_skyScreen->fnDrawScreen(a, b);
1410 
1411 	if (Logic::_scriptVariables[SCREEN] == 32) {
1412 		/* workaround for script bug #786482
1413 		    Under certain circumstances, which never got completely cleared,
1414 		    the gardener can get stuck in an animation, waiting for a sync
1415 		    signal from foster.
1416 			This is most probably caused by foster leaving the screen before
1417 			sending the sync.
1418 			To work around that, we simply send a sync to the gardener every time
1419 			we enter the screen. If he isn't stuck (and thus not waiting for sync)
1420 			it will be ignored anyways */
1421 
1422 		debug(1, "sending gardener sync");
1423 		fnSendSync(ID_SC32_GARDENER, 1, 0);
1424 	}
1425 	return true;
1426 }
1427 
fnAr(uint32 x,uint32 y,uint32 c)1428 bool Logic::fnAr(uint32 x, uint32 y, uint32 c) {
1429 	_compact->downFlag = 1; // assume failure in-case logic is interupted by speech (esp Joey)
1430 
1431 	_compact->arTargetX = (uint16)x;
1432 	_compact->arTargetY = (uint16)y;
1433 	_compact->logic = L_AR; // Set to AR mode
1434 
1435 	_compact->xcood &= 0xfff8;
1436 	_compact->ycood &= 0xfff8;
1437 
1438 	return false; // drop out of script
1439 }
1440 
fnArAnimate(uint32 a,uint32 b,uint32 c)1441 bool Logic::fnArAnimate(uint32 a, uint32 b, uint32 c) {
1442 	_compact->mood = 0; // high level 'not stood still'
1443 	_compact->logic = L_AR_ANIM;
1444 	return false; // drop out of script
1445 }
1446 
fnIdle(uint32 a,uint32 b,uint32 c)1447 bool Logic::fnIdle(uint32 a, uint32 b, uint32 c) {
1448 	// set the player idling
1449 	_compact->logic = 0;
1450 	return true;
1451 }
1452 
fnInteract(uint32 targetId,uint32 b,uint32 c)1453 bool Logic::fnInteract(uint32 targetId, uint32 b, uint32 c) {
1454 	_compact->mode += 4; // next level up
1455 	_compact->logic = L_SCRIPT;
1456 	Compact *cpt = _skyCompact->fetchCpt(targetId);
1457 
1458 	SkyCompact::setSub(_compact, _compact->mode, cpt->actionScript);
1459 	SkyCompact::setSub(_compact, _compact->mode + 2, 0);
1460 
1461 	return false;
1462 }
1463 
fnStartSub(uint32 scr,uint32 b,uint32 c)1464 bool Logic::fnStartSub(uint32 scr, uint32 b, uint32 c) {
1465 	_compact->mode += 4;
1466 	SkyCompact::setSub(_compact, _compact->mode, scr & 0xffff);
1467 	SkyCompact::setSub(_compact, _compact->mode + 2, scr >> 16);
1468 	return false;
1469 }
1470 
fnTheyStartSub(uint32 mega,uint32 scr,uint32 c)1471 bool Logic::fnTheyStartSub(uint32 mega, uint32 scr, uint32 c) {
1472 	Compact *cpt = _skyCompact->fetchCpt(mega);
1473 	cpt->mode += 4;
1474 	SkyCompact::setSub(cpt, cpt->mode, scr & 0xffff);
1475 	SkyCompact::setSub(cpt, cpt->mode + 2, scr >> 16);
1476 	return true;
1477 }
1478 
fnAssignBase(uint32 id,uint32 scr,uint32 c)1479 bool Logic::fnAssignBase(uint32 id, uint32 scr, uint32 c) {
1480 	Compact *cpt = _skyCompact->fetchCpt(id);
1481 	cpt->mode = C_BASE_MODE;
1482 	cpt->logic = L_SCRIPT;
1483 	cpt->baseSub     = (uint16)(scr & 0xffff);
1484 	cpt->baseSub_off = (uint16)(scr >> 16);
1485 	return true;
1486 }
1487 
fnDiskMouse(uint32 a,uint32 b,uint32 c)1488 bool Logic::fnDiskMouse(uint32 a, uint32 b, uint32 c) {
1489 	_skyMouse->spriteMouse(MOUSE_DISK, 11, 11);
1490 	return true;
1491 }
1492 
fnNormalMouse(uint32 a,uint32 b,uint32 c)1493 bool Logic::fnNormalMouse(uint32 a, uint32 b, uint32 c) {
1494 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
1495 	return true;
1496 }
1497 
fnBlankMouse(uint32 a,uint32 b,uint32 c)1498 bool Logic::fnBlankMouse(uint32 a, uint32 b, uint32 c) {
1499 	_skyMouse->spriteMouse(MOUSE_BLANK, 0, 0);
1500 	return true;
1501 }
1502 
fnCrossMouse(uint32 a,uint32 b,uint32 c)1503 bool Logic::fnCrossMouse(uint32 a, uint32 b, uint32 c) {
1504 	if (_scriptVariables[OBJECT_HELD])
1505 		_skyMouse->fnOpenCloseHand(false);
1506 	else
1507 		_skyMouse->spriteMouse(MOUSE_CROSS, 4, 4);
1508 	return true;
1509 }
1510 
fnCursorRight(uint32 a,uint32 b,uint32 c)1511 bool Logic::fnCursorRight(uint32 a, uint32 b, uint32 c) {
1512 	_skyMouse->spriteMouse(MOUSE_RIGHT, 9, 4);
1513 	return true;
1514 }
1515 
fnCursorLeft(uint32 a,uint32 b,uint32 c)1516 bool Logic::fnCursorLeft(uint32 a, uint32 b, uint32 c) {
1517 	_skyMouse->spriteMouse(MOUSE_LEFT, 0, 5);
1518 	return true;
1519 }
1520 
fnCursorDown(uint32 a,uint32 b,uint32 c)1521 bool Logic::fnCursorDown(uint32 a, uint32 b, uint32 c) {
1522 	_skyMouse->spriteMouse(MOUSE_DOWN, 9, 4);
1523 	return true;
1524 }
1525 
fnCursorUp(uint32 a,uint32 b,uint32 c)1526 bool Logic::fnCursorUp(uint32 a, uint32 b, uint32 c) {
1527 	_skyMouse->spriteMouse(MOUSE_UP, 9, 4);
1528 	return true;
1529 }
1530 
fnOpenHand(uint32 a,uint32 b,uint32 c)1531 bool Logic::fnOpenHand(uint32 a, uint32 b, uint32 c) {
1532 	_skyMouse->fnOpenCloseHand(true);
1533 	return true;
1534 }
1535 
fnCloseHand(uint32 a,uint32 b,uint32 c)1536 bool Logic::fnCloseHand(uint32 a, uint32 b, uint32 c) {
1537 	_skyMouse->fnOpenCloseHand(false);
1538 	return true;
1539 }
1540 
fnGetTo(uint32 targetPlaceId,uint32 mode,uint32 c)1541 bool Logic::fnGetTo(uint32 targetPlaceId, uint32 mode, uint32 c) {
1542 	_compact->upFlag = (uint16)mode; // save mode for action script
1543 	_compact->mode += 4; // next level up
1544 	Compact *cpt = _skyCompact->fetchCpt(_compact->place);
1545 	if (!cpt) {
1546 		warning("can't find _compact's getToTable. Place compact is NULL");
1547 		return false;
1548 	}
1549 	uint16 *getToTable = (uint16 *)_skyCompact->fetchCpt(cpt->getToTableId);
1550 	if (!getToTable) {
1551 		warning("Place compact's getToTable is NULL");
1552 		return false;
1553 	}
1554 
1555 	while (*getToTable != targetPlaceId)
1556 		getToTable += 2;
1557 
1558 	// get new script
1559 	SkyCompact::setSub(_compact, _compact->mode, *(getToTable + 1));
1560 	SkyCompact::setSub(_compact, _compact->mode + 2, 0);
1561 
1562 	return false; // drop out of script
1563 }
1564 
fnSetToStand(uint32 a,uint32 b,uint32 c)1565 bool Logic::fnSetToStand(uint32 a, uint32 b, uint32 c) {
1566 	_compact->mood = 1; // high level stood still
1567 
1568 	_compact->grafixProgId = *(uint16 *)_skyCompact->getCompactElem(_compact, C_STAND_UP + _compact->megaSet + _compact->dir * 4);
1569 	_compact->grafixProgPos = 0;
1570 
1571 	uint16 *standList = _skyCompact->getGrafixPtr(_compact);
1572 
1573 	_compact->offset = *standList; // get frames offset
1574 	_compact->logic = L_SIMPLE_MOD;
1575 	_compact->grafixProgPos++;
1576 	simpleAnim();
1577 	return false; // drop out of script
1578 }
1579 
fnTurnTo(uint32 dir,uint32 b,uint32 c)1580 bool Logic::fnTurnTo(uint32 dir, uint32 b, uint32 c) {
1581 	/// turn compact to direction dir
1582 
1583 	uint16 curDir = _compact->dir; // get current direction
1584 	_compact->dir = (uint16)(dir & 0xffff); // set new direction
1585 
1586 	uint16 *tt = _skyCompact->getTurnTable(_compact, curDir);
1587 
1588 	if (!tt[dir])
1589 		return true; // keep going
1590 
1591 	_compact->turnProgId = tt[dir]; // put turn program in
1592 	_compact->turnProgPos = 0;
1593 	_compact->logic = L_TURNING;
1594 
1595 	turn();
1596 
1597 	return false; // drop out of script
1598 }
1599 
fnArrived(uint32 scriptVar,uint32 b,uint32 c)1600 bool Logic::fnArrived(uint32 scriptVar, uint32 b, uint32 c) {
1601 	_compact->leaving = (uint16)(scriptVar & 0xffff);
1602 	_scriptVariables[scriptVar/4]++;
1603 	return true;
1604 }
1605 
fnLeaving(uint32 a,uint32 b,uint32 c)1606 bool Logic::fnLeaving(uint32 a, uint32 b, uint32 c) {
1607 	_compact->atWatch = 0;
1608 
1609 	if (_compact->leaving) {
1610 		_scriptVariables[_compact->leaving/4]--;
1611 		_compact->leaving = 0; // I shall do this only once
1612 	}
1613 
1614 	return true; // keep going
1615 }
1616 
fnSetAlternate(uint32 scr,uint32 b,uint32 c)1617 bool Logic::fnSetAlternate(uint32 scr, uint32 b, uint32 c) {
1618 	_compact->alt = (uint16)(scr & 0xffff);
1619 	_compact->logic = L_ALT;
1620 	return false;
1621 }
1622 
fnAltSetAlternate(uint32 target,uint32 scr,uint32 c)1623 bool Logic::fnAltSetAlternate(uint32 target, uint32 scr, uint32 c) {
1624 	Compact *cpt = _skyCompact->fetchCpt(target);
1625 	cpt->alt = (uint16)(scr & 0xffff);
1626 	cpt->logic = L_ALT;
1627 	return false;
1628 }
1629 
fnKillId(uint32 id,uint32 b,uint32 c)1630 bool Logic::fnKillId(uint32 id, uint32 b, uint32 c) {
1631 	if (id) {
1632 		Compact *cpt = _skyCompact->fetchCpt(id);
1633 		if (cpt->status & (1 << 7))
1634 			_skyGrid->removeObjectFromWalk(cpt);
1635 		cpt->status = 0;
1636 	}
1637 	return true;
1638 }
1639 
fnNoHuman(uint32 a,uint32 b,uint32 c)1640 bool Logic::fnNoHuman(uint32 a, uint32 b, uint32 c) {
1641 	if (!_scriptVariables[MOUSE_STOP]) {
1642 		_scriptVariables[MOUSE_STATUS] &= 1;
1643 		runGetOff();
1644 		fnBlankMouse(0, 0, 0);
1645 	}
1646 	return true;
1647 }
1648 
fnAddHuman(uint32 a,uint32 b,uint32 c)1649 bool Logic::fnAddHuman(uint32 a, uint32 b, uint32 c) {
1650 	return _skyMouse->fnAddHuman();
1651 }
1652 
fnAddButtons(uint32 a,uint32 b,uint32 c)1653 bool Logic::fnAddButtons(uint32 a, uint32 b, uint32 c) {
1654 	_scriptVariables[MOUSE_STATUS] |= 4;
1655 	return true;
1656 }
1657 
fnNoButtons(uint32 a,uint32 b,uint32 c)1658 bool Logic::fnNoButtons(uint32 a, uint32 b, uint32 c) {
1659 	//remove the mouse buttons
1660 	_scriptVariables[MOUSE_STATUS] &= 0xFFFFFFFB;
1661 	return true;
1662 }
1663 
fnSetStop(uint32 a,uint32 b,uint32 c)1664 bool Logic::fnSetStop(uint32 a, uint32 b, uint32 c) {
1665 	_scriptVariables[MOUSE_STOP] |= 1;
1666 	return true;
1667 }
1668 
fnClearStop(uint32 a,uint32 b,uint32 c)1669 bool Logic::fnClearStop(uint32 a, uint32 b, uint32 c) {
1670 	_scriptVariables[MOUSE_STOP] = 0;
1671 	return true;
1672 }
1673 
fnPointerText(uint32 a,uint32 b,uint32 c)1674 bool Logic::fnPointerText(uint32 a, uint32 b, uint32 c) {
1675 	_skyText->fnPointerText(a, _skyMouse->giveMouseX(), _skyMouse->giveMouseY());
1676 	return true;
1677 }
1678 
fnQuit(uint32 a,uint32 b,uint32 c)1679 bool Logic::fnQuit(uint32 a, uint32 b, uint32 c) {
1680 	return false;
1681 }
1682 
fnSpeakMe(uint32 targetId,uint32 mesgNum,uint32 animNum)1683 bool Logic::fnSpeakMe(uint32 targetId, uint32 mesgNum, uint32 animNum) {
1684 	/* WORKAROUND for #2687172: When Mrs. Piermont is talking
1685 	   on the phone in her apartment, ignore her fnSpeakMe calls
1686 	   on other screens, as the lack of speech files for these lines
1687 	   will cause Foster's speech to be aborted if the timing is bad.
1688 	*/
1689 	if (targetId == ID_DANIELLE && animNum == 0x9B && Logic::_scriptVariables[SCREEN] != 38) {
1690 		return false;
1691 	}
1692 
1693 	stdSpeak(_skyCompact->fetchCpt(targetId), mesgNum, animNum, 0);
1694 	return false;	//drop out of script
1695 }
1696 
fnSpeakMeDir(uint32 targetId,uint32 mesgNum,uint32 animNum)1697 bool Logic::fnSpeakMeDir(uint32 targetId, uint32 mesgNum, uint32 animNum) {
1698 	//must be player so don't cause script to drop out
1699 	//this function sets the directional option whereby
1700 	//the anim chosen is linked to c_dir
1701 	animNum += _compact->dir << 1;	//2 sizes (large and small)
1702 	return fnSpeakMe(targetId, mesgNum, animNum);
1703 }
1704 
fnSpeakWait(uint32 id,uint32 message,uint32 animation)1705 bool Logic::fnSpeakWait(uint32 id, uint32 message, uint32 animation) {
1706 	// non player mega char speaks
1707 	// player will wait for it to finish before continuing script processing
1708 	_compact->flag = (uint16)(id & 0xffff);
1709 	_compact->logic = L_LISTEN;
1710 	return fnSpeakMe(id, message, animation);
1711 }
1712 
fnSpeakWaitDir(uint32 a,uint32 b,uint32 c)1713 bool Logic::fnSpeakWaitDir(uint32 a, uint32 b, uint32 c) {
1714 	/* non player mega chr$ speaks	S2(20Jan93tw)
1715 	the player will wait for it to finish
1716 	before continuing script processing
1717 	this function sets the directional option whereby
1718 	the anim chosen is linked to c_dir -
1719 
1720 	_compact is player
1721 	a is ID to speak (not us)
1722 	b is text message number
1723 	c is base of mini table within anim_talk_table */
1724 
1725 #ifdef __DC__
1726 	__builtin_alloca(4); // Works around a gcc bug (wrong-code/11736)
1727 #endif
1728 
1729 	_compact->flag = (uint16)a;
1730 	_compact->logic = L_LISTEN;
1731 
1732 	Compact *speaker = _skyCompact->fetchCpt(a);
1733 	if (c) {
1734 		c += speaker->dir << 1;
1735 		stdSpeak(speaker, b, c, speaker->dir << 1);
1736 	} else
1737 		stdSpeak(speaker, b, c, 0);
1738 
1739 	return false;
1740 }
1741 
fnChooser(uint32 a,uint32 b,uint32 c)1742 bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) {
1743 
1744 	// setup the text questions to be clicked on
1745 	// read from TEXT1 until 0
1746 
1747 	SkyEngine::_systemVars.systemFlags |= SF_CHOOSING; // can't save/restore while choosing
1748 
1749 	_scriptVariables[THE_CHOSEN_ONE] = 0; // clear result
1750 
1751 	uint32 *p = _scriptVariables + TEXT1;
1752 	uint16 ycood = TOP_LEFT_Y; // rolling coordinate
1753 
1754 	while (*p) {
1755 		uint32 textNum = *p++;
1756 
1757 		DisplayedText lowText = _skyText->lowTextManager(textNum, GAME_SCREEN_WIDTH, 0, 241, 0);
1758 
1759 		uint8 *data = lowText.textData;
1760 
1761 		// stipple the text
1762 
1763 		uint32 size = ((DataFileHeader *)data)->s_height * ((DataFileHeader *)data)->s_width;
1764 		uint32 index = 0;
1765 		uint32 width = ((DataFileHeader *)data)->s_width;
1766 		uint32 height = ((DataFileHeader *)data)->s_height;
1767 
1768 		data += sizeof(DataFileHeader);
1769 
1770 		while (index < size) {
1771 			if (index % width <= 1)
1772 				index ^= 1; //index++;
1773 			if (!data[index])
1774 				data[index] = 1;
1775 			index += 2;
1776 		}
1777 
1778 		Compact *textCompact = _skyCompact->fetchCpt(lowText.compactNum);
1779 
1780 		textCompact->getToFlag = (uint16)textNum;
1781 		textCompact->downFlag = (uint16)*p++; // get animation number
1782 
1783 		textCompact->status |= ST_MOUSE; // mouse detects
1784 
1785 		textCompact->xcood = TOP_LEFT_X; // set coordinates
1786 		textCompact->ycood = ycood;
1787 		ycood += height;
1788 	}
1789 
1790 	if (p == _scriptVariables + TEXT1)
1791 		return true;
1792 
1793 	_compact->logic = L_CHOOSE; // player frozen until choice made
1794 	fnAddHuman(0, 0, 0); // bring back mouse
1795 
1796 	return false;
1797 }
1798 
fnHighlight(uint32 itemNo,uint32 pen,uint32 c)1799 bool Logic::fnHighlight(uint32 itemNo, uint32 pen, uint32 c) {
1800 	pen -= 11;
1801 	pen ^= 1;
1802 	pen += 241;
1803 	Compact *textCompact = _skyCompact->fetchCpt(itemNo);
1804 	uint8 *sprData = (uint8 *)SkyEngine::fetchItem(textCompact->flag);
1805 	_skyText->changeTextSpriteColor(sprData, (uint8)pen);
1806 	return true;
1807 }
1808 
fnTextKill(uint32 a,uint32 b,uint32 c)1809 bool Logic::fnTextKill(uint32 a, uint32 b, uint32 c) {
1810 	/// Kill of text items that are mouse detectable
1811 
1812 	uint32 id = FIRST_TEXT_COMPACT;
1813 
1814 	for (int i = 10; i > 0; i--) {
1815 		Compact *cpt = _skyCompact->fetchCpt(id);
1816 		if (cpt->status & (1 << 4))
1817 			cpt->status = 0;
1818 		id++;
1819 	}
1820 	return true;
1821 }
1822 
fnStopMode(uint32 a,uint32 b,uint32 c)1823 bool Logic::fnStopMode(uint32 a, uint32 b, uint32 c) {
1824 	_compact->logic = L_STOPPED;
1825 	return false;
1826 }
1827 
fnWeWait(uint32 id,uint32 b,uint32 c)1828 bool Logic::fnWeWait(uint32 id, uint32 b, uint32 c) {
1829 	/// We have hit another mega
1830 	/// we are going to wait for it to move
1831 
1832 	_compact->waitingFor = (uint16) id;
1833 	stopAndWait();
1834 	return true; // not sure about this
1835 }
1836 
fnSendSync(uint32 mega,uint32 sync,uint32 c)1837 bool Logic::fnSendSync(uint32 mega, uint32 sync, uint32 c) {
1838 	Compact *cpt = _skyCompact->fetchCpt(mega);
1839 	cpt->sync = (uint16)(sync & 0xffff);
1840 	return false;
1841 }
1842 
fnSendFastSync(uint32 mega,uint32 sync,uint32 c)1843 bool Logic::fnSendFastSync(uint32 mega, uint32 sync, uint32 c) {
1844 	Compact *cpt = _skyCompact->fetchCpt(mega);
1845 	cpt->sync = (uint16)(sync & 0xffff);
1846 	return true;
1847 }
1848 
fnSendRequest(uint32 target,uint32 scr,uint32 c)1849 bool Logic::fnSendRequest(uint32 target, uint32 scr, uint32 c) {
1850 	Compact *cpt = _skyCompact->fetchCpt(target);
1851 	cpt->request = (uint16)(scr & 0xffff);
1852 	return false;
1853 }
1854 
fnClearRequest(uint32 target,uint32 b,uint32 c)1855 bool Logic::fnClearRequest(uint32 target, uint32 b, uint32 c) {
1856 	Compact *cpt = _skyCompact->fetchCpt(target);
1857 	cpt->request = 0;
1858 	return true;
1859 }
1860 
fnCheckRequest(uint32 a,uint32 b,uint32 c)1861 bool Logic::fnCheckRequest(uint32 a, uint32 b, uint32 c) {
1862 	/// check for interaction request
1863 
1864 	if (!_compact->request)
1865 		return true;
1866 
1867 	_compact->mode = C_ACTION_MODE; // into action mode
1868 
1869 	_compact->actionSub = _compact->request;
1870 	_compact->actionSub_off = 0;
1871 
1872 	_compact->request = 0; // trash request
1873 	return false; // drop from script
1874 }
1875 
fnStartMenu(uint32 firstObject,uint32 b,uint32 c)1876 bool Logic::fnStartMenu(uint32 firstObject, uint32 b, uint32 c) {
1877 	/// initialize the top menu bar
1878 	// firstObject is o0 for game menu, k0 for linc
1879 
1880 	uint i;
1881 	firstObject /= 4;
1882 
1883 	// (1) FIRST, SET UP THE 2 ARROWS SO THEY APPEAR ON SCREEN
1884 
1885 	Compact *cpt = _skyCompact->fetchCpt(47);
1886 	cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
1887 	cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
1888 
1889 	cpt = _skyCompact->fetchCpt(48);
1890 	cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
1891 	cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
1892 
1893 	// (2) COPY OBJECTS FROM NON-ZERO INVENTORY VARIABLES INTO OBJECT DISPLAY LIST (& COUNT THEM)
1894 
1895 	// sort the objects and pad with blanks
1896 
1897 	uint32 menuLength = 0;
1898 	for (i = firstObject; i < firstObject + ARRAYSIZE(_objectList); i++) {
1899 		if (_scriptVariables[i])
1900 			_objectList[menuLength++] = _scriptVariables[i];
1901 	}
1902 	_scriptVariables[MENU_LENGTH] = menuLength;
1903 
1904 	// (3) OK, NOW TOP UP THE LIST WITH THE REQUIRED NO. OF BLANK OBJECTS (for min display length 11)
1905 
1906 	uint32 blankID = 51;
1907 	for (i = menuLength; i < 11; i++)
1908 		_objectList[i] = blankID++;
1909 
1910 	// (4) KILL ID's OF ALL 20 OBJECTS SO UNWANTED ICONS (SCROLLED OFF) DON'T REMAIN ON SCREEN
1911 	// (There should be a better way of doing this - only kill id of 12th item when menu has scrolled right)
1912 
1913 	for (i = 0; i < ARRAYSIZE(_objectList); i++) {
1914 		if (_objectList[i])
1915 			(_skyCompact->fetchCpt(_objectList[i]))->status = ST_LOGIC;
1916 		else break;
1917 	}
1918 
1919 	// (5) NOW FIND OUT WHICH OBJECT TO START THE DISPLAY FROM (depending on scroll offset)
1920 
1921 	if (menuLength < 11) // check we can scroll
1922 		_scriptVariables[SCROLL_OFFSET] = 0;
1923 	else if (menuLength < _scriptVariables[SCROLL_OFFSET] + 11)
1924 		_scriptVariables[SCROLL_OFFSET] = menuLength - 11;
1925 
1926 	// (6) AND FINALLY, INITIALIZE THE 11 OBJECTS SO THEY APPEAR ON SCREEEN
1927 
1928 	uint16 rollingX = TOP_LEFT_X + 28;
1929 	for (i = 0; i < 11; i++) {
1930 		cpt = _skyCompact->fetchCpt(
1931 				_objectList[_scriptVariables[SCROLL_OFFSET] + i]);
1932 
1933 		cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
1934 		cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
1935 
1936 		cpt->xcood = rollingX;
1937 		rollingX += 24;
1938 
1939 		if (_scriptVariables[MENU] == 2)
1940 			cpt->ycood = 136;
1941 		else
1942 			cpt->ycood = 112;
1943 	}
1944 
1945 	return true;
1946 }
1947 
fnUnhighlight(uint32 item,uint32 b,uint32 c)1948 bool Logic::fnUnhighlight(uint32 item, uint32 b, uint32 c) {
1949 	Compact *cpt = _skyCompact->fetchCpt(item);
1950 	cpt->frame--;
1951 	cpt->getToFlag = 0;
1952 	return true;
1953 }
1954 
fnFaceId(uint32 otherId,uint32 b,uint32 c)1955 bool Logic::fnFaceId(uint32 otherId, uint32 b, uint32 c) {
1956 	/// return the direction to turn to face another id
1957 	/// pass back result in c_just_flag
1958 
1959 	Compact *cpt = _skyCompact->fetchCpt(otherId);
1960 
1961 	int16 x = _compact->xcood - cpt->xcood;
1962 
1963 	if (x < 0) { // we're to the left
1964 		x = -x;
1965 		_compact->getToFlag = 3;
1966 	} else { // it's to the left
1967 		_compact->getToFlag = 2;
1968 	}
1969 
1970 	// now check y
1971 
1972 	// we must find the true bottom of the sprite
1973 	// it is not enough to use y coord because changing
1974 	// sprite offsets can ruin the formula - instead we
1975 	// will use the bottom of the mouse collision area
1976 
1977 	int16 y = _compact->ycood - (cpt->ycood + cpt->mouseRelY + cpt->mouseSizeY);
1978 
1979 	if (y < 0) { // it's below
1980 		y = -y;
1981 		if (y >= x)
1982 			_compact->getToFlag = 1;
1983 	} else { // it's above
1984 		if (y >= x)
1985 			_compact->getToFlag = 0;
1986 	}
1987 	return true;
1988 }
1989 
fnForeground(uint32 sprite,uint32 b,uint32 c)1990 bool Logic::fnForeground(uint32 sprite, uint32 b, uint32 c) {
1991 	/// Make sprite a foreground sprite
1992 	Compact *cpt = _skyCompact->fetchCpt(sprite);
1993 	cpt->status &= 0xfff8;
1994 	cpt->status |= ST_FOREGROUND;
1995 	return true;
1996 }
1997 
fnBackground(uint32 a,uint32 b,uint32 c)1998 bool Logic::fnBackground(uint32 a, uint32 b, uint32 c) {
1999 	/// Make us a background sprite
2000 	_compact->status &= 0xfff8;
2001 	_compact->status |= ST_BACKGROUND;
2002 	return true;
2003 }
2004 
fnNewBackground(uint32 sprite,uint32 b,uint32 c)2005 bool Logic::fnNewBackground(uint32 sprite, uint32 b, uint32 c) {
2006 	/// Make sprite a background sprite
2007 	Compact *cpt = _skyCompact->fetchCpt(sprite);
2008 	cpt->status &= 0xfff8;
2009 	cpt->status |= ST_BACKGROUND;
2010 	return true;
2011 }
2012 
fnSort(uint32 mega,uint32 b,uint32 c)2013 bool Logic::fnSort(uint32 mega, uint32 b, uint32 c) {
2014 	Compact *cpt = _skyCompact->fetchCpt(mega);
2015 	cpt->status &= 0xfff8;
2016 	cpt->status |= ST_SORT;
2017 	return true;
2018 }
2019 
fnNoSpriteEngine(uint32 a,uint32 b,uint32 c)2020 bool Logic::fnNoSpriteEngine(uint32 a, uint32 b, uint32 c) {
2021 	/// stop the compact printing
2022 	/// remove foreground, background & sort
2023 	_compact->status &= 0xfff8;
2024 	return true;
2025 }
2026 
fnNoSpritesA6(uint32 us,uint32 b,uint32 c)2027 bool Logic::fnNoSpritesA6(uint32 us, uint32 b, uint32 c) {
2028 	/// stop the compact printing
2029 	/// remove foreground, background & sort
2030 	Compact *cpt = _skyCompact->fetchCpt(us);
2031 	cpt->status &= 0xfff8;
2032 	return true;
2033 }
2034 
fnResetId(uint32 id,uint32 resetBlock,uint32 c)2035 bool Logic::fnResetId(uint32 id, uint32 resetBlock, uint32 c) {
2036 	/// used when a mega is to be restarted
2037 	/// eg - when a smaller mega turn to larger
2038 	/// - a mega changes rooms...
2039 
2040 	Compact *cpt = _skyCompact->fetchCpt(id);
2041 	uint16 *rst = (uint16 *)_skyCompact->fetchCpt(resetBlock);
2042 
2043 	if (!cpt) {
2044 		warning("fnResetId(): Compact %d (id) == NULL", id);
2045 		return true;
2046 	}
2047 	if (!rst) {
2048 		warning("fnResetId(): Compact %d (resetBlock) == NULL", resetBlock);
2049 		return true;
2050 	}
2051 
2052 	uint16 off;
2053 	while ((off = *rst++) != 0xffff)
2054 		*(uint16 *)_skyCompact->getCompactElem(cpt, off) = *rst++;
2055 	return true;
2056 }
2057 
fnToggleGrid(uint32 a,uint32 b,uint32 c)2058 bool Logic::fnToggleGrid(uint32 a, uint32 b, uint32 c) {
2059 	/// Toggle a mega's grid plotting
2060 	_compact->status ^= ST_GRID_PLOT;
2061 	return true;
2062 }
2063 
fnPause(uint32 cycles,uint32 b,uint32 c)2064 bool Logic::fnPause(uint32 cycles, uint32 b, uint32 c) {
2065 	/// Set mega to L_PAUSE
2066 	_compact->flag = (uint16)(cycles & 0xffff);
2067 	_compact->logic = L_PAUSE;
2068 	return false; // drop out of script
2069 }
2070 
fnRunAnimMod(uint32 animNo,uint32 b,uint32 c)2071 bool Logic::fnRunAnimMod(uint32 animNo, uint32 b, uint32 c) {
2072 	_compact->grafixProgId = animNo;
2073 	_compact->grafixProgPos = 0;
2074 
2075 	_compact->offset = *_skyCompact->getGrafixPtr(_compact);
2076 	_compact->grafixProgPos++;
2077 	_compact->logic = L_MOD_ANIMATE;
2078 	anim();
2079 	return false; // drop from script
2080 }
2081 
fnSimpleMod(uint32 animSeqNo,uint32 b,uint32 c)2082 bool Logic::fnSimpleMod(uint32 animSeqNo, uint32 b, uint32 c) {
2083 	_compact->grafixProgId = animSeqNo;
2084 	_compact->grafixProgPos = 0;
2085 
2086 	_compact->logic = L_SIMPLE_MOD;
2087 	_compact->offset = *_skyCompact->getGrafixPtr(_compact);
2088 	_compact->grafixProgPos++;
2089 	simpleAnim();
2090 	return false;
2091 }
2092 
fnRunFrames(uint32 sequenceNo,uint32 b,uint32 c)2093 bool Logic::fnRunFrames(uint32 sequenceNo, uint32 b, uint32 c) {
2094 	_compact->grafixProgId = sequenceNo;
2095 	_compact->grafixProgPos = 0;
2096 
2097 	_compact->logic = L_FRAMES;
2098 	_compact->offset = *_skyCompact->getGrafixPtr(_compact);
2099 	_compact->grafixProgPos++;
2100 	simpleAnim();
2101 	return false;
2102 }
2103 
fnAwaitSync(uint32 a,uint32 b,uint32 c)2104 bool Logic::fnAwaitSync(uint32 a, uint32 b, uint32 c) {
2105 	if (_compact->sync)
2106 		return true;
2107 
2108 	_compact->logic = L_WAIT_SYNC;
2109 	return false;
2110 }
2111 
fnIncMegaSet(uint32 a,uint32 b,uint32 c)2112 bool Logic::fnIncMegaSet(uint32 a, uint32 b, uint32 c) {
2113 	_compact->megaSet += NEXT_MEGA_SET;
2114 	return true;
2115 }
2116 
fnDecMegaSet(uint32 a,uint32 b,uint32 c)2117 bool Logic::fnDecMegaSet(uint32 a, uint32 b, uint32 c) {
2118 	_compact->megaSet -= NEXT_MEGA_SET;
2119 	return true;
2120 }
2121 
fnSetMegaSet(uint32 mega,uint32 setNo,uint32 c)2122 bool Logic::fnSetMegaSet(uint32 mega, uint32 setNo, uint32 c) {
2123 	Compact *cpt = _skyCompact->fetchCpt(mega);
2124 	cpt->megaSet = (uint16) (setNo * NEXT_MEGA_SET);
2125 	return true;
2126 }
2127 
fnMoveItems(uint32 listNo,uint32 screenNo,uint32 c)2128 bool Logic::fnMoveItems(uint32 listNo, uint32 screenNo, uint32 c) {
2129 	// Move a list of id's to another screen
2130 	uint16 *p = (uint16 *)_skyCompact->fetchCpt(CPT_MOVE_LIST);
2131 	p = (uint16 *)_skyCompact->fetchCpt(p[listNo]);
2132 	for (int i = 0; i < 2; i++) {
2133 		if (!*p)
2134 			return true;
2135 		Compact *cpt = _skyCompact->fetchCpt(*p++);
2136 		cpt->screen = (uint16)(screenNo & 0xffff);
2137 	}
2138 	return true;
2139 }
2140 
fnNewList(uint32 a,uint32 b,uint32 c)2141 bool Logic::fnNewList(uint32 a, uint32 b, uint32 c) {
2142 	/// Reset the chooser list
2143 	for (int i = 0; i < 16; i++)
2144 		_scriptVariables[TEXT1 + i] = 0;
2145 	return true;
2146 }
2147 
fnAskThis(uint32 textNo,uint32 animNo,uint32 c)2148 bool Logic::fnAskThis(uint32 textNo, uint32 animNo, uint32 c) {
2149 	// find first free position
2150 	uint32 *p = _scriptVariables + TEXT1;
2151 	while (*p)
2152 		p += 2;
2153 	*p++ = textNo;
2154 	*p = animNo;
2155 	return true;
2156 }
2157 
fnRandom(uint32 a,uint32 b,uint32 c)2158 bool Logic::fnRandom(uint32 a, uint32 b, uint32 c) {
2159 	_scriptVariables[RND] = _rnd.getRandomNumber(65536) & a;
2160 	return true;
2161 }
2162 
fnPersonHere(uint32 id,uint32 room,uint32 c)2163 bool Logic::fnPersonHere(uint32 id, uint32 room, uint32 c) {
2164 	Compact *cpt = _skyCompact->fetchCpt(id);
2165 	_scriptVariables[RESULT] = cpt->screen == room ? 1 : 0;
2166 	return true;
2167 }
2168 
fnToggleMouse(uint32 a,uint32 b,uint32 c)2169 bool Logic::fnToggleMouse(uint32 a, uint32 b, uint32 c) {
2170 	_skyCompact->fetchCpt(a)->status ^= ST_MOUSE;
2171 	return true;
2172 }
2173 
fnMouseOn(uint32 a,uint32 b,uint32 c)2174 bool Logic::fnMouseOn(uint32 a, uint32 b, uint32 c) {
2175 	//switch on the mouse highlight
2176 	Compact *cpt = _skyCompact->fetchCpt(a);
2177 	cpt->status |= ST_MOUSE;
2178 	return true;
2179 }
2180 
fnMouseOff(uint32 a,uint32 b,uint32 c)2181 bool Logic::fnMouseOff(uint32 a, uint32 b, uint32 c) {
2182 	//switch off the mouse highlight
2183 	Compact *cpt = _skyCompact->fetchCpt(a);
2184 	cpt->status &= ~ST_MOUSE;
2185 	return true;
2186 }
2187 
fnFetchX(uint32 id,uint32 b,uint32 c)2188 bool Logic::fnFetchX(uint32 id, uint32 b, uint32 c) {
2189 	Compact *cpt = _skyCompact->fetchCpt(id);
2190 	_scriptVariables[RESULT] = cpt->xcood;
2191 	return true;
2192 }
2193 
fnFetchY(uint32 id,uint32 b,uint32 c)2194 bool Logic::fnFetchY(uint32 id, uint32 b, uint32 c) {
2195 	Compact *cpt = _skyCompact->fetchCpt(id);
2196 	_scriptVariables[RESULT] = cpt->ycood;
2197 	return true;
2198 }
2199 
fnTestList(uint32 id,uint32 x,uint32 y)2200 bool Logic::fnTestList(uint32 id, uint32 x, uint32 y) {
2201 	_scriptVariables[RESULT] = 0; // assume fail
2202 	uint16 *list = (uint16 *)_skyCompact->fetchCpt(id);
2203 
2204 	while (*list) {
2205 		if ((x >= list[0]) && (x < list[1]) && (y >= list[2]) && (y < list[3]))
2206 			_scriptVariables[RESULT] = list[4];
2207 		list += 5;
2208 	}
2209 	return true;
2210 }
2211 
fnFetchPlace(uint32 id,uint32 b,uint32 c)2212 bool Logic::fnFetchPlace(uint32 id, uint32 b, uint32 c) {
2213 	Compact *cpt = _skyCompact->fetchCpt(id);
2214 	_scriptVariables[RESULT] = cpt->place;
2215 	return true;
2216 }
2217 
fnCustomJoey(uint32 id,uint32 b,uint32 c)2218 bool Logic::fnCustomJoey(uint32 id, uint32 b, uint32 c) {
2219 	/// return id's x & y coordinate & c_mood (i.e. stood still yes/no)
2220 	/// used by Joey-Logic - done in code like this because scripts can't
2221 	/// get access to another megas compact as easily
2222 
2223 	Compact *cpt = _skyCompact->fetchCpt(id);
2224 
2225 	_scriptVariables[PLAYER_X] = cpt->xcood;
2226 	_scriptVariables[PLAYER_Y] = cpt->ycood;
2227 	_scriptVariables[PLAYER_MOOD] = cpt->mood;
2228 	_scriptVariables[PLAYER_SCREEN] = cpt->screen;
2229 	return true;
2230 }
2231 
fnSetPalette(uint32 a,uint32 b,uint32 c)2232 bool Logic::fnSetPalette(uint32 a, uint32 b, uint32 c) {
2233 	_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(a));
2234 	SkyEngine::_systemVars.currentPalette = a;
2235 	return true;
2236 }
2237 
fnTextModule(uint32 a,uint32 b,uint32 c)2238 bool Logic::fnTextModule(uint32 a, uint32 b, uint32 c) {
2239 	_skyText->fnTextModule(a, b);
2240 	return true;
2241 }
2242 
fnChangeName(uint32 id,uint32 textNo,uint32 c)2243 bool Logic::fnChangeName(uint32 id, uint32 textNo, uint32 c) {
2244 	Compact *cpt = _skyCompact->fetchCpt(id);
2245 	cpt->cursorText = (uint16) textNo;
2246 	return true;
2247 }
2248 
fnMiniLoad(uint32 a,uint32 b,uint32 c)2249 bool Logic::fnMiniLoad(uint32 a, uint32 b, uint32 c) {
2250 	_skyDisk->fnMiniLoad((uint16)a);
2251 	return true;
2252 }
2253 
fnFlushBuffers(uint32 a,uint32 b,uint32 c)2254 bool Logic::fnFlushBuffers(uint32 a, uint32 b, uint32 c) {
2255 	_skyDisk->fnFlushBuffers();
2256 	return true;
2257 }
2258 
fnFlushChip(uint32 a,uint32 b,uint32 c)2259 bool Logic::fnFlushChip(uint32 a, uint32 b, uint32 c) {
2260 	// this should be done automatically
2261 	return true;
2262 }
2263 
fnSaveCoods(uint32 a,uint32 b,uint32 c)2264 bool Logic::fnSaveCoods(uint32 a, uint32 b, uint32 c) {
2265 	_skyMouse->fnSaveCoods();
2266 	return true;
2267 }
2268 
fnPlotGrid(uint32 x,uint32 y,uint32 width)2269 bool Logic::fnPlotGrid(uint32 x, uint32 y, uint32 width) {
2270 	_skyGrid->plotGrid(x, y, width, _compact);
2271 	return true;
2272 }
2273 
fnRemoveGrid(uint32 x,uint32 y,uint32 width)2274 bool Logic::fnRemoveGrid(uint32 x, uint32 y, uint32 width) {
2275 	_skyGrid->removeGrid(x, y, width, _compact);
2276 	return true;
2277 }
2278 
fnEyeball(uint32 id,uint32 b,uint32 c)2279 bool Logic::fnEyeball(uint32 id, uint32 b, uint32 c) {
2280 	// set 'result' to frame no. pointing to foster, according to table used
2281 	// eg. FN_eyeball (id_eye_90_table);
2282 
2283 	uint16 *eyeTable = (uint16 *)_skyCompact->fetchCpt(id);
2284 	Compact *cpt = _skyCompact->fetchCpt(ID_BLUE_FOSTER);
2285 
2286 	uint32 x = cpt->xcood; // 168 < x < 416
2287 	x -= 168;
2288 	x >>= 3;
2289 
2290 	uint32 y = cpt->ycood; // 256 < y < 296
2291 	y -= 256;
2292 	y <<= 2;
2293 
2294 	_scriptVariables[RESULT] = eyeTable[x + y] + S91;
2295 	return true;
2296 }
2297 
fnLeaveSection(uint32 sectionNo,uint32 b,uint32 c)2298 bool Logic::fnLeaveSection(uint32 sectionNo, uint32 b, uint32 c) {
2299 	if (SkyEngine::isDemo())
2300 		Engine::quitGame();
2301 
2302 	if (sectionNo == 5) //linc section - has different mouse icons
2303 		_skyMouse->replaceMouseCursors(60301);
2304 
2305 	return true;
2306 }
2307 
fnEnterSection(uint32 sectionNo,uint32 b,uint32 c)2308 bool Logic::fnEnterSection(uint32 sectionNo, uint32 b, uint32 c) {
2309 	if (SkyEngine::isDemo() && (sectionNo > 2))
2310 		_skyControl->showGameQuitMsg();
2311 
2312 	_scriptVariables[CUR_SECTION] = sectionNo;
2313 	SkyEngine::_systemVars.currentMusic = 0;
2314 
2315 	if (sectionNo == 5) //linc section - has different mouse icons
2316 		_skyMouse->replaceMouseCursors(60302);
2317 
2318 	if ((sectionNo != _currentSection) || (SkyEngine::_systemVars.systemFlags & SF_GAME_RESTORED)) {
2319 		_currentSection = sectionNo;
2320 
2321 		sectionNo++;
2322 		_skyMusic->loadSection((byte)sectionNo);
2323 		_skySound->loadSection((byte)sectionNo);
2324 		_skyGrid->loadGrids();
2325 		SkyEngine::_systemVars.systemFlags &= ~SF_GAME_RESTORED;
2326 	}
2327 
2328 	return true;
2329 }
2330 
fnRestoreGame(uint32 a,uint32 b,uint32 c)2331 bool Logic::fnRestoreGame(uint32 a, uint32 b, uint32 c) {
2332 	_skyControl->doLoadSavePanel();
2333 	return false;
2334 }
2335 
fnRestartGame(uint32 a,uint32 b,uint32 c)2336 bool Logic::fnRestartGame(uint32 a, uint32 b, uint32 c) {
2337 	_skyControl->restartGame();
2338 	return false;
2339 }
2340 
fnNewSwingSeq(uint32 a,uint32 b,uint32 c)2341 bool Logic::fnNewSwingSeq(uint32 a, uint32 b, uint32 c) {
2342 	// only certain files work on pc. (huh?! something we should take care of?)
2343 	if ((a == 85) || (a == 106) || (a == 75) || (a == 15)) {
2344 		_skyScreen->startSequenceItem((uint16)a);
2345 	} else {
2346 		debug(1,"Logic::fnNewSwingSeq: ignored seq %d",a);
2347 	}
2348 	return true;
2349 }
2350 
fnWaitSwingEnd(uint32 a,uint32 b,uint32 c)2351 bool Logic::fnWaitSwingEnd(uint32 a, uint32 b, uint32 c) {
2352 	_skyScreen->waitForSequence();
2353 	return true;
2354 }
2355 
fnSkipIntroCode(uint32 a,uint32 b,uint32 c)2356 bool Logic::fnSkipIntroCode(uint32 a, uint32 b, uint32 c) {
2357 	SkyEngine::_systemVars.pastIntro = true;
2358 	return true;
2359 }
2360 
fnBlankScreen(uint32 a,uint32 b,uint32 c)2361 bool Logic::fnBlankScreen(uint32 a, uint32 b, uint32 c) {
2362 	_skyScreen->clearScreen();
2363 	return true;
2364 }
2365 
fnPrintCredit(uint32 a,uint32 b,uint32 c)2366 bool Logic::fnPrintCredit(uint32 a, uint32 b, uint32 c) {
2367 	DisplayedText creditText = _skyText->lowTextManager(a, 240, 0, 248, true);
2368 	Compact *credCompact = _skyCompact->fetchCpt(creditText.compactNum);
2369 	credCompact->xcood = 168;
2370 	if ((a == 558) && (c == 215))
2371 		credCompact->ycood = 211;
2372 	else
2373 		credCompact->ycood = (uint16)c;
2374 	_scriptVariables[RESULT] = creditText.compactNum;
2375 	return true;
2376 }
2377 
fnLookAt(uint32 a,uint32 b,uint32 c)2378 bool Logic::fnLookAt(uint32 a, uint32 b, uint32 c) {
2379 	DisplayedText textInfo = _skyText->lowTextManager(a, 240, 0, 248, true);
2380 	Compact *textCpt = _skyCompact->fetchCpt(textInfo.compactNum);
2381 	textCpt->xcood = 168;
2382 	textCpt->ycood = (uint16)c;
2383 
2384 	_skyScreen->recreate();
2385 	_skyScreen->spriteEngine();
2386 	_skyScreen->flip();
2387 
2388 	fnNoHuman(0, 0, 0);
2389 	_skyMouse->lockMouse();
2390 
2391 	_skyMouse->waitMouseNotPressed(800);
2392 
2393 	_skyMouse->unlockMouse();
2394 	fnAddHuman(0, 0, 0);
2395 	textCpt->status = 0;
2396 
2397 	return true;
2398 }
2399 
fnLincTextModule(uint32 textPos,uint32 textNo,uint32 buttonAction)2400 bool Logic::fnLincTextModule(uint32 textPos, uint32 textNo, uint32 buttonAction) {
2401 	uint16 cnt;
2402 	if (buttonAction & 0x8000)
2403 		for (cnt = LINC_DIGIT_0; cnt <= LINC_DIGIT_9; cnt++)
2404 			_scriptVariables[cnt] = 0;
2405 	buttonAction &= 0x7FFF;
2406 	if (buttonAction < 10)
2407 		_scriptVariables[LINC_DIGIT_0 + buttonAction] = textNo;
2408 
2409 	DisplayedText text = _skyText->lowTextManager(textNo, 220, 0, 215, false);
2410 
2411 	Compact *textCpt = _skyCompact->fetchCpt(text.compactNum);
2412 
2413 	if (textPos < 20) { // line number (for text)
2414 		textCpt->xcood = 152;
2415 		textCpt->ycood = (uint16)textPos * 13 + 170;
2416 	} else if (textPos > 20) { // x coordinate (for numbers)
2417 		textCpt->xcood = (uint16)textPos;
2418 		textCpt->ycood = 214;
2419 	} else warning("::fnLincTextModule: textPos == 20");
2420 	textCpt->getToFlag = (uint16)textNo;
2421 	return true;
2422 }
2423 
fnTextKill2(uint32 a,uint32 b,uint32 c)2424 bool Logic::fnTextKill2(uint32 a, uint32 b, uint32 c) {
2425 	/// Kill all text items
2426 
2427 	uint32 id = FIRST_TEXT_COMPACT;
2428 
2429 	for (int i = 10; i > 0; i--) {
2430 		Compact *cpt = _skyCompact->fetchCpt(id);
2431 		cpt->status = 0;
2432 		id++;
2433 	}
2434 	return true;
2435 }
2436 
fnSetFont(uint32 font,uint32 b,uint32 c)2437 bool Logic::fnSetFont(uint32 font, uint32 b, uint32 c) {
2438 	_skyText->fnSetFont(font);
2439 	return true;
2440 }
2441 
fnStartFx(uint32 sound,uint32 b,uint32 c)2442 bool Logic::fnStartFx(uint32 sound, uint32 b, uint32 c) {
2443 	_skySound->fnStartFx(sound, (uint8)(b & 1));
2444 	return true;
2445 }
2446 
fnStopFx(uint32 a,uint32 b,uint32 c)2447 bool Logic::fnStopFx(uint32 a, uint32 b, uint32 c) {
2448 	_skySound->fnStopFx();
2449 	return true;
2450 }
2451 
fnStartMusic(uint32 a,uint32 b,uint32 c)2452 bool Logic::fnStartMusic(uint32 a, uint32 b, uint32 c) {
2453 	if (!(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF))
2454 		_skyMusic->startMusic((uint16)a);
2455 	SkyEngine::_systemVars.currentMusic = (uint16)a;
2456 	return true;
2457 }
2458 
fnStopMusic(uint32 a,uint32 b,uint32 c)2459 bool Logic::fnStopMusic(uint32 a, uint32 b, uint32 c) {
2460 	_skyMusic->startMusic(0);
2461 	SkyEngine::_systemVars.currentMusic = 0;
2462 	return true;
2463 }
2464 
fnFadeDown(uint32 a,uint32 b,uint32 c)2465 bool Logic::fnFadeDown(uint32 a, uint32 b, uint32 c) {
2466 	_skyScreen->fnFadeDown(a);
2467 	return true;
2468 }
2469 
fnFadeUp(uint32 a,uint32 b,uint32 c)2470 bool Logic::fnFadeUp(uint32 a, uint32 b, uint32 c) {
2471 	SkyEngine::_systemVars.currentPalette = a;
2472 	_skyScreen->fnFadeUp(a,b);
2473 	return true;
2474 }
2475 
fnQuitToDos(uint32 a,uint32 b,uint32 c)2476 bool Logic::fnQuitToDos(uint32 a, uint32 b, uint32 c) {
2477 	Engine::quitGame();
2478 	return false;
2479 }
2480 
fnPauseFx(uint32 a,uint32 b,uint32 c)2481 bool Logic::fnPauseFx(uint32 a, uint32 b, uint32 c) {
2482 	_skySound->fnPauseFx();
2483 	return true;
2484 }
2485 
fnUnPauseFx(uint32 a,uint32 b,uint32 c)2486 bool Logic::fnUnPauseFx(uint32 a, uint32 b, uint32 c) {
2487 	_skySound->fnUnPauseFx();
2488 	return true;
2489 }
2490 
fnPrintf(uint32 a,uint32 b,uint32 c)2491 bool Logic::fnPrintf(uint32 a, uint32 b, uint32 c) {
2492 	debug("fnPrintf(%d, %d, %d)", a, b, c);
2493 	return true;
2494 }
2495 
stdSpeak(Compact * target,uint32 textNum,uint32 animNum,uint32 base)2496 void Logic::stdSpeak(Compact *target, uint32 textNum, uint32 animNum, uint32 base) {
2497 	animNum += target->megaSet / NEXT_MEGA_SET;
2498 	animNum &= 0xFF;
2499 
2500 	uint16 *talkTable = (uint16 *)_skyCompact->fetchCpt(CPT_TALK_TABLE_LIST);
2501 	target->grafixProgId = talkTable[animNum];
2502 	target->grafixProgPos = 0;
2503 	uint16 *animPtr = _skyCompact->getGrafixPtr(target);
2504 
2505 	if (animPtr) {
2506 		target->offset = *animPtr++;
2507 		target->getToFlag = *animPtr++;
2508 		target->grafixProgPos += 2;
2509 	} else
2510 		target->grafixProgId = 0;
2511 
2512 	bool speechFileFound = false;
2513 	if (SkyEngine::isCDVersion())
2514 		speechFileFound = _skySound->startSpeech((uint16)textNum);
2515 
2516 
2517 	// Set the focus region to that area
2518 	// Calculate the point where the character is
2519 	int x = target->xcood - TOP_LEFT_X;
2520 	int y = target->ycood - TOP_LEFT_Y;
2521 	// TODO: Make the box size change based on the object that has the focus
2522 	_skyScreen->setFocusRectangle(Common::Rect::center(x, y, 192, 128));
2523 
2524 
2525 	if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT) || !speechFileFound) {
2526 		// form the text sprite, if player wants subtitles or
2527 		// if we couldn't find the speech file
2528 		DisplayedText textInfo;
2529 		textInfo = _skyText->lowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (uint8)target->spColor, true);
2530 		Compact *textCompact = _skyCompact->fetchCpt(textInfo.compactNum);
2531 		target->spTextId = textInfo.compactNum;	//So we know what text to kill
2532 		byte *textGfx = textInfo.textData;
2533 
2534 		textCompact->screen = target->screen;	//put it on our screen
2535 
2536 		if (_scriptVariables[SCREEN] == target->screen) { // Only use coordinates if we are on the current screen
2537 			//talking on-screen
2538 			//create the x coordinate for the speech text
2539 			//we need the talkers sprite information
2540 			byte *targetGfx = (byte *)SkyEngine::fetchItem(target->frame >> 6);
2541 			uint16 xPos = target->xcood + ((DataFileHeader *)targetGfx)->s_offset_x;
2542 			uint16 width = (((DataFileHeader *)targetGfx)->s_width >> 1);
2543 
2544 			xPos += width - (FIXED_TEXT_WIDTH / 2);	//middle of talker
2545 
2546 			if (xPos < TOP_LEFT_X)
2547 				xPos = TOP_LEFT_X;
2548 
2549 			width = xPos + FIXED_TEXT_WIDTH;
2550 			if ((TOP_LEFT_X + FULL_SCREEN_WIDTH) <= width) {
2551 				xPos = TOP_LEFT_X + FULL_SCREEN_WIDTH;
2552 				xPos -= FIXED_TEXT_WIDTH;
2553 			}
2554 
2555 			textCompact->xcood = xPos;
2556 			uint16 yPos = target->ycood + ((DataFileHeader *)targetGfx)->s_offset_y - 6 - ((DataFileHeader *)textGfx)->s_height;
2557 
2558 			if (yPos < TOP_LEFT_Y)
2559 				yPos = TOP_LEFT_Y;
2560 
2561 			textCompact->ycood = yPos;
2562 
2563 		} else {
2564 			//talking off-screen
2565 			target->spTextId = 0;	//don't kill any text 'cos none was made
2566 			textCompact->status = 0;	//don't display text
2567 		}
2568 		// In CD version, we're doing the timing by checking when the VOC has stopped playing.
2569 		// Setting spTime to 10 thus means that we're doing a pause of 10 gamecycles between
2570 		// each sentence.
2571 		if (speechFileFound)
2572 			target->spTime = 10;
2573 		else
2574 			target->spTime = (uint16)_skyText->_numLetters + 5;
2575 	} else {
2576 		target->spTime = 10;
2577 		target->spTextId = 0;
2578 	}
2579 	target->logic = L_TALK;
2580 }
2581 
2582 } // End of namespace Sky
2583