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/system.h"
24 #include "common/events.h"
25 #include "common/debug-channels.h"
26 #include "common/archive.h"
27 #include "common/config-manager.h"
28 #include "common/savefile.h"
29 #include "common/memstream.h"
30 #include "common/translation.h"
31 
32 #include "engines/advancedDetector.h"
33 #include "engines/util.h"
34 #include "graphics/palette.h"
35 #include "graphics/surface.h"
36 #include "graphics/thumbnail.h"
37 #include "gui/saveload.h"
38 #include "gui/message.h"
39 #include "toon/resource.h"
40 #include "toon/toon.h"
41 #include "toon/anim.h"
42 #include "toon/picture.h"
43 #include "toon/hotspot.h"
44 #include "toon/flux.h"
45 #include "toon/drew.h"
46 #include "toon/path.h"
47 
48 namespace Toon {
49 
init()50 void ToonEngine::init() {
51 	_currentScriptRegion = 0;
52 	_resources = new Resources(this);
53 	_animationManager = new AnimationManager(this);
54 	_moviePlayer = new Movie(this, new ToonstruckSmackerDecoder());
55 	_hotspots = new Hotspots(this);
56 
57 	_mainSurface = new Graphics::Surface();
58 	_mainSurface->create(TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
59 
60 	_finalPalette = new uint8[768];
61 	_backupPalette = new uint8[768];
62 	_additionalPalette1 = new uint8[69];
63 	_additionalPalette2 = new uint8[69];
64 	_cutawayPalette = new uint8[768];
65 	_universalPalette = new uint8[96];
66 	_fluxPalette = new uint8[24];
67 
68 	memset(_finalPalette, 0, 768);
69 	memset(_backupPalette, 0, 768);
70 	memset(_additionalPalette1, 0, 69);
71 	memset(_additionalPalette2, 0, 69);
72 	memset(_cutawayPalette, 0, 768);
73 	memset(_universalPalette, 0, 96);
74 	memset(_fluxPalette, 0, 24);
75 
76 	_conversationData = new int16[4096];
77 	memset(_conversationData, 0, 4096 * sizeof(int16));
78 
79 	_shouldQuit = false;
80 	_scriptStep = 0;
81 
82 	_cursorOffsetX = 0;
83 	_cursorOffsetY = 0;
84 	_currentHotspotItem = 0;
85 
86 	_currentTextLine = 0;
87 	_currentTextLineId = -1;
88 	_currentTextLineX = 0;
89 	_currentTextLineY = 0;
90 	_currentTextLineCharacterId = 0;
91 
92 	_saveBufferStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
93 
94 	_firstFrame = false;
95 
96 	const Common::FSNode gameDataDir(ConfMan.get("path"));
97 	SearchMan.addSubDirectoryMatching(gameDataDir, "MISC");
98 	SearchMan.addSubDirectoryMatching(gameDataDir, "ACT1");
99 	SearchMan.addSubDirectoryMatching(gameDataDir, "ACT2");
100 
101 	syncSoundSettings();
102 
103 	_pathFinding = new PathFinding();
104 
105 	resources()->openPackage("LOCAL.PAK");
106 	resources()->openPackage("ONETIME.PAK");
107 	resources()->openPackage("DREW.PAK");
108 
109 	// load subtitles if available (if fails to load it only return false, so there's no need to check)
110 	resources()->openPackage("SUBTITLES.PAK");
111 
112 	for (int32 i = 0; i < 32; i++)
113 		_characters[i] = NULL;
114 
115 	_characters[0] = new CharacterDrew(this);
116 	_characters[1] = new CharacterFlux(this);
117 	_drew = _characters[0];
118 	_flux = _characters[1];
119 
120 
121 
122 	// preload walk anim for flux and drew
123 	_drew->loadWalkAnimation("STNDWALK.CAF");
124 	_drew->setupPalette();
125 	_drew->loadShadowAnimation("SHADOW.CAF");
126 
127 	_flux->loadWalkAnimation("FXSTWALK.CAF");
128 	_flux->loadShadowAnimation("SHADOW.CAF");
129 
130 	loadAdditionalPalette("UNIVERSE.PAL", 3);
131 	loadAdditionalPalette("FLUX.PAL", 4);
132 	setupGeneralPalette();
133 
134 	_script_func = new ScriptFunc(this);
135 	_gameState = new State();
136 	_gameState->_conversationData = _conversationData;
137 
138 	memset(_sceneAnimations, 0, sizeof(_sceneAnimations));
139 	memset(_sceneAnimationScripts, 0, sizeof(_sceneAnimationScripts));
140 
141 	_drew->setVisible(false);
142 	_flux->setVisible(false);
143 
144 	_gameState->_currentChapter = 1;
145 	initChapter();
146 	loadCursor();
147 	initFonts();
148 
149 	_dialogIcons = new Animation(this);
150 	_dialogIcons->loadAnimation("DIALOGUE.CAF");
151 
152 	_inventoryIcons = new Animation(this);
153 	_inventoryIcons->loadAnimation("INVENTRY.CAF");
154 
155 	_inventoryIconSlots = new Animation(this);
156 	_inventoryIconSlots->loadAnimation("ICONSLOT.CAF");
157 
158 	_genericTexts = new TextResource(this);
159 	_genericTexts->loadTextResource("GENERIC.TRE");
160 
161 	_audioManager = new AudioManager(this, _mixer);
162 	_audioManager->loadAudioPack(0, "GENERIC.SVI", "GENERIC.SVL");
163 	_audioManager->loadAudioPack(2, "GENERIC.SEI", "GENERIC.SEL");
164 
165 	_lastMouseButton = 0;
166 	_mouseButton = 0;
167 	_lastRenderTime = _system->getMillis();
168 }
169 
waitForScriptStep()170 void ToonEngine::waitForScriptStep() {
171 	// Wait after a specified number of script steps when executing a script
172 	// to lower CPU usage
173 	if (++_scriptStep >= 40) {
174 		_system->delayMillis(1);
175 		_scriptStep = 0;
176 	}
177 }
178 
parseInput()179 void ToonEngine::parseInput() {
180 
181 	Common::EventManager *_event = _system->getEventManager();
182 
183 	_mouseX = _event->getMousePos().x;
184 	_mouseY = _event->getMousePos().y;
185 	_mouseButton = _event->getButtonState();
186 
187 	Common::Event event;
188 	while (_event->pollEvent(event)) {
189 
190 		const bool hasModifier = (event.kbd.flags & Common::KBD_NON_STICKY) != 0;
191 		switch (event.type) {
192 		case Common::EVENT_KEYDOWN:
193 			if ((event.kbd.keycode == Common::KEYCODE_ESCAPE || event.kbd.keycode == Common::KEYCODE_SPACE) && !hasModifier) {
194 				_audioManager->stopCurrentVoice();
195 			}
196 			if (event.kbd.keycode == Common::KEYCODE_F5 && !hasModifier) {
197 				if (canSaveGameStateCurrently())
198 					saveGame(-1, "");
199 			}
200 			if (event.kbd.keycode == Common::KEYCODE_F6 && !hasModifier) {
201 				if (canLoadGameStateCurrently())
202 					loadGame(-1);
203 			}
204 			if (event.kbd.keycode == Common::KEYCODE_t && !hasModifier) {
205 				_showConversationText = !_showConversationText;
206 			}
207 			if (event.kbd.keycode == Common::KEYCODE_m && !hasModifier) {
208 				_audioManager->muteMusic(!_audioManager->isMusicMuted());
209 			}
210 			if (event.kbd.keycode == Common::KEYCODE_d && !hasModifier) {
211 				_audioManager->muteVoice(!_audioManager->isVoiceMuted());
212 			}
213 			if (event.kbd.keycode == Common::KEYCODE_s && !hasModifier) {
214 				_audioManager->muteSfx(!_audioManager->isSfxMuted());
215 			}
216 			if (event.kbd.keycode == Common::KEYCODE_F1 && !hasModifier && !_gameState->_inMenu) {
217 				showOptions();
218 			}
219 
220 			if (event.kbd.flags & Common::KBD_ALT) {
221 				int slotNum = event.kbd.keycode - (event.kbd.keycode >= Common::KEYCODE_KP0 ? Common::KEYCODE_KP0 : Common::KEYCODE_0);
222 				if (slotNum >= 0 && slotNum <= 9 && canSaveGameStateCurrently()) {
223 					if (saveGame(slotNum, "")) {
224 						// ok
225 						Common::U32String buf = Common::U32String::format(_("Saved game in slot #%d "), slotNum);
226 						GUI::TimedMessageDialog dialog(buf, 1000);
227 						dialog.runModal();
228 					} else {
229 						Common::U32String buf = Common::U32String::format(_("Could not quick save into slot #%d"), slotNum);
230 						GUI::MessageDialog dialog(buf);
231 						dialog.runModal();
232 
233 					}
234 				}
235 			}
236 
237 			if (event.kbd.flags & Common::KBD_CTRL) {
238 				int slotNum = event.kbd.keycode - (event.kbd.keycode >= Common::KEYCODE_KP0 ? Common::KEYCODE_KP0 : Common::KEYCODE_0);
239 				if (slotNum >= 0 && slotNum <= 9 && canLoadGameStateCurrently()) {
240 					if (loadGame(slotNum)) {
241 						// ok
242 						Common::U32String buf = Common::U32String::format(_("Saved game #%d quick loaded"), slotNum);
243 						GUI::TimedMessageDialog dialog(buf, 1000);
244 						dialog.runModal();
245 					} else {
246 						const char *msg = _s("Could not quick load the saved game #%d");
247 						Common::U32String buf = Common::U32String::format(_(msg), slotNum);
248 						GUI::MessageDialog dialog(buf);
249 						warning(msg, slotNum);
250 						dialog.runModal();
251 					}
252 				}
253 			}
254 			break;
255 		default:
256 			break;
257 		}
258 	}
259 
260 	if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory && !_gameState->_inMenu) {
261 		selectHotspot();
262 		clickEvent();
263 	}
264 }
265 
enableTimer(int32 timerId)266 void ToonEngine::enableTimer(int32 timerId) {
267 	_gameState->_timerEnabled[timerId] = true;
268 }
setTimer(int32 timerId,int32 timerWait)269 void ToonEngine::setTimer(int32 timerId, int32 timerWait) {
270 	_gameState->_timerTimeout[timerId] = getOldMilli() + timerWait * getTickLength();
271 	_gameState->_timerDelay[timerId] = timerWait;
272 }
disableTimer(int32 timerId)273 void ToonEngine::disableTimer(int32 timerId) {
274 	_gameState->_timerEnabled[timerId] = false;
275 }
updateTimers()276 void ToonEngine::updateTimers() {
277 	for (int32 i = 0; i < 2; i++) {
278 		if (_gameState->_timerEnabled[i]) {
279 			if (_gameState->_timerDelay[i] > -1 && getOldMilli() > _gameState->_timerTimeout[i]) {
280 				if (i == 0) {
281 
282 					EMCState *status = &_scriptState[_currentScriptRegion];
283 					_script->init(status, &_scriptData);
284 
285 					// setup registers
286 					status->regs[0] = _mouseX;
287 					status->regs[1] = _mouseY;
288 					status->regs[2] = 0;
289 
290 					_currentScriptRegion++;
291 
292 					_script->start(status, 7);
293 					while (_script->run(status))
294 						waitForScriptStep();
295 
296 					_currentScriptRegion--;
297 
298 					_gameState->_timerTimeout[i] = getOldMilli() + _gameState->_timerDelay[i] * getTickLength();
299 
300 					return;
301 
302 				}
303 			}
304 		}
305 	}
306 }
307 
updateScrolling(bool force,int32 timeIncrement)308 void ToonEngine::updateScrolling(bool force, int32 timeIncrement) {
309 	static int32 lastScrollOffset = 320;
310 	if (!_audioManager->voiceStillPlaying() && !_gameState->_currentScrollLock && (_drew->getFlag() & 1) == 0) {
311 		if (_drew->getFacing() & 3) {
312 			if (_drew->getFacing() <= 4)
313 				lastScrollOffset = 200;
314 			else
315 				lastScrollOffset = 440;
316 		}
317 
318 		if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
319 			return;
320 
321 		int32 desiredScrollValue = _drew->getX() - lastScrollOffset;
322 
323 		if ((_gameState->_locations[_gameState->_currentScene]._flags & 0x80) == 0) {
324 			if (desiredScrollValue < 0)
325 				desiredScrollValue = 0;
326 			if (desiredScrollValue >= _currentPicture->getWidth() - TOON_SCREEN_WIDTH)
327 				desiredScrollValue = _currentPicture->getWidth() - TOON_SCREEN_WIDTH;
328 
329 			if (force) {
330 				_gameState->_currentScrollValue = desiredScrollValue;
331 				return;
332 			} else {
333 				if (_gameState->_currentScrollValue < desiredScrollValue) {
334 					_gameState->_currentScrollValue += timeIncrement / 2;
335 
336 					if (_gameState->_currentScrollValue > desiredScrollValue)
337 						_gameState->_currentScrollValue = desiredScrollValue;
338 				} else if (_gameState->_currentScrollValue > desiredScrollValue) {
339 					_gameState->_currentScrollValue -= timeIncrement / 2;
340 
341 					if (_gameState->_currentScrollValue < desiredScrollValue)
342 						_gameState->_currentScrollValue = desiredScrollValue;
343 				}
344 			}
345 		}
346 	}
347 }
348 
update(int32 timeIncrement)349 void ToonEngine::update(int32 timeIncrement) {
350 	// to make sure we're updating the game at 5fps at least
351 	if (timeIncrement > 200)
352 		timeIncrement = 200;
353 
354 	updateAnimationSceneScripts(timeIncrement);
355 	updateCharacters(timeIncrement);
356 	updateTimer(timeIncrement);
357 	updateTimers();
358 	updateScrolling(false, timeIncrement);
359 	_audioManager->updateAmbientSFX();
360 	_animationManager->update(timeIncrement);
361 	_cursorAnimationInstance->update(timeIncrement);
362 
363 	if (!_audioManager->voiceStillPlaying()) {
364 		_currentTextLine = 0;
365 		_currentTextLineId = -1;
366 	}
367 }
368 
updateTimer(int32 timeIncrement)369 void ToonEngine::updateTimer(int32 timeIncrement) {
370 	if (_gameState->_gameTimer > 0) {
371 		debugC(0, 0xfff, "updateTimer(%d)", (int)timeIncrement);
372 		_gameState->_gameTimer -= timeIncrement;
373 		if (_gameState->_gameTimer < 0)
374 			_gameState->_gameTimer = 0;
375 	}
376 }
377 
render()378 void ToonEngine::render() {
379 
380 	if (_dirtyAll) {
381 		if (_gameState->_inCutaway)
382 			_currentCutaway->draw(*_mainSurface, 0, 0, 0, 0);
383 		else
384 			_currentPicture->draw(*_mainSurface, 0, 0, 0, 0);
385 		_dirtyRects.push_back(Common::Rect(0, 0, TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT));
386 	} else {
387 		if (_gameState->_inCutaway)
388 			_currentCutaway->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
389 		else
390 			_currentPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
391 	}
392 
393 	clearDirtyRects();
394 
395 	//_currentMask->drawMask(*_mainSurface, 0, 0, 0, 0);
396 	_animationManager->render();
397 
398 	drawInfoLine();
399 	drawConversationLine();
400 	drawConversationIcons();
401 	drawSack();
402 	//drawPalette();						// used to debug the current palette
403 	//_drew->plotPath(*_mainSurface);		// used to debug path finding
404 
405 #if 0
406 	if (_mouseX > 0 && _mouseX < 640 && _mouseY > 0 && _mouseY < 400) {
407 		Common::String test;
408 		test = Common::String::format("%d %d / mask %d layer %d z %d", _mouseX, _mouseY, getMask()->getData(_mouseX, _mouseY), getLayerAtPoint(_mouseX, _mouseY), getZAtPoint(_mouseX, _mouseY));
409 
410 		int32 c = *(uint8 *)_mainSurface->getBasePtr(_mouseX, _mouseY);
411 		test = Common::String::format("%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c * 3 + 0], _finalPalette[c * 3 + 1], _finalPalette[c * 3 + 2]);
412 
413 		_fontRenderer->setFont(_fontToon);
414 		_fontRenderer->renderText(40, 150, test, 0);
415 	}
416 #endif
417 
418 	if (_needPaletteFlush) {
419 		flushPalette(false);
420 		_needPaletteFlush = false;
421 	}
422 
423 	if (_firstFrame) {
424 		copyToVirtualScreen(false);
425 		fadeIn(5);
426 		_firstFrame = false;
427 	} else {
428 		copyToVirtualScreen(true);
429 	}
430 
431 	// add a little sleep here
432 	int32 newMillis = (int32)_system->getMillis();
433 	int32 sleepMs = 1; // Minimum delay to allow thread scheduling
434 	if ((newMillis - _lastRenderTime)  < _tickLength * 2)
435 		sleepMs = _tickLength * 2 - (newMillis - _lastRenderTime);
436 	assert(sleepMs >= 0);
437 	_system->delayMillis(sleepMs);
438 	_lastRenderTime = _system->getMillis();
439 }
440 
doMagnifierEffect()441 void ToonEngine::doMagnifierEffect() {
442 	int32 posX = _mouseX + state()->_currentScrollValue - _cursorOffsetX;
443 	int32 posY = _mouseY - _cursorOffsetY - 2;
444 
445 	Graphics::Surface &surface = *_mainSurface;
446 
447 	// fast sqrt table lookup (values up to 144 only)
448 	static const byte intSqrt[] = {
449 		0, 1, 1, 1, 2, 2, 2, 2, 2, 3,
450 		3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
451 		4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
452 		5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
453 		6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
454 		7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
455 		7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
456 		8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
457 		8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
458 		9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
459 		10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
460 		10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
461 		10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
462 		11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
463 		11, 11, 11, 11, 12
464 	};
465 
466 	byte tempBuffer[25 * 25];
467 	for (int32 y = -12; y <= 12; y++) {
468 		int32 cy = CLIP<int32>(posY + y, 0, TOON_BACKBUFFER_HEIGHT-1);
469 		for (int32 x = -12; x <= 12; x++) {
470 			int32 cx = CLIP<int32>(posX + x, 0, TOON_BACKBUFFER_WIDTH-1);
471 			uint8 *curRow = (uint8 *)surface.getBasePtr(cx, cy);
472 			tempBuffer[(y + 12) * 25 + x + 12] = *curRow;
473 		}
474 	}
475 
476 	for (int32 y = -12; y <= 12; y++) {
477 		int32 cy = CLIP<int32>(posY + y, 0, TOON_BACKBUFFER_HEIGHT-1);
478 		for (int32 x = -12; x <= 12; x++) {
479 			int32 dist = y * y + x * x;
480 			if (dist > 144)
481 				continue;
482 			int32 cx = CLIP<int32>(posX + x, 0, TOON_BACKBUFFER_WIDTH-1);
483 			uint8 *curRow = (uint8 *)surface.getBasePtr(cx, cy);
484 			int32 lerp = (512 + intSqrt[dist] * 256 / 12);
485 			*curRow = tempBuffer[(y * lerp / 1024 + 12) * 25 + x * lerp / 1024 + 12];
486 		}
487 	}
488 }
489 
copyToVirtualScreen(bool updateScreen)490 void ToonEngine::copyToVirtualScreen(bool updateScreen) {
491 	// render cursor last
492 	if (!_gameState->_mouseHidden) {
493 		if (_cursorAnimationInstance->getFrame() == 7 && _cursorAnimationInstance->getAnimation() == _cursorAnimation) // magnifier icon needs a special effect
494 			doMagnifierEffect();
495 		_cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false);
496 		_cursorAnimationInstance->render();
497 	}
498 
499 	// Handle dirty rects here
500 	static int32 lastScroll = 0;
501 
502 	if (_dirtyAll || _gameState->_currentScrollValue != lastScroll) {
503 		// we have to refresh everything in case of scrolling.
504 		_system->copyRectToScreen((byte *)_mainSurface->getPixels() + state()->_currentScrollValue, TOON_BACKBUFFER_WIDTH, 0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
505 	} else {
506 
507 		int32 offX = 0;
508 		for (uint i = 0; i < _oldDirtyRects.size(); i++) {
509 			Common::Rect rect = _oldDirtyRects[i];
510 			rect.translate(-state()->_currentScrollValue, 0);
511 			offX = 0;
512 			if (rect.right <= 0)
513 				continue;
514 			if (rect.left < 0) {
515 				offX = -rect.left;
516 				rect.left = 0;
517 			}
518 			rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
519 			if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) {
520 				_system->copyRectToScreen((byte *)_mainSurface->getBasePtr(_oldDirtyRects[i].left + offX, _oldDirtyRects[i].top), TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top);
521 			}
522 		}
523 
524 		for (uint i = 0; i < _dirtyRects.size(); i++) {
525 			Common::Rect rect = _dirtyRects[i];
526 			rect.translate(-state()->_currentScrollValue, 0);
527 			offX = 0;
528 			if (rect.right <= 0)
529 				continue;
530 			if (rect.left < 0) {
531 				offX = -rect.left;
532 				rect.left = 0;
533 			}
534 			rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
535 			if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) {
536 				_system->copyRectToScreen((byte *)_mainSurface->getBasePtr(_dirtyRects[i].left + offX, _dirtyRects[i].top), TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top);
537 			}
538 		}
539 	}
540 	lastScroll = _gameState->_currentScrollValue;
541 
542 	if (updateScreen) {
543 		_system->updateScreen();
544 		_shouldQuit = shouldQuit();	// update game quit flag - this shouldn't be called all the time, as it's a virtual function
545 	}
546 }
547 
doFrame()548 void ToonEngine::doFrame() {
549 
550 	if (_gameState->_inInventory) {
551 		renderInventory();
552 	} else {
553 		render();
554 
555 		int32 currentTimer = _system->getMillis();
556 
557 		update(currentTimer - _oldTimer);
558 		_oldTimer = currentTimer;
559 		_oldTimer2 = currentTimer;
560 	}
561 	parseInput();
562 }
563 
564 enum MainMenuSelections {
565 	MAINMENUHOTSPOT_NONE         = 0,
566 	MAINMENUHOTSPOT_START        = 1,
567 	MAINMENUHOTSPOT_INTRO        = 2,
568 	MAINMENUHOTSPOT_LOADGAME     = 3,
569 	MAINMENUHOTSPOT_HOTKEYS      = 4,
570 	MAINMENUHOTSPOT_CREDITS      = 5,
571 	MAINMENUHOTSPOT_QUIT         = 6,
572 	MAINMENUHOTSPOT_HOTKEYSCLOSE = 7
573 };
574 
575 enum MainMenuMasks {
576 	MAINMENUMASK_BASE       = 1,
577 	MAINMENUMASK_HOTKEYS    = 2,
578 	MAINMENUMASK_EVERYWHERE = 3
579 };
580 
581 enum OptionMenuSelections {
582 	OPTIONMENUHOTSPOT_NONE					= 0,
583 	OPTIONMENUHOTSPOT_PLAY					= 1,
584 	OPTIONMENUHOTSPOT_QUIT					= 2,
585 	OPTIONMENUHOTSPOT_TEXT					= 3,
586 	OPTIONMENUHOTSPOT_TEXTSPEED				= 4,
587 	OPTIONMENUHOTSPOT_VOLUMESFX				= 5,
588 	OPTIONMENUHOTSPOT_VOLUMESFXSLIDER		= 6,
589 	OPTIONMENUHOTSPOT_VOLUMEMUSIC			= 7,
590 	OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER		= 8,
591 	OPTIONMENUHOTSPOT_VOLUMEVOICE			= 9,
592 	OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER		= 10,
593 	OPTIONMENUHOTSPOT_SPEAKERBUTTON			= 11,
594 	OPTIONMENUHOTSPOT_SPEAKERLEVER			= 12,
595 	OPTIONMENUHOTSPOT_VIDEO_MODE			= 13
596 };
597 
598 enum OptionMenuMasks {
599 	OPTIONMENUMASK_EVERYWHERE = 1
600 };
601 
602 
603 struct MenuFile {
604 	int menuMask;
605 	int id;
606 	const char *animationFile;
607 	int animateOnFrame;
608 };
609 
610 #define MAINMENU_ENTRYCOUNT 12
611 static const MenuFile mainMenuFiles[] = {
612 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_START,        "STARTBUT.CAF", 0 }, // "Start" button
613 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_INTRO,        "INTROBUT.CAF", 0 }, // "Intro" button
614 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_LOADGAME,     "LOADBUT.CAF",  0 }, // "Load Game" button
615 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_HOTKEYS,      "HOTBUT.CAF",   0 }, // "Hot Keys" button
616 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_CREDITS,      "CREDBUT.CAF",  0 }, // "Credits" button
617 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_QUIT,         "QUITBUT.CAF",  0 }, // "Quit" button
618 	{ MAINMENUMASK_BASE,       MAINMENUHOTSPOT_NONE,         "LEGALTXT.CAF", 0 }, // Legal Text
619 
620 	{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE,         "TOONGLOW.CAF", 6 }, // Clown glow
621 	{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE,         "TOONSTRK.CAF", 6 }, // Toonstruck title
622 	{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE,         "EYEGLOW.CAF",  4 }, // Clown eye glow
623 	{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE,         "PROPHEAD.CAF", 4 }, // Clown propellor head
624 	{ MAINMENUMASK_HOTKEYS,    MAINMENUHOTSPOT_HOTKEYSCLOSE, "HOTKEYS.CAF",  0 }  // Hotkeys display - clicking on it will close hotkeys
625 };
626 
627 #define OPTIONMENU_ENTRYCOUNT 27
628 static const MenuFile optionMenuFiles[] = {
629 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_PLAY,        "PLAYBUTN.CAF", 0 }, // "Start" button
630 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_QUIT,        "QUITBUTN.CAF", 0 }, // "Intro" button
631 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VIDEO_MODE,        "VIDMODE.CAF", 0 }, // "Start" button
632 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_TEXTSPEED,        "TXTSPEED.CAF", 0 }, // "Intro" button
633 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_TEXT,        "TEXTDIAL.CAF", 0}, // "Intro" button
634 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMESFX,        "SFXBUTN.CAF", 0 }, // "Start" button
635 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMESFXSLIDER,        "SFXSLDR.CAF", 0 }, // "Intro" button
636 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMEVOICE,        "VOICEBTN.CAF", 0 }, // "Start" button
637 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER,        "VOICESLD.CAF", 0 }, // "Intro" button
638 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMEMUSIC,        "MUSICBTN.CAF", 0 }, // "Start" button
639 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER,        "MUSICSLD.CAF", 0 }, // "Intro" button
640 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_SPEAKERBUTTON,        "XTRABUTN.CAF", 0 }, // "Start" button
641 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_SPEAKERLEVER,        "XTRALEVR.CAF", 0}, // "Intro" button
642 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "ANTENNAL.CAF", 6 }, // "Start" button
643 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "ANTENNAR.CAF", 6 }, // "Intro" button
644 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "BIGREDL.CAF", 6 }, // "Start" button
645 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "BIGREDR.CAF", 6 }, // "Intro" button
646 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "GRIDLTEL.CAF", 6 }, // "Start" button
647 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "GRIDLTER.CAF", 6 }, // "Intro" button
648 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "LSPEAKR.CAF", 0 }, // "Start" button
649 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "RSPEAKR.CAF", 0 }, // "Intro" button
650 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "STARLITL.CAF", 6 }, // "Start" button
651 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "STARLITR.CAF", 6 }, // "Intro" button
652 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "CHASE1.CAF", 6 }, // "Intro" button
653 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "CHASE2.CAF", 6 }, // "Intro" button
654 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "CHASE3.CAF", 6 }, // "Intro" button
655 	{ OPTIONMENUMASK_EVERYWHERE,       OPTIONMENUHOTSPOT_NONE,        "CHASE4.CAF", 6 } // "Intro" button
656 };
657 
658 struct MenuEntry {
659 	int menuMask;
660 	int id;
661 	Animation *animation;
662 	Common::Rect rect;
663 	int animateOnFrame;
664 	int animateCurFrame;
665 	int activeFrame;
666 	bool playOnce;
667 };
668 
showOptions()669 bool ToonEngine::showOptions() {
670 
671 	storePalette();
672 	fadeOut(5);
673 	Picture* optionPicture = new Picture(this);
674 	optionPicture->loadPicture("OPTIONS.CPS");
675 	optionPicture->setupPalette();
676 	flushPalette(true);
677 
678 	int16 oldScrollValue = _gameState->_currentScrollValue;
679 	_gameState->_currentScrollValue = 0;
680 
681 	bool oldMouseHidden = _gameState->_mouseHidden;
682 	_gameState->_mouseHidden = false;
683 
684 	MenuEntry entries[OPTIONMENU_ENTRYCOUNT];
685 
686 	for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
687 		entries[entryNr].menuMask = optionMenuFiles[entryNr].menuMask;
688 		entries[entryNr].id = optionMenuFiles[entryNr].id;
689 		entries[entryNr].animation = new Animation(this);
690 		entries[entryNr].animation->loadAnimation(optionMenuFiles[entryNr].animationFile);
691 		if (entries[entryNr].id != OPTIONMENUHOTSPOT_NONE)
692 			entries[entryNr].rect = entries[entryNr].animation->getRect();
693 		entries[entryNr].animateOnFrame = optionMenuFiles[entryNr].animateOnFrame;
694 		entries[entryNr].animateCurFrame = 0;
695 		entries[entryNr].activeFrame = 0;
696 		entries[entryNr].playOnce = false;
697 	}
698 
699 	entries[10].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * (entries[10].animation->_numFrames - 1) / 256;
700 	entries[8].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) * (entries[8].animation->_numFrames - 1) / 256;
701 	entries[6].activeFrame = _audioManager->_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) * (entries[6].animation->_numFrames - 1) / 256;
702 
703 	entries[9].activeFrame = _audioManager->isMusicMuted() ? 0 : 3;
704 	entries[7].activeFrame = _audioManager->isVoiceMuted() ? 0 : 3;
705 	entries[5].activeFrame = _audioManager->isSfxMuted() ? 0 : 3;
706 
707 	entries[2].activeFrame = entries[2].animation->_numFrames - 1;
708 
709 	if (!_showConversationText) {
710 		entries[4].activeFrame = 4;
711 	} else if (_useAlternativeFont) {
712 		entries[4].activeFrame = 8;
713 	} else {
714 		entries[4].activeFrame = 0;
715 	}
716 
717 	setCursor(0);
718 
719 	int menuMask = OPTIONMENUMASK_EVERYWHERE;
720 	int ratioX = 0;
721 	bool doExit = false;
722 	bool exitGame = false;
723 	_gameState->_inMenu = true;
724 	dirtyAllScreen();
725 	_firstFrame = true;
726 
727 	while (!doExit) {
728 
729 		int clickingOn = OPTIONMENUHOTSPOT_NONE;
730 		int clickingOnSprite = 0;
731 		int clickRelease = false;
732 
733 		while (!clickRelease) {
734 
735 			if (_dirtyAll) {
736 				optionPicture->draw(*_mainSurface, 0, 0, 0, 0);
737 				addDirtyRect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
738 			} else {
739 				optionPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
740 			}
741 			clearDirtyRects();
742 
743 			for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
744 				if (entries[entryNr].menuMask & menuMask) {
745 					if (entries[entryNr].animateOnFrame) {
746 						entries[entryNr].animateCurFrame++;
747 						if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) {
748 							entries[entryNr].activeFrame++;
749 							if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames) {
750 								entries[entryNr].activeFrame = 0;
751 								if (entries[entryNr].playOnce) {
752 									entries[entryNr].animateOnFrame = 0;
753 									entries[entryNr].playOnce = false;
754 								}
755 								if (entryNr == 20 && entries[entryNr].animateOnFrame > 0) {
756 									playSFX(-3, 128);
757 								}
758 							}
759 							entries[entryNr].animateCurFrame = 0;
760 						}
761 					}
762 					int32 frameNr = entries[entryNr].activeFrame;
763 					entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0);
764 				}
765 			}
766 
767 			parseInput();
768 
769 			copyToVirtualScreen(true);
770 			if (_firstFrame) {
771 				_firstFrame = false;
772 				fadeIn(5);
773 			}
774 			_system->delayMillis(17);
775 
776 			if (_mouseButton & 1) {
777 				// left mouse button pushed down
778 				clickingOn = OPTIONMENUHOTSPOT_NONE;
779 				for (int entryNr = 0; entryNr < OPTIONMENU_ENTRYCOUNT; entryNr++) {
780 					if (entries[entryNr].menuMask & menuMask) {
781 						if (entries[entryNr].id != OPTIONMENUHOTSPOT_NONE) {
782 							if (entries[entryNr].rect.contains(_mouseX, _mouseY)) {
783 								clickingOn = entries[entryNr].id;
784 								clickingOnSprite = entryNr;
785 								ratioX = (_mouseX - entries[entryNr].rect.left) * 256 / entries[entryNr].rect.width();
786 							}
787 						}
788 					}
789 				}
790 			} else {
791 				// left mouse button released/not pushed down
792 				if (clickingOn != OPTIONMENUHOTSPOT_NONE)
793 					clickRelease = true;
794 			}
795 
796 			// handle sliders
797 			if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEMUSICSLIDER) {
798 				entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
799 				int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
800 				_audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol);
801 			}
802 
803 			if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEVOICESLIDER) {
804 				entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
805 				int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
806 				_audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, vol);
807 			}
808 
809 			if (clickingOn == OPTIONMENUHOTSPOT_VOLUMESFXSLIDER) {
810 				entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
811 				int vol = entries[clickingOnSprite].activeFrame * 256 / entries[clickingOnSprite].animation->_numFrames;
812 				_audioManager->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, vol);
813 			}
814 
815 			if (clickingOn == OPTIONMENUHOTSPOT_TEXTSPEED) {
816 				entries[clickingOnSprite].activeFrame = ratioX * (entries[clickingOnSprite].animation->_numFrames) / 256;
817 			}
818 
819 			if (clickingOn == OPTIONMENUHOTSPOT_PLAY) {
820 				entries[0].activeFrame = entries[0].animation->_numFrames - 1;
821 			} else {
822 				entries[0].activeFrame = 0;
823 			}
824 
825 			if (clickingOn == OPTIONMENUHOTSPOT_QUIT) {
826 				entries[1].activeFrame = entries[1].animation->_numFrames - 1;
827 			} else {
828 				entries[1].activeFrame = 0;
829 			}
830 
831 			if (_shouldQuit) {
832 				clickingOn = OPTIONMENUHOTSPOT_NONE;
833 				clickRelease = true;
834 				doExit = true;
835 			}
836 		}
837 
838 		if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEMUSIC) {
839 			if (entries[9].activeFrame == 0) {
840 				entries[9].activeFrame = 3;
841 				_audioManager->muteMusic(false);
842 			} else {
843 				entries[9].activeFrame = 0;
844 				_audioManager->muteMusic(true);
845 			}
846 			playSFX(-7, 128);
847 		}
848 
849 		if (clickingOn == OPTIONMENUHOTSPOT_VOLUMEVOICE) {
850 			if (entries[7].activeFrame == 0) {
851 				entries[7].activeFrame = 3;
852 				_audioManager->muteVoice(false);
853 			} else {
854 				entries[7].activeFrame = 0;
855 				_audioManager->muteVoice(true);
856 			}
857 			playSFX(-7, 128);
858 		}
859 
860 		if (clickingOn == OPTIONMENUHOTSPOT_VOLUMESFX) {
861 			if (entries[5].activeFrame == 0) {
862 				entries[5].activeFrame = 3;
863 				_audioManager->muteSfx(false);
864 			} else {
865 				entries[5].activeFrame = 0;
866 				_audioManager->muteSfx(true);
867 			}
868 			playSFX(-7, 128);
869 		}
870 
871 		if (clickingOn == OPTIONMENUHOTSPOT_SPEAKERBUTTON) {
872 			entries[11].animateOnFrame = 4;
873 			entries[11].playOnce = true;
874 
875 			entries[19].animateOnFrame = 4;
876 			entries[19].playOnce = true;
877 
878 			playSFX(-10, 128);
879 			_audioManager->playVoice(316, true);
880 		}
881 
882 		if (clickingOn == OPTIONMENUHOTSPOT_SPEAKERLEVER) {
883 
884 			entries[12].activeFrame = 1 - entries[12].activeFrame;
885 			if(entries[12].activeFrame == 1) {
886 				entries[20].animateOnFrame = 4;
887 				entries[20].playOnce = false;
888 				playSFX(-3, 128);
889 			} else {
890 				entries[20].playOnce = true;
891 			}
892 			playSFX(-9, 128);
893 		}
894 
895 		if (clickingOn == OPTIONMENUHOTSPOT_TEXT) {
896 
897 			if (entries[4].activeFrame == 0) {
898 				_showConversationText = false;
899 				entries[4].activeFrame = 4;
900 			} else if (entries[4].activeFrame == 4) {
901 				_showConversationText = true;
902 				setFont(true);
903 				entries[4].activeFrame = 8;
904 			} else if(entries[4].activeFrame == 8) {
905 				_showConversationText = true;
906 				setFont(false);
907 				entries[4].activeFrame = 0;
908 			}
909 
910 			playSFX(-9, 128);
911 		}
912 
913 		// don't allow change to video mode
914 		if (clickingOn == OPTIONMENUHOTSPOT_VIDEO_MODE) {
915 			playSoundWrong();
916 		}
917 
918 		if (clickingOn == OPTIONMENUHOTSPOT_PLAY) {
919 			doExit = true;
920 			exitGame = false;
921 			_audioManager->playSFX(10, 128, true);
922 		}
923 
924 		if (clickingOn == OPTIONMENUHOTSPOT_QUIT) {
925 			doExit = true;
926 			exitGame = true;
927 			_shouldQuit = true;
928 			_audioManager->playSFX(10, 128, true);
929 		}
930 	}
931 
932 	fadeOut(5);
933 	_gameState->_mouseHidden = oldMouseHidden;
934 	_gameState->_inMenu = false;
935 	_firstFrame = true;
936 	_gameState->_currentScrollValue = oldScrollValue;
937 
938 	restorePalette();
939 	dirtyAllScreen();
940 
941 	delete optionPicture;
942 
943 	return exitGame;
944 }
945 
showMainmenu(bool & loadedGame)946 bool ToonEngine::showMainmenu(bool &loadedGame) {
947 	Picture *mainmenuPicture = new Picture(this);
948 	mainmenuPicture->loadPicture("TITLESCR.CPS");
949 	mainmenuPicture->setupPalette();
950 	flushPalette(false);
951 
952 	MenuEntry entries[MAINMENU_ENTRYCOUNT];
953 
954 	for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
955 		entries[entryNr].menuMask = mainMenuFiles[entryNr].menuMask;
956 		entries[entryNr].id = mainMenuFiles[entryNr].id;
957 		entries[entryNr].animation = new Animation(this);
958 		entries[entryNr].animation->loadAnimation(mainMenuFiles[entryNr].animationFile);
959 		if (entries[entryNr].id != MAINMENUHOTSPOT_NONE)
960 			entries[entryNr].rect = entries[entryNr].animation->getRect();
961 		entries[entryNr].animateOnFrame = mainMenuFiles[entryNr].animateOnFrame;
962 		entries[entryNr].animateCurFrame = 0;
963 		entries[entryNr].activeFrame = 0;
964 	}
965 
966 	setCursor(0);
967 
968 	bool doExit = false;
969 	bool exitGame = false;
970 	int menuMask = MAINMENUMASK_BASE;
971 	Common::SeekableReadStream *mainmenuMusicFile = NULL;
972 	AudioStreamInstance *mainmenuMusic = NULL;
973 	bool musicPlaying = false;
974 
975 	_gameState->_inMenu = true;
976 	dirtyAllScreen();
977 
978 	while (!doExit) {
979 		int clickingOn = MAINMENUHOTSPOT_NONE;
980 		int clickRelease = false;
981 
982 		if (!musicPlaying) {
983 			mainmenuMusicFile = resources()->openFile("BR091013.MUS");
984 			if (mainmenuMusicFile) {
985 				mainmenuMusic = new AudioStreamInstance(_audioManager, _mixer, mainmenuMusicFile, true);
986 				mainmenuMusic->play(false);
987 				musicPlaying = true;
988 			}
989 			else {
990 				musicPlaying = false;
991 			}
992 		}
993 
994 		while (!clickRelease) {
995 
996 			if (_dirtyAll) {
997 				mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0);
998 				addDirtyRect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
999 			} else {
1000 				mainmenuPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
1001 			}
1002 
1003 			clearDirtyRects();
1004 
1005 			for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
1006 				if (entries[entryNr].menuMask & menuMask) {
1007 					if (entries[entryNr].animateOnFrame) {
1008 						entries[entryNr].animateCurFrame++;
1009 						if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) {
1010 							entries[entryNr].activeFrame++;
1011 							if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames)
1012 								entries[entryNr].activeFrame = 0;
1013 							entries[entryNr].animateCurFrame = 0;
1014 						}
1015 					}
1016 					int32 frameNr = entries[entryNr].activeFrame;
1017 					if ((entries[entryNr].id == clickingOn) && (clickingOn != MAINMENUHOTSPOT_NONE))
1018 						frameNr = 1;
1019 					entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0);
1020 				}
1021 			}
1022 
1023 			if (_needPaletteFlush) {
1024 				flushPalette(false);
1025 				_needPaletteFlush = false;
1026 			}
1027 
1028 			parseInput();
1029 			copyToVirtualScreen(true);
1030 			_system->delayMillis(17);
1031 
1032 			if (_mouseButton & 1) {
1033 				// left mouse button pushed down
1034 				clickingOn = MAINMENUHOTSPOT_NONE;
1035 				for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
1036 					if (entries[entryNr].menuMask & menuMask) {
1037 						if (entries[entryNr].id != MAINMENUHOTSPOT_NONE) {
1038 							if (entries[entryNr].rect.contains(_mouseX, _mouseY))
1039 								clickingOn = entries[entryNr].id;
1040 						}
1041 					}
1042 				}
1043 			} else {
1044 				// left mouse button released/not pushed down
1045 				if (clickingOn != MAINMENUHOTSPOT_NONE)
1046 					clickRelease = true;
1047 			}
1048 			if (_shouldQuit) {
1049 				clickingOn = MAINMENUHOTSPOT_NONE;
1050 				clickRelease = true;
1051 				doExit = true;
1052 			}
1053 		}
1054 
1055 		if (clickingOn != MAINMENUHOTSPOT_NONE) {
1056 			_audioManager->playSFX(10, 128, true);
1057 		}
1058 
1059 		switch (clickingOn) {
1060 		case MAINMENUHOTSPOT_HOTKEYS:
1061 			menuMask = MAINMENUMASK_HOTKEYS;
1062 			continue;
1063 		case MAINMENUHOTSPOT_HOTKEYSCLOSE:
1064 			menuMask = MAINMENUMASK_BASE;
1065 			continue;
1066 		default:
1067 			break;
1068 		}
1069 
1070 		if (musicPlaying) {
1071 			//stop music
1072 			mainmenuMusic->stop(false);
1073 			delete mainmenuMusicFile;
1074 			musicPlaying = false;
1075 		}
1076 
1077 		switch (clickingOn) {
1078 		case MAINMENUHOTSPOT_START:
1079 			// Start game (actually exit main menu)
1080 			loadedGame = false;
1081 			doExit = true;
1082 			break;
1083 		case MAINMENUHOTSPOT_INTRO:
1084 			// Play intro movies
1085 			getMoviePlayer()->play("209_1M.SMK", 0x10);
1086 			getMoviePlayer()->play("209_2M.SMK", 0x10);
1087 			getMoviePlayer()->play("209_3M.SMK", 0x10);
1088 			break;
1089 		case MAINMENUHOTSPOT_LOADGAME:
1090 			doExit = loadGame(-1);
1091 			loadedGame = doExit;
1092 			exitGame = false;
1093 			break;
1094 		case MAINMENUHOTSPOT_CREDITS:
1095 			// Play credits movie
1096 			getMoviePlayer()->play("CREDITS.SMK", 0x0);
1097 			break;
1098 		case MAINMENUHOTSPOT_QUIT:
1099 			exitGame = true;
1100 			doExit = true;
1101 			break;
1102 		default:
1103 			break;
1104 		}
1105 	}
1106 
1107 	_gameState->_inMenu = false;
1108 
1109 	//delete mainmenuMusic;
1110 	for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++)
1111 		delete entries[entryNr].animation;
1112 	delete mainmenuPicture;
1113 
1114 	return !exitGame;
1115 }
1116 
run()1117 Common::Error ToonEngine::run() {
1118 
1119 	if (!loadToonDat())
1120 		return Common::kUnknownError;
1121 
1122 	initGraphics(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
1123 	init();
1124 
1125 	// do we need to load directly a game?
1126 	bool loadedGame = false;
1127 	int32 slot = ConfMan.getInt("save_slot");
1128 	if (slot > -1) {
1129 		loadedGame = loadGame(slot);
1130 	}
1131 
1132 	if (!loadedGame) {
1133 
1134 		// play producer intro
1135 		// not all demo versions include the logo video
1136 		getMoviePlayer()->play("VIELOGOM.SMK", _isDemo ? 0x12 : 0x10);
1137 
1138 		// show mainmenu
1139 		// the demo does not have a menu and starts a new game right away
1140 		if (!_isDemo && !showMainmenu(loadedGame)) {
1141 			return Common::kNoError;
1142 		}
1143 	}
1144 
1145 	//loadScene(17);
1146 	//loadScene(37);
1147 	if (!loadedGame) {
1148 		newGame();
1149 	}
1150 
1151 	while (!_shouldQuit && _gameState->_currentScene != -1)
1152 		doFrame();
1153 	return Common::kNoError;
1154 }
1155 
ToonEngine(OSystem * syst,const ADGameDescription * gameDescription)1156 ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
1157 	: Engine(syst), _gameDescription(gameDescription),
1158 	_language(gameDescription->language), _rnd("toon") {
1159 	_tickLength = 16;
1160 	_currentPicture = NULL;
1161 	_inventoryPicture = NULL;
1162 	_currentMask = NULL;
1163 	_showConversationText = true;
1164 	_useAlternativeFont = false;
1165 	_isDemo = _gameDescription->flags & ADGF_DEMO;
1166 
1167 	_resources = NULL;
1168 	_animationManager = NULL;
1169 	_moviePlayer = NULL;
1170 	_mainSurface = NULL;
1171 
1172 	_finalPalette = NULL;
1173 	_backupPalette = NULL;
1174 	_additionalPalette1 = NULL;
1175 	_additionalPalette2 = NULL;
1176 	_additionalPalette2Present = false;
1177 	_cutawayPalette = NULL;
1178 	_universalPalette = NULL;
1179 	_fluxPalette = NULL;
1180 
1181 	_roomScaleData = NULL;
1182 	_shadowLUT = NULL;
1183 
1184 	_conversationData = NULL;
1185 
1186 	_fontRenderer = NULL;
1187 	_fontToon = NULL;
1188 	_fontEZ = NULL;
1189 	_hotspots = NULL;
1190 	_genericTexts = NULL;
1191 	_roomTexts = NULL;
1192 	_script_func = NULL;
1193 	_script = NULL;
1194 
1195 	_mouseX = 0;
1196 	_mouseY = 0;
1197 	_mouseButton = 0;
1198 	_lastMouseButton = 0;
1199 
1200 	_saveBufferStream = NULL;
1201 
1202 	_pathFinding = NULL;
1203 	setDebugger(new ToonConsole(this));
1204 
1205 	_cursorAnimation = NULL;
1206 	_cursorAnimationInstance = NULL;
1207 	_dialogIcons = NULL;
1208 	_inventoryIcons = NULL;
1209 	_inventoryIconSlots = NULL;
1210 	_genericTexts = NULL;
1211 	_audioManager = NULL;
1212 	_gameState = NULL;
1213 
1214 	_locationDirNotVisited = NULL;
1215 	_locationDirVisited = NULL;
1216 	_specialInfoLine = NULL;
1217 
1218 	for (int i = 0; i < 64; i++) {
1219 		_sceneAnimations[i]._active = false;
1220 	}
1221 
1222 	for (int i = 0; i < 32; i++) {
1223 		_characters[i] = NULL;
1224 	}
1225 
1226 	memset(&_scriptData, 0, sizeof(EMCData));
1227 
1228 	switch (_language) {
1229 	case Common::EN_GRB:
1230 	case Common::EN_USA:
1231 	case Common::EN_ANY:
1232 		_gameVariant = 0;
1233 		break;
1234 	case Common::FR_FRA:
1235 		_gameVariant = 1;
1236 		break;
1237 	case Common::DE_DEU:
1238 		_gameVariant = 2;
1239 		break;
1240 	case Common::RU_RUS:
1241 		_gameVariant = 3;
1242 		break;
1243 	case Common::ES_ESP:
1244 		_gameVariant = 4;
1245 		break;
1246 	default:
1247 		// 0 - english
1248 		_gameVariant = 0;
1249 		break;
1250 	}
1251 
1252 	for (int i = 0; i < 64; i++) {
1253 		_sceneAnimationScripts[i]._lastTimer = 0;
1254 		_sceneAnimationScripts[i]._frozen = false;
1255 		_sceneAnimationScripts[i]._frozenForConversation = false;
1256 		_sceneAnimationScripts[i]._active = false;
1257 	}
1258 
1259 	_lastProcessedSceneScript = 0;
1260 	_animationSceneScriptRunFlag = false;
1261 	_updatingSceneScriptRunFlag = false;
1262 	_dirtyAll = false;
1263 	_cursorOffsetX = 0;
1264 	_cursorOffsetY = 0;
1265 	_currentTextLine = 0;
1266 	_currentTextLineId = 0;
1267 	_currentTextLineX = 0;
1268 	_currentTextLineY = 0;
1269 	_currentTextLineCharacterId = -1;
1270 	_oldScrollValue = 0;
1271 	_drew = nullptr;
1272 	_flux = nullptr;
1273 	_currentHotspotItem = 0;
1274 	_shouldQuit = false;
1275 	_scriptStep = 0;
1276 	_oldTimer = 0;
1277 	_oldTimer2 = 0;
1278 	_lastRenderTime = 0;
1279 	_firstFrame = false;
1280 	_needPaletteFlush = true;
1281 
1282 	_numVariant = 0;
1283 	_currentCutaway = nullptr;
1284 	for (int i = 0; i < 4; i++) {
1285 		_scriptState[i].ip = nullptr;
1286 		_scriptState[i].dataPtr = nullptr;
1287 		_scriptState[i].retValue = 0;
1288 		_scriptState[i].bp = 0;
1289 		_scriptState[i].sp = 0;
1290 		_scriptState[i].running = false;
1291 	}
1292 	_currentScriptRegion = 0;
1293 	_currentFont = nullptr;
1294 }
1295 
~ToonEngine()1296 ToonEngine::~ToonEngine() {
1297 	delete _currentPicture;
1298 	delete _currentMask;
1299 	delete _inventoryPicture;
1300 
1301 	delete _resources;
1302 	delete _animationManager;
1303 	delete _moviePlayer;
1304 
1305 	if (_mainSurface) {
1306 		_mainSurface->free();
1307 		delete _mainSurface;
1308 	}
1309 
1310 	delete[] _finalPalette;
1311 	delete[] _backupPalette;
1312 	delete[] _additionalPalette1;
1313 	delete[] _additionalPalette2;
1314 	delete[] _cutawayPalette;
1315 	delete[] _universalPalette;
1316 	delete[] _fluxPalette;
1317 
1318 	delete[] _roomScaleData;
1319 	delete[] _shadowLUT;
1320 
1321 	delete[] _conversationData;
1322 
1323 	delete _fontRenderer;
1324 	delete _fontToon;
1325 	delete _fontEZ;
1326 	delete _hotspots;
1327 	delete _genericTexts;
1328 	delete _roomTexts;
1329 	delete _script_func;
1330 
1331 	_script->unload(&_scriptData);
1332 	delete _script;
1333 
1334 	delete _saveBufferStream;
1335 
1336 	delete _pathFinding;
1337 
1338 	for (int32 i = 0; i < 64; i++) {
1339 		if (_sceneAnimations[i]._active) {
1340 			// see if one character shares this instance
1341 			for (int32 c = 0; c < 32; c++) {
1342 				if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
1343 					_characters[c]->setAnimationInstance(0);
1344 				}
1345 			}
1346 			delete _sceneAnimations[i]._originalAnimInstance;
1347 			delete _sceneAnimations[i]._animation;
1348 		}
1349 	}
1350 
1351 	for (int32 i = 0; i < 32; i++)
1352 		delete _characters[i];
1353 
1354 	delete _cursorAnimation;
1355 	delete _cursorAnimationInstance;
1356 	delete _dialogIcons;
1357 	delete _inventoryIcons;
1358 	delete _inventoryIconSlots;
1359 	//delete _genericTexts;
1360 	delete _audioManager;
1361 	delete _gameState;
1362 
1363 	unloadToonDat();
1364 }
1365 
flushPalette(bool deferFlushToNextRender)1366 void ToonEngine::flushPalette(bool deferFlushToNextRender) {
1367 
1368 	if (deferFlushToNextRender) {
1369 		_needPaletteFlush = true;
1370 		return;
1371 	}
1372 	_needPaletteFlush = false;
1373 	_system->getPaletteManager()->setPalette(_finalPalette, 0, 256);
1374 }
setPaletteEntries(uint8 * palette,int32 offset,int32 num)1375 void ToonEngine::setPaletteEntries(uint8 *palette, int32 offset, int32 num) {
1376 	memcpy(_finalPalette + offset * 3, palette, num * 3);
1377 	flushPalette();
1378 }
1379 
simpleUpdate(bool waitCharacterToTalk)1380 void ToonEngine::simpleUpdate(bool waitCharacterToTalk) {
1381 	int32 elapsedTime = _system->getMillis() - _oldTimer2;
1382 	_oldTimer2 = _system->getMillis();
1383 	_oldTimer = _oldTimer2;
1384 
1385 	if (!_audioManager->voiceStillPlaying() && !waitCharacterToTalk) {
1386 		_currentTextLine = 0;
1387 		_currentTextLineId = -1;
1388 	}
1389 
1390 	updateCharacters(elapsedTime);
1391 	updateAnimationSceneScripts(elapsedTime);
1392 	updateTimer(elapsedTime);
1393 	_animationManager->update(elapsedTime);
1394 	_audioManager->updateAmbientSFX();
1395 	render();
1396 }
1397 
fixPaletteEntries(uint8 * palette,int num)1398 void ToonEngine::fixPaletteEntries(uint8 *palette, int num) {
1399 	// some color values are coded on 6bits ( for old 6bits DAC )
1400 	for (int32 i = 0; i < num * 3; i++) {
1401 		int32 a = palette[i];
1402 		a = a * 4;
1403 		if (a > 255)
1404 			a = 255;
1405 		palette[i] = a;
1406 	}
1407 }
1408 
1409 // adapted from KyraEngine
updateAnimationSceneScripts(int32 timeElapsed)1410 void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) {
1411 	static int32 numReentrant = 0;
1412 	numReentrant++;
1413 	const int startScript = _lastProcessedSceneScript;
1414 
1415 	_updatingSceneScriptRunFlag = true;
1416 
1417 	do {
1418 		if (_sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() &&
1419 				!_sceneAnimationScripts[_lastProcessedSceneScript]._frozen && !_sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) {
1420 			_animationSceneScriptRunFlag = true;
1421 
1422 			while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) {
1423 				if (!_script->run(&_sceneAnimationScripts[_lastProcessedSceneScript]._state))
1424 					_animationSceneScriptRunFlag = false;
1425 
1426 				//waitForScriptStep();
1427 
1428 				if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen || _sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation)
1429 					break;
1430 			}
1431 
1432 		}
1433 
1434 		if (!_script->isValid(&_sceneAnimationScripts[_lastProcessedSceneScript]._state)) {
1435 			_script->start(&_sceneAnimationScripts[_lastProcessedSceneScript]._state, 9 + _lastProcessedSceneScript);
1436 			_animationSceneScriptRunFlag = false;
1437 		}
1438 
1439 		++_lastProcessedSceneScript;
1440 		if (_lastProcessedSceneScript >= state()->_locations[state()->_currentScene]._numSceneAnimations)
1441 			_lastProcessedSceneScript = 0;
1442 
1443 	} while (_lastProcessedSceneScript != startScript && !_shouldQuit);
1444 
1445 	_updatingSceneScriptRunFlag = false;
1446 	numReentrant--;
1447 }
1448 
loadScene(int32 SceneId,bool forGameLoad)1449 void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
1450 	_firstFrame = true;
1451 
1452 	_gameState->_lastVisitedScene = _gameState->_currentScene;
1453 	_gameState->_currentScene = SceneId;
1454 
1455 	_saveBufferStream->seek(0);
1456 
1457 	if (SceneId == -1) {
1458 		// this scene -1 is loaded at the very end of the game
1459 		getAudioManager()->stopMusic();
1460 		getMoviePlayer()->play("CREDITS.SMK");
1461 		return;
1462 	}
1463 
1464 	// find out in what chapter we are (the script function ProcessToNextChapter is actually not called )
1465 	// the location flag has the chapter info in it
1466 	int32 flag = _gameState->_locations[SceneId]._flags;
1467 	if (flag) {
1468 		_gameState->_currentChapter = 0;
1469 		do {
1470 			_gameState->_currentChapter++;
1471 			flag >>= 1;
1472 		} while ((flag & 1) == 0);
1473 	}
1474 
1475 	for (int32 i = 0; i < 8; i++) {
1476 		if (_characters[i]) _characters[i]->setFlag(0);
1477 	}
1478 	_drew->playStandingAnim();
1479 	_drew->setVisible(true);
1480 
1481 	// hide flux in chapter 2
1482 	if (_gameState->_currentChapter == 1) {
1483 		_flux->playStandingAnim();
1484 		_flux->setVisible(true);
1485 	} else {
1486 		_flux->setVisible(false);
1487 	}
1488 
1489 	_lastMouseButton = 0;
1490 	_mouseButton = 0;
1491 	_currentHotspotItem = 0;
1492 
1493 	if (!forGameLoad) {
1494 		_gameState->_sackVisible = true;
1495 		_gameState->_inCloseUp = false;
1496 		_gameState->_inConversation = false;
1497 		_gameState->_inInventory = false;
1498 		_gameState->_inCutaway = false;
1499 		_gameState->_currentScrollValue = 0;
1500 		_gameState->_currentScrollLock = false;
1501 		_gameState->_inCloseUp = false;
1502 	}
1503 
1504 	if (_gameState->_mouseState >= 0)
1505 		addItemToInventory(_gameState->_mouseState);
1506 
1507 	_gameState->_mouseState = -1;
1508 	_mouseButton = 0;
1509 	_lastMouseButton = 0x3;
1510 
1511 	Common::String locationName = state()->_locations[SceneId]._name;
1512 
1513 	// load package
1514 	if (!resources()->openPackage(createRoomFilename(locationName + ".PAK"))) {
1515 		const char *msg = _s("Unable to locate the '%s' data file.");
1516 		Common::String roomFileName = createRoomFilename(locationName + ".PAK");
1517 
1518 		Common::U32String buf = Common::U32String::format(_(msg), roomFileName.c_str());
1519 		GUIErrorMessage(buf);
1520 		warning(msg, roomFileName.c_str());
1521 		_shouldQuit = true;
1522 		return;
1523 	}
1524 
1525 	loadAdditionalPalette(locationName + ".NPP", 0);
1526 
1527 	_additionalPalette2Present = false;
1528 	loadAdditionalPalette(locationName + ".NP2", 1);
1529 
1530 	loadAdditionalPalette(locationName + ".CUP", 2);
1531 
1532 	// load artwork
1533 	delete _currentPicture;
1534 	_currentPicture = new Picture(this);
1535 	_currentPicture->loadPicture(locationName + ".CPS");
1536 	_currentPicture->setupPalette();
1537 
1538 	delete _currentMask;
1539 	_currentMask = new Picture(this);
1540 	if (_currentMask->loadPicture(locationName + ".MSC"))
1541 		_pathFinding->init(_currentMask);
1542 
1543 	delete _roomTexts;
1544 	_roomTexts = new TextResource(this);
1545 	_roomTexts->loadTextResource(locationName + ".TRE");
1546 
1547 	uint32 fileSize;
1548 	uint8 *sceneData = resources()->getFileData(locationName + ".DAT", &fileSize);
1549 	if (sceneData) {
1550 		delete[] _roomScaleData;
1551 		_roomScaleData = new uint8[fileSize];
1552 		memcpy(_roomScaleData, sceneData, fileSize);
1553 	}
1554 
1555 	_audioManager->loadAudioPack(1, locationName + ".SVI", createRoomFilename(locationName + ".SVL"));
1556 	_audioManager->loadAudioPack(3, locationName + ".SEI", locationName + ".SEL");
1557 
1558 	if (state()->_locations[SceneId]._flags & 0x40) {
1559 		Common::String cutaway = state()->_locations[SceneId]._cutaway;
1560 		_hotspots->loadRif(locationName + ".RIC", cutaway + ".RIC");
1561 	} else {
1562 		_hotspots->loadRif(locationName + ".RIC", "");
1563 	}
1564 	restoreRifFlags(_gameState->_currentScene);
1565 
1566 	uint32 convfileSize;
1567 	uint8 *convData = resources()->getFileData(locationName + ".CNV", &convfileSize);
1568 	if (convData) {
1569 		assert(convfileSize < 4096 * sizeof(int16));
1570 		memcpy(_conversationData , convData, convfileSize);
1571 		prepareConversations();
1572 	}
1573 
1574 	// load script
1575 
1576 	_oldTimer = _system->getMillis();
1577 	_oldTimer2 = _oldTimer;
1578 
1579 	// fix the weird scaling issue during one frame when entering new scene
1580 	_drew->update(0);
1581 	_flux->update(0);
1582 
1583 	_script->unload(&_scriptData);
1584 	Common::String emcfile = locationName + ".EMC";
1585 	_script->load(emcfile.c_str(), &_scriptData, &_script_func->_opcodes);
1586 	_script->init(&_scriptState[0], &_scriptData);
1587 	_script->init(&_scriptState[1], &_scriptData);
1588 	_script->init(&_scriptState[2], &_scriptData);
1589 	_script->init(&_scriptState[3], &_scriptData);
1590 
1591 	//_script->RoomScript->Decompile("decomp.txt");
1592 	//RoomScript->Decompile2("decomp2.txt");
1593 
1594 	for (int i = 0; i < state()->_locations[SceneId]._numSceneAnimations; i++) {
1595 		_sceneAnimationScripts[i]._data = &_scriptData;
1596 		_script->init(&_sceneAnimationScripts[i]._state, _sceneAnimationScripts[i]._data);
1597 		if (!forGameLoad) {
1598 			_script->start(&_sceneAnimationScripts[i]._state, 9 + i);
1599 			_sceneAnimationScripts[i]._lastTimer = _system->getMillis();
1600 			_sceneAnimationScripts[i]._frozen = false;
1601 			_sceneAnimationScripts[i]._frozenForConversation = false;
1602 		}
1603 	}
1604 
1605 	playRoomMusic();
1606 
1607 	_lastProcessedSceneScript = 0;
1608 	_gameState->_locations[SceneId]._visited = true;
1609 
1610 	setupGeneralPalette();
1611 	createShadowLUT();
1612 
1613 	state()->_mouseHidden = false;
1614 
1615 	clearDirtyRects();
1616 	dirtyAllScreen();
1617 
1618 	if (!forGameLoad) {
1619 
1620 		_script->start(&_scriptState[0], 0);
1621 
1622 		while (_script->run(&_scriptState[0]))
1623 			waitForScriptStep();
1624 
1625 		_script->start(&_scriptState[0], 8);
1626 
1627 		while (_script->run(&_scriptState[0]))
1628 			waitForScriptStep();
1629 
1630 		if (_gameState->_nextSpecialEnterX != -1 && _gameState->_nextSpecialEnterY != -1) {
1631 			_drew->forcePosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY);
1632 			_gameState->_nextSpecialEnterX = -1;
1633 			_gameState->_nextSpecialEnterY = -1;
1634 		}
1635 
1636 		_script->start(&_scriptState[0], 3);
1637 
1638 		while (_script->run(&_scriptState[0]))
1639 			waitForScriptStep();
1640 
1641 		_script->start(&_scriptState[0], 4);
1642 
1643 		while (_script->run(&_scriptState[0]))
1644 			waitForScriptStep();
1645 
1646 	}
1647 }
1648 
setupGeneralPalette()1649 void ToonEngine::setupGeneralPalette() {
1650 	setPaletteEntries(_additionalPalette1, 232, 23);
1651 	setPaletteEntries(_universalPalette, 200, 32);
1652 	setPaletteEntries(_fluxPalette, 192, 8);
1653 
1654 	if (_drew)
1655 		_drew->setupPalette();
1656 }
1657 
loadAdditionalPalette(const Common::String & fileName,int32 mode)1658 void ToonEngine::loadAdditionalPalette(const Common::String &fileName, int32 mode) {
1659 
1660 	uint32 size = 0;
1661 	uint8 *palette = resources()->getFileData(fileName, &size);
1662 	if (!palette)
1663 		return;
1664 
1665 	switch (mode) {
1666 	case 0:
1667 		memcpy(_additionalPalette1, palette, 69);
1668 		fixPaletteEntries(_additionalPalette1, 23);
1669 		break;
1670 	case 1:
1671 		memcpy(_additionalPalette2, palette, 69);
1672 		fixPaletteEntries(_additionalPalette2, 23);
1673 		_additionalPalette2Present = true;
1674 		break;
1675 	case 2:
1676 		memcpy(_cutawayPalette, palette, size);
1677 		fixPaletteEntries(_cutawayPalette, size/3);
1678 		break;
1679 	case 3:
1680 		memcpy(_universalPalette, palette, 96);
1681 		fixPaletteEntries(_universalPalette, 32);
1682 		break;
1683 	case 4:
1684 		memcpy(_fluxPalette, palette, 24);
1685 		fixPaletteEntries(_fluxPalette , 8);
1686 		break;
1687 	default:
1688 		warning("loadAdditionalPalette() - Unknown mode");
1689 	}
1690 }
1691 
initChapter()1692 void ToonEngine::initChapter() {
1693 
1694 	EMCData data;
1695 	EMCState status;
1696 	memset(&data, 0, sizeof(data));
1697 	memset(&status, 0, sizeof(status));
1698 
1699 	delete _script;
1700 	_script = new EMCInterpreter(this);
1701 
1702 	_script->load("_START01.EMC", &data, &_script_func->_opcodes);
1703 	_script->init(&status, &data);
1704 	_script->start(&status, 0);
1705 	while (_script->run(&status))
1706 		waitForScriptStep();
1707 
1708 	_script->unload(&data);
1709 
1710 	setupGeneralPalette();
1711 }
1712 
loadCursor()1713 void ToonEngine::loadCursor() {
1714 	delete _cursorAnimation;
1715 	_cursorAnimation = new Animation(this);
1716 	_cursorAnimation->loadAnimation("MOUSE.CAF");
1717 	delete _cursorAnimationInstance;
1718 	_cursorAnimationInstance = _animationManager->createNewInstance(kAnimationCursor);
1719 	_cursorAnimationInstance->setAnimation(_cursorAnimation);
1720 	_cursorAnimationInstance->setVisible(true);
1721 	_cursorAnimationInstance->setFrame(0);
1722 	_cursorAnimationInstance->setAnimationRange(0, 0);
1723 	_cursorAnimationInstance->setFps(8);
1724 
1725 	setCursor(5);
1726 }
1727 
setCursor(int32 type,bool inventory,int32 offsetX,int offsetY)1728 void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int offsetY) {
1729 
1730 	static const int32 offsets[] = {
1731 		0,   1,  1,  6,  7,  1,  8,   10, 18,  10,
1732 		28,  8,  36, 10, 46, 10, 56,  10, 66,  10,
1733 		76,  10, 86, 10, 96, 10, 106, 10, 116, 10,
1734 		126, 10
1735 	};
1736 
1737 	if (!inventory) {
1738 		_cursorAnimationInstance->setAnimation(_cursorAnimation);
1739 		_cursorAnimationInstance->setAnimationRange(offsets[type * 2 + 0], offsets[type * 2 + 0] + offsets[type * 2 + 1] - 1);
1740 		_cursorAnimationInstance->playAnimation();
1741 	} else {
1742 		_cursorAnimationInstance->setAnimation(_inventoryIcons);
1743 		_cursorAnimationInstance->setAnimationRange(type, type);
1744 		_cursorAnimationInstance->playAnimation();
1745 	}
1746 
1747 	_cursorOffsetX = offsetX;
1748 	_cursorOffsetY = offsetY;
1749 }
1750 
setSceneAnimationScriptUpdate(bool enable)1751 void ToonEngine::setSceneAnimationScriptUpdate(bool enable) {
1752 	_animationSceneScriptRunFlag = enable;
1753 }
1754 
isUpdatingSceneAnimation()1755 bool ToonEngine::isUpdatingSceneAnimation() {
1756 	return _updatingSceneScriptRunFlag;
1757 }
1758 
getCurrentUpdatingSceneAnimation()1759 int32 ToonEngine::getCurrentUpdatingSceneAnimation() {
1760 	return _lastProcessedSceneScript;
1761 }
1762 
randRange(int32 minStart,int32 maxStart)1763 int32 ToonEngine::randRange(int32 minStart, int32 maxStart) {
1764 	return _rnd.getRandomNumberRng(minStart, maxStart);
1765 }
1766 
runEventScript(int32 x,int32 y,int32 mode,int32 id,int32 scriptId)1767 int32 ToonEngine::runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId) {
1768 
1769 	if (_currentScriptRegion >= 4)
1770 		return 0;
1771 
1772 	EMCState *status = &_scriptState[_currentScriptRegion];
1773 	_script->init(status, &_scriptData);
1774 
1775 	// setup registers
1776 	status->regs[0] = x;
1777 	status->regs[1] = y;
1778 	status->regs[2] = 0;
1779 	status->regs[3] = 0;
1780 	status->regs[4] = _gameState->_mouseState; //
1781 	status->regs[5] = 0;
1782 	status->regs[6] = scriptId;
1783 	status->regs[7] = mode;
1784 	status->regs[8] = id;
1785 
1786 	_currentScriptRegion++;
1787 
1788 	_script->start(status, 1);
1789 	while (_script->run(status) && !_shouldQuit)
1790 		waitForScriptStep();
1791 
1792 	_currentScriptRegion--;
1793 
1794 	return status->regs[2];
1795 }
1796 
clickEvent()1797 void ToonEngine::clickEvent() {
1798 	bool leftButton = false;
1799 	bool rightButton = false;
1800 
1801 	if ((_lastMouseButton & 0x1) == 0 && (_mouseButton & 0x1) == 1)
1802 		leftButton = true;
1803 	if ((_lastMouseButton & 0x2) == 0 && (_mouseButton & 0x2) == 2)
1804 		rightButton = true;
1805 
1806 	_lastMouseButton = _mouseButton;
1807 	if (!leftButton && !rightButton)
1808 		return;
1809 
1810 	if (_gameState->_sackVisible) {
1811 		if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
1812 			if (_gameState->_mouseState >= 0 && !rightButton) {
1813 				addItemToInventory(_gameState->_mouseState);
1814 				setCursor(0, false, 0, 0);
1815 				_currentHotspotItem = 0;
1816 				return;
1817 			} else {
1818 				showInventory();
1819 			}
1820 			return;
1821 		}
1822 	}
1823 
1824 	// with inventory
1825 	if (rightButton && _gameState->_mouseState >= 0) {
1826 		addItemToInventory(_gameState->_mouseState);
1827 		setCursor(0, false, 0, 0);
1828 		_currentHotspotItem = 0;
1829 		return;
1830 	}
1831 
1832 	int32 mouseX = _mouseX;
1833 	if (_gameState->_inCutaway) {
1834 		mouseX += TOON_BACKBUFFER_WIDTH;
1835 	}
1836 
1837 	// find hotspot
1838 	int32 hot = _hotspots->find(mouseX + state()->_currentScrollValue , _mouseY);
1839 	HotspotData *currentHot = 0;
1840 	if (hot > -1) {
1841 		currentHot = _hotspots->get(hot);
1842 	}
1843 
1844 	if (_currentHotspotItem == -3) {
1845 		if (_gameState->_mouseState <= 0) {
1846 			if (leftButton)
1847 				createMouseItem(104);
1848 			else
1849 				characterTalk(1104);
1850 		}
1851 	}
1852 	if (_currentHotspotItem == -4) {
1853 		if (_gameState->_mouseState >= 0) {
1854 			if (leftButton)
1855 				if (!handleInventoryOnInventory(0, _gameState->_mouseState)) {
1856 					playSoundWrong();
1857 				}
1858 			return;
1859 		}
1860 	}
1861 
1862 	if (!currentHot) {
1863 		int16 xx, yy;
1864 
1865 		if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
1866 			return;
1867 
1868 		if (_pathFinding->findClosestWalkingPoint(_mouseX + _gameState->_currentScrollValue , _mouseY, &xx, &yy))
1869 			_drew->walkTo(xx, yy);
1870 		return;
1871 	}
1872 
1873 	int commandId = 0;
1874 	if (_gameState->_mouseState < 0) {
1875 		// left or right click
1876 		if (rightButton)
1877 			commandId = 2 + 8;
1878 		else
1879 			commandId = 0 + 8;
1880 	} else {
1881 		commandId = 2 * (_gameState->_mouseState - 1) + 16;
1882 	}
1883 
1884 	_drew->stopWalk();
1885 
1886 	int16 command = currentHot->getData(commandId);
1887 	int16 argument = currentHot->getData(commandId + 1);
1888 	int16 priority = currentHot->getPriority();
1889 
1890 	if (!_gameState->_inCutaway && !_gameState->_inCloseUp) {
1891 		if (leftButton && (currentHot->getData(4) != 2 || _gameState->_mouseState >= 0) && currentHot->getData(5) != -1) {
1892 			if (currentHot->getData(5)) {
1893 				if (!_drew->walkTo(currentHot->getData(5), currentHot->getData(6))) {
1894 					// walk was canceled ?
1895 					return;
1896 				}
1897 			} else {
1898 				if (!_drew->walkTo(_mouseX + _gameState->_currentScrollValue, _mouseY)) {
1899 					// walk was canceled ?
1900 					return;
1901 				}
1902 			}
1903 		}
1904 	}
1905 
1906 	int32 result = 0;
1907 
1908 	switch (command) {
1909 	case 1:
1910 		sayLines(1, argument);
1911 		break;
1912 	case 2:
1913 		result = runEventScript(_mouseX, _mouseY, command, argument, priority);
1914 		break;
1915 	case 3:
1916 		runEventScript(_mouseX, _mouseY, command, argument, priority);
1917 		result = 0;
1918 		break;
1919 	case 4:
1920 		playSFX(argument, 128);
1921 		break;
1922 	case 5:
1923 		break;
1924 	case 6:
1925 		createMouseItem(argument);
1926 		currentHot->setData(7, -1);
1927 		break;
1928 	case 7:
1929 		// switch to CloseUp
1930 		break;
1931 	case 8:
1932 		// face flux
1933 		sayLines(1, argument);
1934 		break;
1935 	case 9:
1936 	case 10:
1937 		sayLines(2, argument);
1938 		break;
1939 	case 11:
1940 		sayLines(3, argument);
1941 		break;
1942 	default:
1943 		playSoundWrong();
1944 		return;
1945 	}
1946 
1947 	if (result == 3) {
1948 		int32 val = _scriptState[_currentScriptRegion].regs[4];
1949 		currentHot->setData(4, currentHot->getData(4) & val);
1950 	}
1951 	if (result == 2 || result == 3) {
1952 		int32 val = _scriptState[_currentScriptRegion].regs[6];
1953 		currentHot->setData(7, val);
1954 	}
1955 
1956 	if (result == 1) {
1957 		int32 val = _scriptState[_currentScriptRegion].regs[4];
1958 		currentHot->setData(4, currentHot->getData(4) & val);
1959 	}
1960 }
1961 
selectHotspot()1962 void ToonEngine::selectHotspot() {
1963 	int16 x1 = 0;
1964 	int16 x2 = 0;
1965 	int16 y1 = 0;
1966 	int16 y2 = 0;
1967 
1968 	int16 mouseX = _mouseX;
1969 
1970 	if (_gameState->_inCutaway)
1971 		mouseX += TOON_BACKBUFFER_WIDTH;
1972 
1973 	if (_gameState->_sackVisible) {
1974 		if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
1975 			_currentHotspotItem = -2;
1976 
1977 			if (_gameState->_mouseState < 0) {
1978 				int mode = 3;
1979 				setCursor(mode);
1980 			} else {
1981 				setCursor(_gameState->_mouseState, true, -18, -14);
1982 			}
1983 
1984 			return;
1985 		}
1986 	}
1987 
1988 	if (_gameState->_mouseState > 0) {
1989 		// picked drew?
1990 		getDrew()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
1991 		if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
1992 			_currentHotspotItem = -4;
1993 			return;
1994 		}
1995 	}
1996 
1997 	if (getFlux()->getVisible()) {
1998 		getFlux()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
1999 		if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
2000 			_currentHotspotItem = -3;
2001 
2002 			if (_gameState->_mouseState < 0) {
2003 				int mode = 3;
2004 				setCursor(mode);
2005 			} else {
2006 				setCursor(_gameState->_mouseState, true, -18, -14);
2007 			}
2008 
2009 			return;
2010 		}
2011 	}
2012 
2013 	int32 hot = _hotspots->find(mouseX + state()->_currentScrollValue, _mouseY);
2014 	if (hot != -1) {
2015 		HotspotData *hotspot = _hotspots->get(hot);
2016 		int32 item = hotspot->getData(14);
2017 		if (hotspot->getType() == 3)
2018 			item += 2000;
2019 
2020 		// update palette based on ticks if we're in "use from inventory mode"
2021 		if (_gameState->_mouseState >= 0) {
2022 
2023 			int32 tick = _system->getMillis() / _tickLength;
2024 			int32 animReverse = tick & 0x10;
2025 			int32 animStep = tick & 0xf;
2026 
2027 			byte color[3];
2028 			if (animReverse == 0) {
2029 				color[0] = 16 * animStep;
2030 				color[1] = 0;
2031 				color[2] = 0;
2032 			} else {
2033 				color[0] = 16 * (15 - animStep);
2034 				color[1] = 0;
2035 				color[2] = 0;
2036 			}
2037 			setPaletteEntries(color, 255, 1);
2038 		}
2039 
2040 #if 0
2041 		if (item == _currentHotspotItem)
2042 			return;
2043 #endif
2044 		_currentHotspotItem = item;
2045 		if (_gameState->_mouseState < 0) {
2046 			int mode = hotspot->getMode();
2047 			setCursor(mode);
2048 		} else {
2049 			setCursor(_gameState->_mouseState, true, -18, -14);
2050 		}
2051 	} else {
2052 		_currentHotspotItem = 0;
2053 
2054 		if (_gameState->_mouseState < 0) {
2055 			setCursor(0);
2056 		} else {
2057 			byte color[3];
2058 			color[0] = 0;
2059 			color[1] = 0;
2060 			color[2] = 0;
2061 			setCursor(_gameState->_mouseState, true, -18, -14);
2062 			setPaletteEntries(color, 255, 1);
2063 		}
2064 	}
2065 }
2066 
exitScene()2067 void ToonEngine::exitScene() {
2068 	fadeOut(5);
2069 
2070 	// disable all scene animation
2071 	for (int32 i = 0; i < 64; i++) {
2072 		if (_sceneAnimations[i]._active) {
2073 			delete _sceneAnimations[i]._animation;
2074 			_sceneAnimations[i]._active = false;
2075 			_animationManager->removeInstance(_sceneAnimations[i]._animInstance);
2076 
2077 			// see if one character shares this instance
2078 			for (int32 c = 0; c < 32; c++) {
2079 				if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
2080 					_characters[c]->setAnimationInstance(NULL);
2081 				}
2082 			}
2083 
2084 			delete _sceneAnimations[i]._originalAnimInstance;
2085 			_sceneAnimations[i]._animInstance = NULL;
2086 			_sceneAnimations[i]._animation = NULL;
2087 			_sceneAnimations[i]._originalAnimInstance = NULL;
2088 		}
2089 	}
2090 	for (int32 i = 0; i < 64; i++) {
2091 		_sceneAnimationScripts[i]._frozen = true;
2092 		_sceneAnimationScripts[i]._active = false;
2093 	}
2094 
2095 	// remove all characters except drew and flux
2096 	for (int32 i = 0; i < 8; i++) {
2097 		if (_characters[i] != _drew && _characters[i] != _flux) {
2098 			if (_characters[i]) {
2099 				delete _characters[i];
2100 				_characters[i] = 0;
2101 			}
2102 		} else {
2103 			_characters[i]->stopSpecialAnim();
2104 		}
2105 	}
2106 
2107 	for (int32 i = 0; i < 2; i++) {
2108 		_gameState->_timerEnabled[i] = false;
2109 	}
2110 
2111 	// put back our item if inventory if needed
2112 	if (_gameState->_mouseState >= 0) {
2113 		addItemToInventory(_gameState->_mouseState);
2114 		_gameState->_mouseState = -1;
2115 	}
2116 
2117 	_audioManager->killAllAmbientSFX();
2118 	_audioManager->stopAllSfxs();
2119 	_audioManager->stopCurrentVoice();
2120 	_currentTextLine = 0;
2121 	_currentTextLineId = -1;
2122 	_currentTextLineCharacterId = 0;
2123 
2124 	Common::String locationName = _gameState->_locations[_gameState->_currentScene]._name;
2125 	resources()->closePackage(createRoomFilename(locationName + ".PAK"));
2126 
2127 	_drew->stopWalk();
2128 	_flux->stopWalk();
2129 
2130 	storeRifFlags(_gameState->_currentScene);
2131 }
2132 
2133 // flip between the cutaway scene and the normal scene
flipScreens()2134 void ToonEngine::flipScreens() {
2135 	_gameState->_inCloseUp = !_gameState->_inCloseUp;
2136 
2137 	if (_gameState->_inCloseUp) {
2138 		_gameState->_currentScrollValue = TOON_SCREEN_WIDTH;
2139 		setPaletteEntries(_cutawayPalette, 1, 128);
2140 		if (_additionalPalette2Present)
2141 			setPaletteEntries(_additionalPalette2, 232, 23);
2142 	} else {
2143 		_gameState->_currentScrollValue = 0;
2144 		_currentPicture->setupPalette();
2145 		setupGeneralPalette();
2146 	}
2147 	flushPalette();
2148 }
2149 
fadeIn(int32 numFrames)2150 void ToonEngine::fadeIn(int32 numFrames) {
2151 	for (int32 f = 0; f < numFrames; f++) {
2152 
2153 		uint8 vmpalette[3 * 256];
2154 		for (int32 i = 0; i < 256; i++) {
2155 			vmpalette[i * 3 + 0] = f * _finalPalette[i * 3 + 0] / (numFrames - 1);
2156 			vmpalette[i * 3 + 1] = f * _finalPalette[i * 3 + 1] / (numFrames - 1);
2157 			vmpalette[i * 3 + 2] = f * _finalPalette[i * 3 + 2] / (numFrames - 1);
2158 		}
2159 		_system->getPaletteManager()->setPalette(vmpalette, 0, 256);
2160 		_system->updateScreen();
2161 		_system->delayMillis(_tickLength);
2162 	}
2163 }
2164 
fadeOut(int32 numFrames)2165 void ToonEngine::fadeOut(int32 numFrames) {
2166 
2167 	uint8 oldpalette[3 * 256];
2168 	_system->getPaletteManager()->grabPalette(oldpalette, 0, 256);
2169 
2170 	for (int32 f = 0; f < numFrames; f++) {
2171 		uint8 vmpalette[3 * 256];
2172 		for (int32 i = 0; i < 256; i++) {
2173 			vmpalette[i * 3 + 0] = (numFrames - f - 1) * oldpalette[i * 3 + 0] / (numFrames - 1);
2174 			vmpalette[i * 3 + 1] = (numFrames - f - 1) * oldpalette[i * 3 + 1] / (numFrames - 1);
2175 			vmpalette[i * 3 + 2] = (numFrames - f - 1) * oldpalette[i * 3 + 2] / (numFrames - 1);
2176 		}
2177 		_system->getPaletteManager()->setPalette(vmpalette, 0, 256);
2178 		_system->updateScreen();
2179 		_system->delayMillis(_tickLength);
2180 	}
2181 }
2182 
initFonts()2183 void ToonEngine::initFonts() {
2184 	_fontRenderer = new FontRenderer(this);
2185 	_fontToon = new Animation(this);
2186 	_fontToon->loadAnimation("TOONFONT.CAF");
2187 
2188 	_fontEZ = new Animation(this);
2189 	_fontEZ->loadAnimation("EZFONT.CAF");
2190 
2191 	setFont(false);
2192 }
2193 
setFont(bool alternative)2194 void ToonEngine::setFont(bool alternative) {
2195 	if (alternative) {
2196 		_currentFont = _fontEZ;
2197 	} else {
2198 		_currentFont = _fontToon;
2199 	}
2200 	_useAlternativeFont = alternative;
2201 }
2202 
drawInfoLine()2203 void ToonEngine::drawInfoLine() {
2204 	if (_currentHotspotItem != 0 && !_gameState->_mouseHidden && !_gameState->_inConversation) {
2205 		const char *infoTool = NULL;
2206 		if (_currentHotspotItem >= 0 && _currentHotspotItem < 2000) {
2207 			infoTool = _roomTexts->getText(_currentHotspotItem);
2208 		} else if (_currentHotspotItem <= -1) {
2209 //			static const char * const specialInfoLine[] = { "Exit non defined", "Bottomless Bag", "Flux", "Drew Blanc" };
2210 			infoTool = _specialInfoLine[-1 - _currentHotspotItem];
2211 		} else {
2212 			int32 loc = _currentHotspotItem - 2000;
2213 			// location names are hardcoded ...
2214 			infoTool = getLocationString(loc, _gameState->_locations[loc]._visited);
2215 		}
2216 		if (infoTool) {
2217 			_fontRenderer->setFontColor(0xc8, 0xdd, 0xe3);
2218 			_fontRenderer->setFont(_currentFont);
2219 			_fontRenderer->renderText(320 + _gameState->_currentScrollValue, 398, infoTool, 5);
2220 		}
2221 	}
2222 }
2223 
getSaveBufferStream()2224 Common::WriteStream *ToonEngine::getSaveBufferStream() {
2225 	return _saveBufferStream;
2226 }
2227 
getLocationString(int32 locationId,bool alreadyVisited)2228 const char *ToonEngine::getLocationString(int32 locationId, bool alreadyVisited) {
2229 	if (alreadyVisited)
2230 		return _locationDirVisited[locationId];
2231 	else
2232 		return _locationDirNotVisited[locationId];
2233 }
2234 
getScaleAtPoint(int32 x,int32 y)2235 int32 ToonEngine::getScaleAtPoint(int32 x, int32 y) {
2236 	if (!_currentMask)
2237 		return 1024;
2238 
2239 	// clamp values
2240 	x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x));
2241 	y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y));
2242 
2243 	int32 maskData = _currentMask->getData(x, y) & 0x1f;
2244 	return _roomScaleData[maskData + 2] * 1024 / 100;
2245 }
2246 
getLayerAtPoint(int32 x,int32 y)2247 int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) {
2248 	if (!_currentMask)
2249 		return 0;
2250 
2251 	// clamp values
2252 	x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x));
2253 	y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y));
2254 
2255 	int32 maskData = _currentMask->getData(x, y) & 0x1f;
2256 	return _roomScaleData[maskData + 130] << 5;
2257 }
2258 
getZAtPoint(int32 x,int32 y)2259 int32 ToonEngine::getZAtPoint(int32 x, int32 y) {
2260 	if (!_currentMask)
2261 		return 0;
2262 	return _currentMask->getData(x, y) & 0x1f;
2263 }
2264 
storeRifFlags(int32 location)2265 void ToonEngine::storeRifFlags(int32 location) {
2266 
2267 	if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount()) {
2268 		_gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
2269 	}
2270 
2271 	for (int32 i = 0; i < _hotspots->getCount(); i++) {
2272 		_gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->get(i)->getData(4);
2273 		_gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->get(i)->getData(7);
2274 	}
2275 }
2276 
restoreRifFlags(int32 location)2277 void ToonEngine::restoreRifFlags(int32 location) {
2278 	if (_hotspots) {
2279 		if (!_gameState->_locations[location]._visited) {
2280 			for (int32 i = 0; i < _hotspots->getCount(); i++) {
2281 				_gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->get(i)->getData(4);
2282 				_gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->get(i)->getData(7);
2283 			}
2284 			_gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
2285 		} else {
2286 			if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount())
2287 				return;
2288 
2289 			for (int32 i = 0; i < _hotspots->getCount(); i++) {
2290 				_hotspots->get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]);
2291 				_hotspots->get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]);
2292 			}
2293 		}
2294 	}
2295 }
2296 
sayLines(int numLines,int dialogId)2297 void ToonEngine::sayLines(int numLines, int dialogId) {
2298 	// exit conversation state
2299 
2300 	// if (inInventory)
2301 	// 	character_talks(dialogid, -1, 0, 0);
2302 	// else
2303 
2304 #if 0
2305 	int oldShowMouse = 0;
2306 
2307 	if (Game.MouseHiddenCount <= 0) {
2308 		Game.MouseHiddenCount = 1;
2309 		oldShowMouse = 1;
2310 	}
2311 #endif
2312 
2313 	int32 currentLine = dialogId;
2314 
2315 	for (int32 i = 0; i < numLines; i++) {
2316 		if (!characterTalk(currentLine))
2317 			break;
2318 
2319 		while (_audioManager->voiceStillPlaying() && !_shouldQuit)
2320 			doFrame();
2321 
2322 		// find next line
2323 		if (currentLine < 1000)
2324 			currentLine = _roomTexts->getNext(currentLine);
2325 		else
2326 			currentLine = _genericTexts->getNext(currentLine - 1000) + 1000;
2327 	}
2328 
2329 #if 0
2330 	if (oldShowMouse)
2331 		Game.MouseHiddenCount = 0;
2332 #endif
2333 }
2334 
simpleCharacterTalk(int32 dialogid)2335 int32 ToonEngine::simpleCharacterTalk(int32 dialogid) {
2336 	int32 myId = 0;
2337 
2338 	if (_audioManager->voiceStillPlaying())
2339 		_audioManager->stopCurrentVoice();
2340 
2341 	if (dialogid < 1000) {
2342 		myId = _roomTexts->getId(dialogid);
2343 		_audioManager->playVoice(myId, false);
2344 	} else {
2345 		myId = _genericTexts->getId(dialogid - 1000);
2346 		if (myId == -1)
2347 			return 0;
2348 		_audioManager->playVoice(myId, true);
2349 	}
2350 
2351 	return 1;
2352 }
2353 
playTalkAnimOnCharacter(int32 animID,int32 characterId,bool talker)2354 void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker) {
2355 	if (animID || talker) {
2356 		if (characterId == 0) {
2357 			_drew->playAnim(animID, 0, (talker ? 8 : 0) + 2);
2358 		} else if (characterId == 1) {
2359 			// stop flux if he is walking
2360 			if (_flux->getFlag() & 1) {
2361 				_flux->stopWalk();
2362 			}
2363 			_flux->playAnim(animID, 0, (talker ? 8 : 0) + 2);
2364 			_flux->setFlag(_flux->getFlag() | 1);
2365 		} else {
2366 			Character *character = getCharacterById(characterId);
2367 			if (character) {
2368 				character->playAnim(animID, 0, (talker ? 8 : 0) + 2);
2369 			}
2370 		}
2371 	} else {
2372 		Character *character = getCharacterById(characterId);
2373 		if (character)
2374 			character->setAnimFlag(character->getAnimFlag() | 1);
2375 	}
2376 }
2377 
characterTalk(int32 dialogid,bool blocking)2378 int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
2379 	if (blocking == false && _audioManager->voiceStillPlaying()) {
2380 		if (_currentTextLineCharacterId == 0 || _currentTextLineCharacterId == 1) {
2381 			// Drew or Flux is already talking, and this voice is not important
2382 			// skip it
2383 			return 0;
2384 		}
2385 	}
2386 
2387 	char *myLine;
2388 	if (dialogid < 1000)
2389 		myLine = _roomTexts->getText(dialogid);
2390 	else
2391 		myLine = _genericTexts->getText(dialogid - 1000);
2392 
2393 	if (!myLine)
2394 		return 0;
2395 
2396 	bool oldMouseHidden = _gameState->_mouseHidden;
2397 	if (blocking)
2398 		_gameState->_mouseHidden = true;
2399 
2400 
2401 	// get what is before the string
2402 	int a = READ_LE_UINT16(myLine - 2);
2403 	char *b = myLine - 2 - 4 * a;
2404 
2405 	char *c = b - 2; // v6
2406 	int numParticipants = READ_LE_UINT16(c); // num dialogue participants
2407 
2408 	char *e = c - 2 - 4 * numParticipants;
2409 	READ_LE_UINT16(e);
2410 
2411 	// if one voice is still playing, wait !
2412 	if (blocking) {
2413 		while (_audioManager->voiceStillPlaying() && !_shouldQuit)
2414 			doFrame();
2415 
2416 		char *cc = c;
2417 		Character *waitChar;
2418 		for (int32 i = 0; i < numParticipants - 1; i++) {
2419 			// listener
2420 			int32 listenerId = READ_LE_UINT16(cc - 2);
2421 			cc -= 4;
2422 			waitChar = getCharacterById(listenerId);
2423 			if (waitChar) {
2424 				while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
2425 					doFrame();
2426 			}
2427 
2428 		}
2429 		int32 talkerId = READ_LE_UINT16(cc - 2);
2430 
2431 		waitChar = getCharacterById(talkerId);
2432 		if (waitChar && !_gameState->_inInventory) {
2433 			while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
2434 				doFrame();
2435 		}
2436 	} else if (_audioManager->voiceStillPlaying())
2437 		_audioManager->stopCurrentVoice();
2438 
2439 	for (int32 i = 0; i < numParticipants - 1; i++) {
2440 		// listener
2441 		int32 listenerId = READ_LE_UINT16(c - 2);
2442 		int32 listenerAnimId = READ_LE_UINT16(c - 4);
2443 		if (blocking) playTalkAnimOnCharacter(listenerAnimId, listenerId, false);
2444 		c -= 4;
2445 	}
2446 
2447 	int32 talkerId = READ_LE_UINT16(c - 2);
2448 	int32 talkerAnimId = READ_LE_UINT16(c - 4);
2449 
2450 	_currentTextLine = myLine;
2451 	_currentTextLineCharacterId = talkerId;
2452 	_currentTextLineId = dialogid;
2453 
2454 	if (blocking) {
2455 		Character *character = getCharacterById(talkerId);
2456 		if (character)
2457 			character->setTalking(true);
2458 
2459 		playTalkAnimOnCharacter(talkerAnimId, talkerId, true);
2460 
2461 		// set once more the values, they may have been overwritten when the engine
2462 		// waits for the character to be ready.
2463 		_currentTextLine = myLine;
2464 		_currentTextLineCharacterId = talkerId;
2465 		_currentTextLineId = dialogid;
2466 	} else {
2467 		Character *character = getCharacterById(talkerId);
2468 		if (character)
2469 			character->stopSpecialAnim();
2470 	}
2471 
2472 	debugC(0, 0xfff, "Talker = %d (num participants : %d) will say '%s'", (int)talkerId , (int)numParticipants, myLine);
2473 
2474 	getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY);
2475 
2476 	if (dialogid < 1000) {
2477 		int myId = _roomTexts->getId(dialogid);
2478 		_audioManager->playVoice(myId, false);
2479 	} else {
2480 		int myId = _genericTexts->getId(dialogid - 1000);
2481 		_audioManager->playVoice(myId, true);
2482 	}
2483 
2484 	if (blocking) {
2485 		while (_audioManager->voiceStillPlaying() && !_shouldQuit)
2486 			doFrame();
2487 		_gameState->_mouseHidden = oldMouseHidden && _gameState->_mouseHidden;
2488 
2489 		Character *character = getCharacterById(talkerId);
2490 		if (character)
2491 			character->setTalking(false);
2492 	}
2493 	return 1;
2494 }
2495 
haveAConversation(int32 convId)2496 void ToonEngine::haveAConversation(int32 convId) {
2497 	setCursor(0);
2498 
2499 	_gameState->_inConversation = true;
2500 	_gameState->_showConversationIcons = false;
2501 	_gameState->_exitConversation = false;
2502 	_gameState->_sackVisible = false;
2503 	Conversation *conv = &state()->_conversationState[convId];
2504 	_gameState->_currentConversationId = convId;
2505 
2506 	// change the music to the "conversation" music if needed.
2507 	playRoomMusic();
2508 
2509 	if (conv->_enable) {
2510 		// fix dialog script based on new flags
2511 		for (int32 i = 0; i < 10; i++) {
2512 			if (conv->state[i]._data2 == 1 || conv->state[i]._data2 == 3) {
2513 				if (getConversationFlag(_gameState->_currentScene, conv->state[i]._data3))
2514 					conv->state[i]._data2 = 1;
2515 				else
2516 					conv->state[i]._data2 = 3;
2517 			}
2518 		}
2519 
2520 		// if current voice stream sub 15130
2521 		processConversationClick(conv , 2);
2522 		doFrame();
2523 	}
2524 
2525 	_mouseButton = 0;
2526 	_gameState->_firstConverstationLine = true;
2527 
2528 	while (!_gameState->_exitConversation && !_shouldQuit) {
2529 		_gameState->_mouseHidden = false;
2530 		_gameState->_showConversationIcons = true;
2531 		int32 oldMouseButton = _mouseButton;
2532 		while (!_shouldQuit) {
2533 			doFrame();
2534 
2535 			if (_mouseButton != 0) {
2536 				if (!oldMouseButton)
2537 					break;
2538 			} else {
2539 				oldMouseButton = 0;
2540 			}
2541 		}
2542 		int selected = -1;
2543 		int a = 0;
2544 		for (int i = 0; i < 10; i++) {
2545 			if (conv->state[i]._data2 == 1) {
2546 				if (_mouseX > 50 + a * 60 && _mouseX < 100 + a * 60 && _mouseY >= 336 && _mouseY <= 386) {
2547 					selected = i;
2548 					break;
2549 				}
2550 				a++;
2551 			}
2552 		}
2553 
2554 		if (_shouldQuit)
2555 			return;
2556 
2557 		_gameState->_showConversationIcons = false;
2558 		_gameState->_mouseHidden = 1;
2559 
2560 		if (selected < 0 || selected == 1 || selected == 3) {
2561 			if (_gameState->_firstConverstationLine)
2562 				processConversationClick(conv, 3);
2563 			else
2564 				processConversationClick(conv, 1);
2565 			break;
2566 		} else {
2567 			processConversationClick(conv, selected);
2568 		}
2569 	}
2570 
2571 	for (int i = 0; i < 10; i++) {
2572 		if (conv->state[i]._data2 == 2) {
2573 			if (i != 3)
2574 				conv->state[i]._data2 = 1;
2575 		}
2576 	}
2577 
2578 	_gameState->_exitConversation = false;
2579 	_gameState->_inConversation = false;
2580 	_gameState->_currentConversationId = -1;
2581 	_gameState->_mouseHidden = false;
2582 	_gameState->_sackVisible = true;
2583 
2584 	// switch back to original music
2585 	playRoomMusic();
2586 }
2587 
drawConversationIcons()2588 void ToonEngine::drawConversationIcons() {
2589 	if (!_gameState->_inConversation || !_gameState->_showConversationIcons)
2590 		return;
2591 	int32 aa = 50 + _gameState->_currentScrollValue;
2592 	for (int32 i = 0; i < 10; i++) {
2593 		if (_gameState->_conversationState[_gameState->_currentConversationId].state[i]._data2 == 1) {
2594 			_dialogIcons->drawFrame(*_mainSurface, (i + _gameState->_currentScene) & 7, aa, 336);
2595 			_dialogIcons->drawFrame(*_mainSurface, 7 + _gameState->_conversationState[_gameState->_currentConversationId].state[i]._data3, aa, 339);
2596 			aa += 60;
2597 		}
2598 	}
2599 }
2600 
prepareConversations()2601 void ToonEngine::prepareConversations() {
2602 	Conversation *allConvs = _gameState->_conversationState;
2603 	for (int32 i = 0; i < 60; i++) {
2604 
2605 		allConvs[i].state[0]._data2 = 1;
2606 		if (!allConvs[i].state[0]._data3) {
2607 			allConvs[i].state[0]._data3 = 1;
2608 		}
2609 		allConvs[i].state[1]._data2 = 1;
2610 		allConvs[i].state[1]._data3 = 6;
2611 		allConvs[i].state[3]._data2 = 2;
2612 
2613 	}
2614 	int numConversations = READ_LE_UINT16(_conversationData + 1);
2615 	int16 *curConversation = _conversationData + 3;
2616 	for (int i = 0; i < numConversations; i++) {
2617 		Conversation *conv = &allConvs[ READ_LE_UINT16(curConversation)];
2618 		if (!conv->_enable) {
2619 
2620 			conv->_enable = 1;
2621 
2622 			int16 offset1 = READ_LE_UINT16(curConversation + 1);
2623 			void *convData1 = (char *)_conversationData + offset1;
2624 			conv->state[0]._data4 = convData1;
2625 
2626 			int16 offset2 = READ_LE_UINT16(curConversation + 2);
2627 			void *convData2 = (char *)_conversationData + offset2;
2628 			conv->state[1]._data4 = convData2;
2629 
2630 			int16 offset3 = READ_LE_UINT16(curConversation + 3);
2631 			void *convData3 = (char *)_conversationData + offset3;
2632 			conv->state[2]._data4 = convData3;
2633 
2634 			int16 offset4 = READ_LE_UINT16(curConversation + 4);
2635 			void *convData4 = (char *)_conversationData + offset4;
2636 			conv->state[3]._data4 = convData4;
2637 		}
2638 		curConversation += 5;
2639 	}
2640 }
2641 
processConversationClick(Conversation * conv,int32 status)2642 void ToonEngine::processConversationClick(Conversation *conv, int32 status) {
2643 	Conversation::ConvState *v2 = (Conversation::ConvState *)&conv->state[status];
2644 
2645 	int16 *i = (int16 *)((char *)v2->_data4 + 2);
2646 
2647 	_gameState->_firstConverstationLine = false;
2648 	while (READ_LE_INT16(i) >= 0) {
2649 		if (READ_LE_INT16(i) < 100) {
2650 			if (_gameState->_exitConversation == false) {
2651 				characterTalk(READ_LE_INT16(i + 1));
2652 			}
2653 		} else {
2654 			runConversationCommand(&i);
2655 		}
2656 		i += 2;
2657 	}
2658 
2659 	int16 command = READ_LE_INT16(i);
2660 	int16 value = READ_LE_INT16(i + 1);
2661 
2662 	if (command == -1) {
2663 		v2->_data2 = 0;
2664 	} else if (command == -2) {
2665 		v2->_data4 = (char *)_conversationData + value;
2666 		v2->_data3 = READ_LE_INT16(v2->_data4);
2667 	} else if (command == -3) {
2668 		v2->_data2 = 2;
2669 		v2->_data4 = (char *)_conversationData + value;
2670 		v2->_data3 = READ_LE_INT16(v2->_data4);
2671 	}
2672 
2673 	int16 *v7 = i + 2;
2674 	int16 v8 = READ_LE_INT16(v7);
2675 	if (v8 == -1) {
2676 		_gameState->_mouseHidden = false;
2677 	} else {
2678 		while (v8 != -1) {
2679 			v7 += 1;
2680 			int16 *v14 = (int16 *)((char *)_conversationData + v8);
2681 
2682 			// find free dialogue slot
2683 			for (int j = 0; j < 10; j++) {
2684 				if (!conv->state[j]._data2) {
2685 					conv->state[j]._data3 = READ_LE_INT16(v14);
2686 					conv->state[j]._data4 = v14;
2687 					if (getConversationFlag(_gameState->_currentScene, conv->state[j]._data3))
2688 						conv->state[j]._data2 = 1;
2689 					else
2690 						conv->state[j]._data2 = 3;
2691 
2692 					v8 = READ_LE_INT16(v7);
2693 					if (v8 == -1)
2694 						return;
2695 					else
2696 						break; // restarts while loop;
2697 				}
2698 			}
2699 		}
2700 	}
2701 }
2702 
2703 // hardcoded conversation flag to know if one dialogue icon must be displayed or not
2704 // based on game events...
getConversationFlag(int32 locationId,int32 param)2705 int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) {
2706 	if (locationId == 1) {
2707 		if (param == 0x34)
2708 			return _gameState->getGameFlag(93);
2709 
2710 		if (param != 55)
2711 			return 1;
2712 
2713 		if (!_gameState->getGameFlag(262))
2714 			return 1;
2715 
2716 		return 0;
2717 	} else if (locationId == 2) {
2718 		if (param == 36 && _gameState->getGameFlag(149))
2719 			return 0;
2720 		return 1;
2721 	} else if (locationId == 7) {
2722 		if (param == 30)
2723 			return _gameState->getGameFlag(132);
2724 		else
2725 			return 1;
2726 	} else if (locationId == 8) {
2727 		if (param == 0x20) {
2728 			if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
2729 				return 1;
2730 			return 0;
2731 		}
2732 		if (param == 33) {
2733 			if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
2734 				return 0;
2735 			return 1;
2736 		}
2737 	} else if (locationId == 0xb) {
2738 		if (param == 0x12) {
2739 			if (!_gameState->hasItemInInventory(71))
2740 				return 1;
2741 			else
2742 				return 0;
2743 		}
2744 		if (param == 74) {
2745 			if (_gameState->hasItemInInventory(71))
2746 				return 1;
2747 			else
2748 				return 0;
2749 		}
2750 		return 1;
2751 	} else if (locationId == 0xc) {
2752 		if (param == 0x3d && _gameState->getGameFlag(154)) {
2753 			return 0;
2754 		}
2755 		if (param == 76 && !_gameState->getGameFlag(79)) {
2756 			return 0;
2757 		}
2758 		if (param == 0x4e && !_gameState->hasItemInInventory(32)) {
2759 			return 0;
2760 		}
2761 		if (param == 0x4f && !_gameState->hasItemInInventory(92)) {
2762 			return 0;
2763 		}
2764 		if (param == 80 && !_gameState->hasItemInInventory(91)) {
2765 			return 0;
2766 		}
2767 		if (param == 0x4d && _gameState->getGameFlag(79)) {
2768 			return 0;
2769 		}
2770 	} else if (locationId == 0xd) {
2771 		if (param == 0x2f && _gameState->getGameFlag(81)) {
2772 			return 0;
2773 		}
2774 		if (param == 48 && _gameState->getGameFlag(81)) {
2775 			return 0;
2776 		}
2777 	} else if (locationId == 0x10) {
2778 		switch (param) {
2779 		case 0x3e8:
2780 			if (!(_gameState->_gameGlobalData[30] & 1))
2781 				return 0;
2782 			break;
2783 		case 0x3e9:
2784 			if (!(_gameState->_gameGlobalData[30] & 2))
2785 				return 0;
2786 			break;
2787 		case 0x3ea:
2788 			if (!(_gameState->_gameGlobalData[30] & 4))
2789 				return 0;
2790 			break;
2791 		case 0x3eb:
2792 			if (!(_gameState->_gameGlobalData[30] & 8))
2793 				return 0;
2794 			break;
2795 		case 0x3ec:
2796 			if (!(_gameState->_gameGlobalData[30] & 16))
2797 				return 0;
2798 			break;
2799 		case 0x3ed:
2800 			if (!(_gameState->_gameGlobalData[30] & 32))
2801 				return 0;
2802 			break;
2803 		case 0x3ee:
2804 			if (!(_gameState->_gameGlobalData[30] & 64))
2805 				return 0;
2806 			break;
2807 		default:
2808 			break;
2809 		};
2810 		return 1;
2811 	} else if (locationId == 0x12) {
2812 		if (param == 0x28 && _gameState->getGameFlag(91)) {
2813 			return 0;
2814 		}
2815 		if (param == 41 && (!_gameState->getGameFlag(96) || _gameState->getGameFlag(91))) {
2816 			return 0;
2817 		}
2818 	} else if (locationId == 0x13) {
2819 		if (param == 0x32 && _gameState->getGameFlag(107)) {
2820 			return 0;
2821 		}
2822 		if (param == 68 && !_gameState->getGameFlag(107)) {
2823 			return 0;
2824 		}
2825 	} else if (locationId == 0x14) {
2826 		if (param == 1000 && !_gameState->getGameFlag(82)) {
2827 			return 0;
2828 		}
2829 	} else if (locationId == 0x25) {
2830 		if (param == 7 && _gameState->_gameGlobalData[28] != 1) {
2831 			return 0;
2832 		}
2833 		if (param == 8 && _gameState->_gameGlobalData[28] != 1) {
2834 			return 0;
2835 		}
2836 		if (param == 9 && _gameState->_gameGlobalData[28] != 1) {
2837 			return 0;
2838 		}
2839 		if (param == 75 && _gameState->_gameGlobalData[28] != 2) {
2840 			return 0;
2841 		}
2842 	} else if (locationId == 72) {
2843 		if (param == 63 && _gameState->getGameFlag(105)) {
2844 			return 0;
2845 		}
2846 		if (param == 67 && !_gameState->getGameFlag(105)) {
2847 			return 0;
2848 		}
2849 		if (param == 0x40 && !_gameState->getGameFlag(105)) {
2850 			return 0;
2851 		}
2852 	}
2853 	return 1;
2854 }
2855 
runConversationCommand(int16 ** command)2856 int32 ToonEngine::runConversationCommand(int16 **command) {
2857 
2858 	int16 *v5 = *command;
2859 
2860 	int v2 = READ_LE_INT16(v5);
2861 	int v4 = READ_LE_INT16(v5 + 1);
2862 	int result = v2 - 100;
2863 	switch (v2) {
2864 	case 100:
2865 		result = runEventScript(_mouseX, _mouseY, 2, v4, 0);
2866 		break;
2867 	case 101:
2868 		_gameState->_exitConversation = true;
2869 		break;
2870 	case 102:
2871 		playSoundWrong();
2872 		break;
2873 	case 104:
2874 		*command = (int16 *)((char *)_conversationData + v4 - 4);
2875 		break;
2876 		//
2877 	case 105:
2878 		if (getConversationFlag(_gameState->_currentScene, v4)) {
2879 			result = READ_LE_INT16(*command + 2);
2880 			*command = (int16 *)((char *)_conversationData + result - 4);
2881 		} else {
2882 			int16 *newPtr = *command + 1;
2883 			*command = newPtr;
2884 		}
2885 		break;
2886 	case 103:
2887 	default:
2888 		break;
2889 	}
2890 	return result;
2891 }
2892 
waitTicks(int32 numTicks,bool breakOnMouseClick)2893 int32 ToonEngine::waitTicks(int32 numTicks, bool breakOnMouseClick) {
2894 	uint32 nextTime = _system->getMillis() + numTicks * _tickLength;
2895 	while (_system->getMillis() < nextTime || numTicks == -1) {
2896 		//if (!_animationSceneScriptRunFlag)
2897 		//	break;
2898 		updateAnimationSceneScripts(0);
2899 		getMouseEvent();
2900 		simpleUpdate();
2901 
2902 		if (breakOnMouseClick && (_mouseButton & 0x2))
2903 			break;
2904 	}
2905 	return 0;
2906 }
2907 
renderInventory()2908 void ToonEngine::renderInventory() {
2909 	if (!_gameState->_inInventory)
2910 		return;
2911 
2912 	if (!_dirtyAll) {
2913 		_inventoryPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
2914 	} else {
2915 		_inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0);
2916 		_dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT));
2917 	}
2918 	clearDirtyRects();
2919 
2920 	// draw items on screen
2921 	for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
2922 		int32 x = 57 * (i % 7) + 114;
2923 		int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
2924 		_inventoryIconSlots->drawFrame(*_mainSurface, i % 12, x + _gameState->_currentScrollValue, y);
2925 		if (_gameState->_inventory[i])
2926 			_inventoryIcons->drawFrame(*_mainSurface, _gameState->_inventory[i], x + _gameState->_currentScrollValue + 2, y + 2);
2927 	}
2928 
2929 	drawConversationLine();
2930 	if (!_audioManager->voiceStillPlaying()) {
2931 		_currentTextLineCharacterId = -1;
2932 		_currentTextLine = 0;
2933 		_currentTextLineId = -1;
2934 	}
2935 
2936 	if (_firstFrame) {
2937 		copyToVirtualScreen(false);
2938 		_firstFrame = false;
2939 		fadeIn(5);
2940 	}
2941 	copyToVirtualScreen();
2942 }
2943 
showInventory()2944 int32 ToonEngine::showInventory() {
2945 	int32 oldScrollValue = _gameState->_currentScrollValue;
2946 
2947 	delete _inventoryPicture;
2948 	_inventoryPicture = new Picture(this);
2949 	fadeOut(5);
2950 	_inventoryPicture->loadPicture("SACK128.CPS");
2951 	_inventoryPicture->setupPalette();
2952 	dirtyAllScreen();
2953 
2954 	if (_gameState->_mouseState >= 0) {
2955 		setCursor(_gameState->_mouseState, true, -18, -14);
2956 
2957 		// make sure we have a free spot
2958 		if (!_gameState->hasItemInInventory(0)) {
2959 			_gameState->_inventory[_gameState->_numInventoryItems] = 0;
2960 			_gameState->_numInventoryItems++;
2961 		}
2962 	} else {
2963 		setCursor(0);
2964 	}
2965 
2966 	_gameState->_inInventory = true;
2967 	_gameState->_currentScrollValue = 0;
2968 
2969 	int32 oldMouseButton = 0x3;
2970 	int32 justPressedButton = 0;
2971 	_firstFrame = true;
2972 
2973 	while (!_shouldQuit) {
2974 		getMouseEvent();
2975 
2976 		justPressedButton = _mouseButton & ~oldMouseButton;
2977 		oldMouseButton = _mouseButton;
2978 
2979 		if (justPressedButton & 0x3) {
2980 			// find out what object we're on
2981 			int32 foundObj = -1;
2982 			for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
2983 				int32 x = 57 * (i % 7) + 114;
2984 				int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
2985 				if (_mouseX >= (_gameState->_currentScrollValue + x - 6) &&
2986 						_mouseX <= (_gameState->_currentScrollValue + x + 44 + 7) &&
2987 						_mouseY >= y - 6 && _mouseY <= y + 50) {
2988 					foundObj = i;
2989 					break;
2990 				}
2991 			}
2992 
2993 			if (justPressedButton & 0x1) {
2994 				if (_gameState->_mouseState < 0) {
2995 					if (foundObj >= 0) {
2996 						// take an object
2997 						int32 item = _gameState->_inventory[foundObj];
2998 
2999 						int32 modItem = getSpecialInventoryItem(item);
3000 						if (modItem) {
3001 							if (modItem == -1) {
3002 								_gameState->_mouseState = item;
3003 								_gameState->_inventory[foundObj] = 0;
3004 							} else {
3005 								_gameState->_mouseState = modItem;
3006 								if (!_gameState->hasItemInInventory(0)) {
3007 									_gameState->_inventory[_gameState->_numInventoryItems] = 0;
3008 									_gameState->_numInventoryItems++;
3009 								}
3010 							}
3011 
3012 							setCursor(_gameState->_mouseState, true, -18, -14);
3013 						}
3014 
3015 					} else {
3016 						break;
3017 					}
3018 				} else {
3019 					if (foundObj >= 0 && _gameState->_inventory[foundObj] == 0) { // empty place
3020 						_gameState->_inventory[foundObj] = _gameState->_mouseState;
3021 						setCursor(0, false);
3022 						_gameState->_mouseState = -1;
3023 					} else if (foundObj >= 0 && _gameState->_inventory[foundObj] > 0) {
3024 						if (!handleInventoryOnInventory(_gameState->_mouseState, _gameState->_inventory[foundObj]))
3025 							playSoundWrong();
3026 					} else {
3027 						// quit the inventory mode with the icon
3028 						break;
3029 					}
3030 				}
3031 
3032 			} else if (justPressedButton & 0x2) { // right button
3033 				if (foundObj >= 0) {
3034 					// talk about the object
3035 					if (!handleInventoryOnInventory(_gameState->_inventory[foundObj], -1))
3036 						characterTalk(1000 + _gameState->_inventory[foundObj]);
3037 				} else {
3038 					// go out
3039 					break;
3040 				}
3041 			}
3042 		}
3043 
3044 		renderInventory();
3045 		_system->delayMillis(10);
3046 	}
3047 
3048 	_gameState->_currentScrollValue = oldScrollValue;
3049 	_gameState->_inInventory = false;
3050 	_mouseButton = 0;
3051 	_lastMouseButton = 0x3;
3052 
3053 	fadeOut(5);
3054 	if (_gameState->_inCloseUp) {
3055 		_gameState->_inCloseUp = false;
3056 		flipScreens();
3057 	} else if (_gameState->_inCutaway) {
3058 		_currentCutaway->setupPalette();
3059 		setupGeneralPalette();
3060 	} else {
3061 		_currentPicture->setupPalette();
3062 		setupGeneralPalette();
3063 	}
3064 	flushPalette();
3065 	dirtyAllScreen();
3066 	_firstFrame = true;
3067 
3068 	return 0;
3069 }
3070 
getMouseEvent()3071 void ToonEngine::getMouseEvent() {
3072 	Common::EventManager *_event = _system->getEventManager();
3073 
3074 	Common::Event event;
3075 	while (_event->pollEvent(event) && !_shouldQuit)
3076 		;
3077 
3078 	_mouseX = _event->getMousePos().x;
3079 	_mouseY = _event->getMousePos().y;
3080 	_mouseButton = _event->getButtonState();
3081 }
3082 
drawSack()3083 void ToonEngine::drawSack() {
3084 	if (_gameState->_sackVisible) {
3085 		_inventoryIcons->drawFrame(*_mainSurface, 0, _gameState->_currentScrollValue, 356);
3086 	}
3087 }
3088 
addItemToInventory(int32 item)3089 void ToonEngine::addItemToInventory(int32 item) {
3090 
3091 	if (item == 103 || item == 104 || item == 89 || item == 82) {
3092 		// can't add that to inventory
3093 		_gameState->_mouseState = -1;
3094 		return;
3095 	}
3096 
3097 	if (item == 41) {
3098 		// confiscated inventory
3099 		for (int32 i = 0; i < _gameState->_numConfiscatedInventoryItems; i++)
3100 			addItemToInventory(_gameState->_confiscatedInventory[i]);
3101 
3102 		_gameState->_numConfiscatedInventoryItems = 0;
3103 		_gameState->_mouseState = -1;
3104 		return;
3105 	}
3106 
3107 	for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
3108 		if (_gameState->_inventory[i] == 0) {
3109 			_gameState->_inventory[i] = item;
3110 			_gameState->_mouseState = -1;
3111 			return;
3112 		}
3113 	}
3114 	_gameState->_inventory[_gameState->_numInventoryItems] = item;
3115 	_gameState->_numInventoryItems++;
3116 	_gameState->_mouseState = -1;
3117 }
3118 
createMouseItem(int32 item)3119 void ToonEngine::createMouseItem(int32 item) {
3120 	_gameState->_mouseState = item;
3121 	setCursor(_gameState->_mouseState, true, -18, -14);
3122 }
3123 
deleteMouseItem()3124 void ToonEngine::deleteMouseItem() {
3125 	_gameState->_mouseState = -1;
3126 	rearrangeInventory();
3127 	setCursor(0);
3128 }
3129 
showCutaway(const Common::String & cutawayPicture)3130 void ToonEngine::showCutaway(const Common::String &cutawayPicture) {
3131 	_gameState->_inCutaway = true;
3132 	_currentCutaway = new Picture(this);
3133 	if (cutawayPicture.empty()) {
3134 		Common::String name = _gameState->_locations[_gameState->_currentScene]._cutaway;
3135 		_currentCutaway->loadPicture(name + ".CPS");
3136 	} else {
3137 		_currentCutaway->loadPicture(cutawayPicture);
3138 	}
3139 	_currentCutaway->setupPalette();
3140 	_oldScrollValue = _gameState->_currentScrollValue;
3141 	_gameState->_currentScrollValue = 0;
3142 	dirtyAllScreen();
3143 	flushPalette();
3144 }
3145 
hideCutaway()3146 void ToonEngine::hideCutaway() {
3147 	_gameState->_inCutaway = false;
3148 	_gameState->_sackVisible = true;
3149 	delete _currentCutaway;
3150 	_gameState->_currentScrollValue = _oldScrollValue;
3151 	_currentCutaway = 0;
3152 	_currentPicture->setupPalette();
3153 	dirtyAllScreen();
3154 	flushPalette();
3155 }
3156 
updateCharacters(int32 timeElapsed)3157 void ToonEngine::updateCharacters(int32 timeElapsed) {
3158 	for (int32 i = 0; i < 8; i++) {
3159 		if (_characters[i]) {
3160 			_characters[i]->update(timeElapsed);
3161 		}
3162 	}
3163 }
3164 
drawPalette()3165 void ToonEngine::drawPalette() {
3166 	for (int32 i = 0; i < 256; i++) {
3167 		int32 x = i % 32;
3168 		int32 y = i / 32;
3169 		_mainSurface->fillRect(Common::Rect(x * 16, y * 16, x * 16 + 16, y * 16 + 16), i);
3170 	}
3171 }
3172 
rearrangeInventory()3173 void ToonEngine::rearrangeInventory() {
3174 	for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
3175 		if (_gameState->_inventory[i] == 0) {
3176 			// move all the following items from one
3177 			for (int32 j = i + 1; j < _gameState->_numInventoryItems; j++) {
3178 				_gameState->_inventory[j - 1] = _gameState->_inventory[j];
3179 			}
3180 			_gameState->_numInventoryItems--;
3181 		}
3182 	}
3183 }
3184 
newGame()3185 void ToonEngine::newGame() {
3186 
3187 	if (_isDemo) {
3188 		addItemToInventory(59);
3189 		addItemToInventory(67);
3190 		addItemToInventory(11);
3191 		addItemToInventory(19);
3192 		loadScene(22);
3193 		//loadScene(_gameState->_currentScene);
3194 	} else {
3195 		//loadScene(4);
3196 		loadScene(_gameState->_currentScene);
3197 	}
3198 }
3199 
playSFX(int32 id,int32 volume)3200 void ToonEngine::playSFX(int32 id, int32 volume) {
3201 	if (id < 0)
3202 		_audioManager->playSFX(-id + 1, volume, true);
3203 	else
3204 		_audioManager->playSFX(id , volume, false);
3205 }
3206 
playSoundWrong()3207 void ToonEngine::playSoundWrong() {
3208 	_audioManager->playSFX(randRange(0,7), 128, true);
3209 }
3210 
getTextPosition(int32 characterId,int32 * retX,int32 * retY)3211 void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) {
3212 	if (characterId < 0)
3213 		characterId = 0;
3214 
3215 	// default position is the center of current screen
3216 	*retX = _gameState->_currentScrollValue + 320;
3217 	*retY = 70;
3218 
3219 	// hardcoded special cases...
3220 	if (characterId == 0) {
3221 		// drew
3222 		int32 x = _drew->getX();
3223 		int32 y = _drew->getY();
3224 		if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
3225 			if (!_gameState->_inCutaway && !_gameState->_inInventory) {
3226 				*retX = x;
3227 				*retY = y - ((_drew->getScale() * 256 / 1024) >> 1) - 45;
3228 			}
3229 		}
3230 	} else if (characterId == 1) {
3231 		// flux
3232 		int16 x = _flux->getX();
3233 		int16 y = _flux->getY();
3234 		if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
3235 			if (!_gameState->_inCutaway) {
3236 				*retX = x;
3237 				*retY = y - ((_drew->getScale() * 100 / 1024) >> 1) - 30;
3238 			}
3239 		}
3240 	} else if (characterId == 5 || characterId == 39) {
3241 		*retX = 80;
3242 		*retY = 120;
3243 	} else if (characterId == 14) {
3244 		*retX = 257;
3245 		*retY = 132;
3246 	} else if (characterId == 18) {
3247 		*retX = 80;
3248 		*retY = 180;
3249 	} else if (characterId == 21) {
3250 		*retX = 363;
3251 		*retY = 193;
3252 	} else if (characterId == 23) {
3253 		*retX = 532;
3254 		*retY = 178;
3255 	} else if (characterId == 33) {
3256 		*retX = 167;
3257 		*retY = 172;
3258 	} else {
3259 
3260 		// more "standard" code by character
3261 		Character *character = getCharacterById(characterId);
3262 		if (character && !_gameState->_inCutaway) {
3263 			if (character->getAnimationInstance()) {
3264 				if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
3265 					int16 x1= 0, y1 = 0, x2 = 0, y2 = 0;
3266 					character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
3267 					*retX = (x1 + x2) / 2;
3268 					*retY = y1;
3269 				}
3270 			}
3271 		}
3272 	}
3273 }
3274 
getCharacterById(int32 charId)3275 Character *ToonEngine::getCharacterById(int32 charId) {
3276 	for (int32 i = 0; i < 8; i++) {
3277 		if (_characters[i] && _characters[i]->getId() == charId)
3278 			return _characters[i];
3279 	}
3280 	return 0;
3281 }
3282 
drawConversationLine()3283 void ToonEngine::drawConversationLine() {
3284 	if (_currentTextLine && _showConversationText) {
3285 		_fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
3286 		_fontRenderer->setFont(_currentFont);
3287 		_fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, _currentTextLine, 0, *_mainSurface);
3288 	}
3289 }
3290 
drawCustomText(int16 x,int16 y,const char * line,Graphics::Surface * frame,byte color)3291 void ToonEngine::drawCustomText(int16 x, int16 y, const char *line, Graphics::Surface *frame, byte color) {
3292 	if (line) {
3293 		byte col = color; // 0xce
3294 		_fontRenderer->setFontColor(0, col, col);
3295 		//_fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
3296 		_gameState->_currentScrollValue = 0;
3297 		_fontRenderer->setFont(_currentFont);
3298 		_fontRenderer->renderMultiLineText(x, y, line, 0, *frame);
3299 	}
3300 }
3301 
pauseEngineIntern(bool pause)3302 void ToonEngine::pauseEngineIntern(bool pause) {
3303 
3304 	Engine::pauseEngineIntern(pause);
3305 
3306 	static int32 pauseStart = 0;
3307 	if (pause) {
3308 		pauseStart = _system->getMillis();
3309 
3310 	} else {
3311 		_oldTimer = _system->getMillis();
3312 		_oldTimer2 = _oldTimer;
3313 
3314 		int32 diff = _oldTimer - pauseStart;
3315 
3316 		// we have to add the difference between the start and the current time
3317 		// to all "timer based" values.
3318 		for (int32 i = 0; i < _gameState->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
3319 			_sceneAnimationScripts[i]._lastTimer += diff;
3320 		}
3321 		for (int32 i = 0; i < 8; i++) {
3322 			if (_characters[i]) {
3323 				_characters[i]->updateTimers(diff);
3324 			}
3325 		}
3326 
3327 		_gameState->_timerTimeout[0] += diff;
3328 		_gameState->_timerTimeout[1] += diff;
3329 	}
3330 }
3331 
canSaveGameStateCurrently()3332 bool ToonEngine::canSaveGameStateCurrently() {
3333 	return !_gameState->_inMenu && !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
3334 }
3335 
canLoadGameStateCurrently()3336 bool ToonEngine::canLoadGameStateCurrently() {
3337 	return !_gameState->_inMenu && !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
3338 }
3339 
getSavegameName(int nr)3340 Common::String ToonEngine::getSavegameName(int nr) {
3341 	return _targetName + Common::String::format(".%03d", nr);
3342 }
3343 
saveGame(int32 slot,const Common::String & saveGameDesc)3344 bool ToonEngine::saveGame(int32 slot, const Common::String &saveGameDesc) {
3345 	int16 savegameId;
3346 	Common::String savegameDescription;
3347 
3348 	if (slot == -1) {
3349 		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
3350 		savegameId = dialog->runModalWithCurrentTarget();
3351 		savegameDescription = dialog->getResultString();
3352 		delete dialog;
3353 	} else {
3354 		savegameId = slot;
3355 		if (!saveGameDesc.empty()) {
3356 			savegameDescription = saveGameDesc;
3357 		} else {
3358 			savegameDescription = Common::String::format("Quick save #%d", (int)slot);
3359 		}
3360 	}
3361 
3362 	if (savegameId < 0)
3363 		return false; // dialog aborted
3364 
3365 	Common::String savegameFile = getSavegameName(savegameId);
3366 	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(savegameFile);
3367 	if (!saveFile)
3368 		return false;
3369 
3370 	// save savegame header
3371 	saveFile->writeSint32BE(TOON_SAVEGAME_VERSION);
3372 
3373 	if (savegameDescription == "") {
3374 		savegameDescription = "Untitled saved game";
3375 	}
3376 
3377 	saveFile->writeSint16BE(savegameDescription.size() + 1);
3378 	saveFile->write(savegameDescription.c_str(), savegameDescription.size() + 1);
3379 
3380 	Graphics::saveThumbnail(*saveFile);
3381 
3382 	TimeDate curTime;
3383 	_system->getTimeAndDate(curTime);
3384 
3385 	uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF);
3386 	uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF);
3387 
3388 	saveFile->writeUint32BE(saveDate);
3389 	saveFile->writeUint16BE(saveTime);
3390 	uint32 playTime = getTotalPlayTime();
3391 	saveFile->writeUint32BE(playTime);
3392 
3393 	// save global state
3394 	_gameState->save(saveFile);
3395 	_gameState->saveConversations(saveFile);
3396 	_hotspots->save(saveFile);
3397 
3398 	// save current time to be able to patch the time when loading
3399 	saveFile->writeSint32BE(getOldMilli());
3400 
3401 	// save script states
3402 	for (int32 i = 0; i < 4; i++) {
3403 		_script->saveState(&_scriptState[i], saveFile);
3404 	}
3405 
3406 	// save animation script states
3407 	for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
3408 		saveFile->writeByte(_sceneAnimationScripts[i]._active);
3409 		saveFile->writeByte(_sceneAnimationScripts[i]._frozen);
3410 		saveFile->writeSint32BE(_sceneAnimationScripts[i]._lastTimer);
3411 		_script->saveState(&_sceneAnimationScripts[i]._state, saveFile);
3412 	}
3413 
3414 	// save scene animations
3415 	for (int32 i = 0; i < 64; i++) {
3416 		_sceneAnimations[i].save(this, saveFile);
3417 	}
3418 
3419 	for (int32 i = 0; i < 8; i++) {
3420 		if (_characters[i]) {
3421 			saveFile->writeSByte(i);
3422 			_characters[i]->save(saveFile);
3423 		}
3424 	}
3425 	saveFile->writeSByte(-1);
3426 
3427 	// save "command buffer"
3428 	saveFile->writeSint16BE(_saveBufferStream->pos());
3429 	if (_saveBufferStream->pos() > 0) {
3430 		saveFile->write(_saveBufferStream->getData(), _saveBufferStream->pos());
3431 		saveFile->writeSint16BE(0);
3432 	}
3433 
3434 	delete saveFile;
3435 
3436 	return true;
3437 }
3438 
loadGame(int32 slot)3439 bool ToonEngine::loadGame(int32 slot) {
3440 	int16 savegameId;
3441 
3442 	if (slot == -1) {
3443 		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
3444 		savegameId = dialog->runModalWithCurrentTarget();
3445 		delete dialog;
3446 	} else {
3447 		savegameId = slot;
3448 	}
3449 	if (savegameId < 0)
3450 		return false; // dialog aborted
3451 
3452 	Common::String savegameFile = getSavegameName(savegameId);
3453 	Common::InSaveFile *loadFile = _saveFileMan->openForLoading(savegameFile);
3454 	if (!loadFile)
3455 		return false;
3456 
3457 	int32 saveGameVersion = loadFile->readSint32BE();
3458 	if ( (saveGameVersion < 4) || (saveGameVersion > TOON_SAVEGAME_VERSION) ) {
3459 		delete loadFile;
3460 		return false;
3461 	}
3462 	int32 saveGameNameSize = loadFile->readSint16BE();
3463 	loadFile->skip(saveGameNameSize);
3464 
3465 	// We don't need the thumbnail here, so just read it and discard it
3466 	Graphics::skipThumbnail(*loadFile);
3467 
3468 	loadFile->skip(6); // date & time skip
3469 
3470 	uint32 playTimeMsec = 0;
3471 	if (saveGameVersion >= 5) {
3472 		playTimeMsec = loadFile->readUint32BE();
3473 	}
3474 	setTotalPlayTime(playTimeMsec);
3475 
3476 	if (_gameState->_currentScene != -1) {
3477 		exitScene();
3478 	}
3479 
3480 	_gameState->load(loadFile);
3481 	loadScene(_gameState->_currentScene, true);
3482 	_gameState->loadConversations(loadFile);
3483 	_hotspots->load(loadFile);
3484 
3485 	// read the old time
3486 	int32 savedTime = loadFile->readSint32BE();
3487 	int32 timerDiff = _system->getMillis() - savedTime;
3488 
3489 	// load script states
3490 	for (int32 i = 0; i < 4; i++) {
3491 		_script->loadState(&_scriptState[i], loadFile);
3492 	}
3493 
3494 	// load animation script states
3495 	for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
3496 		_sceneAnimationScripts[i]._active = loadFile->readByte();
3497 		_sceneAnimationScripts[i]._frozen = loadFile->readByte();
3498 		_sceneAnimationScripts[i]._frozenForConversation = false;
3499 		int32 oldTimer = loadFile->readSint32BE();
3500 		_sceneAnimationScripts[i]._lastTimer = MAX<int32>(0, oldTimer + timerDiff);
3501 		_script->loadState(&_sceneAnimationScripts[i]._state, loadFile);
3502 	}
3503 
3504 	// load scene animations
3505 	for (int32 i = 0; i < 64; i++) {
3506 		_sceneAnimations[i].load(this, loadFile);
3507 	}
3508 
3509 	// scene animations have to be added in reverse order in animation manager to preserve the z order
3510 	for (int32 i = 63; i >= 0; i--) {
3511 		if (_sceneAnimations[i]._active && _sceneAnimations[i]._animInstance) {
3512 			_animationManager->addInstance(_sceneAnimations[i]._animInstance);
3513 		}
3514 	}
3515 
3516 	_gameState->_timerTimeout[0] += timerDiff;
3517 	_gameState->_timerTimeout[1] += timerDiff;
3518 
3519 	_gameState->_conversationData = _conversationData;
3520 	_firstFrame = true;
3521 
3522 	// read characters info
3523 	while (1) {
3524 		int8 c = loadFile->readSByte();
3525 		if (c < 0)
3526 			break;
3527 
3528 		if (!_characters[c]) {
3529 			_characters[c] = new Character(this);
3530 		}
3531 		_characters[c]->load(loadFile);
3532 		//_characters[c]->setVisible(true);
3533 		_characters[c]->update(0);
3534 	}
3535 
3536 	// load "command buffer"
3537 	int32 size = loadFile->readSint16BE();
3538 	if (size) {
3539 		uint8 *buf = new uint8[size + 2];
3540 		loadFile->read(buf, size + 2);
3541 
3542 		Common::MemoryReadStream rStr(buf, size + 2);
3543 		while (1) {
3544 			int16 command = rStr.readSint16BE();
3545 			if (!command) break;
3546 			switch (command) {
3547 			case 1: {
3548 				int16 frame = rStr.readSint16BE();
3549 				int16 animLen = rStr.readSint16BE();
3550 				char animName[32];
3551 				rStr.read(animName, animLen);
3552 				int16 x = rStr.readSint16BE();
3553 				int16 y = rStr.readSint16BE();
3554 //				int16 z = rStr.readSint16BE();
3555 //				int16 layerZ = rStr.readSint16BE();
3556 				rStr.readSint16BE();
3557 				rStr.readSint16BE();
3558 
3559 				Animation *anim = new Animation(this);
3560 				anim->loadAnimation(animName);
3561 				anim->drawFrameOnPicture(frame, x, y);
3562 				delete anim;
3563 				break;
3564 			}
3565 			case 2: {
3566 				int16 x =  rStr.readSint16BE();
3567 				int16 y = rStr.readSint16BE();
3568 				int16 x1 = rStr.readSint16BE();
3569 				int16 y1 = rStr.readSint16BE();
3570 				makeLineNonWalkable(x, y, x1, y1);
3571 				break;
3572 			}
3573 			case 3: {
3574 				int16 x =  rStr.readSint16BE();
3575 				int16 y = rStr.readSint16BE();
3576 				int16 x1 = rStr.readSint16BE();
3577 				int16 y1 = rStr.readSint16BE();
3578 				makeLineWalkable(x, y, x1, y1);
3579 				break;
3580 			}
3581 			case 4: {
3582 				int16 x = rStr.readSint16BE();
3583 				int16 y = rStr.readSint16BE();
3584 				getMask()->floodFillNotWalkableOnMask(x, y);
3585 				break;
3586 			}
3587 			default:
3588 				break;
3589 			}
3590 		}
3591 		_saveBufferStream->write(buf, size);
3592 		delete[] buf;
3593 	}
3594 	delete loadFile;
3595 
3596 	// setup correct palette if we are in a closeup/cutaway or not.
3597 	if (_gameState->_inCloseUp) {
3598 		_gameState->_inCloseUp = false;
3599 		flipScreens();
3600 	} else if (_gameState->_inCutaway) {
3601 		_currentCutaway->setupPalette();
3602 		setupGeneralPalette();
3603 	} else {
3604 		_currentPicture->setupPalette();
3605 		setupGeneralPalette();
3606 	}
3607 	flushPalette();
3608 
3609 	return true;
3610 }
3611 
3612 // another special case for inventory
getSpecialInventoryItem(int32 item)3613 int32 ToonEngine::getSpecialInventoryItem(int32 item) {
3614 	// butter
3615 	if (item == 12) {
3616 		for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
3617 			if (_gameState->_inventory[i] == 12)
3618 				_gameState->_inventory[i] = 11;
3619 		}
3620 		return 11;
3621 
3622 	} else if (item == 84) {
3623 		if (_gameState->getGameFlag(26)) {
3624 			characterTalk(1726);
3625 			return 0;
3626 		} else {
3627 			if (!_gameState->hasItemInInventory(102) && !_gameState->hasItemInInventory(90) && !_gameState->hasItemInInventory(89)) {
3628 				characterTalk(1416);
3629 				return 102;
3630 			} else {
3631 				return 0;
3632 			}
3633 		}
3634 	}
3635 
3636 	return -1;
3637 }
3638 
initCharacter(int32 characterId,int32 animScriptId,int32 sceneAnimationId,int32 animToPlayId)3639 void ToonEngine::initCharacter(int32 characterId, int32 animScriptId, int32 sceneAnimationId, int32 animToPlayId) {
3640 	// find a new index
3641 	int32 characterIndex = -1;
3642 	for (int32 i = 0; i < 8; i++) {
3643 		if (_characters[i] && _characters[i]->getId() == characterId) {
3644 			characterIndex = i;
3645 			break;
3646 		}
3647 
3648 		if (!_characters[i]) {
3649 			characterIndex = i;
3650 			break;
3651 		}
3652 	}
3653 
3654 	if (characterIndex == -1) {
3655 		return;
3656 	}
3657 
3658 	_characters[characterIndex] = new Character(this);
3659 	_characters[characterIndex]->setId(characterId);
3660 	_characters[characterIndex]->setAnimScript(animScriptId);
3661 	_characters[characterIndex]->setDefaultSpecialAnimationId(animToPlayId);
3662 	_characters[characterIndex]->setSceneAnimationId(sceneAnimationId);
3663 	_characters[characterIndex]->setFlag(0);
3664 	_characters[characterIndex]->setVisible(true);
3665 	if (sceneAnimationId != -1)
3666 		_characters[characterIndex]->setAnimationInstance(_sceneAnimations[sceneAnimationId]._animInstance);
3667 }
3668 
handleInventoryOnFlux(int32 itemId)3669 int32 ToonEngine::handleInventoryOnFlux(int32 itemId) {
3670 
3671 	switch (itemId) {
3672 	case 8:
3673 		sayLines(1, 1332);
3674 		break;
3675 	case 0x14:
3676 	case 0x15:
3677 	case 0x45:
3678 		sayLines(1, 1304);
3679 		break;
3680 	case 0x68:
3681 		_gameState->_mouseState = 0;
3682 		setCursor(0, false, 0, 0);
3683 		break;
3684 	case 116:
3685 		sayLines(1, 1306);
3686 		break;
3687 	default:
3688 		return false;
3689 	}
3690 	return true;
3691 }
3692 
storePalette()3693 void ToonEngine::storePalette() {
3694 	memcpy(_backupPalette, _finalPalette, 768);
3695 }
3696 
restorePalette()3697 void ToonEngine::restorePalette() {
3698 	memcpy(_finalPalette, _backupPalette, 768);
3699 	flushPalette();
3700 }
3701 
getSpecialConversationMusic(int32 conversationId)3702 const char *ToonEngine::getSpecialConversationMusic(int32 conversationId) {
3703 	static const char * const specialMusic[] = {
3704 		0, 0,
3705 		"BR091013", "BR091013",
3706 		"NET1214", "NET1214",
3707 		0, 0,
3708 		"CAR1365B", "CAR1365B",
3709 		0, 0,
3710 		0, 0,
3711 		"CAR14431", "CAR14431",
3712 		0, 0,
3713 		0, 0,
3714 		"SCD16520", "SCD16520",
3715 		"SCD16520", "SCD16520",
3716 		"SCD16522", "SCD16522",
3717 		0, 0,
3718 		"KPM8719", "KPM8719",
3719 		0, 0,
3720 		"CAR1368B", "CAR1368B",
3721 		0, 0,
3722 		0, 0,
3723 		"KPM6337", "KPM6337",
3724 		"CAR20471", "CAR20471",
3725 		"CAR136_1", "KPM87_57",
3726 		0, 0,
3727 		"CAR13648", "CAR13648",
3728 		0, 0,
3729 		0, 0,
3730 		0, 0,
3731 		0, 0,
3732 		"SCD16526", "SCD16526",
3733 		0, 0,
3734 		0, 0,
3735 		0, 0,
3736 		0, 0,
3737 		0, 0,
3738 		0, 0,
3739 		0, 0,
3740 		0, 0,
3741 		0, 0,
3742 		0, 0,
3743 		0, 0,
3744 		0, 0,
3745 		0, 0,
3746 		0, 0,
3747 		0, 0,
3748 		0, 0,
3749 		0, 0,
3750 		0, 0,
3751 		0, 0,
3752 		0, 0,
3753 		0, 0,
3754 		0, 0,
3755 		0, 0,
3756 		0, 0,
3757 		0, 0,
3758 		0, 0,
3759 		0, 0,
3760 		0, 0,
3761 		0, 0,
3762 		0, 0,
3763 		0, 0,
3764 		0, 0,
3765 		0, 0,
3766 		0, 0,
3767 		0, 0,
3768 		0, 0
3769 	};
3770 
3771 	return specialMusic[randRange(0, 1) + conversationId * 2];
3772 }
3773 
viewInventoryItem(const Common::String & str,int32 lineId,int32 itemDest)3774 void ToonEngine::viewInventoryItem(const Common::String &str, int32 lineId, int32 itemDest) {
3775 	storePalette();
3776 	fadeOut(5);
3777 
3778 	Picture *pic = new Picture(this);
3779 	pic->loadPicture(str);
3780 	pic->setupPalette();
3781 	dirtyAllScreen();
3782 	flushPalette();
3783 
3784 	if (lineId) {
3785 		characterTalk(lineId, false);
3786 	}
3787 
3788 	uint32 oldMouseButton = _mouseButton;
3789 	uint32 justPressedButton = 0;
3790 	_firstFrame = true;
3791 
3792 	int32 oldScrollValue = _gameState->_currentScrollValue;
3793 	_gameState->_currentScrollValue = 0;
3794 
3795 	while (!_shouldQuit) {
3796 		getMouseEvent();
3797 
3798 		justPressedButton = _mouseButton & ~oldMouseButton;
3799 		oldMouseButton = _mouseButton;
3800 
3801 		if (justPressedButton) {
3802 			break;
3803 		}
3804 
3805 		if (!_dirtyAll) {
3806 			pic->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
3807 		} else {
3808 			pic->draw(*_mainSurface, 0, 0, 0, 0);
3809 			_dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT));
3810 		}
3811 		clearDirtyRects();
3812 
3813 		drawConversationLine();
3814 		if (!_audioManager->voiceStillPlaying()) {
3815 			_currentTextLineCharacterId = -1;
3816 			_currentTextLine = 0;
3817 			_currentTextLineId = -1;
3818 		}
3819 
3820 		if (_firstFrame) {
3821 			copyToVirtualScreen(false);
3822 			_firstFrame = false;
3823 			fadeIn(5);
3824 		}
3825 
3826 		copyToVirtualScreen();
3827 	}
3828 
3829 	fadeOut(5);
3830 	dirtyAllScreen();
3831 	restorePalette();
3832 	_firstFrame = true;
3833 	_gameState->_currentScrollValue = oldScrollValue;
3834 	delete pic;
3835 }
3836 
handleInventoryOnInventory(int32 itemDest,int32 itemSrc)3837 int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) {
3838 	switch (itemDest) {
3839 	case 0:
3840 		return handleInventoryOnDrew(itemSrc);
3841 	case 1:
3842 		if (itemSrc == 71) {
3843 			sayLines(2, 1212);
3844 			return 1;
3845 		}
3846 		break;
3847 	case 5:
3848 		if (itemSrc == 15) {
3849 			characterTalk(1492);
3850 		} else if (itemSrc == 0x2f) {
3851 			characterTalk(1488);
3852 		} else if (itemSrc == 88) {
3853 			sayLines(2, 1478);
3854 		} else {
3855 			return 0;
3856 		}
3857 		break;
3858 	case 6:
3859 		if (itemSrc == -1) {
3860 			viewInventoryItem("BLUEPRNT.CPS", 1006, itemDest);
3861 			return 1;
3862 		} else
3863 			return 0;
3864 		break;
3865 	case 8:
3866 		if (itemSrc == -1) {
3867 			viewInventoryItem("BOOK.CPS", 0, itemDest);
3868 			return 1;
3869 		} else {
3870 			return 0;
3871 		}
3872 		break;
3873 	case 11:
3874 		if (itemSrc == 0xb) {
3875 			_gameState->_mouseState = -1;
3876 			replaceItemFromInventory(11, 12);
3877 			setCursor(0, false, 0, 0);
3878 			rearrangeInventory();
3879 			return 1;
3880 			//
3881 		} else if (itemSrc == 24) {
3882 			characterTalk(1244);
3883 			return 1;
3884 		} else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
3885 			sayLines(2, 1212);
3886 			return 1;
3887 		}
3888 		break;
3889 	case 12:
3890 		if (itemSrc == 24) {
3891 			characterTalk(1244);
3892 			return 1;
3893 		} else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
3894 			sayLines(2, 1212);
3895 			return 1;
3896 		}
3897 		break;
3898 	case 13:
3899 		if (itemSrc == 0x35 || itemSrc == 0x36) {
3900 			characterTalk(1204);
3901 			return 1;
3902 		} else if (itemSrc >= 0x6b && itemSrc <= 0x72) {
3903 			characterTalk(1312);
3904 			return 1;
3905 		}
3906 		break;
3907 	case 14:
3908 		if (itemSrc == -1) {
3909 			deleteItemFromInventory(14);
3910 			addItemToInventory(15);
3911 			addItemToInventory(42);
3912 			rearrangeInventory();
3913 			return 1;
3914 		} else if (itemSrc == 43) {
3915 			characterTalk(1410);
3916 			return 1;
3917 		} else if (itemSrc == 49) {
3918 			characterTalk(1409);
3919 			return 1;
3920 		}
3921 		break;
3922 	case 16:
3923 		if (itemSrc == 55) {
3924 			characterTalk(1400);
3925 			replaceItemFromInventory(55, 98);
3926 			return 1;
3927 		}
3928 		break;
3929 	case 19:
3930 		if (itemSrc == 0x34) {
3931 			characterTalk(1322);
3932 			return 1;
3933 		} else if (itemSrc == 107) {
3934 			sayLines(2 , 1300);
3935 			replaceItemFromInventory(107, 111);
3936 			_gameState->_mouseState = -1;
3937 			setCursor(0, false, 0, 0);
3938 			rearrangeInventory();
3939 			return 1;
3940 		} else if (itemSrc == 0x6c) {
3941 			sayLines(2, 1300);
3942 			replaceItemFromInventory(108, 112);
3943 			_gameState->_mouseState = -1;
3944 			setCursor(0, false, 0, 0);
3945 			rearrangeInventory();
3946 			return 1;
3947 		} else if (itemSrc == 0x6d) {
3948 			sayLines(2, 1300);
3949 			replaceItemFromInventory(109, 113);
3950 			_gameState->_mouseState = -1;
3951 			setCursor(0, false, 0, 0);
3952 			rearrangeInventory();
3953 			return 1;
3954 		} else if (itemSrc == 110) {
3955 			sayLines(2, 1300);
3956 			replaceItemFromInventory(110, 114);
3957 			_gameState->_mouseState = -1;
3958 			setCursor(0, false, 0, 0);
3959 			rearrangeInventory();
3960 			return 1;
3961 		}
3962 		break;
3963 	case 20:
3964 		if (itemSrc == 35) {
3965 			createMouseItem(21);
3966 			replaceItemFromInventory(35, 36);
3967 			return 1;
3968 		} else if (itemSrc == 0x24) {
3969 			createMouseItem(21);
3970 			replaceItemFromInventory(36, 37);
3971 			return 1;
3972 		} else if (itemSrc == 37) {
3973 			deleteItemFromInventory(37);
3974 			createMouseItem(21);
3975 			rearrangeInventory();
3976 			return 1;
3977 		} else if (itemSrc == 0x6b || itemSrc == 0x6c || itemSrc == 0x6f || itemSrc == 0x70) {
3978 			sayLines(2, 1292);
3979 			return 1;
3980 		}
3981 		break;
3982 	case 21:
3983 		switch (itemSrc) {
3984 		case 107:
3985 			characterTalk(1296);
3986 			replaceItemFromInventory(107, 109);
3987 			_gameState->_mouseState = -1;
3988 			setCursor(0, false, 0, 0);
3989 			rearrangeInventory();
3990 			return 1;
3991 		case 108:
3992 			characterTalk(1298);
3993 			replaceItemFromInventory(108, 110);
3994 			_gameState->_mouseState = -1;
3995 			setCursor(0, false, 0, 0);
3996 			rearrangeInventory();
3997 			return 1;
3998 		case 111:
3999 			characterTalk(1296);
4000 			replaceItemFromInventory(111, 113);
4001 			_gameState->_mouseState = -1;
4002 			setCursor(0, false, 0, 0);
4003 			rearrangeInventory();
4004 			return 1;
4005 		case 112:
4006 			characterTalk(1298);
4007 			replaceItemFromInventory(112, 114);
4008 			_gameState->_mouseState = -1;
4009 			setCursor(0, false, 0, 0);
4010 			rearrangeInventory();
4011 			return 1;
4012 		default:
4013 			break;
4014 		}
4015 		break;
4016 	case 22:
4017 		if (itemSrc == 32) {
4018 			characterTalk(1252);
4019 			return 1;
4020 		}
4021 		break;
4022 	case 24:
4023 		if (itemSrc == 0xc) {
4024 			characterTalk(1244);
4025 			return 1;
4026 		} else if (itemSrc == 79) {
4027 			characterTalk(1280);
4028 			return 1;
4029 		}
4030 		break;
4031 	case 26:
4032 		if (itemSrc == 0x5e) {
4033 			characterTalk(1316);
4034 			return 1;
4035 		} else if (itemSrc == 95) {
4036 			characterTalk(1320);
4037 			return 1;
4038 		}
4039 		break;
4040 	case 31:
4041 		if (itemSrc == 61) {
4042 			characterTalk(1412);
4043 			deleteItemFromInventory(61);
4044 			createMouseItem(62);
4045 			rearrangeInventory();
4046 			return 1;
4047 		}
4048 		break;
4049 	case 32:
4050 		if (itemSrc == 22) {
4051 			characterTalk(1252);
4052 			return 1;
4053 		}
4054 		break;
4055 	case 33:
4056 		if (itemSrc == 117) {
4057 			characterTalk(1490);
4058 			return 1;
4059 		}
4060 		break;
4061 	case 34:
4062 		if (itemSrc == 61) {
4063 			characterTalk(1414);
4064 			return 1;
4065 		}
4066 		break;
4067 	case 35:
4068 		if (itemSrc == -1) {
4069 			characterTalk(1035);
4070 			return 1;
4071 		} else if (itemSrc == 20) {
4072 			replaceItemFromInventory(20, 21);
4073 			createMouseItem(36);
4074 			return 1;
4075 		} else if (itemSrc == 68) {
4076 			replaceItemFromInventory(68, 69);
4077 			createMouseItem(36);
4078 			return 1;
4079 		} else if (itemSrc >= 107 && itemSrc <= 114) {
4080 			characterTalk(1314);
4081 			return 1;
4082 		} else {
4083 			characterTalk(1208);
4084 			return 1;
4085 		}
4086 		break;
4087 	case 36:
4088 		if (itemSrc == -1) {
4089 			characterTalk(1035);
4090 			return 1;
4091 		} else if (itemSrc == 20) {
4092 			replaceItemFromInventory(20, 21);
4093 			createMouseItem(37);
4094 			return 1;
4095 		} else if (itemSrc == 68) {
4096 			replaceItemFromInventory(68, 69);
4097 			createMouseItem(37);
4098 			return 1;
4099 		} else if (itemSrc >= 107 && itemSrc <= 114) {
4100 			characterTalk(1314);
4101 			return 1;
4102 		} else {
4103 			characterTalk(1208);
4104 			return 1;
4105 		}
4106 		break;
4107 	case 37:
4108 		if (itemSrc == -1) {
4109 			characterTalk(1035);
4110 			return 1;
4111 		} else if (itemSrc == 20) {
4112 			replaceItemFromInventory(20, 21);
4113 			_gameState->_mouseState = -1;
4114 			setCursor(0, false, 0, 0);
4115 			rearrangeInventory();
4116 			return 1;
4117 		} else if (itemSrc == 68) {
4118 			replaceItemFromInventory(68, 69);
4119 			_gameState->_mouseState = -1;
4120 			setCursor(0, false, 0, 0);
4121 			rearrangeInventory();
4122 			return 1;
4123 		} else if (itemSrc >= 107 && itemSrc <= 114) {
4124 			characterTalk(1314);
4125 			return 1;
4126 		} else {
4127 			characterTalk(1208);
4128 			return 1;
4129 		}
4130 		break;
4131 	case 38:
4132 		if (itemSrc == 15) {
4133 			characterTalk(1492);
4134 			return 1;
4135 		} else if (itemSrc == 0x2f) {
4136 			characterTalk(1488);
4137 			return 1;
4138 		} else if (itemSrc == 88) {
4139 			sayLines(2, 1478);
4140 			return 1;
4141 		}
4142 		break;
4143 	case 40:
4144 		if (itemSrc == 53) {
4145 			replaceItemFromInventory(53, 54);
4146 			characterTalk(1222);
4147 			return 1;
4148 		} else if (itemSrc == 0x36) {
4149 			characterTalk(1228);
4150 			return 1;
4151 		} else if (itemSrc == 0x5b) {
4152 			characterTalk(1230);
4153 			return 1;
4154 		} else if (itemSrc == 92) {
4155 			characterTalk(1220);
4156 			return 1;
4157 		}
4158 		break;
4159 	case 43:
4160 		if (itemSrc == 14) {
4161 			characterTalk(1410);
4162 			return 1;
4163 		}
4164 		break;
4165 	case 47:
4166 		if (itemSrc == -1)
4167 			characterTalk(1047);
4168 		else
4169 			characterTalk(1488);
4170 
4171 		return 1;
4172 	case 49:
4173 		if (itemSrc == 0xe) {
4174 			characterTalk(1409);
4175 			return 1;
4176 		} else if (itemSrc == 38 || itemSrc == 5 || itemSrc == 0x42) {
4177 			characterTalk(1476);
4178 			return 1;
4179 		} else if (itemSrc == 0x34) {
4180 			characterTalk(1260);
4181 			return 1;
4182 		} else if (itemSrc == 0x47) {
4183 			characterTalk(1246);
4184 			return 1;
4185 		} else if (itemSrc == 0x36) {
4186 			sayLines(2, 1324);
4187 			return 1;
4188 		}
4189 		break;
4190 	case 52:
4191 		if (itemSrc == 0x13) {
4192 			characterTalk(1322);
4193 			return 1;
4194 		} else if (itemSrc == 94) {
4195 			characterTalk(1282);
4196 			return 1;
4197 		}
4198 		break;
4199 	case 53:
4200 		if (itemSrc == 40) {
4201 			createMouseItem(54);
4202 			characterTalk(1222);
4203 			return 1;
4204 		} else if (itemSrc == 0x31) {
4205 			sayLines(2, 1324);
4206 			return 1;
4207 		} else if (itemSrc == 0x34) {
4208 			characterTalk(1310);
4209 			return 1;
4210 		} else if (itemSrc == 91) {
4211 			characterTalk(1218);
4212 			return 1;
4213 		}
4214 
4215 		break;
4216 	case 54:
4217 		if (itemSrc == 40) {
4218 			characterTalk(1228);
4219 			return 1;
4220 		} else if (itemSrc == 0x34) {
4221 			characterTalk(1310);
4222 			return 1;
4223 		} else if (itemSrc == 0x5b) {
4224 			characterTalk(1226);
4225 			replaceItemFromInventory(91, 92);
4226 			return 1;
4227 		} else if (itemSrc == 92) {
4228 			characterTalk(1220);
4229 			return 1;
4230 		}
4231 
4232 		break;
4233 	case 55:
4234 		if (itemSrc == 16) {
4235 			createMouseItem(98);
4236 			characterTalk(1400);
4237 			return 1;
4238 		}
4239 		break;
4240 	case 61:
4241 		if (itemSrc == 0x1f) {
4242 			characterTalk(1412);
4243 			deleteItemFromInventory(31);
4244 			createMouseItem(62);
4245 			rearrangeInventory();
4246 			return 1;
4247 		} else if (itemSrc == 0x21 || itemSrc == 0x22) {
4248 			characterTalk(1414);
4249 			return 1;
4250 		}
4251 		break;
4252 	case 64:
4253 		if (itemSrc == 0xb) {
4254 			sayLines(2, 1212);
4255 			return 1;
4256 		} else if (itemSrc == 0x5e || itemSrc == 0x5f) {
4257 			characterTalk(1318);
4258 			return 1;
4259 		}
4260 		break;
4261 	case 66:
4262 		if (itemSrc == 15) {
4263 			characterTalk(1492);
4264 			return 1;
4265 		} else if (itemSrc == 0x2f) {
4266 			characterTalk(1488);
4267 			return 1;
4268 		} else if (itemSrc == 88) {
4269 			sayLines(2, 1478);
4270 			characterTalk(1478);
4271 			return 1;
4272 		}
4273 		break;
4274 	case 67:
4275 		if (itemSrc == 79) {
4276 			sayLines(2, 1212);
4277 			return 1;
4278 		}
4279 		break;
4280 	case 68:
4281 		if (itemSrc == 35) {
4282 			createMouseItem(69);
4283 			replaceItemFromInventory(35, 36);
4284 			return 1;
4285 		} else if (itemSrc == 0x24) {
4286 			createMouseItem(69);
4287 			replaceItemFromInventory(36, 37);
4288 			return 1;
4289 		} else if (itemSrc == 37) {
4290 			deleteItemFromInventory(37);
4291 			createMouseItem(69);
4292 			rearrangeInventory();
4293 			return 1;
4294 		} else if (itemSrc == 0x6b || itemSrc == 113 || itemSrc == 0x6f || itemSrc == 109) {
4295 			sayLines(2, 1288);
4296 			return 1;
4297 		}
4298 		break;
4299 	case 69:
4300 		switch (itemSrc) {
4301 		case 107:
4302 			characterTalk(1296);
4303 			replaceItemFromInventory(107, 108);
4304 			_gameState->_mouseState = -1;
4305 			setCursor(0, false, 0, 0);
4306 			rearrangeInventory();
4307 			return 1;
4308 		case 109:
4309 			characterTalk(1298);
4310 			replaceItemFromInventory(109, 110);
4311 			_gameState->_mouseState = -1;
4312 			setCursor(0, false, 0, 0);
4313 			rearrangeInventory();
4314 			return 1;
4315 		case 111:
4316 			characterTalk(1296);
4317 			replaceItemFromInventory(111, 112);
4318 			_gameState->_mouseState = -1;
4319 			setCursor(0, false, 0, 0);
4320 			rearrangeInventory();
4321 			return 1;
4322 		case 113:
4323 			characterTalk(1298);
4324 			replaceItemFromInventory(113, 114);
4325 			_gameState->_mouseState = -1;
4326 			setCursor(0, false, 0, 0);
4327 			rearrangeInventory();
4328 			return 1;
4329 		default:
4330 			break;
4331 		}
4332 		break;
4333 	case 71:
4334 		if (itemSrc == 0xc || itemSrc == 1 || itemSrc == 0x41 || itemSrc == 67 || itemSrc == 0x4c || itemSrc == 57) {
4335 			sayLines(2, 1212);
4336 			return 1;
4337 		} else if (itemSrc == 79) {
4338 			characterTalk(1238);
4339 			return 1;
4340 		}
4341 		break;
4342 	case 79:
4343 		if (itemSrc == 1 || itemSrc == 67 || itemSrc == 76 || itemSrc == 57 || itemSrc == 0x41) {
4344 			sayLines(2, 1212);
4345 			return 1;
4346 		} else if (itemSrc == 0x18) {
4347 			characterTalk(1280);
4348 			return 1;
4349 		} else if (itemSrc == 0x47) {
4350 			characterTalk(1238);
4351 			return 1;
4352 		}
4353 		break;
4354 	case 82:
4355 		if (itemSrc == 84) {
4356 			sayLines(2, 1424);
4357 			return 1;
4358 		} else if (itemSrc == 0x58) {
4359 			deleteItemFromInventory(88);
4360 			createMouseItem(89);
4361 			rearrangeInventory();
4362 			characterTalk(1428);
4363 			return 1;
4364 		} else if (itemSrc == 117) {
4365 			sayLines(2, 1496);
4366 			return 1;
4367 		}
4368 		break;
4369 	case 84:
4370 		if (itemSrc == 0x58) {
4371 			replaceItemFromInventory(88, 90);
4372 			characterTalk(1090);
4373 			return 1;
4374 		} else if (itemSrc == 117) {
4375 			characterTalk(1494);
4376 			return 1;
4377 		}
4378 		break;
4379 	case 88:
4380 		if (itemSrc == 82) {
4381 			deleteItemFromInventory(82);
4382 			createMouseItem(89);
4383 			rearrangeInventory();
4384 			characterTalk(1428);
4385 			return 1;
4386 		} else if (itemSrc == 0x54) {
4387 			createMouseItem(90);
4388 			characterTalk(1090);
4389 			return 1;
4390 		} else if (itemSrc == 102) {
4391 			deleteItemFromInventory(102);
4392 			createMouseItem(90);
4393 			rearrangeInventory();
4394 			characterTalk(1090);
4395 			return 1;
4396 		}
4397 		break;
4398 	case 89:
4399 		if (itemSrc == 117) {
4400 			sayLines(2, 1496);
4401 			return 1;
4402 		}
4403 		break;
4404 	case 90:
4405 		if (itemSrc == 117) {
4406 			sayLines(2, 1494);
4407 			return 1;
4408 		}
4409 		break;
4410 	case 91:
4411 		if (itemSrc == 0x28) {
4412 			characterTalk(1230);
4413 			return 1;
4414 		} else if (itemSrc == 54) {
4415 			createMouseItem(92);
4416 			return 1;
4417 		}
4418 		break;
4419 	case 92:
4420 		if (itemSrc == 0x28 || itemSrc == 54) {
4421 			characterTalk(1220);
4422 			return 1;
4423 		}
4424 		break;
4425 	case 94:
4426 		if (itemSrc == 26) {
4427 			characterTalk(1316);
4428 			return 1;
4429 		} else if (itemSrc == 0x34) {
4430 			characterTalk(1282);
4431 			return 1;
4432 		} else if (itemSrc == 64) {
4433 			characterTalk(1318);
4434 			return 1;
4435 		}
4436 		break;
4437 	case 95:
4438 		if (itemSrc == 26) {
4439 			characterTalk(1320);
4440 			return 1;
4441 		} else if (itemSrc == 0x40) {
4442 			characterTalk(1318);
4443 			return 1;
4444 		} else if (itemSrc == 115) {
4445 			characterTalk(1284);
4446 			replaceItemFromInventory(115, 116);
4447 			createMouseItem(93);
4448 			return 1;
4449 		}
4450 		break;
4451 	case 96:
4452 		if (itemSrc == 0x34) {
4453 			characterTalk(1234);
4454 			return 1;
4455 		} else if (itemSrc == 71) {
4456 			sayLines(2, 1212);
4457 			return 1;
4458 		}
4459 		break;
4460 	case 97:
4461 		if (itemSrc == 15) {
4462 			characterTalk(1492);
4463 			return 1;
4464 		} else if (itemSrc == 0x2f) {
4465 			characterTalk(1488);
4466 			return 1;
4467 		} else if (itemSrc == 88) {
4468 			sayLines(2, 1478);
4469 			return 1;
4470 		}
4471 		break;
4472 	case 100:
4473 		if (itemSrc == 117) {
4474 			characterTalk(1490);
4475 			return 1;
4476 		}
4477 		break;
4478 	case 102:
4479 		if (itemSrc == -1) {
4480 			characterTalk(1102);
4481 			return 1;
4482 		} else if (itemSrc == 84) {
4483 			_gameState->_mouseState = -1;
4484 			setCursor(0, false, 0, 0);
4485 			rearrangeInventory();
4486 			characterTalk(1418);
4487 			return 1;
4488 		} else if (itemSrc == 88) {
4489 			deleteItemFromInventory(88);
4490 			createMouseItem(90);
4491 			rearrangeInventory();
4492 			characterTalk(1090);
4493 			return 1;
4494 		} else if (itemSrc == 117) {
4495 			characterTalk(1494);
4496 			return 1;
4497 		} else {
4498 			characterTalk(1426);
4499 			return 1;
4500 		}
4501 		break;
4502 	case 106:
4503 		if (itemSrc == 13) {
4504 			characterTalk(1308);
4505 			return 1;
4506 		}
4507 		break;
4508 	case 107:
4509 		if (itemSrc == 19) {
4510 			sayLines(2, 1300);
4511 			deleteItemFromInventory(19);
4512 			createMouseItem(111);
4513 			rearrangeInventory();
4514 			return 1;
4515 		} else if (itemSrc == 0x15) {
4516 			characterTalk(1296);
4517 			deleteItemFromInventory(21);
4518 			createMouseItem(109);
4519 			rearrangeInventory();
4520 			return 1;
4521 		} else if (itemSrc == 0x23) {
4522 			characterTalk(1314);
4523 			return 1;
4524 		} else if (itemSrc == 69) {
4525 			characterTalk(1296);
4526 			deleteItemFromInventory(69);
4527 			createMouseItem(108);
4528 			rearrangeInventory();
4529 			return 1;
4530 		}
4531 		break;
4532 	case 108:
4533 		if (itemSrc == 19) {
4534 			sayLines(2, 1300);
4535 			deleteItemFromInventory(19);
4536 			createMouseItem(112);
4537 			rearrangeInventory();
4538 			return 1;
4539 		} else if (itemSrc == 0x15) {
4540 			characterTalk(1298);
4541 			deleteItemFromInventory(21);
4542 			createMouseItem(110);
4543 			rearrangeInventory();
4544 			return 1;
4545 		} else if (itemSrc == 35) {
4546 			characterTalk(1314);
4547 			return 1;
4548 		}
4549 		break;
4550 	case 109:
4551 		if (itemSrc == 19) {
4552 			sayLines(2, 1300);
4553 			deleteItemFromInventory(19);
4554 			createMouseItem(113);
4555 			rearrangeInventory();
4556 			return 1;
4557 		} else if (itemSrc == 0x23) {
4558 			characterTalk(1314);
4559 			return 1;
4560 		} else if (itemSrc == 69) {
4561 			characterTalk(1298);
4562 			deleteItemFromInventory(69);
4563 			createMouseItem(110);
4564 			rearrangeInventory();
4565 			return 1;
4566 		}
4567 		break;
4568 	case 110:
4569 		if (itemSrc == 0x13) {
4570 			sayLines(2, 1300);
4571 			deleteItemFromInventory(19);
4572 			createMouseItem(114);
4573 			rearrangeInventory();
4574 			return 1;
4575 		} else if (itemSrc == 35) {
4576 			characterTalk(1314);
4577 			return 1;
4578 		}
4579 		break;
4580 	case 111:
4581 		if (itemSrc == 21) {
4582 			characterTalk(1296);
4583 			deleteItemFromInventory(21);
4584 			createMouseItem(113);
4585 			rearrangeInventory();
4586 			return 1;
4587 		} else if (itemSrc == 0x23) {
4588 			characterTalk(1314);
4589 			return 1;
4590 		} else if (itemSrc == 69) {
4591 			characterTalk(1296);
4592 			deleteItemFromInventory(69);
4593 			createMouseItem(112);
4594 			rearrangeInventory();
4595 			return 1;
4596 		}
4597 		break;
4598 	case 112:
4599 		if (itemSrc == 0x15) {
4600 			characterTalk(1298);
4601 			deleteItemFromInventory(21);
4602 			createMouseItem(114);
4603 			rearrangeInventory();
4604 			return 1;
4605 		} else if (itemSrc == 35) {
4606 			characterTalk(1314);
4607 			return 1;
4608 		}
4609 		break;
4610 	case 113:
4611 		if (itemSrc == 0x23) {
4612 			characterTalk(1314);
4613 			return 1;
4614 		} else if (itemSrc == 69) {
4615 			characterTalk(1298);
4616 			deleteItemFromInventory(69);
4617 			createMouseItem(114);
4618 			rearrangeInventory();
4619 			return 1;
4620 		}
4621 		break;
4622 	case 114:
4623 		if (itemSrc == 35) {
4624 			characterTalk(1314);
4625 			return 1;
4626 		}
4627 		break;
4628 	case 115:
4629 		if (itemSrc == 95) {
4630 			replaceItemFromInventory(95, 93);
4631 			createMouseItem(116);
4632 			return 1;
4633 		}
4634 		break;
4635 	case 117:
4636 		if (itemSrc == 90 || itemSrc == 33) {
4637 			characterTalk(1490);
4638 		} else if (itemSrc == 102 || itemSrc == 84) {
4639 			characterTalk(1494);
4640 		} else if (itemSrc == 0x59 || itemSrc == 0x52) {
4641 			characterTalk(1496);
4642 		}
4643 		break;
4644 	default:
4645 		break;
4646 	}
4647 	return 0;
4648 }
handleInventoryOnDrew(int32 itemId)4649 int32 ToonEngine::handleInventoryOnDrew(int32 itemId) {
4650 	switch (itemId) {
4651 	case 1:
4652 		sayLines(1, 1232);
4653 		return 1;
4654 	case 2:
4655 		sayLines(2, 1202);
4656 		return 1;
4657 	case 7:
4658 		if (_gameState->_currentScene == 32) {
4659 			runEventScript(_mouseX, _mouseY, 2, 107, 0);
4660 		} else if (_gameState->_currentScene < 37) {
4661 			sayLines(2, 1258);
4662 		} else {
4663 			sayLines(2, 1462);
4664 		}
4665 		return 1;
4666 	case 8:
4667 		sayLines(2, 1328);
4668 		return 1;
4669 	case 0xc:
4670 		sayLines(1, 1266);
4671 		return 1;
4672 	case 0xd:
4673 		sayLines(1, 1206);
4674 		return 1;
4675 	case 16:
4676 		sayLines(1, 1438);
4677 		return 1;
4678 	case 0x12:
4679 		if (_gameState->_currentScene == 30) {
4680 			runEventScript(_mouseX, _mouseY, 2, 106, 0);
4681 			_gameState->_mouseState = -1;
4682 		} else {
4683 			sayLines(2, 1200);
4684 		}
4685 		return 1;
4686 	case 0x14:
4687 		sayLines(1, 1216);
4688 		return 1;
4689 	case 22:
4690 		if (_gameState->_currentScene != 39 && _gameState->_currentScene != 50 && _gameState->_currentScene != 49) {
4691 			if (_gameState->_currentScene < 37) {
4692 				sayLines(1, 1256);
4693 			} else {
4694 				sayLines(1, 1456);
4695 			}
4696 		} else {
4697 			runEventScript(_mouseX, _mouseY, 2, 100 , 0);
4698 		}
4699 		return 1;
4700 	case 0x18:
4701 		sayLines(1, 1216);
4702 		return 1;
4703 	case 0x23:
4704 		sayLines(1, 1210);
4705 		return 1;
4706 	case 0x31:
4707 		sayLines(1, 1262);
4708 		return 1;
4709 	case 50:
4710 		if (_gameState->_currentScene == 37) {
4711 			runEventScript(_mouseX, _mouseY, 2, 103, 0);
4712 			return 1;
4713 		};
4714 		break;
4715 	case 0x36:
4716 		if (_gameState->_currentScene == 46) {
4717 			runEventScript(_mouseX, _mouseY, 2, 102, 0);
4718 		} else {
4719 			sayLines(1, 1224);
4720 		}
4721 		return 1;
4722 	case 0x37:
4723 		sayLines(1, 1408);
4724 		return 1;
4725 	case 0x20:
4726 		sayLines(1, 1254);
4727 		return 1;
4728 	case 0x21:
4729 		sayLines(1, 1268);
4730 		return 1;
4731 	case 0x22:
4732 		if (_gameState->_currentScene == 52) {
4733 			runEventScript(_mouseX, _mouseY, 2, 104, 0);
4734 			return 1;
4735 		} else {
4736 			_gameState->_mouseHidden = true;
4737 			_drew->setFacing(4);
4738 			sayLines(1, 1465);
4739 			sayLines(1, randRange(0, 1) + 1468);
4740 			createMouseItem(33);
4741 			_gameState->_mouseHidden = false;
4742 			return 1;
4743 		}
4744 		break;
4745 	case 31:
4746 		sayLines(1, 1436);
4747 		return 1;
4748 	case 0x1a:
4749 		sayLines(1, 1216);
4750 		return 1;
4751 	case 0x39:
4752 		sayLines(1, 1270);
4753 		return 1;
4754 	case 0x3a:
4755 		sayLines(1, 1444);
4756 		return 1;
4757 	case 0x3b:
4758 		sayLines(1, 1272);
4759 		return 1;
4760 	case 0x3f:
4761 		if (_gameState->_currentScene != 10 && _gameState->_currentScene != 30 && _gameState->_currentScene != 22) {
4762 			sayLines(1, 1274);
4763 		} else {
4764 			runEventScript(_mouseX, _mouseY, 2, 109, 0);
4765 		}
4766 		return 1;
4767 	case 0x41:
4768 		sayLines(1, 1232);
4769 		return 1;
4770 
4771 	case 0x4b:
4772 		if (_gameState->_currentScene != 53) {
4773 			_gameState->_mouseHidden = true;
4774 			_drew->setFacing(4);
4775 			sayLines(1, 1437);
4776 			sayLines(2, 1440);
4777 			_gameState->_mouseHidden = false;
4778 		} else {
4779 			runEventScript(_mouseX, _mouseY, 2 , 101, 0);
4780 		}
4781 		return 1;
4782 	case 79:
4783 		sayLines(1, 1242);
4784 		return 1;
4785 	case 0x4c:
4786 		sayLines(1, 1232);
4787 		return 1;
4788 	case 71:
4789 		sayLines(1, 1250);
4790 		return 1;
4791 	case 0x43:
4792 		sayLines(1, 1216);
4793 		return 1;
4794 	case 0x60:
4795 		sayLines(2, 1236);
4796 		return 1;
4797 	case 99:
4798 		if (_gameState->_currentScene == 43) {
4799 			runEventScript(_mouseX, _mouseY, 2, 105, 0);
4800 		}
4801 		_gameState->_mouseState = -1;
4802 		setCursor(0, false, 0, 0);
4803 		sayLines(1, 1555);
4804 		return 1;
4805 	case 0x5a:
4806 		sayLines(1, 1432);
4807 		return 1;
4808 	case 0x58:
4809 		sayLines(1, 1432);
4810 		return 1;
4811 	case 0x65:
4812 		if (_gameState->_currentScene == 52) {
4813 			runEventScript(_mouseX, _mouseY, 2, 104, 0);
4814 		} else {
4815 			_gameState->_mouseHidden = true;
4816 			_drew->setFacing(4);
4817 			sayLines(1, 1464);
4818 			sayLines(1, 1468 + randRange(0, 1));
4819 			createMouseItem(100);
4820 			_gameState->_mouseHidden = false;
4821 		}
4822 		return 1;
4823 	case 0x74:
4824 		sayLines(1, 1286);
4825 		return 1;
4826 	case 0x75:
4827 		sayLines(1, 1482);
4828 		return 1;
4829 	case 118:
4830 		sayLines(2, 1500);
4831 		return 1;
4832 	case 115:
4833 		sayLines(1, 1216);
4834 		return 1;
4835 	case 0x67:
4836 		if (_gameState->_currentScene == 52 || _gameState->_currentScene == 53) {
4837 			runEventScript(_mouseX, _mouseY, 2, 108, 0);
4838 		}
4839 		return 1;
4840 	default:
4841 		break;
4842 	}
4843 	return 0;
4844 }
4845 
deleteItemFromInventory(int32 item)4846 void ToonEngine::deleteItemFromInventory(int32 item) {
4847 	for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
4848 		if (_gameState->_inventory[i] == item) {
4849 			_gameState->_inventory[i] = 0;
4850 			rearrangeInventory();
4851 			return;
4852 		}
4853 	}
4854 }
4855 
replaceItemFromInventory(int32 item,int32 newitem)4856 void ToonEngine::replaceItemFromInventory(int32 item, int32 newitem) {
4857 	for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
4858 		if (_gameState->_inventory[i] == item) {
4859 			_gameState->_inventory[i] = newitem;
4860 			return;
4861 		}
4862 	}
4863 }
4864 
pauseSceneAnimationScript(int32 animScriptId,int32 tickToWait)4865 int32 ToonEngine::pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait) {
4866 	int32 nextTicks = getTickLength() * tickToWait + getSceneAnimationScript(animScriptId)->_lastTimer;
4867 	if (nextTicks < getOldMilli()) {
4868 		getSceneAnimationScript(animScriptId)->_lastTimer = getOldMilli() + getTickLength() * tickToWait;
4869 	} else {
4870 		getSceneAnimationScript(animScriptId)->_lastTimer = nextTicks;
4871 	}
4872 	return nextTicks;
4873 }
4874 
createRoomFilename(const Common::String & name)4875 Common::String ToonEngine::createRoomFilename(const Common::String& name) {
4876 	Common::String file = Common::String::format("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str());
4877 	return file;
4878 }
4879 
createShadowLUT()4880 void ToonEngine::createShadowLUT() {
4881 	// here we create the redirection table that will be used to draw shadows
4882 	// for each color of the palette we find the closest color in the palette that could be used for shadowed color.
4883 
4884 	// In the original program, the scale factor is 0.77f
4885 	// we will use 77 / 100 here.
4886 
4887 	if (!_shadowLUT) {
4888 		_shadowLUT = new uint8[256];
4889 	}
4890 
4891 	uint32 scaleNum = 77;
4892 	uint32 scaleDenom = 100;
4893 
4894 	for (int32 i = 0; i < 255; i++) {
4895 
4896 		// goal color
4897 		uint32 destR = _finalPalette[i * 3 + 0] * scaleNum / scaleDenom;
4898 		uint32 destG = _finalPalette[i * 3 + 1] * scaleNum / scaleDenom;
4899 		uint32 destB = _finalPalette[i * 3 + 2] * scaleNum / scaleDenom;
4900 
4901 		// search only in the "picture palette" which is in colors 1-128 and 200-255
4902 		int32 colorDist = 0xffffff;
4903 		int32 foundColor = 0;
4904 
4905 		for (int32 c = 1; c < 129; c++) {
4906 
4907 			int32 diffR = _finalPalette[c * 3 + 0] - destR;
4908 			int32 diffG = _finalPalette[c * 3 + 1] - destG;
4909 			int32 diffB = _finalPalette[c * 3 + 2] - destB;
4910 
4911 			if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
4912 				colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
4913 				foundColor = c;
4914 			}
4915 		}
4916 
4917 		for (int32 c = 200; c < 256; c++) {
4918 
4919 			int32 diffR = _finalPalette[c * 3 + 0] - destR;
4920 			int32 diffG = _finalPalette[c * 3 + 1] - destG;
4921 			int32 diffB = _finalPalette[c * 3 + 2] - destB;
4922 
4923 			if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
4924 				colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
4925 				foundColor = c;
4926 			}
4927 		}
4928 
4929 		_shadowLUT[i] = foundColor;
4930 
4931 	}
4932 }
4933 
loadToonDat()4934 bool ToonEngine::loadToonDat() {
4935 	Common::File in;
4936 	Common::U32String errorMessage;
4937 	Common::String filename = "toon.dat";
4938 	int majVer, minVer;
4939 
4940 	in.open(filename.c_str());
4941 
4942 	if (!in.isOpen()) {
4943 		const char *msg = _s("Unable to locate the '%s' engine data file.");
4944 		errorMessage = Common::U32String::format(_(msg), filename.c_str());
4945 		GUIErrorMessage(errorMessage);
4946 		warning(msg, filename.c_str());
4947 		return false;
4948 	}
4949 
4950 	// Read header
4951 	char buf[4+1];
4952 	in.read(buf, 4);
4953 	buf[4] = '\0';
4954 
4955 	if (strcmp(buf, "TOON")) {
4956 		const char *msg = _s("The '%s' engine data file is corrupt.");
4957 		errorMessage = Common::U32String::format(_(msg), filename.c_str());
4958 		GUIErrorMessage(errorMessage);
4959 		warning(msg, filename.c_str());
4960 		return false;
4961 	}
4962 
4963 	majVer = in.readByte();
4964 	minVer = in.readByte();
4965 
4966 	if ((majVer != TOON_DAT_VER_MAJ) || (minVer != TOON_DAT_VER_MIN)) {
4967 		const char *msg = _s("Incorrect version of the '%s' engine data file found. Expected %d.%d but got %d.%d.");
4968 		errorMessage = Common::U32String::format(_(msg), filename.c_str(), TOON_DAT_VER_MAJ, TOON_DAT_VER_MIN, majVer, minVer);
4969 		GUIErrorMessage(errorMessage);
4970 
4971 		warning(msg, filename.c_str(), TOON_DAT_VER_MAJ, TOON_DAT_VER_MIN, majVer, minVer);
4972 		return false;
4973 	}
4974 
4975 	_numVariant = in.readUint16BE();
4976 
4977 	_locationDirNotVisited = loadTextsVariants(in);
4978 	_locationDirVisited = loadTextsVariants(in);
4979 	_specialInfoLine = loadTextsVariants(in);
4980 
4981 	return true;
4982 }
4983 
unloadToonDat()4984 void ToonEngine::unloadToonDat() {
4985 	unloadTextsVariants(_locationDirNotVisited);
4986 	unloadTextsVariants(_locationDirVisited);
4987 	unloadTextsVariants(_specialInfoLine);
4988 }
4989 
loadTextsVariants(Common::File & in)4990 char **ToonEngine::loadTextsVariants(Common::File &in) {
4991 	int  len;
4992 	char **res = 0;
4993 	char *pos = 0;
4994 
4995 	for (int varnt = 0; varnt < _numVariant; varnt++) {
4996 		int numTexts = in.readUint16BE();
4997 		int entryLen = in.readUint16BE();
4998 		pos = (char *)malloc(entryLen);
4999 		if (varnt == _gameVariant) {
5000 			res = (char **)malloc(sizeof(char *) * numTexts);
5001 			res[0] = pos;
5002 			in.read(res[0], entryLen);
5003 			res[0] += DATAALIGNMENT;
5004 		} else {
5005 			in.read(pos, entryLen);
5006 			free(pos);
5007 			continue;
5008 		}
5009 
5010 		pos += DATAALIGNMENT;
5011 
5012 		for (int i = 1; i < numTexts; i++) {
5013 			pos -= 2;
5014 
5015 			len = READ_BE_UINT16(pos);
5016 			pos += 2 + len;
5017 
5018 			if (varnt == _gameVariant)
5019 				res[i] = pos;
5020 		}
5021 	}
5022 
5023 	return res;
5024 }
5025 
unloadTextsVariants(char ** texts)5026 void ToonEngine::unloadTextsVariants(char **texts) {
5027 	if (!texts)
5028 		return;
5029 
5030 	free(*texts - DATAALIGNMENT);
5031 	free(texts);
5032 }
5033 
makeLineNonWalkable(int32 x,int32 y,int32 x2,int32 y2)5034 void ToonEngine::makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2) {
5035 	_currentMask->drawLineOnMask(x, y, x2, y2, false);
5036 }
5037 
makeLineWalkable(int32 x,int32 y,int32 x2,int32 y2)5038 void ToonEngine::makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2) {
5039 	_currentMask->drawLineOnMask(x, y, x2, y2, true);
5040 }
5041 
playRoomMusic()5042 void ToonEngine::playRoomMusic() {
5043 	if (_gameState->_inConversation) {
5044 		const char* music = getSpecialConversationMusic(_gameState->_currentConversationId);
5045 		if (music) {
5046 			_audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, music);
5047 			return;
5048 		}
5049 	}
5050 
5051 	_audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music);
5052 }
5053 
dirtyAllScreen()5054 void ToonEngine::dirtyAllScreen()
5055 {
5056 	_dirtyRects.clear();
5057 	_dirtyAll = true;
5058 }
5059 
addDirtyRect(int32 left,int32 top,int32 right,int32 bottom)5060 void ToonEngine::addDirtyRect( int32 left, int32 top, int32 right, int32 bottom ) {
5061 	left = MIN<int32>(MAX<int32>(left, 0), TOON_BACKBUFFER_WIDTH);
5062 	right = MIN<int32>(MAX<int32>(right, 0), TOON_BACKBUFFER_WIDTH);
5063 	top = MIN<int32>(MAX<int32>(top, 0), TOON_BACKBUFFER_HEIGHT);
5064 	bottom = MIN<int32>(MAX<int32>(bottom, 0), TOON_BACKBUFFER_HEIGHT);
5065 
5066 	if (bottom - top <= 0 || right - left <= 0)
5067 		return;
5068 
5069 	Common::Rect rect(left, top, right, bottom);
5070 
5071 	for (uint32 i = 0; i < _dirtyRects.size(); i++) {
5072 		if (_dirtyRects[i].contains(rect))
5073 			return;
5074 		if (rect.contains(_dirtyRects[i])) {
5075 			_dirtyRects.remove_at(i);
5076 			i--;
5077 		}
5078 	}
5079 
5080 	// check also in the old rect (of the old frame)
5081 	for (int32 i = _oldDirtyRects.size() - 1 ; i >= 0; i--) {
5082 		if (rect.contains(_oldDirtyRects[i])) {
5083 			_oldDirtyRects.remove_at(i);
5084 		}
5085 	}
5086 
5087 	_dirtyRects.push_back(rect);
5088 }
5089 
clearDirtyRects()5090 void ToonEngine::clearDirtyRects() {
5091 	_oldDirtyRects = _dirtyRects;
5092 	_dirtyRects.clear();
5093 	_dirtyAll = false;
5094 }
save(ToonEngine * vm,Common::WriteStream * stream)5095 void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) {
5096 	stream->writeByte(_active);
5097 	stream->writeSint32BE(_id);
5098 
5099 	if (!_active)
5100 		return;
5101 
5102 	if (_animInstance) {
5103 		stream->writeByte(1);
5104 		_animInstance->save(stream);
5105 	} else {
5106 		stream->writeByte(0);
5107 	}
5108 
5109 	if (!_animation) {
5110 		stream->writeByte(0);
5111 	} else {
5112 		stream->writeByte(strlen(_animation->_name) + 1);
5113 		stream->write(_animation->_name, strlen(_animation->_name) + 1);
5114 	}
5115 }
load(ToonEngine * vm,Common::ReadStream * stream)5116 void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) {
5117 
5118 	_active = stream->readByte();
5119 	_id = stream->readSint32BE();
5120 
5121 	if (!_active)
5122 		return;
5123 
5124 	if (stream->readByte() == 1) {
5125 		_animInstance = vm->getAnimationManager()->createNewInstance(kAnimationScene);
5126 		_animInstance->load(stream);
5127 		// we add them at the end of loading in reverse order
5128 		//vm->getAnimationManager()->addInstance(_animInstance);
5129 		_originalAnimInstance = _animInstance;
5130 	} else {
5131 		_animInstance = NULL;
5132 		_originalAnimInstance = NULL;
5133 	}
5134 
5135 	// load animation if any
5136 	char animationName[256];
5137 	*animationName = 0;
5138 	int8 strSize = stream->readByte();
5139 	if (!strSize) {
5140 		_animation = 0;
5141 		if (_animInstance)
5142 			_animInstance->setAnimation(0);
5143 	} else {
5144 		stream->read(animationName, strSize);
5145 		animationName[strSize] = 0;
5146 
5147 		_animation = new Animation(vm);
5148 		_animation->loadAnimation(animationName);
5149 
5150 		if (_animInstance) {
5151 			_animInstance->setAnimation(_animation, false);
5152 		}
5153 	}
5154 }
5155 
5156 } // End of namespace Toon
5157