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