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 /*
24 * This code is based on original Sfinx source code
25 * Copyright (c) 1994-1997 Janusz B. Wisniewski and L.K. Avalon
26 */
27
28 #include "cge2/snail.h"
29 #include "cge2/fileio.h"
30 #include "cge2/hero.h"
31 #include "cge2/text.h"
32 #include "cge2/sound.h"
33 #include "cge2/events.h"
34 #include "common/config-manager.h"
35
36 namespace CGE2 {
37
38 const char *CommandHandler::_commandText[] = {
39 "NOP", "USE", "PAUSE", "INF", "CAVE", "SETX", "SETY", "SETZ", "ADD",
40 "FLASH", "CYCLE", "CLEAR", "MOUSE", "MAP", "MIDI", ".DUMMY.", "WAIT",
41 "HIDE", "ROOM", "SAY", "SOUND", "KILL", "RSEQ", "SEQ", "SEND", "SWAP",
42 "KEEP", "GIVE", "GETPOS", "GOTO", "PORT", "NEXT", "NNEXT", "MTNEXT",
43 "FTNEXT", "RNNEXT", "RMTNEXT", "RFTNEXT", "RMNEAR", "RMMTAKE", "RMFTAKE",
44 "SETREF", "WALKTO", "REACH", "COVER", "UNCOVER", "EXEC", "GHOST",
45 nullptr };
46
CommandHandler(CGE2Engine * vm,bool turbo)47 CommandHandler::CommandHandler(CGE2Engine *vm, bool turbo)
48 : _turbo(turbo), _textDelay(false), _timerExpiry(0), _talkEnable(true),
49 _head(0), _tail(0), _commandList((Command *)malloc(sizeof(Command)* 256)),
50 _vm(vm) {
51 }
52
~CommandHandler()53 CommandHandler::~CommandHandler() {
54 free(_commandList);
55 }
56
runCommand()57 void CommandHandler::runCommand() {
58 if (!_turbo && _vm->_soundStat._wait) {
59 if (*(_vm->_soundStat._wait))
60 return;
61
62 ++_vm->_soundStat._ref[0];
63 if (_vm->_fx->exist(_vm->_soundStat._ref[1], _vm->_soundStat._ref[0])) {
64 int16 oldRepeat = _vm->_sound->getRepeat();
65 _vm->_sound->setRepeat(1);
66 _vm->_sound->play(Audio::Mixer::kSpeechSoundType, _vm->_soundStat._ref[1], _vm->_soundStat._ref[0], _vm->_sound->_smpinf._span);
67 _vm->_sound->setRepeat(oldRepeat);
68 return;
69 }
70 _vm->_soundStat._wait = nullptr;
71 }
72
73 uint8 tmpHead = _head;
74 while (_tail != tmpHead) {
75 Command tailCmd = _commandList[_tail];
76
77 if (!_turbo) { // only for the slower one
78 if (_vm->_waitRef)
79 break;
80
81 if (_timerExpiry) {
82 // Delay in progress
83 if (_timerExpiry > g_system->getMillis())
84 // Delay not yet ended
85 break;
86
87 // Delay is finished
88 _timerExpiry = 0;
89 } else if (_textDelay) {
90 if (_vm->_talk) {
91 _vm->snKill((Sprite *)_vm->_talk);
92 _vm->_talk = nullptr;
93 }
94 _textDelay = false;
95 }
96
97 if (_vm->_talk && tailCmd._commandType != kCmdPause)
98 break;
99 }
100 ++_tail;
101 _vm->_taken = false;
102 Sprite *spr = nullptr;
103 if (tailCmd._commandType > kCmdSpr)
104 spr = (tailCmd._ref < 0) ? ((Sprite *)tailCmd._spritePtr) : _vm->locate(tailCmd._ref);
105
106 Common::String sprStr;
107 if (tailCmd._commandType != kCmdGhost && spr && *spr->_file)
108 // In case of kCmdGhost _spritePtr stores a pointer to a Bitmap, not to a Sprite...
109 sprStr = Common::String(spr->_file);
110 else
111 sprStr = "None";
112
113 if (sprStr.empty())
114 sprStr = "None";
115 debugC(1, kCGE2DebugOpcode, "Command: %s; Ref: %d; Val: %d; Sprite: %s;", getComStr(tailCmd._commandType), tailCmd._ref, tailCmd._val, sprStr.c_str());
116
117 switch (tailCmd._commandType) {
118 case kCmdUse:
119 break;
120 case kCmdPause:
121 _timerExpiry = g_system->getMillis() + tailCmd._val * kCommandFrameDelay;
122 if (_vm->_talk)
123 _textDelay = true;
124 break;
125 case kCmdWait:
126 if (spr && spr->active() && (spr->_scene == _vm->_now || spr->_scene == 0)) {
127 _vm->_waitSeq = tailCmd._val;
128 _vm->_waitRef = spr->_ref;
129 }
130 break;
131 case kCmdHide:
132 _vm->snHide(spr, tailCmd._val);
133 break;
134 case kCmdSay:
135 _vm->snSay(spr, tailCmd._val);
136 break;
137 case kCmdInf:
138 if (_talkEnable)
139 _vm->inf(((tailCmd._val) >= 0) ? _vm->_text->getText(tailCmd._val) : (const char *)tailCmd._spritePtr);
140 break;
141 case kCmdCave:
142 _vm->switchScene(tailCmd._val);
143 break;
144 case kCmdMidi:
145 _vm->snMidi(tailCmd._val);
146 break;
147 case kCmdKill:
148 _vm->snKill(spr);
149 break;
150 case kCmdSeq:
151 _vm->snSeq(spr, tailCmd._val);
152 break;
153 case kCmdRSeq:
154 _vm->snRSeq(spr, tailCmd._val);
155 break;
156 case kCmdSend:
157 _vm->snSend(spr, tailCmd._val);
158 break;
159 case kCmdSwap:
160 _vm->snSwap(spr, tailCmd._val);
161 break;
162 case kCmdCover:
163 _vm->snCover(spr, tailCmd._val);
164 break;
165 case kCmdUncover:
166 _vm->snUncover(spr, (tailCmd._val >= 0) ? _vm->locate(tailCmd._val) : ((Sprite *)tailCmd._spritePtr));
167 break;
168 case kCmdKeep:
169 _vm->snKeep(spr, tailCmd._val);
170 break;
171 case kCmdGive:
172 _vm->snGive(spr, tailCmd._val);
173 break;
174 case kCmdSetX:
175 _vm->_point[tailCmd._val]->_x = tailCmd._ref;
176 break;
177 case kCmdSetY:
178 _vm->_point[tailCmd._val]->_y = tailCmd._ref;
179 break;
180 case kCmdSetZ:
181 _vm->_point[tailCmd._val]->_z = tailCmd._ref;
182 break;
183 case kCmdAdd:
184 *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) + *(_vm->_point[tailCmd._val]);
185 break;
186 case kCmdGetPos:
187 if (spr)
188 *(_vm->_point[tailCmd._val]) = spr->_pos3D;
189 break;
190 case kCmdGoto:
191 _vm->snGoto(spr, tailCmd._val);
192 break;
193 case kCmdPort:
194 _vm->snPort(spr, tailCmd._val);
195 break;
196 case kCmdNext:
197 break;
198 case kCmdMouse:
199 _vm->snMouse(tailCmd._val != 0);
200 break;
201 case kCmdNNext:
202 _vm->snNNext(spr, kNear, tailCmd._val);
203 break;
204 case kCmdMTNext:
205 _vm->snNNext(spr, kMTake, tailCmd._val);
206 break;
207 case kCmdFTNext:
208 _vm->snNNext(spr, kFTake, tailCmd._val);
209 break;
210 case kCmdRNNext:
211 _vm->snRNNext(spr, tailCmd._val);
212 break;
213 case kCmdRMTNext:
214 _vm->snRMTNext(spr, tailCmd._val);
215 break;
216 case kCmdRFTNext:
217 _vm->snRFTNext(spr, tailCmd._val);
218 break;
219 case kCmdRMNear:
220 _vm->snRmNear(spr);
221 break;
222 case kCmdRMMTake:
223 _vm->snRmMTake(spr);
224 break;
225 case kCmdRMFTake:
226 _vm->snRmFTake(spr);
227 break;
228 case kCmdSetRef:
229 _vm->snSetRef(spr, tailCmd._val);
230 break;
231 case kCmdFlash:
232 _vm->snFlash(tailCmd._val != 0);
233 break;
234 case kCmdCycle:
235 _vm->snCycle(tailCmd._val);
236 break;
237 case kCmdWalk:
238 _vm->snWalk(spr, tailCmd._val);
239 break;
240 case kCmdReach:
241 _vm->snReach(spr, tailCmd._val);
242 break;
243 case kCmdSound:
244 _vm->snSound(spr, tailCmd._val);
245 _vm->_sound->setRepeat(1);
246 break;
247 case kCmdMap:
248 _vm->_heroTab[tailCmd._ref & 1]->_ptr->_ignoreMap = tailCmd._val == 0;
249 break;
250 case kCmdRoom:
251 _vm->snRoom(spr, tailCmd._val);
252 break;
253 case kCmdExec:
254 switch (tailCmd._cbType) {
255 case kQGame:
256 _vm->qGame();
257 break;
258 case kXScene:
259 _vm->xScene();
260 break;
261 default:
262 error("Unknown Callback Type in SNEXEC");
263 break;
264 }
265 break;
266 case kCmdGhost:
267 _vm->snGhost((Bitmap *)tailCmd._spritePtr);
268 break;
269 case kCmdNop: // Do nothing.
270 break;
271 default:
272 warning("Unhandled command");
273 break;
274 }
275
276 if (_vm->_taken && spr)
277 _vm->_spare->dispose(spr);
278
279 if (!_turbo)
280 break;
281 }
282 }
283
snKill(Sprite * spr)284 void CGE2Engine::snKill(Sprite *spr) {
285 if (spr) {
286 if (spr->_flags._kept)
287 releasePocket(spr);
288 Sprite *nx = spr->_next;
289 hide1(spr);
290 _vga->_showQ->remove(spr);
291 _eventManager->clearEvent(spr);
292 if (spr->_flags._kill) {
293 _spare->take(spr->_ref);
294 delete spr;
295 } else {
296 spr->setScene(-1);
297 _spare->dispose(spr);
298 }
299 if (nx && nx->_flags._slav)
300 snKill(nx);
301 }
302 }
303
snHide(Sprite * spr,int val)304 void CGE2Engine::snHide(Sprite *spr, int val) {
305 if (spr) {
306 spr->_flags._hide = (val >= 0) ? (val != 0) : (!spr->_flags._hide);
307 if (spr->_flags._shad)
308 spr->_prev->_flags._hide = spr->_flags._hide;
309 }
310 }
311
snMidi(int val)312 void CGE2Engine::snMidi(int val) {
313 if (val < 0)
314 _midiPlayer->killMidi();
315 else if (_music)
316 _midiPlayer->loadMidi(val);
317 }
318
snSeq(Sprite * spr,int val)319 void CGE2Engine::snSeq(Sprite *spr, int val) {
320 if (spr) {
321 if (isHero(spr) && (val == 0))
322 ((Hero*)spr)->park();
323 else
324 spr->step(val);
325 }
326 }
327
snRSeq(Sprite * spr,int val)328 void CGE2Engine::snRSeq(Sprite *spr, int val) {
329 if (spr)
330 snSeq(spr, spr->_seqPtr + val);
331 }
332
snSend(Sprite * spr,int val)333 void CGE2Engine::snSend(Sprite *spr, int val) {
334 if (!spr)
335 return;
336
337 // Sending", spr->_file
338 // from scene", spr->_scene
339 // to scene", val
340 bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr);
341 bool val1 = (val == 0 || val == _now);
342 spr->_scene = val;
343 releasePocket(spr);
344 if (val1 != was1) {
345 if (was1) {
346 // deactivating
347 hide1(spr);
348 spr->_flags._slav = false;
349 if ((spr == _heroTab[_sex]->_ptr) && (_heroTab[!_sex]->_ptr->_scene == _now))
350 switchHero(!_sex);
351 _spare->dispose(spr);
352 } else {
353 // activating
354 if (byte(spr->_ref) == 0)
355 _bitmapPalette = _vga->_sysPal;
356 _vga->_showQ->insert(spr);
357 if (isHero(spr)) {
358 V2D p = *_heroTab[spr->_ref & 1]->_posTab[val];
359 spr->gotoxyz(V3D(p.x, 0, p.y));
360 ((Hero*)spr)->setCurrent();
361 }
362 _taken = false;
363 _bitmapPalette = nullptr;
364 }
365 }
366 }
367
snSwap(Sprite * spr,int val)368 void CGE2Engine::snSwap(Sprite *spr, int val) {
369 bool tak = _taken;
370 Sprite *xspr = locate(val);
371 if (spr && xspr) {
372 bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr);
373 bool xwas1 = (_vga->_showQ->locate(val) != nullptr);
374
375 int tmp = spr->_scene;
376 spr->setScene(xspr->_scene);
377 xspr->setScene(tmp);
378
379 SWAP(spr->_pos2D, xspr->_pos2D);
380 SWAP(spr->_pos3D, xspr->_pos3D);
381 if (spr->_flags._kept)
382 swapInPocket(spr, xspr);
383 if (xwas1 != was1) {
384 if (was1) {
385 hide1(spr);
386 _spare->dispose(spr);
387 } else
388 expandSprite(spr);
389 if (xwas1) {
390 hide1(xspr);
391 _spare->dispose(xspr);
392 } else {
393 expandSprite(xspr);
394 _taken = false;
395 }
396 }
397 }
398 if (_taken)
399 _spare->dispose(xspr);
400 _taken = tak;
401 }
402
snCover(Sprite * spr,int val)403 void CGE2Engine::snCover(Sprite *spr, int val) {
404 bool tak = _taken;
405 Sprite *xspr = locate(val);
406 if (spr && xspr) {
407 spr->_flags._hide = true;
408 xspr->setScene(spr->_scene);
409 xspr->gotoxyz(spr->_pos3D);
410 expandSprite(xspr);
411 if ((xspr->_flags._shad = spr->_flags._shad) == true) {
412 _vga->_showQ->insert(_vga->_showQ->remove(spr->_prev), xspr);
413 spr->_flags._shad = false;
414 }
415 feedSnail(xspr, kNear, _heroTab[_sex]->_ptr);
416 _taken = false;
417 }
418 if (_taken)
419 _spare->dispose(xspr);
420 _taken = tak;
421 }
422
snUncover(Sprite * spr,Sprite * spr2)423 void CGE2Engine::snUncover(Sprite *spr, Sprite *spr2) {
424 if (spr && spr2) {
425 spr->_flags._hide = false;
426 spr->setScene(spr2->_scene);
427 if ((spr->_flags._shad = spr2->_flags._shad) == true) {
428 _vga->_showQ->insert(_vga->_showQ->remove(spr2->_prev), spr);
429 spr2->_flags._shad = false;
430 }
431 spr->gotoxyz(spr2->_pos3D);
432 snSend(spr2, -1);
433 if (spr->_time == 0)
434 ++spr->_time;
435 }
436 }
437
snKeep(Sprite * spr,int stp)438 void CGE2Engine::snKeep(Sprite *spr, int stp) {
439 int sex = _sex;
440 if (stp > 127) {
441 _sex = stp & 1; // for another hero
442 stp = -1;
443 }
444 HeroTab *ht = _heroTab[_sex];
445 selectPocket(-1);
446 int pp = ht->_pocPtr;
447
448 if (spr && !spr->_flags._kept && ht->_pocket[pp] == nullptr) {
449 V3D pos(14, -10, -1);
450 int16 oldRepeat = _sound->getRepeat();
451 _sound->setRepeat(1);
452 snSound(ht->_ptr, 3);
453 _sound->setRepeat(oldRepeat);
454 if (_taken) {
455 _vga->_showQ->insert(spr);
456 _taken = false;
457 }
458 ht->_pocket[pp] = spr;
459 spr->setScene(0);
460 spr->_flags._kept = true;
461 if (!_sex)
462 pos._x += kScrWidth - 58;
463 if (pp & 1)
464 pos._x += 29;
465 if (pp >> 1)
466 pos._y -= 20;
467 pos._y -= (spr->_siz.y / 2);
468 spr->gotoxyz(pos);
469 if (stp >= 0)
470 spr->step(stp);
471 }
472 _sex = sex;
473 selectPocket(-1);
474 }
475
snGive(Sprite * spr,int val)476 void CGE2Engine::snGive(Sprite *spr, int val) {
477 if (spr) {
478 int p = findActivePocket(spr->_ref);
479 if (p >= 0) {
480 releasePocket(spr);
481 spr->setScene(_now);
482 if (val >= 0)
483 spr->step(val);
484 }
485 }
486 selectPocket(-1);
487 }
488
snGoto(Sprite * spr,int val)489 void CGE2Engine::snGoto(Sprite *spr, int val) {
490 if (spr) {
491 V3D eye = *_eye;
492 if (spr->_scene > 0)
493 setEye(*_eyeTab[spr->_scene]);
494 spr->gotoxyz(*_point[val]);
495 setEye(eye);
496 }
497 }
498
snPort(Sprite * spr,int port)499 void CGE2Engine::snPort(Sprite *spr, int port) {
500 if (spr)
501 spr->_flags._port = (port < 0) ? !spr->_flags._port : (port != 0);
502 }
503
snMouse(bool on)504 void CGE2Engine::snMouse(bool on) {
505 if (on)
506 _mouse->on();
507 else
508 _mouse->off();
509 }
510
snNNext(Sprite * spr,Action act,int val)511 void CGE2Engine::snNNext(Sprite *spr, Action act, int val) {
512 if (spr) {
513 if (val > 255)
514 val = spr->labVal(act, val >> 8);
515 spr->_actionCtrl[act]._ptr = val;
516 }
517 }
518
snRNNext(Sprite * spr,int val)519 void CGE2Engine::snRNNext(Sprite *spr, int val) {
520 if (spr)
521 spr->_actionCtrl[kNear]._ptr += val;
522 }
523
snRMTNext(Sprite * spr,int val)524 void CGE2Engine::snRMTNext(Sprite *spr, int val) {
525 if (spr)
526 spr->_actionCtrl[kMTake]._ptr += val;
527 }
528
snRFTNext(Sprite * spr,int val)529 void CGE2Engine::snRFTNext(Sprite * spr, int val) {
530 if (spr)
531 spr->_actionCtrl[kFTake]._ptr += val;
532 }
533
snRmNear(Sprite * spr)534 void CGE2Engine::snRmNear(Sprite *spr) {
535 if (spr)
536 spr->_actionCtrl[kNear]._cnt = 0;
537 }
538
snRmMTake(Sprite * spr)539 void CGE2Engine::snRmMTake(Sprite *spr) {
540 if (spr)
541 spr->_actionCtrl[kMTake]._cnt = 0;
542 }
543
snRmFTake(Sprite * spr)544 void CGE2Engine::snRmFTake(Sprite *spr) {
545 if (spr)
546 spr->_actionCtrl[kFTake]._cnt = 0;
547 }
548
snSetRef(Sprite * spr,int val)549 void CGE2Engine::snSetRef(Sprite *spr, int val) {
550 if (spr)
551 spr->_ref = val;
552 }
553
snFlash(bool on)554 void CGE2Engine::snFlash(bool on) {
555 if (on) {
556 Dac *pal = (Dac *)malloc(sizeof(Dac) * kPalCount);
557 if (pal) {
558 memcpy(pal, _vga->_sysPal, kPalSize);
559 for (int i = 0; i < kPalCount; i++) {
560 int c;
561 c = pal[i]._r << 1;
562 pal[i]._r = (c < 64) ? c : 63;
563 c = pal[i]._g << 1;
564 pal[i]._g = (c < 64) ? c : 63;
565 c = pal[i]._b << 1;
566 pal[i]._b = (c < 64) ? c : 63;
567 }
568 _vga->setColors(pal, 64);
569 }
570
571 free(pal);
572 } else
573 _vga->setColors(_vga->_sysPal, 64);
574 _dark = false;
575 }
576
snCycle(int cnt)577 void CGE2Engine::snCycle(int cnt) {
578 _vga->_rot._len = cnt;
579 }
580
snWalk(Sprite * spr,int val)581 void CGE2Engine::snWalk(Sprite *spr, int val) {
582 if (isHero(spr)) {
583 if (val < kMaxPoint)
584 ((Hero *)spr)->walkTo(*_point[val]);
585 else {
586 Sprite *s = _vga->_showQ->locate(val);
587 if (s)
588 ((Hero *)spr)->walkTo(s);
589 }
590 ((Hero *)spr)->_time = 1;
591 }
592 }
593
snReach(Sprite * spr,int val)594 void CGE2Engine::snReach(Sprite *spr, int val) {
595 if (isHero(spr))
596 ((Hero *)spr)->reach(val);
597 }
598
snSound(Sprite * spr,int wav,Audio::Mixer::SoundType soundType)599 void CGE2Engine::snSound(Sprite *spr, int wav, Audio::Mixer::SoundType soundType) {
600 if (wav == -1)
601 _sound->stop();
602 else {
603 if (_sound->_smpinf._counter && wav < 20)
604 return;
605 if (_soundStat._wait && ((wav & 255) > 80))
606 return;
607
608 _soundStat._ref[1] = wav;
609 _soundStat._ref[0] = !_fx->exist(_soundStat._ref[1]);
610 _sound->play(soundType, _soundStat._ref[1], _soundStat._ref[0],
611 (spr) ? (spr->_pos2D.x / (kScrWidth / 16)) : 8);
612 }
613 }
614
snRoom(Sprite * spr,bool on)615 void CGE2Engine::snRoom(Sprite *spr, bool on) {
616 if (!isHero(spr))
617 return;
618
619 int sex = spr->_ref & 1;
620 Sprite **p = _heroTab[sex]->_pocket;
621 if (on) {
622 if (freePockets(sex) == 0 && p[kPocketMax] == nullptr) {
623 SWAP(p[kPocketMax], p[kPocketMax - 1]);
624 snHide(p[kPocketMax], 1);
625 }
626 } else if (p[kPocketMax]) {
627 for (int i = 0; i < kPocketMax; i++) {
628 if (p[i] == nullptr) {
629 snHide(p[kPocketMax], 0);
630 SWAP(p[kPocketMax], p[i]);
631 break;
632 }
633 }
634 }
635 }
636
snGhost(Bitmap * bmp)637 void CGE2Engine::snGhost(Bitmap *bmp) {
638 V2D p(this, bmp->_map & 0xFFFF, bmp->_map >> 16);
639 bmp->hide(p);
640 bmp->release();
641 delete[] bmp->_b;
642 bmp->_b = nullptr;
643 delete bmp;
644 bmp = nullptr;
645 }
646
snSay(Sprite * spr,int val)647 void CGE2Engine::snSay(Sprite *spr, int val) {
648 if (spr && spr->active() && _commandHandler->_talkEnable) {
649 //-- mouth animation
650 if (isHero(spr) && spr->seqTest(-1))
651 ((Hero *)spr)->say();
652 if (_sayCap)
653 _text->say(_text->getText(val), spr);
654 if (_sayVox) {
655 int i = val;
656 if (i < 256)
657 i -= 100;
658 int16 oldRepeat = _sound->getRepeat();
659 _sound->setRepeat(1);
660 if (!ConfMan.getBool("tts_enabled_speech") || getLanguage() == Common::PL_POL)
661 snSound(spr, i, Audio::Mixer::kSpeechSoundType);
662 _sound->setRepeat(oldRepeat);
663 _soundStat._wait = &_sound->_smpinf._counter;
664 }
665 }
666 }
667
hide1(Sprite * spr)668 void CGE2Engine::hide1(Sprite *spr) {
669 _commandHandlerTurbo->addCommand(kCmdGhost, -1, 0, spr->ghost());
670 }
671
swapInPocket(Sprite * spr,Sprite * xspr)672 void CGE2Engine::swapInPocket(Sprite *spr, Sprite *xspr) {
673 for (int i = 0; i < 2; i++) {
674 for (int j = 0; j < kPocketMax; j++) {
675 Sprite *&poc = _heroTab[i]->_pocket[j];
676 if (poc == spr) {
677 spr->_flags._kept = false;
678 poc = xspr;
679 xspr->_flags._kept = true;
680 xspr->_flags._port = false;
681 return;
682 }
683 }
684 }
685 }
686
expandSprite(Sprite * spr)687 Sprite *CGE2Engine::expandSprite(Sprite *spr) {
688 if (spr)
689 _vga->_showQ->insert(spr);
690 return spr;
691 }
692
qGame()693 void CGE2Engine::qGame() {
694 // Write out the user's progress
695 saveGame(0, Common::String("Automatic Savegame"));
696
697 busy(false);
698 _vga->sunset();
699 _endGame = true;
700 }
701
xScene()702 void CGE2Engine::xScene() {
703 sceneDown();
704 sceneUp(_req);
705 }
706
addCommand(CommandType com,int ref,int val,void * ptr)707 void CommandHandler::addCommand(CommandType com, int ref, int val, void *ptr) {
708 if (ref == -2)
709 ref = 142 - _vm->_sex;
710 Command *headCmd = &_commandList[_head++];
711 headCmd->_commandType = com;
712 headCmd->_ref = ref;
713 headCmd->_val = val;
714 headCmd->_spritePtr = ptr;
715 headCmd->_cbType = kNullCB;
716 if (headCmd->_commandType == kCmdClear) {
717 clear();
718 }
719 }
720
addCallback(CommandType com,int ref,int val,CallbackType cbType)721 void CommandHandler::addCallback(CommandType com, int ref, int val, CallbackType cbType) {
722 Command *headCmd = &_commandList[_head++];
723 headCmd->_commandType = com;
724 headCmd->_ref = ref;
725 headCmd->_val = val;
726 headCmd->_spritePtr = nullptr;
727 headCmd->_cbType = cbType;
728 if (headCmd->_commandType == kCmdClear) {
729 _tail = _head;
730 _vm->killText();
731 _timerExpiry = 0;
732 }
733 }
734
insertCommand(CommandType com,int ref,int val,void * ptr)735 void CommandHandler::insertCommand(CommandType com, int ref, int val, void *ptr) {
736 if (ref == -2)
737 ref = 142 - _vm->_sex;
738 --_tail;
739 Command *tailCmd = &_commandList[_tail];
740 tailCmd->_commandType = com;
741 tailCmd->_ref = ref;
742 tailCmd->_val = val;
743 tailCmd->_spritePtr = ptr;
744 tailCmd->_cbType = kNullCB;
745 if (com == kCmdClear) {
746 _tail = _head;
747 _vm->killText();
748 _timerExpiry = 0;
749 }
750 }
751
idle()752 bool CommandHandler::idle() {
753 return (!_vm->_waitRef && _head == _tail);
754 }
755
clear()756 void CommandHandler::clear() {
757 _tail = _head;
758 _vm->killText();
759 _timerExpiry = 0;
760 }
761
getComId(const char * com)762 int CommandHandler::getComId(const char *com) {
763 int i = _vm->takeEnum(_commandText, com);
764 return (i < 0) ? i : i + kCmdCom0 + 1;
765 }
766
getComStr(CommandType cmdType)767 const char *CommandHandler::getComStr(CommandType cmdType) {
768 return _commandText[cmdType - kCmdNop];
769 }
770
feedSnail(Sprite * spr,Action snq,Hero * hero)771 void CGE2Engine::feedSnail(Sprite *spr, Action snq, Hero *hero) {
772 if (!spr || !spr->active())
773 return;
774
775 int cnt = spr->_actionCtrl[snq]._cnt;
776 if (cnt) {
777 byte ptr = spr->_actionCtrl[snq]._ptr;
778 CommandHandler::Command *comtab = spr->snList(snq);
779 CommandHandler::Command *c = &comtab[ptr];
780 CommandHandler::Command *q = &comtab[cnt];
781
782 if (hero != nullptr) {
783 int pocFre = freePockets(hero->_ref & 1);
784 int pocReq = 0;
785 CommandHandler::Command *p = c;
786 for (; p < q && p->_commandType != kCmdNext; p++) { // scan commands
787 // drop from pocket?
788 if ((p->_commandType == kCmdSend && p->_val != _now)
789 || p->_commandType == kCmdGive) {
790 int ref = p->_ref;
791 if (ref < 0)
792 ref = spr->_ref;
793 if (findActivePocket(ref) >= 0)
794 --pocReq;
795 }
796 // make/dispose additional room?
797 if (p->_commandType == kCmdRoom) {
798 if (p->_val == 0)
799 ++pocReq;
800 else
801 --pocReq;
802 }
803 // put into pocket?
804 if (p->_commandType == kCmdKeep)
805 ++pocReq;
806 // overloaded?
807 if (pocReq > pocFre) {
808 pocFul();
809 return;
810 }
811 }
812 }
813
814 while (c < q) {
815 if ((c->_val == -1) && (c->_commandType == kCmdWalk || c->_commandType == kCmdReach))
816 c->_val = spr->_ref;
817
818 if (c->_commandType == kCmdNext) {
819 Sprite *s;
820
821 switch (c->_ref) {
822 case -2:
823 s = hero;
824 break;
825 case -1:
826 s = spr;
827 break;
828 default:
829 s = _vga->_showQ->locate(c->_ref);
830 break;
831 }
832
833 if (s && s->_actionCtrl[snq]._cnt) {
834 int v;
835 switch (c->_val) {
836 case -1:
837 v = int(c - comtab + 1);
838 break;
839 case -2:
840 v = int(c - comtab);
841 break;
842 case -3:
843 v = -1;
844 break;
845 default:
846 v = c->_val;
847 if ((v > 255) && s)
848 v = s->labVal(snq, v >> 8);
849 break;
850 }
851 if (v >= 0) {
852 s->_actionCtrl[snq]._ptr = v;
853 if (spr->_ref == 1537 && s->_actionCtrl[snq]._ptr == 26)
854 {
855 debug(1, "Carpet Clothes Horse Rehanging Workaround Triggered!");
856 s->_actionCtrl[snq]._ptr = 8;
857 }
858 }
859 }
860
861 if (s == spr)
862 break;
863 }
864
865 _commandHandler->addCommand(c->_commandType, c->_ref, c->_val, spr);
866
867 ++c;
868 }
869 }
870
871 }
872
873 } // End of namespace CGE2.
874