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