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 &currentCommentData) {
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