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