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 * Additional copyright for this file:
8 * Copyright (C) 1995 Presto Studios, Inc.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "buried/avi_frames.h"
27 #include "buried/biochip_right.h"
28 #include "buried/buried.h"
29 #include "buried/frame_window.h"
30 #include "buried/gameui.h"
31 #include "buried/graphics.h"
32 #include "buried/inventory_window.h"
33 #include "buried/livetext.h"
34 #include "buried/navarrow.h"
35 #include "buried/resources.h"
36 #include "buried/scene_view.h"
37 #include "buried/sound.h"
38 #include "buried/video_window.h"
39 #include "buried/environ/scene_base.h"
40
41 #include "common/ptr.h"
42 #include "common/stream.h"
43 #include "common/system.h"
44 #include "graphics/surface.h"
45
46 namespace Buried {
47
SceneViewWindow(BuriedEngine * vm,Window * parent)48 SceneViewWindow::SceneViewWindow(BuriedEngine *vm, Window *parent) : Window(vm, parent) {
49 _currentScene = nullptr;
50 _preBuffer = nullptr;
51 _walkMovie = nullptr;
52 _useScenePaint = true;
53 _timer = 0;
54 _currentSprite.image = nullptr;
55 _useSprite = true;
56 _infoWindowDisplayed = false;
57 _bioChipWindowDisplayed = false;
58 _burnedLetterDisplayed = false;
59 _asyncMovie = nullptr;
60 _asyncMovieStartFrame = 0;
61 _loopAsyncMovie = false;
62 _paused = false;
63 _cycleEnabled = ((FrameWindow *)(_parent->getParent()))->isFrameCyclingDefault();
64 _forceCycleEnabled = false;
65 _disableArthur = false;
66 _demoSoundEffectHandle = -1;
67
68 _preBuffer = new Graphics::Surface();
69 _preBuffer->create(DIB_FRAME_WIDTH, DIB_FRAME_HEIGHT, g_system->getScreenFormat());
70
71 _rect = Common::Rect(64, 128, 496, 317);
72
73 _timer = setTimer(100);
74 _demoSoundTimer = _vm->isDemo() ? setTimer(10) : 0;
75 _curCursor = kCursorArrow;
76 _stillFrames = new AVIFrames();
77 _cycleFrames = new AVIFrames();
78
79 memset(&_globalFlags, 0, sizeof(_globalFlags));
80 }
81
~SceneViewWindow()82 SceneViewWindow::~SceneViewWindow() {
83 if (_currentScene) {
84 _currentScene->preDestructor();
85 delete _currentScene;
86 }
87
88 killTimer(_timer);
89
90 if (_preBuffer) {
91 _preBuffer->free();
92 delete _preBuffer;
93 }
94
95 delete _stillFrames;
96 delete _cycleFrames;
97 delete _walkMovie;
98 delete _asyncMovie;
99 }
100
startNewGame(bool walkthrough)101 bool SceneViewWindow::startNewGame(bool walkthrough) {
102 Location newLocation;
103
104 if (_vm->isDemo()) {
105 newLocation.timeZone = 1;
106 newLocation.environment = 4;
107 newLocation.node = 0;
108 newLocation.facing = 0;
109 newLocation.orientation = 1;
110 newLocation.depth = 0;
111 } else {
112 newLocation.timeZone = 4;
113 newLocation.environment = 3;
114 newLocation.node = 3;
115 newLocation.facing = 0;
116 newLocation.orientation = 1;
117 newLocation.depth = 0;
118 }
119
120 jumpToScene(newLocation);
121
122 if (_vm->isDemo()) {
123 displayLiveText("To return to the main menu, click the 'Menu' button on the Interface Biochip Display to the right, then click Quit.");
124 startDemoAmbientSound();
125
126 // This is unlabeled in the original source, but it looks like a hidden feature
127 // to access a bonus puzzle in the demo. (Complete with a typo, but who's counting?)
128 if (((FrameWindow *)(_parent->getParent()))->_reviewerMode)
129 ((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemCopperMedallion);
130 } else if (walkthrough) {
131 // Set the mode flag
132 _globalFlags.generalWalkthroughMode = 1;
133
134 // Set specific state flags for walkthrough mode
135 _globalFlags.cgSmithyStatus = 6;
136 _globalFlags.cgTapestryFlag = 1;
137 _globalFlags.myTPCodeWheelLeftIndex = 8;
138 _globalFlags.myTPCodeWheelRightIndex = 12;
139 _globalFlags.myTPCodeWheelStatus = 1;
140 _globalFlags.myWGPlacedRope = 1;
141
142 // Add the translate biochip
143 ((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemBioChipTranslate);
144 }
145
146 invalidateWindow(false);
147 return true;
148 }
149
startNewGameIntro(bool walkthrough)150 bool SceneViewWindow::startNewGameIntro(bool walkthrough) {
151 Location newLocation;
152 newLocation.timeZone = 10;
153 newLocation.environment = 0;
154 newLocation.node = 0;
155 newLocation.facing = 0;
156 newLocation.orientation = 0;
157 newLocation.depth = 0;
158
159 jumpToScene(newLocation);
160
161 if (walkthrough) {
162 // Set the mode flag
163 _globalFlags.generalWalkthroughMode = 1;
164
165 // Set specific state flags for walkthrough mode
166 _globalFlags.cgSmithyStatus = 6;
167 _globalFlags.cgTapestryFlag = 1;
168 _globalFlags.myTPCodeWheelLeftIndex = 8;
169 _globalFlags.myTPCodeWheelRightIndex = 12;
170 _globalFlags.myTPCodeWheelStatus = 1;
171 _globalFlags.myWGPlacedRope = 1;
172
173 // Add the translate biochip
174 ((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemBioChipTranslate);
175 }
176
177 invalidateWindow(false);
178 return true;
179 }
180
startNewGame(const Location & startingLocation)181 bool SceneViewWindow::startNewGame(const Location &startingLocation) {
182 jumpToSceneRestore(startingLocation);
183
184 if (_globalFlags.generalWalkthroughMode == 1) {
185 if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0))
186 displayLiveText(_vm->getString(IDS_PLAY_MODE_WALKTHROUGH_TEXT));
187 else
188 displayLiveText("You are playing in Walkthrough mode.");
189 } else {
190 if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0))
191 displayLiveText(_vm->getString(IDS_PLAY_MODE_NORMAL_TEXT));
192 else
193 displayLiveText("You are playing in Adventure mode.");
194 }
195
196 return true;
197 }
198
showDeathScene(int deathSceneIndex)199 bool SceneViewWindow::showDeathScene(int deathSceneIndex) {
200 return ((FrameWindow *)(_parent->getParent()))->showDeathScene(deathSceneIndex, _globalFlags, ((GameUIWindow *)_parent)->_inventoryWindow->getItemArray());
201 }
202
showCompletionScene()203 bool SceneViewWindow::showCompletionScene() {
204 return ((FrameWindow *)(_parent->getParent()))->showCompletionScene(_globalFlags);
205 }
206
getSceneStaticData(const Location & location,LocationStaticData & sceneStaticData)207 bool SceneViewWindow::getSceneStaticData(const Location &location, LocationStaticData &sceneStaticData) {
208 int curTimeZone = -1;
209 int curEnvironment = -1;
210
211 if (!_currentNavigationDatabase.empty()) {
212 curTimeZone = _currentNavigationDatabase[0].location.timeZone;
213 curEnvironment = _currentNavigationDatabase[0].location.environment;
214 }
215
216 if (location.timeZone != curTimeZone || location.environment != curEnvironment) {
217 _currentNavigationDatabase.clear();
218
219 int resID = _vm->computeNavDBResourceID(location.timeZone, location.environment);
220 Common::SeekableReadStream *resource = _vm->getNavData(resID);
221 resource->skip(2);
222
223 while (resource->pos() < resource->size()) {
224 LocationStaticData locationStaticData;
225
226 locationStaticData.location.timeZone = resource->readSint16LE();
227 locationStaticData.location.environment = resource->readSint16LE();
228 locationStaticData.location.node = resource->readSint16LE();
229 locationStaticData.location.facing = resource->readSint16LE();
230 locationStaticData.location.orientation = resource->readSint16LE();
231 locationStaticData.location.depth = resource->readSint16LE();
232
233 locationStaticData.destUp.destinationScene.timeZone = resource->readSint16LE();
234 locationStaticData.destUp.destinationScene.environment = resource->readSint16LE();
235 locationStaticData.destUp.destinationScene.node = resource->readSint16LE();
236 locationStaticData.destUp.destinationScene.facing = resource->readSint16LE();
237 locationStaticData.destUp.destinationScene.orientation = resource->readSint16LE();
238 locationStaticData.destUp.destinationScene.depth = resource->readSint16LE();
239 locationStaticData.destUp.transitionType = resource->readSint16LE();
240 locationStaticData.destUp.transitionData = resource->readSint16LE();
241 locationStaticData.destUp.transitionStartFrame = resource->readSint32LE();
242 locationStaticData.destUp.transitionLength = resource->readSint32LE();
243
244 locationStaticData.destLeft.destinationScene.timeZone = resource->readSint16LE();
245 locationStaticData.destLeft.destinationScene.environment = resource->readSint16LE();
246 locationStaticData.destLeft.destinationScene.node = resource->readSint16LE();
247 locationStaticData.destLeft.destinationScene.facing = resource->readSint16LE();
248 locationStaticData.destLeft.destinationScene.orientation = resource->readSint16LE();
249 locationStaticData.destLeft.destinationScene.depth = resource->readSint16LE();
250 locationStaticData.destLeft.transitionType = resource->readSint16LE();
251 locationStaticData.destLeft.transitionData = resource->readSint16LE();
252 locationStaticData.destLeft.transitionStartFrame = resource->readSint32LE();
253 locationStaticData.destLeft.transitionLength = resource->readSint32LE();
254
255 locationStaticData.destRight.destinationScene.timeZone = resource->readSint16LE();
256 locationStaticData.destRight.destinationScene.environment = resource->readSint16LE();
257 locationStaticData.destRight.destinationScene.node = resource->readSint16LE();
258 locationStaticData.destRight.destinationScene.facing = resource->readSint16LE();
259 locationStaticData.destRight.destinationScene.orientation = resource->readSint16LE();
260 locationStaticData.destRight.destinationScene.depth = resource->readSint16LE();
261 locationStaticData.destRight.transitionType = resource->readSint16LE();
262 locationStaticData.destRight.transitionData = resource->readSint16LE();
263 locationStaticData.destRight.transitionStartFrame = resource->readSint32LE();
264 locationStaticData.destRight.transitionLength = resource->readSint32LE();
265
266 locationStaticData.destDown.destinationScene.timeZone = resource->readSint16LE();
267 locationStaticData.destDown.destinationScene.environment = resource->readSint16LE();
268 locationStaticData.destDown.destinationScene.node = resource->readSint16LE();
269 locationStaticData.destDown.destinationScene.facing = resource->readSint16LE();
270 locationStaticData.destDown.destinationScene.orientation = resource->readSint16LE();
271 locationStaticData.destDown.destinationScene.depth = resource->readSint16LE();
272 locationStaticData.destDown.transitionType = resource->readSint16LE();
273 locationStaticData.destDown.transitionData = resource->readSint16LE();
274 locationStaticData.destDown.transitionStartFrame = resource->readSint32LE();
275 locationStaticData.destDown.transitionLength = resource->readSint32LE();
276
277 locationStaticData.destForward.destinationScene.timeZone = resource->readSint16LE();
278 locationStaticData.destForward.destinationScene.environment = resource->readSint16LE();
279 locationStaticData.destForward.destinationScene.node = resource->readSint16LE();
280 locationStaticData.destForward.destinationScene.facing = resource->readSint16LE();
281 locationStaticData.destForward.destinationScene.orientation = resource->readSint16LE();
282 locationStaticData.destForward.destinationScene.depth = resource->readSint16LE();
283 locationStaticData.destForward.transitionType = resource->readSint16LE();
284 locationStaticData.destForward.transitionData = resource->readSint16LE();
285 locationStaticData.destForward.transitionStartFrame = resource->readSint32LE();
286 locationStaticData.destForward.transitionLength = resource->readSint32LE();
287
288 locationStaticData.classID = resource->readSint16LE();
289 locationStaticData.navFrameIndex = resource->readSint32LE();
290 locationStaticData.miscFrameIndex = resource->readSint32LE();
291 locationStaticData.miscFrameCount = resource->readSint32LE();
292 locationStaticData.cycleStartFrame = resource->readSint32LE();
293 locationStaticData.cycleFrameCount = resource->readSint32LE();
294
295 _currentNavigationDatabase.push_back(locationStaticData);
296 }
297
298 if (_currentNavigationDatabase.empty())
299 return false;
300 }
301
302 for (uint32 i = 0; i < _currentNavigationDatabase.size(); i++) {
303 if (location.timeZone == _currentNavigationDatabase[i].location.timeZone &&
304 location.environment == _currentNavigationDatabase[i].location.environment &&
305 location.node == _currentNavigationDatabase[i].location.node &&
306 location.facing == _currentNavigationDatabase[i].location.facing &&
307 location.orientation == _currentNavigationDatabase[i].location.orientation &&
308 location.depth == _currentNavigationDatabase[i].location.depth) {
309 sceneStaticData = _currentNavigationDatabase[i];
310 return true;
311 }
312 }
313
314 return false;
315 }
316
jumpToScene(const Location & newLocation)317 bool SceneViewWindow::jumpToScene(const Location &newLocation) {
318 Location oldLocation;
319 oldLocation.timeZone = -1;
320 oldLocation.environment = -1;
321 oldLocation.node = -1;
322 oldLocation.facing = -1;
323 oldLocation.orientation = -1;
324 oldLocation.depth = -1;
325
326 Location passedLocation;
327 passedLocation.timeZone = -1;
328 passedLocation.environment = -1;
329 passedLocation.node = -1;
330 passedLocation.facing = -1;
331 passedLocation.orientation = -1;
332 passedLocation.depth = -1;
333
334 // Destroy any window displayed
335 if (_infoWindowDisplayed)
336 ((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
337 if (_bioChipWindowDisplayed)
338 ((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
339 if (_burnedLetterDisplayed)
340 ((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
341
342 LocationStaticData newSceneStaticData;
343 if (!getSceneStaticData(newLocation, newSceneStaticData))
344 return false;
345
346 if (_currentScene)
347 oldLocation = _currentScene->_staticData.location;
348
349 // Clear the live text window
350 if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment)
351 ((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText();
352
353 // Call the pre-transition function
354 if (_currentScene)
355 _currentScene->preExitRoom(this, passedLocation);
356
357 if (newLocation.timeZone != oldLocation.timeZone && newLocation.timeZone != -2)
358 initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
359 if (newLocation.environment != oldLocation.environment && newLocation.environment != -2)
360 initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
361
362 SceneBase *newScene = constructSceneObject(this, newSceneStaticData, passedLocation);
363
364 if (_currentScene && _currentScene->postExitRoom(this, passedLocation) == SC_DEATH) {
365 newScene->preDestructor();
366 delete newScene;
367
368 return false;
369 }
370
371 if (!newScene)
372 error("Failed to create scene");
373
374 if (_currentScene) {
375 _currentScene->preDestructor();
376 delete _currentScene;
377 _currentScene = nullptr;
378 }
379
380 if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment || oldLocation.timeZone < 0)
381 startEnvironmentAmbient(passedLocation.timeZone, passedLocation.environment, newLocation.timeZone, newLocation.environment);
382
383 _currentScene = newScene;
384
385 if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
386 flushCycleFrameCache();
387
388 invalidateWindow(false);
389
390 if (_currentScene->preEnterRoom(this, passedLocation) == SC_END_PROCESSING)
391 return true;
392
393 if (_globalFlags.bcCloakingEnabled != 1)
394 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(newScene->_staticData);
395
396 if (newLocation.timeZone != oldLocation.timeZone)
397 ((GameUIWindow *)_parent)->changeCurrentDate(newLocation.timeZone);
398
399 invalidateWindow(false);
400
401 _currentScene->postEnterRoom(this, passedLocation);
402
403 _parent->invalidateWindow(false);
404
405 if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI))
406 playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
407
408 ((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
409
410 return true;
411 }
412
jumpToSceneRestore(const Location & newLocation)413 bool SceneViewWindow::jumpToSceneRestore(const Location &newLocation) {
414 Location oldLocation(-2, -2, -2, -2, -2, -2);
415 Location passedLocation(-2, -2, -2, -2, -2, -2);
416
417 if (_infoWindowDisplayed)
418 ((GameUIWindow *)getParent())->_inventoryWindow->destroyInfoWindow();
419 if (_bioChipWindowDisplayed)
420 ((GameUIWindow *)getParent())->_bioChipRightWindow->destroyBioChipViewWindow();
421 if (_burnedLetterDisplayed)
422 ((GameUIWindow *)getParent())->_inventoryWindow->destroyBurnedLetterWindow();
423
424 // Get the static scene data for this new location
425 LocationStaticData newSceneStaticData;
426 if (!getSceneStaticData(newLocation, newSceneStaticData))
427 return false;
428 if (_currentScene)
429 oldLocation = _currentScene->_staticData.location;
430
431 // Clear the live text window
432 if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment)
433 ((GameUIWindow *)getParent())->_liveTextWindow->updateLiveText("");
434
435 // If we have a scene, call the pre-transition function
436 if (_currentScene)
437 _currentScene->preExitRoom(this, passedLocation);
438
439 // The original resets the environment upon loading, which is clearly not correct.
440 // We won't.
441
442 //if (newLocation.timeZone != oldLocation.timeZone && newLocation.timeZone != -2)
443 // initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
444 //if (newLocation.timeZone != oldLocation.timeZone && newLocation.environment != -2)
445 // initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
446
447 // Create the new scene object
448 SceneBase *newScene = constructSceneObject(this, newSceneStaticData, passedLocation);
449
450 // Call the post-transition function
451 if (_currentScene && _currentScene->postExitRoom(this, passedLocation) == SC_DEATH) {
452 newScene->preDestructor();
453 delete newScene;
454
455 return false;
456 }
457
458 if (_currentScene) {
459 _currentScene->preDestructor();
460 delete _currentScene;
461 _currentScene = nullptr;
462 }
463
464 // Change the ambient music
465 if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment || oldLocation.timeZone < 0)
466 startEnvironmentAmbient(passedLocation.timeZone, passedLocation.environment, newLocation.timeZone, newLocation.environment);
467
468 _currentScene = newScene;
469
470 if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
471 flushCycleFrameCache();
472
473 if (_currentScene->preEnterRoom(this, passedLocation) == SC_END_PROCESSING)
474 return true;
475
476 if (_globalFlags.bcCloakingEnabled != 1)
477 ((GameUIWindow *)getParent())->_navArrowWindow->updateAllArrows(newScene->_staticData);
478
479 if (newLocation.timeZone != oldLocation.timeZone)
480 ((GameUIWindow *)getParent())->changeCurrentDate(newLocation.timeZone);
481
482 invalidateWindow(false);
483
484 _currentScene->postEnterRoom(this, passedLocation);
485 getParent()->invalidateWindow(false);
486
487 // Check AI database for a spontaneous comment
488 if (((GameUIWindow *)getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI))
489 playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
490
491 // Notify biochip right window of change
492 ((GameUIWindow *)getParent())->_bioChipRightWindow->sceneChanged();
493
494 return true;
495 }
496
moveInDirection(Direction direction)497 bool SceneViewWindow::moveInDirection(Direction direction) {
498 if (!_currentScene)
499 return false;
500
501 ((GameUIWindow *)_parent)->_navArrowWindow->updateArrow(direction, NavArrowWindow::BUTTON_SELECTED);
502
503 DestinationScene destinationData;
504
505 switch (direction) {
506 case kDirectionUp: // Up
507 destinationData = _currentScene->_staticData.destUp;
508 break;
509 case kDirectionLeft: // Left
510 destinationData = _currentScene->_staticData.destLeft;
511 break;
512 case kDirectionRight: // Right
513 destinationData = _currentScene->_staticData.destRight;
514 break;
515 case kDirectionDown: // Down
516 destinationData = _currentScene->_staticData.destDown;
517 break;
518 case kDirectionForward: // Forward
519 destinationData = _currentScene->_staticData.destForward;
520 break;
521 }
522
523 return moveToDestination(destinationData);
524 }
525
moveToDestination(const DestinationScene & destinationData)526 bool SceneViewWindow::moveToDestination(const DestinationScene &destinationData) {
527 // Close any information window
528 if (_infoWindowDisplayed)
529 ((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
530 if (_bioChipWindowDisplayed)
531 ((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
532 if (_burnedLetterDisplayed)
533 ((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
534
535 // Check to see if this is a valid move
536 if (destinationData.destinationScene.timeZone == -1) {
537 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
538 return true;
539 }
540
541 assert(_currentScene); // clone2727: sanity check -- the original code was broken
542
543 LocationStaticData newSceneStaticData;
544 if (!getSceneStaticData(destinationData.destinationScene, newSceneStaticData)) {
545 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
546 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
547 return true;
548 }
549
550 Location oldLocation = _currentScene->_staticData.location;
551
552 // Disable locate and clear the live text window
553 if (newSceneStaticData.location.timeZone != oldLocation.timeZone ||
554 newSceneStaticData.location.environment != oldLocation.environment ||
555 newSceneStaticData.location.node != oldLocation.node) {
556 ((GameUIWindow *)_parent)->_bioChipRightWindow->disableEvidenceCapture();
557 ((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText();
558 }
559
560 // Disable the arrow window
561 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(false);
562
563 // Get thr esults from the pre-exit room function
564 int retVal = _currentScene->preExitRoom(this, destinationData.destinationScene);
565
566 // If we died, return here
567 if (retVal == SC_DEATH)
568 return false;
569
570 // If we did not return success, the move is disallowed
571 if (retVal != SC_TRUE) {
572 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
573 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
574 return true;
575 }
576
577 // Initialize the time zone and environment
578 if (newSceneStaticData.location.timeZone != oldLocation.timeZone && newSceneStaticData.location.timeZone != -2)
579 initializeTimeZoneAndEnvironment(this, newSceneStaticData.location.timeZone, -1);
580 if (newSceneStaticData.location.environment != oldLocation.environment && newSceneStaticData.location.environment != -2)
581 initializeTimeZoneAndEnvironment(this, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment);
582
583 // If we are movinto a different node or time zone, reset the evidence flag
584 if (newSceneStaticData.location.timeZone != oldLocation.timeZone ||
585 newSceneStaticData.location.environment != oldLocation.environment ||
586 newSceneStaticData.location.node != oldLocation.node) {
587 if (_globalFlags.bcLocateEnabled == 1) {
588 _globalFlags.bcLocateEnabled = 0;
589 ((GameUIWindow *)_parent)->_bioChipRightWindow->invalidateWindow(false);
590 }
591 }
592
593 // Create the new scene object
594 SceneBase *newScene = constructSceneObject(this, newSceneStaticData, oldLocation);
595
596 if (!newScene)
597 error("Failed to create new scene");
598
599 // Switch on the type of transition
600 if (destinationData.transitionType == TRANSITION_VIDEO) {
601 // Play transition
602 playTransition(destinationData, newScene->_staticData.navFrameIndex);
603
604 // Change the ambient sound
605 if (newSceneStaticData.location.timeZone != oldLocation.timeZone || newSceneStaticData.location.environment != oldLocation.environment || oldLocation.timeZone < 0)
606 startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment, false);
607 } else {
608 // Change the ambient sound
609 if (newSceneStaticData.location.timeZone != oldLocation.timeZone || newSceneStaticData.location.environment != oldLocation.environment || oldLocation.timeZone < 0)
610 startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment);
611
612 // Play transition
613 playTransition(destinationData, newScene->_staticData.navFrameIndex);
614 }
615
616 // Call the post-exit function
617 retVal = _currentScene->postExitRoom(this, destinationData.destinationScene);
618
619 if (retVal == SC_DEATH) {
620 newScene->preDestructor();
621 delete newScene;
622 return false;
623 }
624
625 if (retVal != SC_TRUE) {
626 newScene->preDestructor();
627 delete newScene;
628 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
629 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
630 return true;
631 }
632
633 // Delete the current scene
634 _currentScene->preDestructor();
635 delete _currentScene;
636 _currentScene = newScene;
637
638 // If this scene has no cycle frames, flush the cycle frame cache
639 if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
640 flushCycleFrameCache();
641
642 // Call the pre-enter function, exiting this function if SC_END_PROCESSING is returned
643 if (_currentScene->preEnterRoom(this, oldLocation) == SC_END_PROCESSING)
644 return true;
645
646 // Send new navigation data to navigation buttons
647 if (_globalFlags.bcCloakingEnabled != 1)
648 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(newScene->_staticData);
649
650 // If this is a different time zone, then change the date
651 if (newSceneStaticData.location.timeZone != oldLocation.timeZone)
652 ((GameUIWindow *)_parent)->changeCurrentDate(newSceneStaticData.location.timeZone);
653
654 // Call for a repaint
655 invalidateWindow(false);
656
657 // Call the post-enter function
658 _currentScene->postEnterRoom(this, oldLocation);
659
660 // Invalidate this too, for some reason
661 _parent->invalidateWindow(false);
662
663 // Check the AI database for this environment to see if there is a spontaneous comment for this scene
664 if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI) && !_disableArthur)
665 playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
666
667 // Notify the right BioChip window of change
668 ((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
669
670 // Re-enable navigation arrows
671 ((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
672
673 // Hardcoded demo ambient
674 if (_vm->isDemo() && newSceneStaticData.location.environment != oldLocation.environment)
675 startDemoAmbientSound();
676
677 return true;
678 }
679
timeSuitJump(int destination)680 bool SceneViewWindow::timeSuitJump(int destination) {
681 // Determine if this is a valid jump
682 if (destination < 0 || destination > 4)
683 return false;
684
685 // Clear any live text
686 ((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText();
687
688 // Disable movement controls
689 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(0, 0, 0, 0, 0);
690
691 Location newLocation, oldLocation;
692
693 switch (destination) {
694 case 0:
695 newLocation = Location(2, 1, 6, 1, 1, 0);
696 oldLocation = Location(-2, -2, -2, -2, -2, -2);
697 break;
698 case 1:
699 newLocation = Location(1, 1, 3, 3, 1, 0);
700 oldLocation = Location(-2, -2, -2, -2, -2, -2);
701 break;
702 case 2:
703 newLocation = Location(5, 1, 2, 4, 1, 0);
704 oldLocation = Location(-2, -2, -2, -2, -2, -2);
705 break;
706 case 3:
707 if (_globalFlags.generalWalkthroughMode == 0)
708 newLocation = Location(6, 10, 0, 0, 0, 0);
709 else
710 newLocation = Location(6, 1, 1, 1, 2, 0);
711 oldLocation = Location(-2, -2, -2, -2, -2, -2);
712 break;
713 case 4:
714 newLocation = Location(4, 3, 3, 0, 1, 0);
715 oldLocation = Location(-2, -2, -2, -2, -2, -2);
716 break;
717 }
718
719 // Save the old location to use for the scene we are leaving
720 Location specOldLocation = oldLocation;
721
722 // Call the pre-transition function
723 if (_currentScene)
724 _currentScene->preExitRoom(this, specOldLocation);
725
726 // Make sure the right BioChip is displayed
727 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipJump);
728
729 // Play the movie
730 Common::ScopedPtr<VideoWindow> jumpMovie(new VideoWindow(_vm, ((GameUIWindow *)_parent)->_bioChipRightWindow));
731 if (!jumpMovie->openVideo(_vm->getFilePath(IDS_BC_JUMP_MOVIE_FILENAME)))
732 error("Failed to play small jump movie");
733
734 // Reposition
735 jumpMovie->setWindowPos(0, 0, 28, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
736
737 // Notify the BioChip of the change
738 ((GameUIWindow *)_parent)->_bioChipRightWindow->jumpInitiated(false);
739
740 // Show and disable the movie window
741 jumpMovie->enableWindow(false);
742 jumpMovie->showWindow(kWindowShow);
743
744 // Start fading down the current ambient and start the jump audio file
745 _vm->_sound->setAmbientSound();
746 _vm->_sound->playInterfaceSound(_vm->getFilePath(IDS_BC_JUMP_AUDIO_FILENAME));
747
748 // Play the movie
749 jumpMovie->playToFrame(24);
750
751 while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped && _vm->_sound->isInterfaceSoundPlaying()) {
752 _vm->yield();
753 _vm->_sound->timerCallback();
754 }
755
756 if (_vm->shouldQuit())
757 return true;
758
759 // Make sure the interface sound has stopped
760 _vm->_sound->stopInterfaceSound();
761
762 _vm->_sound->timerCallback();
763 jumpMovie.reset(new VideoWindow(_vm, this));
764
765 Common::String fileName;
766 switch (destination) {
767 case 0:
768 fileName = _vm->getFilePath(IDS_MAYAN_JUMP_MOVIE_FILENAME);
769 break;
770 case 1:
771 fileName = _vm->getFilePath(IDS_CASTLE_JUMP_MOVIE_FILENAME);
772 break;
773 case 2:
774 fileName = _vm->getFilePath(IDS_DAVINCI_JUMP_MOVIE_FILENAME);
775 break;
776 case 3:
777 fileName = _vm->getFilePath(IDS_AILAB_JUMP_MOVIE_FILENAME);
778 break;
779 case 4:
780 fileName = _vm->getFilePath(IDS_FUTAPT_JUMP_MOVIE_FILENAME);
781 break;
782 }
783
784 if (!jumpMovie->openVideo(fileName))
785 error("Failed to play movie '%s'", fileName.c_str());
786
787 jumpMovie->setWindowPos(0, 0, 0, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
788
789 // Show and disable the window
790 jumpMovie->enableWindow(false);
791 jumpMovie->showWindow(kWindowShow);
792
793 _vm->_sound->stop();
794
795 // Play the movie
796 jumpMovie->playVideo();
797
798 while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped)
799 _vm->yield();
800
801 if (_vm->shouldQuit())
802 return true;
803
804 _vm->_sound->restart();
805 jumpMovie.reset();
806
807 // Initialize the time zone and environment
808 initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
809 initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
810
811 // Get the static scene data for this new location
812 LocationStaticData newSceneStaticData;
813 if (!getSceneStaticData(newLocation, newSceneStaticData))
814 return false;
815
816 // And the old location
817 if (_currentScene)
818 oldLocation = _currentScene->_staticData.location;
819
820 // Create the new scene object
821 // The original passed oldLocation, but that's wrong for jumping. I mean, that's
822 // why David made specOldLocation in the first place. This prevents the wrong location
823 // being passed for the castle intro with the guard getting shot by the arrow.
824 SceneBase *newScene = constructSceneObject(this, newSceneStaticData, specOldLocation);
825
826 if (_currentScene) {
827 // Post-transition function
828 if (_currentScene->postExitRoom(this, specOldLocation) == SC_DEATH) {
829 newScene->preDestructor();
830 delete newScene;
831
832 return false;
833 }
834
835 // Delete the old scene
836 _currentScene->preDestructor();
837 delete _currentScene;
838 }
839
840 if (!newScene)
841 error("Failed to create scene object for time zone %d", destination);
842
843 // Set the new scene
844 _currentScene = newScene;
845
846 // If this scene has no cycle frames, flush the cycle frame cache
847 if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
848 flushCycleFrameCache();
849
850 // Update navigation buttons, if not cloaked
851 if (_globalFlags.bcCloakingEnabled != 1)
852 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
853
854 // Change the date if the time zone changed
855 if (newLocation.timeZone != oldLocation.timeZone)
856 ((GameUIWindow *)_parent)->changeCurrentDate(newLocation.timeZone);
857
858 // Call for a repaint
859 invalidateWindow(false);
860 _vm->_sound->timerCallback();
861
862 // Time to show and play the right-hand small movie to the mid point, with proper sound
863 jumpMovie.reset(new VideoWindow(_vm, ((GameUIWindow *)_parent)->_bioChipRightWindow));
864
865 if (!jumpMovie->openVideo(_vm->getFilePath(IDS_BC_JUMP_MOVIE_FILENAME)))
866 error("Failed to play small jump movie");
867
868 jumpMovie->setWindowPos(0, 0, 28, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
869
870 // Notify the BioChip of the change
871 ((GameUIWindow *)_parent)->_bioChipRightWindow->jumpEnded(false);
872
873 jumpMovie->enableWindow(false);
874 jumpMovie->showWindow(kWindowShow);
875
876 // Start the ambient fading back up, and play the jump sound
877 startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newLocation.timeZone, newLocation.environment);
878 _vm->_sound->playInterfaceSound(_vm->getFilePath(IDS_BC_JUMP_AUDIO_FILENAME));
879
880 // Play the movie
881 jumpMovie->seekToFrame(24);
882 jumpMovie->playToFrame(48);
883
884 while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped && _vm->_sound->isInterfaceSoundPlaying()) {
885 _vm->yield();
886 _vm->_sound->timerCallback();
887 }
888
889 if (_vm->shouldQuit())
890 return true;
891
892 // Forceably stop the interface sound
893 _vm->_sound->stopInterfaceSound();
894
895 // Destroy the movie
896 jumpMovie.reset();
897
898 // Repaint the BioChip view window
899 ((GameUIWindow *)_parent)->_bioChipRightWindow->invalidateWindow(false);
900
901 // Repaint the window
902 invalidateWindow(false);
903
904 // Call the post-enter function
905 _currentScene->postEnterRoom(this, oldLocation);
906 _parent->invalidateWindow(false);
907
908 if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI))
909 playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
910
911 // Notify the right BioChip window of the change
912 ((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
913
914 return true;
915 }
916
playTransition(const DestinationScene & destinationData,int navFrame)917 bool SceneViewWindow::playTransition(const DestinationScene &destinationData, int navFrame) {
918 // Call the appropriate function for the transition type
919 switch (destinationData.transitionType) {
920 case TRANSITION_PUSH:
921 if (_vm->isControlDown()) {
922 if (navFrame >= 0) {
923 LocationStaticData destinationStaticData;
924 if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
925 return false;
926
927 changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
928
929 Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
930 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
931 newBackground->free();
932 delete newBackground;
933 }
934 return true;
935 } else {
936 LocationStaticData destinationStaticData;
937 if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
938 return false;
939
940 Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
941 Graphics::Surface *curBackground = _preBuffer;
942
943 bool retVal = false;
944 if (destinationData.transitionData == 0 || destinationData.transitionData == 3)
945 retVal = pushTransition(curBackground, newBackground, destinationData.transitionData, _vm->_gfx->computeVPushOffset(_vm->getTransitionSpeed()), 0);
946 else
947 retVal = pushTransition(curBackground, newBackground, destinationData.transitionData, _vm->_gfx->computeHPushOffset(_vm->getTransitionSpeed()), 0);
948
949 newBackground->free();
950 delete newBackground;
951 return retVal;
952 }
953 case TRANSITION_WALK:
954 if (_vm->isControlDown()) {
955 if (navFrame >= 0) {
956 LocationStaticData destinationStaticData;
957 if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
958 return false;
959
960 changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
961
962 Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
963 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
964 newBackground->free();
965 delete newBackground;
966 }
967 return true;
968 } else {
969 // The demo has a hardcoded door open sound
970 // This, and the code below the walkTransition call, are glue around the
971 // demo's sound implementation. The demo is based on an alpha which uses
972 // waveOut to play sounds, as opposed to the final which uses WAIL.
973 if (_vm->isDemo() && destinationData.destinationScene.depth == 1) {
974 // Stop the current ambient sound
975 // onTimer() will restart it
976 _vm->_sound->setAmbientSound();
977
978 if (_currentScene->_staticData.location.environment == 4)
979 _demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGMBDO.WAV");
980 else
981 _demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGBSDO.WAV");
982 }
983
984 bool retVal = walkTransition(_currentScene->_staticData.location, destinationData, navFrame);
985
986 // And also a door close sound
987 if (_vm->isDemo() && destinationData.destinationScene.environment != _currentScene->_staticData.location.environment) {
988 // Stop the current ambient sound
989 // onTimer() will restart it
990 _vm->_sound->setAmbientSound();
991
992 if (_currentScene->_staticData.location.environment == 4)
993 _demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGBSDC.WAV");
994 else
995 _demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGMBDC.WAV");
996 }
997
998 return retVal;
999 }
1000 case TRANSITION_VIDEO:
1001 if (_vm->isControlDown() && false) { // TODO: debug mode check (maybe?)
1002 if (navFrame >= 0) {
1003 LocationStaticData destinationStaticData;
1004 if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
1005 return false;
1006
1007 changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
1008
1009 Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
1010 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
1011 newBackground->free();
1012 delete newBackground;
1013 }
1014 return true;
1015 } else {
1016 return videoTransition(_currentScene->_staticData.location, destinationData, navFrame);
1017 }
1018 }
1019
1020 return false;
1021 }
1022
videoTransition(const Location & location,DestinationScene destinationData,int navFrame)1023 bool SceneViewWindow::videoTransition(const Location &location, DestinationScene destinationData, int navFrame) {
1024 TempCursorChange cursorChange(kCursorWait);
1025
1026 _paused = true;
1027 bool audioStream = true;
1028
1029 // If the start frame is less than 0, open up the animation database and retrieve all of the info
1030 if (destinationData.transitionStartFrame < 0) {
1031 Common::Array<AnimEvent> animEvents = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
1032
1033 bool found = false;
1034 uint i = 0;
1035 for (; i < animEvents.size(); i++) {
1036 if (animEvents[i].animationID == destinationData.transitionData) {
1037 found = true;
1038 break;
1039 }
1040 }
1041
1042 if (!found) {
1043 _paused = false;
1044 return false;
1045 }
1046
1047 destinationData.transitionData = animEvents[i].fileNameID;
1048 destinationData.transitionStartFrame = animEvents[i].startFrame;
1049 destinationData.transitionLength = animEvents[i].frameCount;
1050
1051 if (animEvents[i].audioStreamCount < 1)
1052 audioStream = false;
1053 }
1054
1055 LocationStaticData destinationStaticData;
1056 if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData)) {
1057 _paused = false;
1058 return false;
1059 }
1060
1061 changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
1062
1063 Graphics::Surface *newBackground = nullptr;
1064 if (destinationStaticData.navFrameIndex >= 0)
1065 newBackground = getStillFrameCopy(navFrame);
1066
1067 // Open the movie
1068 Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
1069
1070 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, destinationData.transitionData);
1071 if (!animationMovie->openVideo(fileName))
1072 error("Failed to open video transition movie '%s'", fileName.c_str());
1073
1074 if (audioStream)
1075 _vm->_sound->stop();
1076
1077 animationMovie->seekToFrame(destinationData.transitionStartFrame);
1078 animationMovie->showWindow(kWindowShow);
1079 animationMovie->playToFrame(destinationData.transitionStartFrame + destinationData.transitionLength - 1);
1080
1081 while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
1082 _vm->yield();
1083 _vm->_sound->timerCallback();
1084 }
1085
1086 if (_vm->shouldQuit()) {
1087 newBackground->free();
1088 delete newBackground;
1089
1090 return true;
1091 }
1092
1093 animationMovie.reset();
1094
1095 if (audioStream)
1096 _vm->_sound->restart();
1097
1098 if (newBackground) {
1099 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
1100 newBackground->free();
1101 delete newBackground;
1102 }
1103
1104 _paused = false;
1105
1106 return true;
1107 }
1108
walkTransition(const Location & location,const DestinationScene & destinationData,int navFrame)1109 bool SceneViewWindow::walkTransition(const Location &location, const DestinationScene &destinationData, int navFrame) {
1110 _paused = true;
1111 TempCursorChange cursorChange(kCursorWait);
1112 Graphics::Surface *newBackground = nullptr;
1113
1114 if (navFrame >= 0) {
1115 changeStillFrameMovie(_vm->getFilePath(destinationData.destinationScene.timeZone, destinationData.destinationScene.environment, SF_STILLS));
1116 newBackground = getStillFrameCopy(navFrame);
1117 }
1118
1119 Common::String walkFileName = _vm->getFilePath(location.timeZone, location.environment, SF_NAVIGATION);
1120 if (_walkMovieFileName != walkFileName) {
1121 delete _walkMovie;
1122 _walkMovie = new VideoWindow(_vm, this);
1123 _walkMovie->setWindowPos(kWindowPosTop, 0, 0, 0, 0, kWindowPosNoActivate | kWindowPosNoZOrder | kWindowPosNoSize);
1124
1125 if (!_walkMovie->openVideo(walkFileName))
1126 error("Failed to open walk movie '%s'", walkFileName.c_str());
1127
1128 _walkMovieFileName = walkFileName;
1129 }
1130
1131 _vm->_sound->timerCallback(); // necessary?
1132
1133 _walkMovie->seekToFrame(destinationData.transitionStartFrame);
1134
1135 if (navFrame < 0) {
1136 // FIXME: Is this possible?
1137 _paused = false;
1138 return true;
1139 }
1140
1141 _walkMovie->showWindow(kWindowShow);
1142 _walkMovie->invalidateWindow(false);
1143
1144 // Start the footsteps
1145 _vm->_sound->startFootsteps(destinationData.transitionData);
1146
1147 _walkMovie->playToFrame(destinationData.transitionStartFrame + destinationData.transitionLength - 1);
1148 while (!_vm->shouldQuit() && _walkMovie->getMode() != VideoWindow::kModeStopped) {
1149 _vm->yield();
1150 _vm->_sound->timerCallback();
1151 }
1152
1153 if (_vm->shouldQuit()) {
1154 newBackground->free();
1155 delete newBackground;
1156
1157 return true;
1158 }
1159
1160 _vm->_sound->stopFootsteps();
1161
1162 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
1163 newBackground->free();
1164 delete newBackground;
1165
1166 _walkMovie->showWindow(kWindowHide);
1167 _paused = false;
1168
1169 return true;
1170 }
1171
pushTransition(Graphics::Surface * curBackground,Graphics::Surface * newBackground,int direction,uint stripSize,int totalTime)1172 bool SceneViewWindow::pushTransition(Graphics::Surface *curBackground, Graphics::Surface *newBackground, int direction, uint stripSize, int totalTime) {
1173 // Check the validity of the parameters
1174 if (!curBackground || !newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
1175 return false;
1176
1177 // Change the cursor to an hourglass
1178 TempCursorChange cursorChange(kCursorWait);
1179 _useScenePaint = false;
1180
1181 switch (direction) {
1182 case 0: // Push down
1183 for (int i = 0; i < DIB_FRAME_HEIGHT; i += stripSize) {
1184 curBackground->move(0, stripSize, curBackground->h);
1185
1186 for (uint j = 0; j < stripSize; j++)
1187 memcpy(curBackground->getBasePtr(0, j), newBackground->getBasePtr(0, curBackground->h - (i + stripSize) + j), newBackground->w * newBackground->format.bytesPerPixel);
1188
1189 invalidateWindow(false);
1190 _vm->yield();
1191 }
1192 break;
1193 case 1: // Push right
1194 for (int i = 0; i < DIB_FRAME_WIDTH; i += stripSize) {
1195 curBackground->move(stripSize, 0, curBackground->h);
1196
1197 for (int j = 0; j < curBackground->h; j++)
1198 memcpy(curBackground->getBasePtr(0, j), newBackground->getBasePtr(newBackground->w - (i + stripSize), j), stripSize * newBackground->format.bytesPerPixel);
1199
1200 invalidateWindow(false);
1201 _vm->yield();
1202 }
1203 break;
1204 case 2: // Push left
1205 for (int i = 0; i < DIB_FRAME_WIDTH; i += stripSize) {
1206 curBackground->move(-stripSize, 0, curBackground->h);
1207
1208 for (int j = 0; j < curBackground->h; j++)
1209 memcpy(curBackground->getBasePtr(curBackground->w - (int)stripSize, j), newBackground->getBasePtr(i, j), stripSize * newBackground->format.bytesPerPixel);
1210
1211 invalidateWindow(false);
1212 _vm->yield();
1213 }
1214 break;
1215 case 3: // Push up
1216 for (int i = 0; i < DIB_FRAME_HEIGHT; i += stripSize) {
1217 curBackground->move(0, -stripSize, curBackground->h);
1218
1219 for (uint j = 0; j < stripSize; j++)
1220 memcpy(curBackground->getBasePtr(0, curBackground->h - stripSize + j), newBackground->getBasePtr(0, i + j), newBackground->w * newBackground->format.bytesPerPixel);
1221
1222 invalidateWindow(false);
1223 _vm->yield();
1224 }
1225 break;
1226 }
1227
1228 _useScenePaint = true;
1229 return true;
1230 }
1231
pushNewTransition(Graphics::Surface * newBackground,int direction,int stripSize,int totalTime)1232 bool SceneViewWindow::pushNewTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
1233 // Check the validity of the parameters
1234 if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
1235 return false;
1236
1237 // Call the push transition function
1238 if (direction == 0 || direction == 3)
1239 return pushTransition(_preBuffer, newBackground, direction, _vm->_gfx->computeVPushOffset(_vm->getTransitionSpeed()), totalTime);
1240
1241 return pushTransition(_preBuffer, newBackground, direction, _vm->_gfx->computeHPushOffset(_vm->getTransitionSpeed()), totalTime);
1242 }
1243
slideInTransition(Graphics::Surface * newBackground,int direction,int stripSize,int totalTime)1244 bool SceneViewWindow::slideInTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
1245 // Check the validity of the parameters
1246 if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
1247 return false;
1248
1249 // Change the cursor to an hourglass
1250 TempCursorChange cursorChange(kCursorWait);
1251
1252 switch (direction) {
1253 case 0: // Push down
1254 for (uint i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
1255 for (uint j = 0; j < i; j++)
1256 memcpy(_preBuffer->getBasePtr(0, j), newBackground->getBasePtr(0, DIB_FRAME_HEIGHT - j), newBackground->w * newBackground->format.bytesPerPixel);
1257
1258 invalidateWindow(false);
1259 _vm->yield();
1260 }
1261 break;
1262 case 1: // Push right
1263 for (uint i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
1264 for (uint j = 0; j < DIB_FRAME_HEIGHT; j++)
1265 memcpy(_preBuffer->getBasePtr(0, j), newBackground->getBasePtr(DIB_FRAME_WIDTH - i, j), i * newBackground->format.bytesPerPixel);
1266
1267 invalidateWindow(false);
1268 _vm->yield();
1269 }
1270 break;
1271 case 2: // Push left
1272 for (uint i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
1273 for (uint j = 0; j < DIB_FRAME_HEIGHT; j++)
1274 memcpy(_preBuffer->getBasePtr(0, DIB_FRAME_WIDTH - i), newBackground->getBasePtr(0, j), i * newBackground->format.bytesPerPixel);
1275
1276 invalidateWindow(false);
1277 _vm->yield();
1278 }
1279 break;
1280 case 3: // Push up
1281 for (uint i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
1282 for (uint j = 0; j < i; j++)
1283 memcpy(_preBuffer->getBasePtr(0, DIB_FRAME_HEIGHT - j), newBackground->getBasePtr(0, j), newBackground->w * newBackground->format.bytesPerPixel);
1284
1285 invalidateWindow(false);
1286 _vm->yield();
1287 }
1288 break;
1289 }
1290
1291 return true;
1292 }
1293
slideOutTransition(Graphics::Surface * newBackground,int direction,int stripSize,int totalTime)1294 bool SceneViewWindow::slideOutTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
1295 // Check the validity of the parameters
1296 if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
1297 return false;
1298
1299 // Change the cursor to an hourglass
1300 TempCursorChange cursorChange(kCursorWait);
1301
1302 Graphics::Surface curBackground;
1303 curBackground.copyFrom(*_preBuffer);
1304 _useScenePaint = false;
1305
1306 switch (direction) {
1307 case 0: // Push down
1308 for (int i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
1309 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
1310 _vm->_gfx->crossBlit(_preBuffer, 0, i, 432, 189 - i, &curBackground, 0, 0);
1311 invalidateWindow(false);
1312 _vm->yield();
1313 }
1314 break;
1315 case 1: // Push right
1316 for (int i = DIB_FRAME_WIDTH; i >= 0; i -= stripSize) {
1317 if (i < DIB_FRAME_WIDTH)
1318 _vm->_gfx->crossBlit(_preBuffer, i, 0, DIB_FRAME_WIDTH - i, 189, newBackground, i, 0);
1319 _vm->_gfx->crossBlit(_preBuffer, 0, 0, i, 189, &curBackground, DIB_FRAME_WIDTH - i, 0);
1320 invalidateWindow(false);
1321 _vm->yield();
1322 }
1323 break;
1324 case 2: // Push left
1325 for (int i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
1326 _vm->_gfx->crossBlit(_preBuffer, 0, 0, i, 189, newBackground, 0, 0);
1327 _vm->_gfx->crossBlit(_preBuffer, i, 0, 432 - i, 189, &curBackground, 0, 0);
1328 invalidateWindow(false);
1329 _vm->yield();
1330 }
1331 break;
1332 case 3: // Push up
1333 for (int i = DIB_FRAME_HEIGHT; i >= 0; i -= stripSize) {
1334 _vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
1335 _vm->_gfx->crossBlit(_preBuffer, 0, 189 - i, 432, i, &curBackground, 0, 0);
1336 invalidateWindow(false);
1337 _vm->yield();
1338 }
1339 break;
1340 }
1341
1342 curBackground.free();
1343 _useScenePaint = true;
1344 return true;
1345 }
1346
changeStillFrameMovie(const Common::String & fileName)1347 bool SceneViewWindow::changeStillFrameMovie(const Common::String &fileName) {
1348 return _stillFrames->open(fileName);
1349 }
1350
changeCycleFrameMovie(const Common::String & fileName)1351 bool SceneViewWindow::changeCycleFrameMovie(const Common::String &fileName) {
1352 // Only continue if cycling is enabled
1353 if (!isCyclingEnabled()) {
1354 return false;
1355 }
1356
1357 if (((FrameWindow *)(_parent->getParent()))->isFrameCachingAllowed())
1358 return _cycleFrames->open(fileName, 5);
1359
1360 return _cycleFrames->open(fileName);
1361 }
1362
getStillFrameCopy(int frameIndex)1363 Graphics::Surface *SceneViewWindow::getStillFrameCopy(int frameIndex) {
1364 return _stillFrames->getFrameCopy(frameIndex);
1365 }
1366
getStillFrame(int frameIndex)1367 const Graphics::Surface *SceneViewWindow::getStillFrame(int frameIndex) {
1368 return _stillFrames->getFrame(frameIndex);
1369 }
1370
getCycleFrameCopy(int frameIndex)1371 Graphics::Surface *SceneViewWindow::getCycleFrameCopy(int frameIndex) {
1372 if (!isCyclingEnabled())
1373 return 0;
1374
1375 return _cycleFrames->getFrameCopy(frameIndex);
1376 }
1377
getCycleFrame(int frameIndex)1378 const Graphics::Surface *SceneViewWindow::getCycleFrame(int frameIndex) {
1379 if (!isCyclingEnabled())
1380 return 0;
1381
1382 return _cycleFrames->getFrame(frameIndex);
1383 }
1384
enableCycleFrameCache(bool enable)1385 bool SceneViewWindow::enableCycleFrameCache(bool enable) {
1386 if (!isCyclingEnabled())
1387 return false;
1388
1389 _cycleFrames->enableFrameCache(enable);
1390 return true;
1391 }
1392
flushCycleFrameCache()1393 bool SceneViewWindow::flushCycleFrameCache() {
1394 if (!isCyclingEnabled())
1395 return false;
1396
1397 return _cycleFrames->flushFrameCache();
1398 }
1399
enableCycling(bool enable)1400 bool SceneViewWindow::enableCycling(bool enable) {
1401 bool oldStatus = isCyclingEnabled();
1402 _cycleEnabled = enable;
1403
1404 if (oldStatus != isCyclingEnabled())
1405 handleCyclingChange();
1406
1407 return true;
1408 }
1409
forceEnableCycling(bool enable)1410 bool SceneViewWindow::forceEnableCycling(bool enable) {
1411 bool oldStatus = isCyclingEnabled();
1412 _forceCycleEnabled = enable;
1413
1414 if (oldStatus != isCyclingEnabled())
1415 handleCyclingChange();
1416
1417 return true;
1418 }
1419
handleCyclingChange()1420 void SceneViewWindow::handleCyclingChange() {
1421 if (isCyclingEnabled()) {
1422 // Re-enabling -> set up the cycle movie again
1423 if (_currentScene) {
1424 const LocationStaticData &staticData = _currentScene->_staticData;
1425 if (staticData.cycleStartFrame >= 0)
1426 changeCycleFrameMovie(_vm->getFilePath(staticData.location.timeZone, staticData.location.environment, SF_CYCLES));
1427 }
1428 } else {
1429 // Disabling -> close the cycle movie
1430 _cycleFrames->flushFrameCache();
1431 _cycleFrames->close();
1432 }
1433 }
1434
closeCycleFrameMovie()1435 bool SceneViewWindow::closeCycleFrameMovie() {
1436 _cycleFrames->close();
1437 return true;
1438 }
1439
getGlobalFlag(int offset)1440 int SceneViewWindow::getGlobalFlag(int offset) {
1441 // TODO: Verify the offset
1442 const byte *data = (const byte *)&_globalFlags;
1443 return READ_UINT16(data + offset);
1444 }
1445
getGlobalFlagByte(int offset)1446 byte SceneViewWindow::getGlobalFlagByte(int offset) {
1447 // TODO: Verify the offset
1448
1449 if (offset < 0)
1450 return 0;
1451
1452 const byte *data = (const byte *)&_globalFlags;
1453 return data[offset];
1454 }
1455
setGlobalFlag(int offset,int value)1456 bool SceneViewWindow::setGlobalFlag(int offset, int value) {
1457 // TODO: Verify the offset
1458
1459 byte *data = (byte *)&_globalFlags;
1460 WRITE_UINT16(data + offset, value);
1461 return true;
1462 }
1463
setGlobalFlagByte(int offset,byte value)1464 bool SceneViewWindow::setGlobalFlagByte(int offset, byte value) {
1465 // TODO: Verify the offset
1466
1467 byte *data = (byte *)&_globalFlags;
1468 data[offset] = value;
1469 return true;
1470 }
1471
getGlobalFlagDWord(int offset)1472 uint32 SceneViewWindow::getGlobalFlagDWord(int offset) {
1473 // TODO: Verify the offset
1474 const byte *data = (const byte *)&_globalFlags;
1475 return READ_UINT32(data + offset);
1476 }
1477
setGlobalFlagDWord(int offset,uint32 value)1478 bool SceneViewWindow::setGlobalFlagDWord(int offset, uint32 value) {
1479 // TODO: Verify the offset
1480
1481 byte *data = (byte *)&_globalFlags;
1482 WRITE_UINT32(data + offset, value);
1483 return true;
1484 }
1485
addNumberToGlobalFlagTable(int tableOffset,int curItemCountOffset,int maxItems,byte numberToAdd)1486 bool SceneViewWindow::addNumberToGlobalFlagTable(int tableOffset, int curItemCountOffset, int maxItems, byte numberToAdd) {
1487 // TODO: Rewrite this
1488 byte *data = (byte *)&_globalFlags;
1489 byte *itemCountPtr = data + curItemCountOffset;
1490 int itemCount = *itemCountPtr;
1491
1492 if (itemCount >= maxItems)
1493 return false;
1494
1495 byte *tableEntries = data + tableOffset;
1496 for (int i = 0; i < itemCount; i++)
1497 if (tableEntries[i] == numberToAdd)
1498 return false;
1499
1500 tableEntries[itemCount] = numberToAdd;
1501 *itemCountPtr = itemCount + 1;
1502 return true;
1503 }
1504
getNumberFromGlobalFlagTable(int tableOffset,int tableIndex)1505 byte SceneViewWindow::getNumberFromGlobalFlagTable(int tableOffset, int tableIndex) {
1506 const byte *data = (const byte *)&_globalFlags;
1507 return data[tableOffset + tableIndex];
1508 }
1509
isNumberInGlobalFlagTable(int tableOffset,int curItemCountOffset,byte numberToCheck)1510 bool SceneViewWindow::isNumberInGlobalFlagTable(int tableOffset, int curItemCountOffset, byte numberToCheck) {
1511 const byte *data = (const byte *)&_globalFlags;
1512 int itemCount = *(data + curItemCountOffset);
1513
1514 const byte *tableEntries = data + tableOffset;
1515
1516 for (int i = 0; i < itemCount; i++)
1517 if (tableEntries[i] == numberToCheck)
1518 return true;
1519
1520 return false;
1521 }
1522
getCurrentSceneLocation(Location & location)1523 bool SceneViewWindow::getCurrentSceneLocation(Location &location) {
1524 if (!_currentScene)
1525 return false;
1526
1527 location = _currentScene->_staticData.location;
1528 return true;
1529 }
1530
playSynchronousAnimation(int animationID)1531 bool SceneViewWindow::playSynchronousAnimation(int animationID) {
1532 TempCursorChange cursorChange(kCursorWait);
1533
1534 Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
1535
1536 bool found = false;
1537 uint i = 0;
1538 for (; i < animDatabase.size(); i++) {
1539 if (animDatabase[i].animationID == animationID) {
1540 found = true;
1541 break;
1542 }
1543 }
1544
1545 if (!found)
1546 return false;
1547
1548 Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
1549 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
1550 if (!animationMovie->openVideo(fileName))
1551 error("Failed to open video '%s'", fileName.c_str());
1552
1553 // Switch to the second audio stream if translation is enabled
1554 if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
1555 animationMovie->setAudioTrack(2);
1556
1557 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
1558 return false;
1559
1560 animationMovie->seekToFrame(animDatabase[i].startFrame);
1561 animationMovie->enableWindow(false);
1562 animationMovie->showWindow(kWindowShow);
1563 _parent->invalidateWindow(false);
1564
1565 // Empty the input queue
1566 _vm->removeMouseMessages(this);
1567 _vm->removeKeyboardMessages(this);
1568
1569 // Stop background sound if the video has sound
1570 if (animDatabase[i].audioStreamCount > 0)
1571 _vm->_sound->stop();
1572
1573 animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
1574
1575 while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
1576 _vm->yield();
1577 _vm->_sound->timerCallback();
1578 }
1579
1580 if (_vm->shouldQuit())
1581 return true;
1582
1583 _vm->removeMouseMessages(this);
1584 _vm->removeKeyboardMessages(this);
1585
1586 // Restart background sound if the video had sound
1587 if (animDatabase[i].audioStreamCount > 0)
1588 _vm->_sound->restart();
1589
1590 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
1591 return false;
1592
1593 return true;
1594 }
1595
playSynchronousAnimationExtern(int animationID)1596 bool SceneViewWindow::playSynchronousAnimationExtern(int animationID) {
1597 TempCursorChange cursorChange(kCursorWait);
1598
1599 Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
1600 Common::String fileName = _vm->getFilePath(animationID);
1601 if (!animationMovie->openVideo(fileName))
1602 error("Failed to open video '%s'", fileName.c_str());
1603
1604 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
1605 return false;
1606
1607 animationMovie->enableWindow(false);
1608 animationMovie->showWindow(kWindowShow);
1609 _parent->invalidateWindow(false);
1610
1611 // Empty the input queue
1612 _vm->removeMouseMessages(this);
1613 _vm->removeKeyboardMessages(this);
1614
1615 _vm->_sound->stop();
1616 animationMovie->playVideo();
1617
1618 while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
1619 _vm->yield();
1620 _vm->_sound->timerCallback();
1621 }
1622
1623 if (_vm->shouldQuit())
1624 return true;
1625
1626 _vm->_sound->restart();
1627 _vm->removeMouseMessages(this);
1628 _vm->removeKeyboardMessages(this);
1629
1630 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
1631 return false;
1632
1633 return true;
1634 }
1635
playPlacedSynchronousAnimation(int animationID,int left,int top)1636 bool SceneViewWindow::playPlacedSynchronousAnimation(int animationID, int left, int top) {
1637 TempCursorChange cursorChange(kCursorWait);
1638
1639 Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
1640
1641 bool found = false;
1642 uint i = 0;
1643 for (; i < animDatabase.size(); i++) {
1644 if (animDatabase[i].animationID == animationID) {
1645 found = true;
1646 break;
1647 }
1648 }
1649
1650 if (!found)
1651 return false;
1652
1653 Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
1654 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
1655 if (!animationMovie->openVideo(fileName))
1656 error("Failed to open video '%s'", fileName.c_str());
1657
1658 animationMovie->setWindowPos(kWindowPosTopMost, left, top, 0, 0, kWindowPosNoSize | kWindowPosNoActivate | kWindowPosNoZOrder);
1659
1660 // Switch to the second audio stream if translation is enabled
1661 if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
1662 animationMovie->setAudioTrack(2);
1663
1664 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
1665 return false;
1666
1667 animationMovie->seekToFrame(animDatabase[i].startFrame);
1668 animationMovie->enableWindow(false);
1669 animationMovie->showWindow(kWindowShow);
1670 _parent->invalidateWindow(false);
1671
1672 // Empty the input queue
1673 _vm->removeMouseMessages(this);
1674 _vm->removeKeyboardMessages(this);
1675
1676 // Stop background sound if the video has sound
1677 if (animDatabase[i].audioStreamCount > 0)
1678 _vm->_sound->stop();
1679
1680 animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
1681
1682 while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
1683 _vm->yield();
1684 _vm->_sound->timerCallback();
1685 }
1686
1687 if (_vm->shouldQuit())
1688 return true;
1689
1690 _vm->removeMouseMessages(this);
1691 _vm->removeKeyboardMessages(this);
1692
1693 // Restart background sound if the video had sound
1694 if (animDatabase[i].audioStreamCount > 0)
1695 _vm->_sound->restart();
1696
1697 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
1698 return false;
1699
1700 return true;
1701 }
1702
playClippedSynchronousAnimation(int animationID,int left,int top,int right,int bottom)1703 bool SceneViewWindow::playClippedSynchronousAnimation(int animationID, int left, int top, int right, int bottom) {
1704 TempCursorChange cursorChange(kCursorWait);
1705
1706 Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
1707
1708 bool found = false;
1709 uint i = 0;
1710 for (; i < animDatabase.size(); i++) {
1711 if (animDatabase[i].animationID == animationID) {
1712 found = true;
1713 break;
1714 }
1715 }
1716
1717 if (!found)
1718 return false;
1719
1720 Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
1721 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
1722 if (!animationMovie->openVideo(fileName))
1723 error("Failed to open video '%s'", fileName.c_str());
1724
1725 animationMovie->setWindowPos(kWindowPosTopMost, left, top, right - left, bottom - top, kWindowPosNoActivate | kWindowPosNoZOrder);
1726
1727 animationMovie->setSourceRect(Common::Rect(left, top, right, bottom));
1728 animationMovie->setDestRect(Common::Rect(0, 0, right - left, bottom - top));
1729
1730 // Switch to the second audio stream if translation is enabled
1731 if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
1732 animationMovie->setAudioTrack(2);
1733
1734 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
1735 return false;
1736
1737 animationMovie->seekToFrame(animDatabase[i].startFrame);
1738 animationMovie->enableWindow(false);
1739 animationMovie->showWindow(kWindowShow);
1740 _parent->invalidateWindow(false);
1741
1742 // Empty the input queue
1743 _vm->removeMouseMessages(this);
1744 _vm->removeKeyboardMessages(this);
1745
1746 // Stop background sound if the video has sound
1747 if (animDatabase[i].audioStreamCount > 0)
1748 _vm->_sound->stop();
1749
1750 animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
1751
1752 while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
1753 _vm->yield();
1754 _vm->_sound->timerCallback();
1755 }
1756
1757 if (_vm->shouldQuit())
1758 return true;
1759
1760 _vm->removeMouseMessages(this);
1761 _vm->removeKeyboardMessages(this);
1762
1763 // Restart background sound if the video had sound
1764 if (animDatabase[i].audioStreamCount > 0)
1765 _vm->_sound->restart();
1766
1767 if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
1768 return false;
1769
1770 return true;
1771 }
1772
startAsynchronousAnimation(int animationID,bool loopAnimation)1773 bool SceneViewWindow::startAsynchronousAnimation(int animationID, bool loopAnimation) {
1774 return startPlacedAsynchronousAnimation(0, 0, 432, 189, animationID, loopAnimation);
1775 }
1776
startAsynchronousAnimation(int fileNameID,int startPosition,int playStartPosition,int frameCount,bool loopAnimation)1777 bool SceneViewWindow::startAsynchronousAnimation(int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
1778 return startPlacedAsynchronousAnimation(0, 0, 432, 189, fileNameID, startPosition, playStartPosition, frameCount, loopAnimation);
1779 }
1780
startAsynchronousAnimationExtern(int fileNameID,int startPosition,int playStartPosition,int frameCount,bool loopAnimation)1781 bool SceneViewWindow::startAsynchronousAnimationExtern(int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
1782 return startPlacedAsynchronousAnimationExtern(0, 0, 432, 189, fileNameID, startPosition, playStartPosition, frameCount, loopAnimation);
1783 }
1784
stopAsynchronousAnimation()1785 bool SceneViewWindow::stopAsynchronousAnimation() {
1786 if (!_currentScene)
1787 return false;
1788
1789 if (!_asyncMovie)
1790 return false;
1791
1792 _asyncMovie->stopVideo();
1793
1794 if (!_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_STOPPED))
1795 return false;
1796
1797 delete _asyncMovie;
1798 _asyncMovie = nullptr;
1799 _asyncMovieFileName.clear();
1800 _asyncMovieStartFrame = 0;
1801 _asyncMovieFrameCount = 0;
1802 _loopAsyncMovie = false;
1803
1804 return true;
1805 }
1806
isAsynchronousAnimationStillPlaying()1807 bool SceneViewWindow::isAsynchronousAnimationStillPlaying() {
1808 if (!_asyncMovie)
1809 return false;
1810
1811 return _asyncMovie->getMode() != VideoWindow::kModeStopped;
1812 }
1813
getAsynchronousAnimationCurrentPosition()1814 int SceneViewWindow::getAsynchronousAnimationCurrentPosition() {
1815 if (!_asyncMovie)
1816 return -1;
1817
1818 return _asyncMovie->getCurFrame();
1819 }
1820
asynchronousAnimationTimerCallback()1821 bool SceneViewWindow::asynchronousAnimationTimerCallback() {
1822 if (!_asyncMovie)
1823 return false;
1824
1825 if (_asyncMovie->getMode() == VideoWindow::kModeStopped) {
1826 if (_loopAsyncMovie) {
1827 _asyncMovie->seekToFrame(_asyncMovieStartFrame);
1828 _asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
1829
1830 if (_currentScene && _currentScene->movieCallback(this, _asyncMovie, -1, MOVIE_LOOPING_RESTART) == SC_FALSE)
1831 return false;
1832 } else {
1833 if (_currentScene) {
1834 if (_currentScene->movieCallback(this, _asyncMovie, -1, MOVIE_STOPPED) == SC_TRUE) {
1835 stopAsynchronousAnimation();
1836 return true;
1837 }
1838
1839 return false;
1840 } else {
1841 stopAsynchronousAnimation();
1842 }
1843 }
1844 }
1845
1846 return true;
1847 }
1848
startPlacedAsynchronousAnimation(int left,int top,int width,int height,int animationID,bool loopAnimation)1849 bool SceneViewWindow::startPlacedAsynchronousAnimation(int left, int top, int width, int height, int animationID, bool loopAnimation) {
1850 if (!_currentScene)
1851 return false;
1852
1853 if (_walkMovie) {
1854 delete _walkMovie;
1855 _walkMovie = nullptr;
1856 _walkMovieFileName.clear();
1857 }
1858
1859 Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
1860
1861 if (animDatabase.empty())
1862 return false;
1863
1864 const AnimEvent *animData = nullptr;
1865
1866 for (uint i = 0; i < animDatabase.size() && !animData; i++)
1867 if (animDatabase[i].animationID == animationID)
1868 animData = &animDatabase[i];
1869
1870 if (!animData)
1871 return false;
1872
1873 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animData->fileNameID);
1874
1875 if (fileName != _asyncMovieFileName) {
1876 _asyncMovieFileName.clear();
1877
1878 if (_asyncMovie) {
1879 _asyncMovie->stopVideo();
1880 _asyncMovie->closeVideo();
1881 } else {
1882 _asyncMovie = new VideoWindow(_vm, this);
1883 }
1884
1885 if (!_asyncMovie->openVideo(fileName))
1886 return false;
1887
1888 _asyncMovieFileName = fileName;
1889 }
1890
1891 _asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
1892 _asyncMovie->enableWindow(false);
1893
1894 _asyncMovieStartFrame = animData->startFrame;
1895 _asyncMovieFrameCount = animData->frameCount;
1896 _loopAsyncMovie = loopAnimation;
1897
1898 if (_currentScene->movieCallback(this, _asyncMovie, animationID, MOVIE_START) == SC_FALSE)
1899 return false;
1900
1901 _asyncMovie->seekToFrame(animData->startFrame);
1902 _asyncMovie->showWindow(kWindowShow);
1903 _asyncMovie->playToFrame(animData->startFrame + animData->frameCount - 1);
1904
1905 return true;
1906 }
1907
startPlacedAsynchronousAnimation(int left,int top,int width,int height,int fileNameID,int startPosition,int playStartPosition,int frameCount,bool loopAnimation)1908 bool SceneViewWindow::startPlacedAsynchronousAnimation(int left, int top, int width, int height, int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
1909 if (!_currentScene)
1910 return false;
1911
1912 if (_walkMovie) {
1913 delete _walkMovie;
1914 _walkMovie = nullptr;
1915 _walkMovieFileName.clear();
1916 }
1917
1918 Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, fileNameID);
1919
1920 if (fileName != _asyncMovieFileName) {
1921 _asyncMovieFileName.clear();
1922
1923 if (_asyncMovie) {
1924 _asyncMovie->stopVideo();
1925 _asyncMovie->closeVideo();
1926 } else {
1927 _asyncMovie = new VideoWindow(_vm, this);
1928 }
1929
1930 if (!_asyncMovie->openVideo(fileName))
1931 return false;
1932
1933 _asyncMovieFileName = fileName;
1934 }
1935
1936 _asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
1937 _asyncMovie->enableWindow(false);
1938
1939 _asyncMovieStartFrame = (startPosition < 0) ? 0 : startPosition;
1940 _asyncMovieFrameCount = (frameCount < 0) ? _asyncMovie->getFrameCount() : frameCount;
1941 _loopAsyncMovie = loopAnimation;
1942
1943 if (_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_START) == SC_FALSE)
1944 return false;
1945
1946 _asyncMovie->seekToFrame((playStartPosition < 0) ? 0 : playStartPosition);
1947 _asyncMovie->showWindow(kWindowShow);
1948 _asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
1949
1950 return true;
1951 }
1952
startPlacedAsynchronousAnimationExtern(int left,int top,int width,int height,int fileNameID,int startPosition,int playStartPosition,int frameCount,bool loopAnimation)1953 bool SceneViewWindow::startPlacedAsynchronousAnimationExtern(int left, int top, int width, int height, int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
1954 if (!_currentScene)
1955 return false;
1956
1957 if (_walkMovie) {
1958 delete _walkMovie;
1959 _walkMovie = nullptr;
1960 _walkMovieFileName.clear();
1961 }
1962
1963 Common::String fileName = _vm->getFilePath(fileNameID);
1964
1965 if (fileName != _asyncMovieFileName) {
1966 _asyncMovieFileName.clear();
1967
1968 if (_asyncMovie) {
1969 _asyncMovie->stopVideo();
1970 _asyncMovie->closeVideo();
1971 } else {
1972 _asyncMovie = new VideoWindow(_vm, this);
1973 }
1974
1975 if (!_asyncMovie->openVideo(fileName))
1976 return false;
1977
1978 _asyncMovieFileName = fileName;
1979 }
1980
1981 _asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
1982 _asyncMovie->enableWindow(false);
1983
1984 _asyncMovieStartFrame = (startPosition < 0) ? 0 : startPosition;
1985 _asyncMovieFrameCount = (frameCount < 0) ? _asyncMovie->getFrameCount() : frameCount;
1986 _loopAsyncMovie = loopAnimation;
1987
1988 if (_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_START) == SC_FALSE)
1989 return false;
1990
1991 _asyncMovie->seekToFrame((playStartPosition < 0) ? 0 : playStartPosition);
1992 _asyncMovie->showWindow(kWindowShow);
1993 _asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
1994
1995 return true;
1996 }
1997
retrieveAICommentEntry(const Location & commentLocation,int commentType,const Common::Array<AIComment> & commentDatabase,int & lastFoundIndex,AIComment & currentCommentData)1998 bool SceneViewWindow::retrieveAICommentEntry(const Location &commentLocation, int commentType, const Common::Array<AIComment> &commentDatabase, int &lastFoundIndex, AIComment ¤tCommentData) {
1999 if (commentDatabase.empty() || (uint32)lastFoundIndex >= commentDatabase.size())
2000 return false;
2001
2002 const AIComment *commentData = &commentDatabase[lastFoundIndex];
2003
2004 bool entryFound = false;
2005
2006 if (_globalFlags.generalWalkthroughMode == 1 && commentType == AI_COMMENT_TYPE_SPONTANEOUS) {
2007 // Look for any spontaneous comments
2008 for (; lastFoundIndex < (int)commentDatabase.size() && !entryFound; lastFoundIndex++) {
2009 if ((commentData->commentFlags & AI_COMMENT_TYPE_SPONTANEOUS || (commentData->commentFlags & AI_COMMENT_TYPE_HELP && commentData->dependencyValueA == 0))
2010 && (commentLocation.timeZone == commentData->location.timeZone || commentData->location.timeZone == -1)
2011 && (commentLocation.environment == commentData->location.environment || commentData->location.environment == -1)
2012 && (commentLocation.node == commentData->location.node || commentData->location.node == -1)
2013 && (commentLocation.facing == commentData->location.facing || commentData->location.facing == -1)
2014 && (commentLocation.orientation == commentData->location.orientation || commentData->location.orientation == -1)
2015 && (commentLocation.depth == commentData->location.depth || commentData->location.depth == -1)) {
2016 entryFound = true;
2017 } else {
2018 commentData++;
2019 }
2020 }
2021 } else {
2022 for (; lastFoundIndex < (int)commentDatabase.size() && !entryFound; lastFoundIndex++) {
2023 if ((commentData->commentFlags & commentType)
2024 && (commentLocation.timeZone == commentData->location.timeZone || commentData->location.timeZone == -1)
2025 && (commentLocation.environment == commentData->location.environment || commentData->location.environment == -1)
2026 && (commentLocation.node == commentData->location.node || commentData->location.node == -1)
2027 && (commentLocation.facing == commentData->location.facing || commentData->location.facing == -1)
2028 && (commentLocation.orientation == commentData->location.orientation || commentData->location.orientation == -1)
2029 && (commentLocation.depth == commentData->location.depth || commentData->location.depth == -1)) {
2030 entryFound = true;
2031 } else {
2032 commentData++;
2033 }
2034 }
2035 }
2036
2037 if (entryFound)
2038 currentCommentData = *commentData;
2039
2040 currentCommentData.location = commentLocation;
2041
2042 return entryFound;
2043 }
2044
checkAICommentDependencies(const Location & commentLocation,const AIComment & commentData)2045 bool SceneViewWindow::checkAICommentDependencies(const Location &commentLocation, const AIComment &commentData) {
2046 // Ignore comments designed for solely adventure mode in walkthrough mode
2047 if (_globalFlags.generalWalkthroughMode == 1 && commentData.commentFlags & AI_COMMENT_DISABLE_IN_WALKTHROUGH)
2048 return false;
2049
2050 byte flagValueA = 0;
2051 if (commentData.commentFlags & AI_DEPENDENCY_FLAG_NON_BASE_DERIVED_A)
2052 flagValueA = getGlobalFlagByte(commentData.dependencyFlagOffsetA);
2053 else
2054 flagValueA = _globalFlags.aiData[commentData.dependencyFlagOffsetA];
2055
2056 bool dependencyA;
2057 if (commentData.commentFlags & AI_DEPENDENCY_CHECK_FOR_MINIMUM_A)
2058 dependencyA = flagValueA >= commentData.dependencyValueA;
2059 else
2060 dependencyA = flagValueA <= commentData.dependencyValueA;
2061
2062 if (!dependencyA)
2063 return false;
2064
2065 if (commentData.commentFlags & AI_COMMENT_FLAG_SPECIAL_LOGIC)
2066 return checkCustomAICommentDependencies(commentLocation, commentData);
2067
2068 byte flagValueB = 0;
2069 if (commentData.commentFlags & AI_DEPENDENCY_FLAG_NON_BASE_DERIVED_B)
2070 flagValueB = getGlobalFlagByte(commentData.dependencyFlagOffsetB);
2071 else
2072 flagValueB = _globalFlags.aiData[commentData.dependencyFlagOffsetB];
2073
2074 bool dependencyB;
2075 if (commentData.commentFlags & AI_DEPENDENCY_CHECK_FOR_MINIMUM_B)
2076 dependencyB = flagValueB >= commentData.dependencyValueB;
2077 else
2078 dependencyB = flagValueB <= commentData.dependencyValueB;
2079
2080 return dependencyB;
2081 }
2082
playAICommentFromData(const AIComment & commentData)2083 bool SceneViewWindow::playAICommentFromData(const AIComment &commentData) {
2084 if (_vm->_sound->isAsynchronousAICommentPlaying())
2085 return false;
2086
2087 Common::String commentFileName = "BITDATA/";
2088
2089 switch (commentData.location.timeZone) {
2090 case 1: // Castle
2091 commentFileName += "CASTLE/";
2092
2093 switch (commentData.location.environment) {
2094 case 1:
2095 commentFileName += "CGTT";
2096 break;
2097 case 2:
2098 commentFileName += "CGTS";
2099 break;
2100 case 3:
2101 commentFileName += "CGMW";
2102 break;
2103 case 4:
2104 commentFileName += "CGMB";
2105 break;
2106 case 5:
2107 commentFileName += "CGBS";
2108 break;
2109 case 6:
2110 commentFileName += "CGKC";
2111 break;
2112 case 7:
2113 commentFileName += "CGST";
2114 break;
2115 case 8:
2116 commentFileName += "CGKS";
2117 break;
2118 case 9:
2119 commentFileName += "CGSR";
2120 break;
2121 case 10:
2122 commentFileName += "CGTR";
2123 break;
2124 default:
2125 return false;
2126 }
2127 break;
2128 case 2: // Mayan
2129 commentFileName += "MAYAN/";
2130
2131 switch (commentData.location.environment) {
2132 case 1:
2133 commentFileName += "MYTP";
2134 break;
2135 case 2:
2136 commentFileName += "MYMC";
2137 break;
2138 case 3:
2139 commentFileName += "MYWG";
2140 break;
2141 case 4:
2142 commentFileName += "MYWT";
2143 break;
2144 case 5:
2145 commentFileName += "MYAG";
2146 break;
2147 case 6:
2148 commentFileName += "MYDG";
2149 break;
2150 default:
2151 return false;
2152 }
2153 break;
2154 case 4: // Future Apartment
2155 commentFileName += "FUTAPT/";
2156
2157 switch (commentData.location.environment) {
2158 case 1:
2159 commentFileName += "FAKI";
2160 break;
2161 case 2:
2162 commentFileName += "FAER";
2163 break;
2164 case 3:
2165 commentFileName += "FAMN";
2166 break;
2167 default:
2168 return false;
2169 }
2170 break;
2171 case 5: // Da Vinci
2172 commentFileName += "DAVINCI/";
2173
2174 switch (commentData.location.environment) {
2175 case 1:
2176 commentFileName += "DSPT";
2177 break;
2178 case 2:
2179 commentFileName += "DSCT";
2180 break;
2181 case 3:
2182 commentFileName += "DSGD";
2183 break;
2184 case 4:
2185 commentFileName += "DSWS";
2186 break;
2187 case 5:
2188 commentFileName += "DSCY";
2189 break;
2190 default:
2191 return false;
2192 }
2193 break;
2194 case 6: // Space Station
2195 commentFileName += "AILAB/";
2196
2197 switch (commentData.location.environment) {
2198 case 1:
2199 commentFileName += "AIHW";
2200 break;
2201 case 2:
2202 commentFileName += "AICR";
2203 break;
2204 case 3:
2205 commentFileName += "AIDB";
2206 break;
2207 case 4:
2208 commentFileName += "AISC";
2209 break;
2210 case 5:
2211 commentFileName += "AINX";
2212 break;
2213 case 6:
2214 commentFileName += "AIIC";
2215 break;
2216 case 7:
2217 commentFileName += "AISW";
2218 break;
2219 case 8:
2220 commentFileName += "AIMR";
2221 break;
2222 case 9:
2223 // There is no 9.
2224 return false;
2225 case 10:
2226 commentFileName += "AIHW";
2227 break;
2228 default:
2229 return false;
2230 }
2231 break;
2232 default:
2233 return false;
2234 }
2235
2236 commentFileName += "_";
2237
2238 if (commentData.commentFlags & AI_COMMENT_TYPE_INFORMATION)
2239 commentFileName += "I";
2240 if (commentData.commentFlags & AI_COMMENT_TYPE_HELP)
2241 commentFileName += "H";
2242 if (commentData.commentFlags & AI_COMMENT_TYPE_SPONTANEOUS)
2243 commentFileName += "C";
2244 if (commentData.commentFlags & AI_COMMENT_TYPE_OTHER)
2245 commentFileName += "O";
2246
2247 commentFileName += Common::String::format("%02d.BTA", commentData.commentID);
2248
2249 Cursor currentCursor = _vm->_gfx->setCursor(kCursorWait);
2250 bool playedSuccessfully = _vm->_sound->playAsynchronousAIComment(commentFileName);
2251 _vm->_gfx->setCursor(currentCursor);
2252
2253 if (playedSuccessfully) {
2254 _lastAICommentFileName = commentFileName;
2255
2256 // This is pure evil. Ugh.
2257 // The [g|s]etGlobalFlagByte nonsense, anyway.
2258
2259 byte flagValue = 0;
2260 if (commentData.commentFlags & AI_STATUS_FLAG_NON_BASE_DERIVED)
2261 flagValue = getGlobalFlagByte(commentData.statusFlagOffset);
2262 else
2263 flagValue = _globalFlags.aiData[commentData.statusFlagOffset];
2264
2265 flagValue++;
2266
2267 if (commentData.commentFlags & AI_STATUS_FLAG_NON_BASE_DERIVED)
2268 setGlobalFlagByte(commentData.statusFlagOffset, flagValue);
2269 else
2270 _globalFlags.aiData[commentData.statusFlagOffset] = flagValue;
2271
2272 return true;
2273 }
2274
2275 return false;
2276 }
2277
playAIComment(int commentType)2278 bool SceneViewWindow::playAIComment(int commentType) {
2279 if (!_currentScene)
2280 return false;
2281
2282 if (_vm->_sound->isAsynchronousAICommentPlaying())
2283 return false;
2284
2285 return playAIComment(_currentScene->_staticData.location, commentType);
2286 }
2287
playAIComment(const Location & commentLocation,int commentType)2288 bool SceneViewWindow::playAIComment(const Location &commentLocation, int commentType) {
2289 // Make sure no other comments are playing
2290 if (_vm->_sound->isAsynchronousAICommentPlaying())
2291 return false;
2292
2293 Common::Array<AIComment> commentDatabase = getAICommentDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
2294
2295 if (commentDatabase.empty())
2296 return false;
2297
2298 AIComment currentCommentData;
2299 int lastFoundEntry = 0;
2300 bool playedSuccessfully = false;
2301
2302 while (retrieveAICommentEntry(commentLocation, commentType, commentDatabase, lastFoundEntry, currentCommentData) && !playedSuccessfully)
2303 if (checkAICommentDependencies(commentLocation, currentCommentData))
2304 playedSuccessfully = playAICommentFromData(currentCommentData);
2305
2306 if (playedSuccessfully) {
2307 if (commentType == AI_COMMENT_TYPE_HELP && _globalFlags.generalWalkthroughMode == 0)
2308 _globalFlags.scoreHintsTotal++;
2309
2310 return true;
2311 }
2312
2313 return false;
2314 }
2315
checkForAIComment(int commentType)2316 bool SceneViewWindow::checkForAIComment(int commentType) {
2317 if (!_currentScene)
2318 return false;
2319
2320 return checkForAIComment(_currentScene->_staticData.location, commentType);
2321 }
2322
checkForAIComment(const Location & commentLocation,int commentType)2323 bool SceneViewWindow::checkForAIComment(const Location &commentLocation, int commentType) {
2324 Common::Array<AIComment> commentDatabase = getAICommentDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
2325
2326 if (commentDatabase.empty())
2327 return false;
2328
2329 AIComment currentCommentData;
2330 int lastFoundEntry = 0;
2331
2332 while (retrieveAICommentEntry(commentLocation, commentType, commentDatabase, lastFoundEntry, currentCommentData))
2333 if (checkAICommentDependencies(commentLocation, currentCommentData))
2334 return true;
2335
2336 return false;
2337 }
2338
infoWindowDisplayed(bool flag)2339 bool SceneViewWindow::infoWindowDisplayed(bool flag) {
2340 if (flag && !_walkMovie) {
2341 delete _walkMovie;
2342 _walkMovie = nullptr;
2343 _walkMovieFileName.clear();
2344 changeCycleFrameMovie();
2345 }
2346
2347 if (_asyncMovie) {
2348 if (flag)
2349 _asyncMovie->showWindow(kWindowHide);
2350 else
2351 _asyncMovie->showWindow(kWindowShow);
2352 }
2353
2354 if (_bioChipWindowDisplayed && flag)
2355 ((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
2356
2357 _infoWindowDisplayed = flag;
2358 return true;
2359 }
2360
bioChipWindowDisplayed(bool flag)2361 bool SceneViewWindow::bioChipWindowDisplayed(bool flag) {
2362 if (flag && !_walkMovie) {
2363 delete _walkMovie;
2364 _walkMovie = nullptr;
2365 _walkMovieFileName.clear();
2366 changeCycleFrameMovie();
2367 }
2368
2369 if (_asyncMovie) {
2370 if (flag)
2371 _asyncMovie->showWindow(kWindowHide);
2372 else
2373 _asyncMovie->showWindow(kWindowShow);
2374 }
2375
2376 if (_infoWindowDisplayed && flag)
2377 ((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
2378
2379 _bioChipWindowDisplayed = flag;
2380 return true;
2381 }
2382
burnedLetterWindowDisplayed(bool flag)2383 bool SceneViewWindow::burnedLetterWindowDisplayed(bool flag) {
2384 if (flag && !_walkMovie) {
2385 delete _walkMovie;
2386 _walkMovie = nullptr;
2387 _walkMovieFileName.clear();
2388 changeCycleFrameMovie();
2389 }
2390
2391 if (_asyncMovie) {
2392 if (flag)
2393 _asyncMovie->showWindow(kWindowHide);
2394 else
2395 _asyncMovie->showWindow(kWindowShow);
2396 }
2397
2398 if (_burnedLetterDisplayed)
2399 ((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
2400
2401 _burnedLetterDisplayed = flag;
2402 return true;
2403 }
2404
isAuxWindowDisplayed()2405 bool SceneViewWindow::isAuxWindowDisplayed() {
2406 return _burnedLetterDisplayed || _infoWindowDisplayed || _bioChipWindowDisplayed;
2407 }
2408
onLButtonDown(const Common::Point & point,uint flags)2409 void SceneViewWindow::onLButtonDown(const Common::Point &point, uint flags) {
2410 if (_currentScene && _globalFlags.bcLocateEnabled == 0)
2411 _currentScene->mouseDown(this, point);
2412 }
2413
onLButtonUp(const Common::Point & point,uint flags)2414 void SceneViewWindow::onLButtonUp(const Common::Point &point, uint flags) {
2415 if (_currentScene) {
2416 if (_globalFlags.bcLocateEnabled == 0)
2417 _currentScene->mouseUp(this, point);
2418 else
2419 _currentScene->locateAttempted(this, point);
2420 }
2421 }
2422
onMouseMove(const Common::Point & point,uint flags)2423 void SceneViewWindow::onMouseMove(const Common::Point &point, uint flags) {
2424 _curMousePos = point;
2425 if (_currentScene)
2426 _currentScene->mouseMove(this, point);
2427 }
2428
onKeyUp(const Common::KeyState & key,uint flags)2429 void SceneViewWindow::onKeyUp(const Common::KeyState &key, uint flags) {
2430 switch (key.keycode) {
2431 case Common::KEYCODE_a:
2432 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI)) {
2433 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipAI);
2434 return;
2435 }
2436 break;
2437 case Common::KEYCODE_b:
2438 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipBlank)) {
2439 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipBlank);
2440 return;
2441 }
2442 break;
2443 case Common::KEYCODE_c:
2444 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipCloak)) {
2445 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipCloak);
2446 return;
2447 }
2448 break;
2449 case Common::KEYCODE_e:
2450 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipEvidence)) {
2451 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipEvidence);
2452 return;
2453 }
2454 break;
2455 case Common::KEYCODE_f:
2456 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipFiles)) {
2457 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipFiles);
2458 return;
2459 }
2460 break;
2461 case Common::KEYCODE_i:
2462 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipInterface)) {
2463 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipInterface);
2464 return;
2465 }
2466 break;
2467 case Common::KEYCODE_j:
2468 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipJump)) {
2469 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipJump);
2470 return;
2471 }
2472 break;
2473 case Common::KEYCODE_t:
2474 if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipTranslate)) {
2475 ((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipTranslate);
2476 return;
2477 }
2478 break;
2479 case Common::KEYCODE_p:
2480 if (key.flags & Common::KBD_CTRL) {
2481 // TODO: Pause game
2482 return;
2483 }
2484 break;
2485 case Common::KEYCODE_q:
2486 if (key.flags & Common::KBD_CTRL) {
2487 // Return to main menu
2488 if (_vm->runQuitDialog())
2489 ((FrameWindow *)_vm->_mainWindow)->showMainMenu();
2490 return;
2491 }
2492 break;
2493 case Common::KEYCODE_SPACE:
2494 if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI) && _globalFlags.bcCloakingEnabled != 1) {
2495 if (!_lastAICommentFileName.empty() && !_vm->_sound->isAsynchronousAICommentPlaying()) {
2496 TempCursorChange cursorChange(kCursorWait);
2497 _vm->_sound->playAsynchronousAIComment(_lastAICommentFileName);
2498 }
2499 return;
2500 }
2501 break;
2502 default:
2503 break;
2504 }
2505
2506 if (_currentScene)
2507 _currentScene->onCharacter(this, key);
2508 }
2509
onPaint()2510 void SceneViewWindow::onPaint() {
2511 // Original didn't draw if the async movie was playing, but that doesn't seem right.
2512 if (_currentScene && !_infoWindowDisplayed && !_bioChipWindowDisplayed) {
2513 if (_currentScene->_staticData.navFrameIndex >= -1) {
2514 if (_useScenePaint)
2515 _currentScene->paint(this, _preBuffer);
2516 } else {
2517 return;
2518 }
2519
2520 // If we have a sprite, update the prebuffer with it now
2521 if (_currentSprite.image && _useSprite)
2522 _vm->_gfx->opaqueTransparentBlit(_preBuffer, _currentSprite.xPos, _currentSprite.yPos, _currentSprite.width, _currentSprite.height, _currentSprite.image, 0, 0, 0, _currentSprite.redTrans, _currentSprite.greenTrans, _currentSprite.blueTrans);
2523
2524 // Update the screen
2525 _vm->_gfx->blit(_preBuffer, _rect.left, _rect.top);
2526
2527 if (_useScenePaint)
2528 _currentScene->gdiPaint(this);
2529 }
2530 }
2531
onTimer(uint timer)2532 void SceneViewWindow::onTimer(uint timer) {
2533 // Check first to see if this is the demo's sound timer
2534 if (timer == _demoSoundTimer) {
2535 // If no sound is playing, restart the ambient
2536 if (!_vm->_sound->isAmbientSoundPlaying() && !_vm->_sound->isSoundEffectPlaying(_demoSoundEffectHandle)) {
2537 // Reset the sound effect handle
2538 _demoSoundEffectHandle = -1;
2539 startDemoAmbientSound();
2540 }
2541
2542 return;
2543 }
2544
2545 SoundManager *sound = _vm->_sound; // Take a copy in case we die while in the timer
2546 sound->timerCallback();
2547
2548 if (_paused)
2549 return;
2550
2551 if (_asyncMovie)
2552 asynchronousAnimationTimerCallback();
2553
2554 if (_currentScene && !_infoWindowDisplayed && !_bioChipWindowDisplayed && !_burnedLetterDisplayed)
2555 _currentScene->timerCallback(this);
2556
2557 sound->timerCallback();
2558 }
2559
onSetCursor(uint message)2560 bool SceneViewWindow::onSetCursor(uint message) {
2561 // Check the scene cursor callback function to see if we need to change the cursor
2562 int newCursor = (int)kCursorArrow;
2563 if (_currentScene)
2564 newCursor = _currentScene->specifyCursor(this, _curMousePos);
2565
2566 // If the locate button is enabled, follow different logic
2567 if (_globalFlags.bcLocateEnabled == 1) {
2568 if (_curCursor >= 0 || (newCursor < 0 && newCursor != _curCursor)) {
2569 // If the new cursor is less than zero, use it, otherwise use the default locate cursor
2570 if (newCursor < 0) {
2571 if (newCursor == -2)
2572 _curCursor = (int)kCursorLocateB;
2573 else
2574 _curCursor = (int)kCursorLocateA;
2575 } else {
2576 _curCursor = (int)kCursorLocateA;
2577 }
2578 }
2579 } else {
2580 _curCursor = newCursor;
2581 }
2582
2583 _vm->_gfx->setCursor((Cursor)_curCursor);
2584 return true;
2585 }
2586
onEnable(bool enable)2587 void SceneViewWindow::onEnable(bool enable) {
2588 // If we're enabling, clear out the message queue of mouse messages
2589 _vm->removeMouseMessages(this);
2590 }
2591
resetNavigationArrows()2592 bool SceneViewWindow::resetNavigationArrows() {
2593 if (!_currentScene)
2594 return false;
2595
2596 if (_globalFlags.bcCloakingEnabled != 1)
2597 ((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
2598
2599 return true;
2600 }
2601
draggingItem(int itemID,const Common::Point & location,int itemFlags)2602 int SceneViewWindow::draggingItem(int itemID, const Common::Point &location, int itemFlags) {
2603 if (!_currentScene)
2604 return 0;
2605
2606 return _currentScene->draggingItem(this, itemID, location, itemFlags);
2607 }
2608
droppedItem(int itemID,const Common::Point & location,int itemFlags)2609 int SceneViewWindow::droppedItem(int itemID, const Common::Point &location, int itemFlags) {
2610 if (!_currentScene)
2611 return 0;
2612
2613 return _currentScene->droppedItem(this, itemID, location, itemFlags);
2614 }
2615
updatePrebufferWithSprite(Sprite & spriteData)2616 bool SceneViewWindow::updatePrebufferWithSprite(Sprite &spriteData) {
2617 if (_currentSprite.image != spriteData.image && _currentSprite.image != 0) {
2618 _currentSprite.image->free();
2619 delete _currentSprite.image;
2620 }
2621
2622 _currentSprite = spriteData;
2623 invalidateWindow(false);
2624 return true;
2625 }
2626
changeSpriteStatus(bool status)2627 bool SceneViewWindow::changeSpriteStatus(bool status) {
2628 bool prevStatus = _useSprite;
2629 _useSprite = status;
2630 return prevStatus;
2631 }
2632
resetCursor()2633 bool SceneViewWindow::resetCursor() {
2634 _vm->_gfx->setCursor((Cursor)_curCursor);
2635 return true;
2636 }
2637
displayLiveText(const Common::String & text,bool notifyUser)2638 bool SceneViewWindow::displayLiveText(const Common::String &text, bool notifyUser) {
2639 if (((GameUIWindow *)_parent)->_liveTextWindow)
2640 return ((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText(text, notifyUser);
2641
2642 return false;
2643 }
2644
displayTranslationText(const Common::String & text)2645 bool SceneViewWindow::displayTranslationText(const Common::String &text) {
2646 if (((GameUIWindow *)_parent)->_liveTextWindow)
2647 return ((GameUIWindow *)_parent)->_liveTextWindow->updateTranslationText(text);
2648
2649 return false;
2650 }
2651
getAnimationDatabase(int timeZone,int environment)2652 Common::Array<AnimEvent> SceneViewWindow::getAnimationDatabase(int timeZone, int environment) {
2653 Common::SeekableReadStream *stream = _vm->getAnimData(_vm->computeAnimDBResourceID(timeZone, environment));
2654 stream->readUint16LE();
2655
2656 Common::Array<AnimEvent> animEvents;
2657 while (stream->pos() < stream->size()) {
2658 AnimEvent animEvent;
2659 animEvent.animationID = stream->readSint16LE();
2660 animEvent.fileNameID = stream->readSint16LE();
2661 animEvent.audioStreamCount = stream->readSint16LE();
2662 animEvent.startFrame = stream->readSint32LE();
2663 animEvent.frameCount = stream->readSint32LE();
2664 animEvents.push_back(animEvent);
2665 }
2666
2667 delete stream;
2668 return animEvents;
2669 }
2670
getAICommentDatabase(int timeZone,int environment)2671 Common::Array<AIComment> SceneViewWindow::getAICommentDatabase(int timeZone, int environment) {
2672 Common::SeekableReadStream *stream = _vm->getAIData(_vm->computeAIDBResourceID(timeZone, environment));
2673 Common::Array<AIComment> comments;
2674
2675 if (!stream)
2676 return comments;
2677
2678 uint16 count = stream->readUint16LE();
2679
2680 for (uint16 i = 0; i < count; i++) {
2681 AIComment comment;
2682 comment.location.timeZone = stream->readSint16LE();
2683 comment.location.environment = stream->readSint16LE();
2684 comment.location.node = stream->readSint16LE();
2685 comment.location.facing = stream->readSint16LE();
2686 comment.location.orientation = stream->readSint16LE();
2687 comment.location.depth = stream->readSint16LE();
2688 comment.commentID = stream->readUint16LE();
2689 comment.commentFlags = stream->readUint16LE();
2690 comment.dependencyFlagOffsetA = stream->readUint16LE();
2691 comment.dependencyValueA = stream->readUint16LE();
2692 comment.dependencyFlagOffsetB = stream->readUint16LE();
2693 comment.dependencyValueB = stream->readUint16LE();
2694 comment.statusFlagOffset = stream->readUint16LE();
2695 comments.push_back(comment);
2696 }
2697
2698 delete stream;
2699 return comments;
2700 }
2701
startDemoAmbientSound()2702 void SceneViewWindow::startDemoAmbientSound() {
2703 assert(_currentScene);
2704
2705 if (_currentScene->_staticData.location.environment == 5)
2706 _vm->_sound->setAmbientSound("CASTLE/CGBSSNG.WAV", false, 127);
2707 else
2708 _vm->_sound->setAmbientSound("CASTLE/CGMBSNG.WAV", false, 127);
2709 }
2710
2711 } // End of namespace Buried
2712