scummvm/engines/buried/scene_view.cpp
2021-03-22 00:07:45 +01:00

2688 lines
86 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1995 Presto Studios, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "buried/avi_frames.h"
#include "buried/biochip_right.h"
#include "buried/buried.h"
#include "buried/frame_window.h"
#include "buried/gameui.h"
#include "buried/graphics.h"
#include "buried/inventory_window.h"
#include "buried/livetext.h"
#include "buried/navarrow.h"
#include "buried/resources.h"
#include "buried/scene_view.h"
#include "buried/sound.h"
#include "buried/video_window.h"
#include "buried/environ/scene_base.h"
#include "common/ptr.h"
#include "common/stream.h"
#include "common/system.h"
#include "graphics/surface.h"
namespace Buried {
SceneViewWindow::SceneViewWindow(BuriedEngine *vm, Window *parent) : Window(vm, parent) {
_currentScene = nullptr;
_preBuffer = nullptr;
_walkMovie = nullptr;
_useScenePaint = true;
_timer = 0;
_currentSprite.image = nullptr;
_useSprite = true;
_infoWindowDisplayed = false;
_bioChipWindowDisplayed = false;
_burnedLetterDisplayed = false;
_asyncMovie = nullptr;
_asyncMovieStartFrame = 0;
_loopAsyncMovie = false;
_paused = false;
_cycleEnabled = ((FrameWindow *)(_parent->getParent()))->isFrameCyclingDefault();
_forceCycleEnabled = false;
_disableArthur = false;
_demoSoundEffectHandle = -1;
_preBuffer = new Graphics::Surface();
_preBuffer->create(DIB_FRAME_WIDTH, DIB_FRAME_HEIGHT, g_system->getScreenFormat());
_rect = Common::Rect(64, 128, 496, 317);
_timer = setTimer(100);
_demoSoundTimer = _vm->isDemo() ? setTimer(10) : 0;
_curCursor = kCursorArrow;
_stillFrames = new AVIFrames();
_cycleFrames = new AVIFrames();
memset(&_globalFlags, 0, sizeof(_globalFlags));
}
SceneViewWindow::~SceneViewWindow() {
if (_currentScene) {
_currentScene->preDestructor();
delete _currentScene;
}
killTimer(_timer);
if (_preBuffer) {
_preBuffer->free();
delete _preBuffer;
}
delete _stillFrames;
delete _cycleFrames;
delete _walkMovie;
delete _asyncMovie;
}
bool SceneViewWindow::startNewGame(bool walkthrough) {
Location newLocation;
if (_vm->isDemo()) {
newLocation.timeZone = 1;
newLocation.environment = 4;
newLocation.node = 0;
newLocation.facing = 0;
newLocation.orientation = 1;
newLocation.depth = 0;
} else {
newLocation.timeZone = 4;
newLocation.environment = 3;
newLocation.node = 3;
newLocation.facing = 0;
newLocation.orientation = 1;
newLocation.depth = 0;
}
jumpToScene(newLocation);
if (_vm->isDemo()) {
displayLiveText("To return to the main menu, click the 'Menu' button on the Interface Biochip Display to the right, then click Quit.");
startDemoAmbientSound();
// This is unlabeled in the original source, but it looks like a hidden feature
// to access a bonus puzzle in the demo. (Complete with a typo, but who's counting?)
if (((FrameWindow *)(_parent->getParent()))->_reviewerMode)
((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemCopperMedallion);
} else if (walkthrough) {
// Set the mode flag
_globalFlags.generalWalkthroughMode = 1;
// Set specific state flags for walkthrough mode
_globalFlags.cgSmithyStatus = 6;
_globalFlags.cgTapestryFlag = 1;
_globalFlags.myTPCodeWheelLeftIndex = 8;
_globalFlags.myTPCodeWheelRightIndex = 12;
_globalFlags.myTPCodeWheelStatus = 1;
_globalFlags.myWGPlacedRope = 1;
// Add the translate biochip
((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemBioChipTranslate);
}
invalidateWindow(false);
return true;
}
bool SceneViewWindow::startNewGameIntro(bool walkthrough) {
Location newLocation;
newLocation.timeZone = 10;
newLocation.environment = 0;
newLocation.node = 0;
newLocation.facing = 0;
newLocation.orientation = 0;
newLocation.depth = 0;
jumpToScene(newLocation);
if (walkthrough) {
// Set the mode flag
_globalFlags.generalWalkthroughMode = 1;
// Set specific state flags for walkthrough mode
_globalFlags.cgSmithyStatus = 6;
_globalFlags.cgTapestryFlag = 1;
_globalFlags.myTPCodeWheelLeftIndex = 8;
_globalFlags.myTPCodeWheelRightIndex = 12;
_globalFlags.myTPCodeWheelStatus = 1;
_globalFlags.myWGPlacedRope = 1;
// Add the translate biochip
((GameUIWindow *)_parent)->_inventoryWindow->addItem(kItemBioChipTranslate);
}
invalidateWindow(false);
return true;
}
bool SceneViewWindow::startNewGame(const Location &startingLocation) {
jumpToSceneRestore(startingLocation);
if (_globalFlags.generalWalkthroughMode == 1) {
if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0))
displayLiveText(_vm->getString(IDS_PLAY_MODE_WALKTHROUGH_TEXT));
else
displayLiveText("You are playing in Walkthrough mode.");
} else {
if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0))
displayLiveText(_vm->getString(IDS_PLAY_MODE_NORMAL_TEXT));
else
displayLiveText("You are playing in Adventure mode.");
}
return true;
}
bool SceneViewWindow::showDeathScene(int deathSceneIndex) {
return ((FrameWindow *)(_parent->getParent()))->showDeathScene(deathSceneIndex, _globalFlags, ((GameUIWindow *)_parent)->_inventoryWindow->getItemArray());
}
bool SceneViewWindow::showCompletionScene() {
return ((FrameWindow *)(_parent->getParent()))->showCompletionScene(_globalFlags);
}
bool SceneViewWindow::getSceneStaticData(const Location &location, LocationStaticData &sceneStaticData) {
int curTimeZone = -1;
int curEnvironment = -1;
if (!_currentNavigationDatabase.empty()) {
curTimeZone = _currentNavigationDatabase[0].location.timeZone;
curEnvironment = _currentNavigationDatabase[0].location.environment;
}
if (location.timeZone != curTimeZone || location.environment != curEnvironment) {
_currentNavigationDatabase.clear();
int resID = _vm->computeNavDBResourceID(location.timeZone, location.environment);
Common::SeekableReadStream *resource = _vm->getNavData(resID);
resource->skip(2);
while (resource->pos() < resource->size()) {
LocationStaticData locationStaticData;
locationStaticData.location.timeZone = resource->readSint16LE();
locationStaticData.location.environment = resource->readSint16LE();
locationStaticData.location.node = resource->readSint16LE();
locationStaticData.location.facing = resource->readSint16LE();
locationStaticData.location.orientation = resource->readSint16LE();
locationStaticData.location.depth = resource->readSint16LE();
locationStaticData.destUp.destinationScene.timeZone = resource->readSint16LE();
locationStaticData.destUp.destinationScene.environment = resource->readSint16LE();
locationStaticData.destUp.destinationScene.node = resource->readSint16LE();
locationStaticData.destUp.destinationScene.facing = resource->readSint16LE();
locationStaticData.destUp.destinationScene.orientation = resource->readSint16LE();
locationStaticData.destUp.destinationScene.depth = resource->readSint16LE();
locationStaticData.destUp.transitionType = resource->readSint16LE();
locationStaticData.destUp.transitionData = resource->readSint16LE();
locationStaticData.destUp.transitionStartFrame = resource->readSint32LE();
locationStaticData.destUp.transitionLength = resource->readSint32LE();
locationStaticData.destLeft.destinationScene.timeZone = resource->readSint16LE();
locationStaticData.destLeft.destinationScene.environment = resource->readSint16LE();
locationStaticData.destLeft.destinationScene.node = resource->readSint16LE();
locationStaticData.destLeft.destinationScene.facing = resource->readSint16LE();
locationStaticData.destLeft.destinationScene.orientation = resource->readSint16LE();
locationStaticData.destLeft.destinationScene.depth = resource->readSint16LE();
locationStaticData.destLeft.transitionType = resource->readSint16LE();
locationStaticData.destLeft.transitionData = resource->readSint16LE();
locationStaticData.destLeft.transitionStartFrame = resource->readSint32LE();
locationStaticData.destLeft.transitionLength = resource->readSint32LE();
locationStaticData.destRight.destinationScene.timeZone = resource->readSint16LE();
locationStaticData.destRight.destinationScene.environment = resource->readSint16LE();
locationStaticData.destRight.destinationScene.node = resource->readSint16LE();
locationStaticData.destRight.destinationScene.facing = resource->readSint16LE();
locationStaticData.destRight.destinationScene.orientation = resource->readSint16LE();
locationStaticData.destRight.destinationScene.depth = resource->readSint16LE();
locationStaticData.destRight.transitionType = resource->readSint16LE();
locationStaticData.destRight.transitionData = resource->readSint16LE();
locationStaticData.destRight.transitionStartFrame = resource->readSint32LE();
locationStaticData.destRight.transitionLength = resource->readSint32LE();
locationStaticData.destDown.destinationScene.timeZone = resource->readSint16LE();
locationStaticData.destDown.destinationScene.environment = resource->readSint16LE();
locationStaticData.destDown.destinationScene.node = resource->readSint16LE();
locationStaticData.destDown.destinationScene.facing = resource->readSint16LE();
locationStaticData.destDown.destinationScene.orientation = resource->readSint16LE();
locationStaticData.destDown.destinationScene.depth = resource->readSint16LE();
locationStaticData.destDown.transitionType = resource->readSint16LE();
locationStaticData.destDown.transitionData = resource->readSint16LE();
locationStaticData.destDown.transitionStartFrame = resource->readSint32LE();
locationStaticData.destDown.transitionLength = resource->readSint32LE();
locationStaticData.destForward.destinationScene.timeZone = resource->readSint16LE();
locationStaticData.destForward.destinationScene.environment = resource->readSint16LE();
locationStaticData.destForward.destinationScene.node = resource->readSint16LE();
locationStaticData.destForward.destinationScene.facing = resource->readSint16LE();
locationStaticData.destForward.destinationScene.orientation = resource->readSint16LE();
locationStaticData.destForward.destinationScene.depth = resource->readSint16LE();
locationStaticData.destForward.transitionType = resource->readSint16LE();
locationStaticData.destForward.transitionData = resource->readSint16LE();
locationStaticData.destForward.transitionStartFrame = resource->readSint32LE();
locationStaticData.destForward.transitionLength = resource->readSint32LE();
locationStaticData.classID = resource->readSint16LE();
locationStaticData.navFrameIndex = resource->readSint32LE();
locationStaticData.miscFrameIndex = resource->readSint32LE();
locationStaticData.miscFrameCount = resource->readSint32LE();
locationStaticData.cycleStartFrame = resource->readSint32LE();
locationStaticData.cycleFrameCount = resource->readSint32LE();
_currentNavigationDatabase.push_back(locationStaticData);
}
if (_currentNavigationDatabase.empty())
return false;
}
for (uint32 i = 0; i < _currentNavigationDatabase.size(); i++) {
if (location.timeZone == _currentNavigationDatabase[i].location.timeZone &&
location.environment == _currentNavigationDatabase[i].location.environment &&
location.node == _currentNavigationDatabase[i].location.node &&
location.facing == _currentNavigationDatabase[i].location.facing &&
location.orientation == _currentNavigationDatabase[i].location.orientation &&
location.depth == _currentNavigationDatabase[i].location.depth) {
sceneStaticData = _currentNavigationDatabase[i];
return true;
}
}
return false;
}
bool SceneViewWindow::jumpToScene(const Location &newLocation) {
Location oldLocation;
oldLocation.timeZone = -1;
oldLocation.environment = -1;
oldLocation.node = -1;
oldLocation.facing = -1;
oldLocation.orientation = -1;
oldLocation.depth = -1;
Location passedLocation;
passedLocation.timeZone = -1;
passedLocation.environment = -1;
passedLocation.node = -1;
passedLocation.facing = -1;
passedLocation.orientation = -1;
passedLocation.depth = -1;
// Destroy any window displayed
if (_infoWindowDisplayed)
((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
if (_bioChipWindowDisplayed)
((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
if (_burnedLetterDisplayed)
((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
LocationStaticData newSceneStaticData;
if (!getSceneStaticData(newLocation, newSceneStaticData))
return false;
if (_currentScene)
oldLocation = _currentScene->_staticData.location;
// Clear the live text window
if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment)
((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText();
// Call the pre-transition function
if (_currentScene)
_currentScene->preExitRoom(this, passedLocation);
if (newLocation.timeZone != oldLocation.timeZone && newLocation.timeZone != -2)
initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
if (newLocation.environment != oldLocation.environment && newLocation.environment != -2)
initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
SceneBase *newScene = constructSceneObject(this, newSceneStaticData, passedLocation);
if (_currentScene && _currentScene->postExitRoom(this, passedLocation) == SC_DEATH)
return false;
if (!newScene)
error("Failed to create scene");
if (_currentScene) {
_currentScene->preDestructor();
delete _currentScene;
_currentScene = nullptr;
}
if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment || oldLocation.timeZone < 0)
startEnvironmentAmbient(passedLocation.timeZone, passedLocation.environment, newLocation.timeZone, newLocation.environment);
_currentScene = newScene;
if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
flushCycleFrameCache();
invalidateWindow(false);
if (_currentScene->preEnterRoom(this, passedLocation) == SC_END_PROCESSING)
return true;
if (_globalFlags.bcCloakingEnabled != 1)
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(newScene->_staticData);
if (newLocation.timeZone != oldLocation.timeZone)
((GameUIWindow *)_parent)->changeCurrentDate(newLocation.timeZone);
invalidateWindow(false);
_currentScene->postEnterRoom(this, passedLocation);
_parent->invalidateWindow(false);
if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI))
playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
return true;
}
bool SceneViewWindow::jumpToSceneRestore(const Location &newLocation) {
Location oldLocation(-2, -2, -2, -2, -2, -2);
Location passedLocation(-2, -2, -2, -2, -2, -2);
if (_infoWindowDisplayed)
((GameUIWindow *)getParent())->_inventoryWindow->destroyInfoWindow();
if (_bioChipWindowDisplayed)
((GameUIWindow *)getParent())->_bioChipRightWindow->destroyBioChipViewWindow();
if (_burnedLetterDisplayed)
((GameUIWindow *)getParent())->_inventoryWindow->destroyBurnedLetterWindow();
// Get the static scene data for this new location
LocationStaticData newSceneStaticData;
if (!getSceneStaticData(newLocation, newSceneStaticData))
return false;
if (_currentScene)
oldLocation = _currentScene->_staticData.location;
// Clear the live text window
if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment)
((GameUIWindow *)getParent())->_liveTextWindow->updateLiveText("");
// If we have a scene, call the pre-transition function
if (_currentScene)
_currentScene->preExitRoom(this, passedLocation);
// The original resets the environment upon loading, which is clearly not correct.
// We won't.
//if (newLocation.timeZone != oldLocation.timeZone && newLocation.timeZone != -2)
// initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
//if (newLocation.timeZone != oldLocation.timeZone && newLocation.environment != -2)
// initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
// Create the new scene object
SceneBase *newScene = constructSceneObject(this, newSceneStaticData, passedLocation);
// Call the post-transition function
if (_currentScene && _currentScene->postExitRoom(this, passedLocation) == SC_DEATH)
return false;
if (_currentScene) {
_currentScene->preDestructor();
delete _currentScene;
_currentScene = nullptr;
}
// Change the ambient music
if (newLocation.timeZone != oldLocation.timeZone || newLocation.environment != oldLocation.environment || oldLocation.timeZone < 0)
startEnvironmentAmbient(passedLocation.timeZone, passedLocation.environment, newLocation.timeZone, newLocation.environment);
_currentScene = newScene;
if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
flushCycleFrameCache();
if (_currentScene->preEnterRoom(this, passedLocation) == SC_END_PROCESSING)
return true;
if (_globalFlags.bcCloakingEnabled != 1)
((GameUIWindow *)getParent())->_navArrowWindow->updateAllArrows(newScene->_staticData);
if (newLocation.timeZone != oldLocation.timeZone)
((GameUIWindow *)getParent())->changeCurrentDate(newLocation.timeZone);
invalidateWindow(false);
_currentScene->postEnterRoom(this, passedLocation);
getParent()->invalidateWindow(false);
// Check AI database for a spontaneous comment
if (((GameUIWindow *)getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI))
playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
// Notify biochip right window of change
((GameUIWindow *)getParent())->_bioChipRightWindow->sceneChanged();
return true;
}
bool SceneViewWindow::moveInDirection(int direction) {
if (!_currentScene)
return false;
((GameUIWindow *)_parent)->_navArrowWindow->updateArrow(direction, NavArrowWindow::BUTTON_SELECTED);
DestinationScene destinationData;
switch (direction) {
case 0: // Up
destinationData = _currentScene->_staticData.destUp;
break;
case 1: // Left
destinationData = _currentScene->_staticData.destLeft;
break;
case 2: // Right
destinationData = _currentScene->_staticData.destRight;
break;
case 3: // Down
destinationData = _currentScene->_staticData.destDown;
break;
case 4: // Forward
destinationData = _currentScene->_staticData.destForward;
break;
}
return moveToDestination(destinationData);
}
bool SceneViewWindow::moveToDestination(const DestinationScene &destinationData) {
// Close any information window
if (_infoWindowDisplayed)
((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
if (_bioChipWindowDisplayed)
((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
if (_burnedLetterDisplayed)
((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
// Check to see if this is a valid move
if (destinationData.destinationScene.timeZone == -1) {
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
return true;
}
assert(_currentScene); // clone2727: sanity check -- the original code was broken
LocationStaticData newSceneStaticData;
if (!getSceneStaticData(destinationData.destinationScene, newSceneStaticData)) {
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
return true;
}
Location oldLocation = _currentScene->_staticData.location;
// Disable locate and clear the live text window
if (newSceneStaticData.location.timeZone != oldLocation.timeZone ||
newSceneStaticData.location.environment != oldLocation.environment ||
newSceneStaticData.location.node != oldLocation.node) {
((GameUIWindow *)_parent)->_bioChipRightWindow->disableEvidenceCapture();
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(false);
}
// Disable the arrow window
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(false);
// Get thr esults from the pre-exit room function
int retVal = _currentScene->preExitRoom(this, destinationData.destinationScene);
// If we died, return here
if (retVal == SC_DEATH)
return false;
// If we did not return success, the move is disallowed
if (retVal != SC_TRUE) {
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
return true;
}
// Initialize the time zone and environment
if (newSceneStaticData.location.timeZone != oldLocation.timeZone && newSceneStaticData.location.timeZone != -2)
initializeTimeZoneAndEnvironment(this, newSceneStaticData.location.timeZone, -1);
if (newSceneStaticData.location.environment != oldLocation.environment && newSceneStaticData.location.environment != -2)
initializeTimeZoneAndEnvironment(this, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment);
// If we are movinto a different node or time zone, reset the evidence flag
if (newSceneStaticData.location.timeZone != oldLocation.timeZone ||
newSceneStaticData.location.environment != oldLocation.environment ||
newSceneStaticData.location.node != oldLocation.node) {
if (_globalFlags.bcLocateEnabled == 1) {
_globalFlags.bcLocateEnabled = 0;
((GameUIWindow *)_parent)->_bioChipRightWindow->invalidateWindow(false);
}
}
// Create the new scene object
SceneBase *newScene = constructSceneObject(this, newSceneStaticData, oldLocation);
if (!newScene)
error("Failed to create new scene");
// Switch on the type of transition
if (destinationData.transitionType == TRANSITION_VIDEO) {
// Play transition
playTransition(destinationData, newScene->_staticData.navFrameIndex);
// Change the ambient sound
if (newSceneStaticData.location.timeZone != oldLocation.timeZone || newSceneStaticData.location.environment != oldLocation.environment || oldLocation.timeZone < 0)
startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment, false);
} else {
// Change the ambient sound
if (newSceneStaticData.location.timeZone != oldLocation.timeZone || newSceneStaticData.location.environment != oldLocation.environment || oldLocation.timeZone < 0)
startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newSceneStaticData.location.timeZone, newSceneStaticData.location.environment);
// Play transition
playTransition(destinationData, newScene->_staticData.navFrameIndex);
}
// Call the post-exit function
retVal = _currentScene->postExitRoom(this, destinationData.destinationScene);
if (retVal == SC_DEATH)
return false;
if (retVal != SC_TRUE) {
newScene->preDestructor();
delete newScene;
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
return true;
}
// Delete the current scene
_currentScene->preDestructor();
delete _currentScene;
_currentScene = newScene;
// If this scene has no cycle frames, flush the cycle frame cache
if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
flushCycleFrameCache();
// Call the pre-enter function, exiting this function if SC_END_PROCESSING is returned
if (_currentScene->preEnterRoom(this, oldLocation) == SC_END_PROCESSING)
return true;
// Send new navigation data to navigation buttons
if (_globalFlags.bcCloakingEnabled != 1)
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(newScene->_staticData);
// If this is a different time zone, then change the date
if (newSceneStaticData.location.timeZone != oldLocation.timeZone)
((GameUIWindow *)_parent)->changeCurrentDate(newSceneStaticData.location.timeZone);
// Call for a repaint
invalidateWindow(false);
// Call the post-enter function
_currentScene->postEnterRoom(this, oldLocation);
// Invalidate this too, for some reason
_parent->invalidateWindow(false);
// Check the AI database for this environment to see if there is a spontaneous comment for this scene
if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI) && !_disableArthur)
playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
// Notify the right BioChip window of change
((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
// Re-enable navigation arrows
((GameUIWindow *)_parent)->_navArrowWindow->enableWindow(true);
// Hardcoded demo ambient
if (_vm->isDemo() && newSceneStaticData.location.environment != oldLocation.environment)
startDemoAmbientSound();
return true;
}
bool SceneViewWindow::timeSuitJump(int destination) {
// Determine if this is a valid jump
if (destination < 0 || destination > 4)
return false;
// Clear any live text
((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText();
// Disable movement controls
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(0, 0, 0, 0, 0);
Location newLocation, oldLocation;
switch (destination) {
case 0:
newLocation = Location(2, 1, 6, 1, 1, 0);
oldLocation = Location(-2, -2, -2, -2, -2, -2);
break;
case 1:
newLocation = Location(1, 1, 3, 3, 1, 0);
oldLocation = Location(-2, -2, -2, -2, -2, -2);
break;
case 2:
newLocation = Location(5, 1, 2, 4, 1, 0);
oldLocation = Location(-2, -2, -2, -2, -2, -2);
break;
case 3:
if (_globalFlags.generalWalkthroughMode == 0)
newLocation = Location(6, 10, 0, 0, 0, 0);
else
newLocation = Location(6, 1, 1, 1, 2, 0);
oldLocation = Location(-2, -2, -2, -2, -2, -2);
break;
case 4:
newLocation = Location(4, 3, 3, 0, 1, 0);
oldLocation = Location(-2, -2, -2, -2, -2, -2);
break;
}
// Save the old location to use for the scene we are leaving
Location specOldLocation = oldLocation;
// Call the pre-transition function
if (_currentScene)
_currentScene->preExitRoom(this, specOldLocation);
// Make sure the right BioChip is displayed
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipJump);
// Play the movie
Common::ScopedPtr<VideoWindow> jumpMovie(new VideoWindow(_vm, ((GameUIWindow *)_parent)->_bioChipRightWindow));
if (!jumpMovie->openVideo(_vm->getFilePath(IDS_BC_JUMP_MOVIE_FILENAME)))
error("Failed to play small jump movie");
// Reposition
jumpMovie->setWindowPos(0, 0, 28, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
// Notify the BioChip of the change
((GameUIWindow *)_parent)->_bioChipRightWindow->jumpInitiated(false);
// Show and disable the movie window
jumpMovie->enableWindow(false);
jumpMovie->showWindow(kWindowShow);
// Start fading down the current ambient and start the jump audio file
_vm->_sound->setAmbientSound();
_vm->_sound->playInterfaceSound(_vm->getFilePath(IDS_BC_JUMP_AUDIO_FILENAME));
// Play the movie
jumpMovie->playToFrame(24);
while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped && _vm->_sound->isInterfaceSoundPlaying()) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
// Make sure the interface sound has stopped
_vm->_sound->stopInterfaceSound();
_vm->_sound->timerCallback();
jumpMovie.reset(new VideoWindow(_vm, this));
Common::String fileName;
switch (destination) {
case 0:
fileName = _vm->getFilePath(IDS_MAYAN_JUMP_MOVIE_FILENAME);
break;
case 1:
fileName = _vm->getFilePath(IDS_CASTLE_JUMP_MOVIE_FILENAME);
break;
case 2:
fileName = _vm->getFilePath(IDS_DAVINCI_JUMP_MOVIE_FILENAME);
break;
case 3:
fileName = _vm->getFilePath(IDS_AILAB_JUMP_MOVIE_FILENAME);
break;
case 4:
fileName = _vm->getFilePath(IDS_FUTAPT_JUMP_MOVIE_FILENAME);
break;
}
if (!jumpMovie->openVideo(fileName))
error("Failed to play movie '%s'", fileName.c_str());
jumpMovie->setWindowPos(0, 0, 0, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
// Show and disable the window
jumpMovie->enableWindow(false);
jumpMovie->showWindow(kWindowShow);
_vm->_sound->stop();
// Play the movie
jumpMovie->playVideo();
while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped)
_vm->yield();
if (_vm->shouldQuit())
return true;
_vm->_sound->restart();
jumpMovie.reset();
// Initialize the time zone and environment
initializeTimeZoneAndEnvironment(this, newLocation.timeZone, -1);
initializeTimeZoneAndEnvironment(this, newLocation.timeZone, newLocation.environment);
// Get the static scene data for this new location
LocationStaticData newSceneStaticData;
if (!getSceneStaticData(newLocation, newSceneStaticData))
return false;
// And the old location
if (_currentScene)
oldLocation = _currentScene->_staticData.location;
// Create the new scene object
// The original passed oldLocation, but that's wrong for jumping. I mean, that's
// why David made specOldLocation in the first place. This prevents the wrong location
// being passed for the castle intro with the guard getting shot by the arrow.
SceneBase *newScene = constructSceneObject(this, newSceneStaticData, specOldLocation);
if (_currentScene) {
// Post-transition function
if (_currentScene->postExitRoom(this, specOldLocation) == SC_DEATH)
return false;
// Delete the old scene
_currentScene->preDestructor();
delete _currentScene;
}
if (!newScene)
error("Failed to create scene object for time zone %d", destination);
// Set the new scene
_currentScene = newScene;
// If this scene has no cycle frames, flush the cycle frame cache
if (isCyclingEnabled() && newSceneStaticData.cycleStartFrame == -1)
flushCycleFrameCache();
// Update navigation buttons, if not cloaked
if (_globalFlags.bcCloakingEnabled != 1)
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
// Change the date if the time zone changed
if (newLocation.timeZone != oldLocation.timeZone)
((GameUIWindow *)_parent)->changeCurrentDate(newLocation.timeZone);
// Call for a repaint
invalidateWindow(false);
_vm->_sound->timerCallback();
// Time to show and play the right-hand small movie to the mid point, with proper sound
jumpMovie.reset(new VideoWindow(_vm, ((GameUIWindow *)_parent)->_bioChipRightWindow));
if (!jumpMovie->openVideo(_vm->getFilePath(IDS_BC_JUMP_MOVIE_FILENAME)))
error("Failed to play small jump movie");
jumpMovie->setWindowPos(0, 0, 28, 0, 0, kWindowPosNoSize | kWindowPosNoZOrder | kWindowPosHideWindow);
// Notify the BioChip of the change
((GameUIWindow *)_parent)->_bioChipRightWindow->jumpEnded(false);
jumpMovie->enableWindow(false);
jumpMovie->showWindow(kWindowShow);
// Start the ambient fading back up, and play the jump sound
startEnvironmentAmbient(oldLocation.timeZone, oldLocation.environment, newLocation.timeZone, newLocation.environment);
_vm->_sound->playInterfaceSound(_vm->getFilePath(IDS_BC_JUMP_AUDIO_FILENAME));
// Play the movie
jumpMovie->seekToFrame(24);
jumpMovie->playToFrame(48);
while (!_vm->shouldQuit() && jumpMovie->getMode() != VideoWindow::kModeStopped && _vm->_sound->isInterfaceSoundPlaying()) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
// Forceably stop the interface sound
_vm->_sound->stopInterfaceSound();
// Destroy the movie
jumpMovie.reset();
// Repaint the BioChip view window
((GameUIWindow *)_parent)->_bioChipRightWindow->invalidateWindow(false);
// Repaint the window
invalidateWindow(false);
// Call the post-enter function
_currentScene->postEnterRoom(this, oldLocation);
_parent->invalidateWindow(false);
if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI))
playAIComment(newSceneStaticData.location, AI_COMMENT_TYPE_SPONTANEOUS);
// Notify the right BioChip window of the change
((GameUIWindow *)_parent)->_bioChipRightWindow->sceneChanged();
return true;
}
bool SceneViewWindow::playTransition(const DestinationScene &destinationData, int navFrame) {
// Call the appropriate function for the transition type
switch (destinationData.transitionType) {
case TRANSITION_PUSH:
if (_vm->isControlDown()) {
if (navFrame >= 0) {
LocationStaticData destinationStaticData;
if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
return false;
changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
newBackground->free();
delete newBackground;
}
return true;
} else {
LocationStaticData destinationStaticData;
if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
return false;
Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
Graphics::Surface *curBackground = _preBuffer;
bool retVal = false;
if (destinationData.transitionData == 0 || destinationData.transitionData == 3)
retVal = pushTransition(curBackground, newBackground, destinationData.transitionData, _vm->_gfx->computeVPushOffset(_vm->getTransitionSpeed()), 0);
else
retVal = pushTransition(curBackground, newBackground, destinationData.transitionData, _vm->_gfx->computeHPushOffset(_vm->getTransitionSpeed()), 0);
newBackground->free();
delete newBackground;
return retVal;
}
case TRANSITION_WALK:
if (_vm->isControlDown()) {
if (navFrame >= 0) {
LocationStaticData destinationStaticData;
if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
return false;
changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
newBackground->free();
delete newBackground;
}
return true;
} else {
// The demo has a hardcoded door open sound
// This, and the code below the walkTransition call, are glue around the
// demo's sound implementation. The demo is based on an alpha which uses
// waveOut to play sounds, as opposed to the final which uses WAIL.
if (_vm->isDemo() && destinationData.destinationScene.depth == 1) {
// Stop the current ambient sound
// onTimer() will restart it
_vm->_sound->setAmbientSound();
if (_currentScene->_staticData.location.environment == 4)
_demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGMBDO.WAV");
else
_demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGBSDO.WAV");
}
bool retVal = walkTransition(_currentScene->_staticData.location, destinationData, navFrame);
// And also a door close sound
if (_vm->isDemo() && destinationData.destinationScene.environment != _currentScene->_staticData.location.environment) {
// Stop the current ambient sound
// onTimer() will restart it
_vm->_sound->setAmbientSound();
if (_currentScene->_staticData.location.environment == 4)
_demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGBSDC.WAV");
else
_demoSoundEffectHandle = _vm->_sound->playSoundEffect("CASTLE/CGMBDC.WAV");
}
return retVal;
}
case TRANSITION_VIDEO:
if (_vm->isControlDown() && false) { // TODO: debug mode check (maybe?)
if (navFrame >= 0) {
LocationStaticData destinationStaticData;
if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData))
return false;
changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
Graphics::Surface *newBackground = getStillFrameCopy(navFrame);
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
newBackground->free();
delete newBackground;
}
return true;
} else {
return videoTransition(_currentScene->_staticData.location, destinationData, navFrame);
}
}
return false;
}
bool SceneViewWindow::videoTransition(const Location &location, DestinationScene destinationData, int navFrame) {
TempCursorChange cursorChange(kCursorWait);
_paused = true;
bool audioStream = true;
// If the start frame is less than 0, open up the animation database and retrieve all of the info
if (destinationData.transitionStartFrame < 0) {
Common::Array<AnimEvent> animEvents = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
bool found = false;
uint i = 0;
for (; i < animEvents.size(); i++) {
if (animEvents[i].animationID == destinationData.transitionData) {
found = true;
break;
}
}
if (!found) {
_paused = false;
return false;
}
destinationData.transitionData = animEvents[i].fileNameID;
destinationData.transitionStartFrame = animEvents[i].startFrame;
destinationData.transitionLength = animEvents[i].frameCount;
if (animEvents[i].audioStreamCount < 1)
audioStream = false;
}
LocationStaticData destinationStaticData;
if (!getSceneStaticData(destinationData.destinationScene, destinationStaticData)) {
_paused = false;
return false;
}
changeStillFrameMovie(_vm->getFilePath(destinationStaticData.location.timeZone, destinationStaticData.location.environment, SF_STILLS));
Graphics::Surface *newBackground = nullptr;
if (destinationStaticData.navFrameIndex >= 0)
newBackground = getStillFrameCopy(navFrame);
// Open the movie
Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, destinationData.transitionData);
if (!animationMovie->openVideo(fileName))
error("Failed to open video transition movie '%s'", fileName.c_str());
if (audioStream)
_vm->_sound->stop();
animationMovie->seekToFrame(destinationData.transitionStartFrame);
animationMovie->showWindow(kWindowShow);
animationMovie->playToFrame(destinationData.transitionStartFrame + destinationData.transitionLength - 1);
while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
animationMovie.reset();
if (audioStream)
_vm->_sound->restart();
if (newBackground) {
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
newBackground->free();
delete newBackground;
}
_paused = false;
return true;
}
bool SceneViewWindow::walkTransition(const Location &location, const DestinationScene &destinationData, int navFrame) {
_paused = true;
TempCursorChange cursorChange(kCursorWait);
Graphics::Surface *newBackground = nullptr;
if (navFrame >= 0) {
changeStillFrameMovie(_vm->getFilePath(destinationData.destinationScene.timeZone, destinationData.destinationScene.environment, SF_STILLS));
newBackground = getStillFrameCopy(navFrame);
}
Common::String walkFileName = _vm->getFilePath(location.timeZone, location.environment, SF_NAVIGATION);
if (_walkMovieFileName != walkFileName) {
delete _walkMovie;
_walkMovie = new VideoWindow(_vm, this);
_walkMovie->setWindowPos(kWindowPosTop, 0, 0, 0, 0, kWindowPosNoActivate | kWindowPosNoZOrder | kWindowPosNoSize);
if (!_walkMovie->openVideo(walkFileName))
error("Failed to open walk movie '%s'", walkFileName.c_str());
_walkMovieFileName = walkFileName;
}
_vm->_sound->timerCallback(); // necessary?
_walkMovie->seekToFrame(destinationData.transitionStartFrame);
if (navFrame < 0) {
// FIXME: Is this possible?
_paused = false;
return true;
}
_walkMovie->showWindow(kWindowShow);
_walkMovie->invalidateWindow(false);
// Start the footsteps
_vm->_sound->startFootsteps(destinationData.transitionData);
_walkMovie->playToFrame(destinationData.transitionStartFrame + destinationData.transitionLength - 1);
while (!_vm->shouldQuit() && _walkMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
_vm->_sound->stopFootsteps();
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
newBackground->free();
delete newBackground;
_walkMovie->showWindow(kWindowHide);
_paused = false;
return true;
}
bool SceneViewWindow::pushTransition(Graphics::Surface *curBackground, Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
// Check the validity of the parameters
if (!curBackground || !newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
return false;
// Change the cursor to an hourglass
TempCursorChange cursorChange(kCursorWait);
_useScenePaint = false;
switch (direction) {
case 0: // Push down
for (int i = 0; i < DIB_FRAME_HEIGHT; i += stripSize) {
curBackground->move(0, stripSize, curBackground->h);
for (int j = 0; j < stripSize; j++)
memcpy(curBackground->getBasePtr(0, j), newBackground->getBasePtr(0, curBackground->h - (i + stripSize) + j), newBackground->w * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 1: // Push right
for (int i = 0; i < DIB_FRAME_WIDTH; i += stripSize) {
curBackground->move(stripSize, 0, curBackground->h);
for (int j = 0; j < curBackground->h; j++)
memcpy(curBackground->getBasePtr(0, j), newBackground->getBasePtr(newBackground->w - (i + stripSize), j), stripSize * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 2: // Push left
for (int i = 0; i < DIB_FRAME_WIDTH; i += stripSize) {
curBackground->move(-stripSize, 0, curBackground->h);
for (int j = 0; j < curBackground->h; j++)
memcpy(curBackground->getBasePtr(curBackground->w - stripSize, j), newBackground->getBasePtr(i, j), stripSize * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 3: // Push up
for (int i = 0; i < DIB_FRAME_HEIGHT; i += stripSize) {
curBackground->move(0, -stripSize, curBackground->h);
for (int j = 0; j < stripSize; j++)
memcpy(curBackground->getBasePtr(0, curBackground->h - stripSize + j), newBackground->getBasePtr(0, i + j), newBackground->w * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
}
_useScenePaint = true;
return true;
}
bool SceneViewWindow::pushNewTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
// Check the validity of the parameters
if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
return false;
// Call the push transition function
if (direction == 0 || direction == 3)
return pushTransition(_preBuffer, newBackground, direction, _vm->_gfx->computeVPushOffset(_vm->getTransitionSpeed()), totalTime);
return pushTransition(_preBuffer, newBackground, direction, _vm->_gfx->computeHPushOffset(_vm->getTransitionSpeed()), totalTime);
}
bool SceneViewWindow::slideInTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
// Check the validity of the parameters
if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
return false;
// Change the cursor to an hourglass
TempCursorChange cursorChange(kCursorWait);
switch (direction) {
case 0: // Push down
for (int i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
for (int j = 0; j < i; j++)
memcpy(_preBuffer->getBasePtr(0, j), newBackground->getBasePtr(0, DIB_FRAME_HEIGHT - j), newBackground->w * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 1: // Push right
for (int i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
for (int j = 0; j < DIB_FRAME_HEIGHT; j++)
memcpy(_preBuffer->getBasePtr(0, j), newBackground->getBasePtr(DIB_FRAME_WIDTH - i, j), i * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 2: // Push left
for (int i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
for (int j = 0; j < DIB_FRAME_HEIGHT; j++)
memcpy(_preBuffer->getBasePtr(0, DIB_FRAME_WIDTH - i), newBackground->getBasePtr(0, j), i * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
case 3: // Push up
for (int i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
for (int j = 0; j < i; j++)
memcpy(_preBuffer->getBasePtr(0, DIB_FRAME_HEIGHT - j), newBackground->getBasePtr(0, j), newBackground->w * newBackground->format.bytesPerPixel);
invalidateWindow(false);
_vm->yield();
}
break;
}
return true;
}
bool SceneViewWindow::slideOutTransition(Graphics::Surface *newBackground, int direction, int stripSize, int totalTime) {
// Check the validity of the parameters
if (!newBackground || direction < 0 || direction > 4 || stripSize <= 0 || totalTime < 0)
return false;
// Change the cursor to an hourglass
TempCursorChange cursorChange(kCursorWait);
Graphics::Surface curBackground;
curBackground.copyFrom(*_preBuffer);
_useScenePaint = false;
switch (direction) {
case 0: // Push down
for (int i = stripSize; i <= DIB_FRAME_HEIGHT; i += stripSize) {
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
_vm->_gfx->crossBlit(_preBuffer, 0, i, 432, 189 - i, &curBackground, 0, 0);
invalidateWindow(false);
_vm->yield();
}
break;
case 1: // Push right
for (int i = DIB_FRAME_WIDTH; i >= 0; i -= stripSize) {
if (i < DIB_FRAME_WIDTH)
_vm->_gfx->crossBlit(_preBuffer, i, 0, DIB_FRAME_WIDTH - i, 189, newBackground, i, 0);
_vm->_gfx->crossBlit(_preBuffer, 0, 0, i, 189, &curBackground, DIB_FRAME_WIDTH - i, 0);
invalidateWindow(false);
_vm->yield();
}
break;
case 2: // Push left
for (int i = stripSize; i <= DIB_FRAME_WIDTH; i += stripSize) {
_vm->_gfx->crossBlit(_preBuffer, 0, 0, i, 189, newBackground, 0, 0);
_vm->_gfx->crossBlit(_preBuffer, i, 0, 432 - i, 189, &curBackground, 0, 0);
invalidateWindow(false);
_vm->yield();
}
break;
case 3: // Push up
for (int i = DIB_FRAME_HEIGHT; i >= 0; i -= stripSize) {
_vm->_gfx->crossBlit(_preBuffer, 0, 0, 432, 189, newBackground, 0, 0);
_vm->_gfx->crossBlit(_preBuffer, 0, 189 - i, 432, i, &curBackground, 0, 0);
invalidateWindow(false);
_vm->yield();
}
break;
}
curBackground.free();
_useScenePaint = true;
return true;
}
bool SceneViewWindow::changeStillFrameMovie(const Common::String &fileName) {
return _stillFrames->open(fileName);
}
bool SceneViewWindow::changeCycleFrameMovie(const Common::String &fileName) {
// Only continue if cycling is enabled
if (!isCyclingEnabled()) {
return false;
}
if (((FrameWindow *)(_parent->getParent()))->isFrameCachingAllowed())
return _cycleFrames->open(fileName, 5);
return _cycleFrames->open(fileName);
}
Graphics::Surface *SceneViewWindow::getStillFrameCopy(int frameIndex) {
return _stillFrames->getFrameCopy(frameIndex);
}
const Graphics::Surface *SceneViewWindow::getStillFrame(int frameIndex) {
return _stillFrames->getFrame(frameIndex);
}
Graphics::Surface *SceneViewWindow::getCycleFrameCopy(int frameIndex) {
if (!isCyclingEnabled())
return 0;
return _cycleFrames->getFrameCopy(frameIndex);
}
const Graphics::Surface *SceneViewWindow::getCycleFrame(int frameIndex) {
if (!isCyclingEnabled())
return 0;
return _cycleFrames->getFrame(frameIndex);
}
bool SceneViewWindow::enableCycleFrameCache(bool enable) {
if (!isCyclingEnabled())
return false;
_cycleFrames->enableFrameCache(enable);
return true;
}
bool SceneViewWindow::flushCycleFrameCache() {
if (!isCyclingEnabled())
return false;
return _cycleFrames->flushFrameCache();
}
bool SceneViewWindow::enableCycling(bool enable) {
bool oldStatus = isCyclingEnabled();
_cycleEnabled = enable;
if (oldStatus != isCyclingEnabled())
handleCyclingChange();
return true;
}
bool SceneViewWindow::forceEnableCycling(bool enable) {
bool oldStatus = isCyclingEnabled();
_forceCycleEnabled = enable;
if (oldStatus != isCyclingEnabled())
handleCyclingChange();
return true;
}
void SceneViewWindow::handleCyclingChange() {
if (isCyclingEnabled()) {
// Re-enabling -> set up the cycle movie again
if (_currentScene) {
const LocationStaticData &staticData = _currentScene->_staticData;
if (staticData.cycleStartFrame >= 0)
changeCycleFrameMovie(_vm->getFilePath(staticData.location.timeZone, staticData.location.environment, SF_CYCLES));
}
} else {
// Disabling -> close the cycle movie
_cycleFrames->flushFrameCache();
_cycleFrames->close();
}
}
bool SceneViewWindow::closeCycleFrameMovie() {
_cycleFrames->close();
return true;
}
int SceneViewWindow::getGlobalFlag(int offset) {
// TODO: Verify the offset
const byte *data = (const byte *)&_globalFlags;
return READ_UINT16(data + offset);
}
byte SceneViewWindow::getGlobalFlagByte(int offset) {
// TODO: Verify the offset
if (offset < 0)
return 0;
const byte *data = (const byte *)&_globalFlags;
return data[offset];
}
bool SceneViewWindow::setGlobalFlag(int offset, int value) {
// TODO: Verify the offset
byte *data = (byte *)&_globalFlags;
WRITE_UINT16(data + offset, value);
return true;
}
bool SceneViewWindow::setGlobalFlagByte(int offset, byte value) {
// TODO: Verify the offset
byte *data = (byte *)&_globalFlags;
data[offset] = value;
return true;
}
uint32 SceneViewWindow::getGlobalFlagDWord(int offset) {
// TODO: Verify the offset
const byte *data = (const byte *)&_globalFlags;
return READ_UINT32(data + offset);
}
bool SceneViewWindow::setGlobalFlagDWord(int offset, uint32 value) {
// TODO: Verify the offset
byte *data = (byte *)&_globalFlags;
WRITE_UINT32(data + offset, value);
return true;
}
bool SceneViewWindow::addNumberToGlobalFlagTable(int tableOffset, int curItemCountOffset, int maxItems, byte numberToAdd) {
// TODO: Rewrite this
byte *data = (byte *)&_globalFlags;
byte *itemCountPtr = data + curItemCountOffset;
int itemCount = *itemCountPtr;
if (itemCount >= maxItems)
return false;
byte *tableEntries = data + tableOffset;
for (int i = 0; i < itemCount; i++)
if (tableEntries[i] == numberToAdd)
return false;
tableEntries[itemCount] = numberToAdd;
*itemCountPtr = itemCount + 1;
return true;
}
byte SceneViewWindow::getNumberFromGlobalFlagTable(int tableOffset, int tableIndex) {
const byte *data = (const byte *)&_globalFlags;
return data[tableOffset + tableIndex];
}
bool SceneViewWindow::isNumberInGlobalFlagTable(int tableOffset, int curItemCountOffset, byte numberToCheck) {
const byte *data = (const byte *)&_globalFlags;
int itemCount = *(data + curItemCountOffset);
const byte *tableEntries = data + tableOffset;
for (int i = 0; i < itemCount; i++)
if (tableEntries[i] == numberToCheck)
return true;
return false;
}
bool SceneViewWindow::getCurrentSceneLocation(Location &location) {
if (!_currentScene)
return false;
location = _currentScene->_staticData.location;
return true;
}
bool SceneViewWindow::playSynchronousAnimation(int animationID) {
TempCursorChange cursorChange(kCursorWait);
Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
bool found = false;
uint i = 0;
for (; i < animDatabase.size(); i++) {
if (animDatabase[i].animationID == animationID) {
found = true;
break;
}
}
if (!found)
return false;
Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
if (!animationMovie->openVideo(fileName))
error("Failed to open video '%s'", fileName.c_str());
// Switch to the second audio stream if translation is enabled
if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
animationMovie->setAudioTrack(2);
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
return false;
animationMovie->seekToFrame(animDatabase[i].startFrame);
animationMovie->enableWindow(false);
animationMovie->showWindow(kWindowShow);
_parent->invalidateWindow(false);
// Empty the input queue
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Stop background sound if the video has sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->stop();
animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Restart background sound if the video had sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->restart();
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
return false;
return true;
}
bool SceneViewWindow::playSynchronousAnimationExtern(int animationID) {
TempCursorChange cursorChange(kCursorWait);
Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
Common::String fileName = _vm->getFilePath(animationID);
if (!animationMovie->openVideo(fileName))
error("Failed to open video '%s'", fileName.c_str());
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
return false;
animationMovie->enableWindow(false);
animationMovie->showWindow(kWindowShow);
_parent->invalidateWindow(false);
// Empty the input queue
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
_vm->_sound->stop();
animationMovie->playVideo();
while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
_vm->_sound->restart();
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
return false;
return true;
}
bool SceneViewWindow::playPlacedSynchronousAnimation(int animationID, int left, int top) {
TempCursorChange cursorChange(kCursorWait);
Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
bool found = false;
uint i = 0;
for (; i < animDatabase.size(); i++) {
if (animDatabase[i].animationID == animationID) {
found = true;
break;
}
}
if (!found)
return false;
Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
if (!animationMovie->openVideo(fileName))
error("Failed to open video '%s'", fileName.c_str());
animationMovie->setWindowPos(kWindowPosTopMost, left, top, 0, 0, kWindowPosNoSize | kWindowPosNoActivate | kWindowPosNoZOrder);
// Switch to the second audio stream if translation is enabled
if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
animationMovie->setAudioTrack(2);
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
return false;
animationMovie->seekToFrame(animDatabase[i].startFrame);
animationMovie->enableWindow(false);
animationMovie->showWindow(kWindowShow);
_parent->invalidateWindow(false);
// Empty the input queue
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Stop background sound if the video has sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->stop();
animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Restart background sound if the video had sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->restart();
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
return false;
return true;
}
bool SceneViewWindow::playClippedSynchronousAnimation(int animationID, int left, int top, int right, int bottom) {
TempCursorChange cursorChange(kCursorWait);
Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
bool found = false;
uint i = 0;
for (; i < animDatabase.size(); i++) {
if (animDatabase[i].animationID == animationID) {
found = true;
break;
}
}
if (!found)
return false;
Common::ScopedPtr<VideoWindow> animationMovie(new VideoWindow(_vm, this));
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animDatabase[i].fileNameID);
if (!animationMovie->openVideo(fileName))
error("Failed to open video '%s'", fileName.c_str());
animationMovie->setWindowPos(kWindowPosTopMost, left, top, right - left, bottom - top, kWindowPosNoActivate | kWindowPosNoZOrder);
animationMovie->setSourceRect(Common::Rect(left, top, right, bottom));
animationMovie->setDestRect(Common::Rect(0, 0, right - left, bottom - top));
// Switch to the second audio stream if translation is enabled
if (_globalFlags.bcTranslateEnabled == 1 && animDatabase[i].audioStreamCount > 1)
animationMovie->setAudioTrack(2);
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_START) == SC_FALSE)
return false;
animationMovie->seekToFrame(animDatabase[i].startFrame);
animationMovie->enableWindow(false);
animationMovie->showWindow(kWindowShow);
_parent->invalidateWindow(false);
// Empty the input queue
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Stop background sound if the video has sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->stop();
animationMovie->playToFrame(animDatabase[i].startFrame + animDatabase[i].frameCount - 1);
while (!_vm->shouldQuit() && animationMovie->getMode() != VideoWindow::kModeStopped) {
_vm->yield();
_vm->_sound->timerCallback();
}
if (_vm->shouldQuit())
return true;
_vm->removeMouseMessages(this);
_vm->removeKeyboardMessages(this);
// Restart background sound if the video had sound
if (animDatabase[i].audioStreamCount > 0)
_vm->_sound->restart();
if (_currentScene && _currentScene->movieCallback(this, animationMovie.get(), animationID, MOVIE_STOPPED) == SC_FALSE)
return false;
return true;
}
bool SceneViewWindow::startAsynchronousAnimation(int animationID, bool loopAnimation) {
return startPlacedAsynchronousAnimation(0, 0, 432, 189, animationID, loopAnimation);
}
bool SceneViewWindow::startAsynchronousAnimation(int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
return startPlacedAsynchronousAnimation(0, 0, 432, 189, fileNameID, startPosition, playStartPosition, frameCount, loopAnimation);
}
bool SceneViewWindow::startAsynchronousAnimationExtern(int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
return startPlacedAsynchronousAnimationExtern(0, 0, 432, 189, fileNameID, startPosition, playStartPosition, frameCount, loopAnimation);
}
bool SceneViewWindow::stopAsynchronousAnimation() {
if (!_currentScene)
return false;
if (!_asyncMovie)
return false;
_asyncMovie->stopVideo();
_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_STOPPED);
delete _asyncMovie;
_asyncMovie = nullptr;
_asyncMovieFileName.clear();
_asyncMovieStartFrame = 0;
_asyncMovieFrameCount = 0;
_loopAsyncMovie = false;
return true;
}
bool SceneViewWindow::isAsynchronousAnimationStillPlaying() {
if (!_asyncMovie)
return false;
return _asyncMovie->getMode() != VideoWindow::kModeStopped;
}
int SceneViewWindow::getAsynchronousAnimationCurrentPosition() {
if (!_asyncMovie)
return -1;
return _asyncMovie->getCurFrame();
}
bool SceneViewWindow::asynchronousAnimationTimerCallback() {
if (!_asyncMovie)
return false;
if (_asyncMovie->getMode() == VideoWindow::kModeStopped) {
if (_loopAsyncMovie) {
_asyncMovie->seekToFrame(_asyncMovieStartFrame);
_asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
if (_currentScene && _currentScene->movieCallback(this, _asyncMovie, -1, MOVIE_LOOPING_RESTART) == SC_FALSE)
return false;
} else {
if (_currentScene) {
if (_currentScene->movieCallback(this, _asyncMovie, -1, MOVIE_STOPPED) == SC_TRUE) {
stopAsynchronousAnimation();
return true;
}
return false;
} else {
stopAsynchronousAnimation();
}
}
}
return true;
}
bool SceneViewWindow::startPlacedAsynchronousAnimation(int left, int top, int width, int height, int animationID, bool loopAnimation) {
if (!_currentScene)
return false;
if (_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
}
Common::Array<AnimEvent> animDatabase = getAnimationDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
if (animDatabase.empty())
return false;
const AnimEvent *animData = nullptr;
for (uint i = 0; i < animDatabase.size() && !animData; i++)
if (animDatabase[i].animationID == animationID)
animData = &animDatabase[i];
if (!animData)
return false;
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, animData->fileNameID);
if (fileName != _asyncMovieFileName) {
_asyncMovieFileName.clear();
if (_asyncMovie) {
_asyncMovie->stopVideo();
_asyncMovie->closeVideo();
} else {
_asyncMovie = new VideoWindow(_vm, this);
}
if (!_asyncMovie->openVideo(fileName))
return false;
_asyncMovieFileName = fileName;
}
_asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
_asyncMovie->enableWindow(false);
_asyncMovieStartFrame = animData->startFrame;
_asyncMovieFrameCount = animData->frameCount;
_loopAsyncMovie = loopAnimation;
if (_currentScene->movieCallback(this, _asyncMovie, animationID, MOVIE_START) == SC_FALSE)
return false;
_asyncMovie->seekToFrame(animData->startFrame);
_asyncMovie->showWindow(kWindowShow);
_asyncMovie->playToFrame(animData->startFrame + animData->frameCount - 1);
return true;
}
bool SceneViewWindow::startPlacedAsynchronousAnimation(int left, int top, int width, int height, int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
if (!_currentScene)
return false;
if (_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
}
Common::String fileName = _vm->getFilePath(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment, fileNameID);
if (fileName != _asyncMovieFileName) {
_asyncMovieFileName.clear();
if (_asyncMovie) {
_asyncMovie->stopVideo();
_asyncMovie->closeVideo();
} else {
_asyncMovie = new VideoWindow(_vm, this);
}
if (!_asyncMovie->openVideo(fileName))
return false;
_asyncMovieFileName = fileName;
}
_asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
_asyncMovie->enableWindow(false);
_asyncMovieStartFrame = (startPosition < 0) ? 0 : startPosition;
_asyncMovieFrameCount = (frameCount < 0) ? _asyncMovie->getFrameCount() : frameCount;
_loopAsyncMovie = loopAnimation;
if (_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_START) == SC_FALSE)
return false;
_asyncMovie->seekToFrame((playStartPosition < 0) ? 0 : playStartPosition);
_asyncMovie->showWindow(kWindowShow);
_asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
return true;
}
bool SceneViewWindow::startPlacedAsynchronousAnimationExtern(int left, int top, int width, int height, int fileNameID, int startPosition, int playStartPosition, int frameCount, bool loopAnimation) {
if (!_currentScene)
return false;
if (_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
}
Common::String fileName = _vm->getFilePath(fileNameID);
if (fileName != _asyncMovieFileName) {
_asyncMovieFileName.clear();
if (_asyncMovie) {
_asyncMovie->stopVideo();
_asyncMovie->closeVideo();
} else {
_asyncMovie = new VideoWindow(_vm, this);
}
if (!_asyncMovie->openVideo(fileName))
return false;
_asyncMovieFileName = fileName;
}
_asyncMovie->setWindowPos(0, left, top, width, height, kWindowPosNoZOrder);
_asyncMovie->enableWindow(false);
_asyncMovieStartFrame = (startPosition < 0) ? 0 : startPosition;
_asyncMovieFrameCount = (frameCount < 0) ? _asyncMovie->getFrameCount() : frameCount;
_loopAsyncMovie = loopAnimation;
if (_currentScene->movieCallback(this, _asyncMovie, 0, MOVIE_START) == SC_FALSE)
return false;
_asyncMovie->seekToFrame((playStartPosition < 0) ? 0 : playStartPosition);
_asyncMovie->showWindow(kWindowShow);
_asyncMovie->playToFrame(_asyncMovieStartFrame + _asyncMovieFrameCount - 1);
return true;
}
bool SceneViewWindow::retrieveAICommentEntry(const Location &commentLocation, int commentType, const Common::Array<AIComment> &commentDatabase, int &lastFoundIndex, AIComment &currentCommentData) {
if (commentDatabase.empty() || (uint32)lastFoundIndex >= commentDatabase.size())
return false;
const AIComment *commentData = &commentDatabase[lastFoundIndex];
bool entryFound = false;
if (_globalFlags.generalWalkthroughMode == 1 && commentType == AI_COMMENT_TYPE_SPONTANEOUS) {
// Look for any spontaneous comments
for (; lastFoundIndex < (int)commentDatabase.size() && !entryFound; lastFoundIndex++) {
if ((commentData->commentFlags & AI_COMMENT_TYPE_SPONTANEOUS || (commentData->commentFlags & AI_COMMENT_TYPE_HELP && commentData->dependencyValueA == 0))
&& (commentLocation.timeZone == commentData->location.timeZone || commentData->location.timeZone == -1)
&& (commentLocation.environment == commentData->location.environment || commentData->location.environment == -1)
&& (commentLocation.node == commentData->location.node || commentData->location.node == -1)
&& (commentLocation.facing == commentData->location.facing || commentData->location.facing == -1)
&& (commentLocation.orientation == commentData->location.orientation || commentData->location.orientation == -1)
&& (commentLocation.depth == commentData->location.depth || commentData->location.depth == -1)) {
entryFound = true;
} else {
commentData++;
}
}
} else {
for (; lastFoundIndex < (int)commentDatabase.size() && !entryFound; lastFoundIndex++) {
if ((commentData->commentFlags & commentType)
&& (commentLocation.timeZone == commentData->location.timeZone || commentData->location.timeZone == -1)
&& (commentLocation.environment == commentData->location.environment || commentData->location.environment == -1)
&& (commentLocation.node == commentData->location.node || commentData->location.node == -1)
&& (commentLocation.facing == commentData->location.facing || commentData->location.facing == -1)
&& (commentLocation.orientation == commentData->location.orientation || commentData->location.orientation == -1)
&& (commentLocation.depth == commentData->location.depth || commentData->location.depth == -1)) {
entryFound = true;
} else {
commentData++;
}
}
}
if (entryFound)
currentCommentData = *commentData;
currentCommentData.location = commentLocation;
return entryFound;
}
bool SceneViewWindow::checkAICommentDependencies(const Location &commentLocation, const AIComment &commentData) {
// Ignore comments designed for solely adventure mode in walkthrough mode
if (_globalFlags.generalWalkthroughMode == 1 && commentData.commentFlags & AI_COMMENT_DISABLE_IN_WALKTHROUGH)
return false;
byte flagValueA = 0;
if (commentData.commentFlags & AI_DEPENDENCY_FLAG_NON_BASE_DERIVED_A)
flagValueA = getGlobalFlagByte(commentData.dependencyFlagOffsetA);
else
flagValueA = _globalFlags.aiData[commentData.dependencyFlagOffsetA];
bool dependencyA;
if (commentData.commentFlags & AI_DEPENDENCY_CHECK_FOR_MINIMUM_A)
dependencyA = flagValueA >= commentData.dependencyValueA;
else
dependencyA = flagValueA <= commentData.dependencyValueA;
if (!dependencyA)
return false;
if (commentData.commentFlags & AI_COMMENT_FLAG_SPECIAL_LOGIC)
return checkCustomAICommentDependencies(commentLocation, commentData);
byte flagValueB = 0;
if (commentData.commentFlags & AI_DEPENDENCY_FLAG_NON_BASE_DERIVED_B)
flagValueB = getGlobalFlagByte(commentData.dependencyFlagOffsetB);
else
flagValueB = _globalFlags.aiData[commentData.dependencyFlagOffsetB];
bool dependencyB;
if (commentData.commentFlags & AI_DEPENDENCY_CHECK_FOR_MINIMUM_B)
dependencyB = flagValueB >= commentData.dependencyValueB;
else
dependencyB = flagValueB <= commentData.dependencyValueB;
return dependencyB;
}
bool SceneViewWindow::playAICommentFromData(const AIComment &commentData) {
if (_vm->_sound->isAsynchronousAICommentPlaying())
return false;
Common::String commentFileName = "BITDATA/";
switch (commentData.location.timeZone) {
case 1: // Castle
commentFileName += "CASTLE/";
switch (commentData.location.environment) {
case 1:
commentFileName += "CGTT";
break;
case 2:
commentFileName += "CGTS";
break;
case 3:
commentFileName += "CGMW";
break;
case 4:
commentFileName += "CGMB";
break;
case 5:
commentFileName += "CGBS";
break;
case 6:
commentFileName += "CGKC";
break;
case 7:
commentFileName += "CGST";
break;
case 8:
commentFileName += "CGKS";
break;
case 9:
commentFileName += "CGSR";
break;
case 10:
commentFileName += "CGTR";
break;
default:
return false;
}
break;
case 2: // Mayan
commentFileName += "MAYAN/";
switch (commentData.location.environment) {
case 1:
commentFileName += "MYTP";
break;
case 2:
commentFileName += "MYMC";
break;
case 3:
commentFileName += "MYWG";
break;
case 4:
commentFileName += "MYWT";
break;
case 5:
commentFileName += "MYAG";
break;
case 6:
commentFileName += "MYDG";
break;
default:
return false;
}
break;
case 4: // Future Apartment
commentFileName += "FUTAPT/";
switch (commentData.location.environment) {
case 1:
commentFileName += "FAKI";
break;
case 2:
commentFileName += "FAER";
break;
case 3:
commentFileName += "FAMN";
break;
default:
return false;
}
break;
case 5: // Da Vinci
commentFileName += "DAVINCI/";
switch (commentData.location.environment) {
case 1:
commentFileName += "DSPT";
break;
case 2:
commentFileName += "DSCT";
break;
case 3:
commentFileName += "DSGD";
break;
case 4:
commentFileName += "DSWS";
break;
case 5:
commentFileName += "DSCY";
break;
default:
return false;
}
break;
case 6: // Space Station
commentFileName += "AILAB/";
switch (commentData.location.environment) {
case 1:
commentFileName += "AIHW";
break;
case 2:
commentFileName += "AICR";
break;
case 3:
commentFileName += "AIDB";
break;
case 4:
commentFileName += "AISC";
break;
case 5:
commentFileName += "AINX";
break;
case 6:
commentFileName += "AIIC";
break;
case 7:
commentFileName += "AISW";
break;
case 8:
commentFileName += "AIMR";
break;
case 9:
// There is no 9.
return false;
case 10:
commentFileName += "AIHW";
break;
default:
return false;
}
break;
default:
return false;
}
commentFileName += "_";
if (commentData.commentFlags & AI_COMMENT_TYPE_INFORMATION)
commentFileName += "I";
if (commentData.commentFlags & AI_COMMENT_TYPE_HELP)
commentFileName += "H";
if (commentData.commentFlags & AI_COMMENT_TYPE_SPONTANEOUS)
commentFileName += "C";
if (commentData.commentFlags & AI_COMMENT_TYPE_OTHER)
commentFileName += "O";
commentFileName += Common::String::format("%02d.BTA", commentData.commentID);
Cursor currentCursor = _vm->_gfx->setCursor(kCursorWait);
bool playedSuccessfully = _vm->_sound->playAsynchronousAIComment(commentFileName);
_vm->_gfx->setCursor(currentCursor);
if (playedSuccessfully) {
_lastAICommentFileName = commentFileName;
// This is pure evil. Ugh.
// The [g|s]etGlobalFlagByte nonsense, anyway.
byte flagValue = 0;
if (commentData.commentFlags & AI_STATUS_FLAG_NON_BASE_DERIVED)
flagValue = getGlobalFlagByte(commentData.statusFlagOffset);
else
flagValue = _globalFlags.aiData[commentData.statusFlagOffset];
flagValue++;
if (commentData.commentFlags & AI_STATUS_FLAG_NON_BASE_DERIVED)
setGlobalFlagByte(commentData.statusFlagOffset, flagValue);
else
_globalFlags.aiData[commentData.statusFlagOffset] = flagValue;
return true;
}
return false;
}
bool SceneViewWindow::playAIComment(int commentType) {
if (!_currentScene)
return false;
if (_vm->_sound->isAsynchronousAICommentPlaying())
return false;
return playAIComment(_currentScene->_staticData.location, commentType);
}
bool SceneViewWindow::playAIComment(const Location &commentLocation, int commentType) {
// Make sure no other comments are playing
if (_vm->_sound->isAsynchronousAICommentPlaying())
return false;
Common::Array<AIComment> commentDatabase = getAICommentDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
if (commentDatabase.empty())
return false;
AIComment currentCommentData;
int lastFoundEntry = 0;
bool playedSuccessfully = false;
while (retrieveAICommentEntry(commentLocation, commentType, commentDatabase, lastFoundEntry, currentCommentData) && !playedSuccessfully)
if (checkAICommentDependencies(commentLocation, currentCommentData))
playedSuccessfully = playAICommentFromData(currentCommentData);
if (playedSuccessfully) {
if (commentType == AI_COMMENT_TYPE_HELP && _globalFlags.generalWalkthroughMode == 0)
_globalFlags.scoreHintsTotal++;
return true;
}
return false;
}
bool SceneViewWindow::checkForAIComment(int commentType) {
if (!_currentScene)
return false;
return checkForAIComment(_currentScene->_staticData.location, commentType);
}
bool SceneViewWindow::checkForAIComment(const Location &commentLocation, int commentType) {
Common::Array<AIComment> commentDatabase = getAICommentDatabase(_currentScene->_staticData.location.timeZone, _currentScene->_staticData.location.environment);
if (commentDatabase.empty())
return false;
AIComment currentCommentData;
int lastFoundEntry = 0;
while (retrieveAICommentEntry(commentLocation, commentType, commentDatabase, lastFoundEntry, currentCommentData))
if (checkAICommentDependencies(commentLocation, currentCommentData))
return true;
return false;
}
bool SceneViewWindow::infoWindowDisplayed(bool flag) {
if (flag && !_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
changeCycleFrameMovie();
}
if (_asyncMovie) {
if (flag)
_asyncMovie->showWindow(kWindowHide);
else
_asyncMovie->showWindow(kWindowShow);
}
if (_bioChipWindowDisplayed && flag)
((GameUIWindow *)_parent)->_bioChipRightWindow->destroyBioChipViewWindow();
_infoWindowDisplayed = flag;
return true;
}
bool SceneViewWindow::bioChipWindowDisplayed(bool flag) {
if (flag && !_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
changeCycleFrameMovie();
}
if (_asyncMovie) {
if (flag)
_asyncMovie->showWindow(kWindowHide);
else
_asyncMovie->showWindow(kWindowShow);
}
if (_infoWindowDisplayed && flag)
((GameUIWindow *)_parent)->_inventoryWindow->destroyInfoWindow();
_bioChipWindowDisplayed = flag;
return true;
}
bool SceneViewWindow::burnedLetterWindowDisplayed(bool flag) {
if (flag && !_walkMovie) {
delete _walkMovie;
_walkMovie = nullptr;
_walkMovieFileName.clear();
changeCycleFrameMovie();
}
if (_asyncMovie) {
if (flag)
_asyncMovie->showWindow(kWindowHide);
else
_asyncMovie->showWindow(kWindowShow);
}
if (_burnedLetterDisplayed)
((GameUIWindow *)_parent)->_inventoryWindow->destroyBurnedLetterWindow();
_burnedLetterDisplayed = flag;
return true;
}
bool SceneViewWindow::isAuxWindowDisplayed() {
return _burnedLetterDisplayed || _infoWindowDisplayed || _bioChipWindowDisplayed;
}
void SceneViewWindow::onLButtonDown(const Common::Point &point, uint flags) {
if (_currentScene && _globalFlags.bcLocateEnabled == 0)
_currentScene->mouseDown(this, point);
}
void SceneViewWindow::onLButtonUp(const Common::Point &point, uint flags) {
if (_currentScene) {
if (_globalFlags.bcLocateEnabled == 0)
_currentScene->mouseUp(this, point);
else
_currentScene->locateAttempted(this, point);
}
}
void SceneViewWindow::onMouseMove(const Common::Point &point, uint flags) {
_curMousePos = point;
if (_currentScene)
_currentScene->mouseMove(this, point);
}
void SceneViewWindow::onKeyUp(const Common::KeyState &key, uint flags) {
switch (key.keycode) {
case Common::KEYCODE_a:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipAI);
return;
}
break;
case Common::KEYCODE_b:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipBlank)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipBlank);
return;
}
break;
case Common::KEYCODE_c:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipCloak)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipCloak);
return;
}
break;
case Common::KEYCODE_e:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipEvidence)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipEvidence);
return;
}
break;
case Common::KEYCODE_f:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipFiles)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipFiles);
return;
}
break;
case Common::KEYCODE_i:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipInterface)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipInterface);
return;
}
break;
case Common::KEYCODE_j:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipJump)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipJump);
return;
}
break;
case Common::KEYCODE_t:
if ((key.flags & Common::KBD_CTRL) && ((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipTranslate)) {
((GameUIWindow *)_parent)->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipTranslate);
return;
}
break;
case Common::KEYCODE_p:
if (key.flags & Common::KBD_CTRL) {
// TODO: Pause game
return;
}
break;
case Common::KEYCODE_q:
if (key.flags & Common::KBD_CTRL) {
// Return to main menu
if (_vm->runQuitDialog())
((FrameWindow *)_vm->_mainWindow)->showMainMenu();
return;
}
break;
case Common::KEYCODE_SPACE:
if (((GameUIWindow *)_parent)->_inventoryWindow->isItemInInventory(kItemBioChipAI) && _globalFlags.bcCloakingEnabled != 1) {
if (!_lastAICommentFileName.empty() && !_vm->_sound->isAsynchronousAICommentPlaying()) {
TempCursorChange cursorChange(kCursorWait);
_vm->_sound->playAsynchronousAIComment(_lastAICommentFileName);
}
return;
}
break;
default:
break;
}
if (_currentScene)
_currentScene->onCharacter(this, key);
}
void SceneViewWindow::onPaint() {
// Original didn't draw if the async movie was playing, but that doesn't seem right.
if (_currentScene && !_infoWindowDisplayed && !_bioChipWindowDisplayed) {
if (_currentScene->_staticData.navFrameIndex >= -1) {
if (_useScenePaint)
_currentScene->paint(this, _preBuffer);
} else {
return;
}
// If we have a sprite, update the prebuffer with it now
if (_currentSprite.image && _useSprite)
_vm->_gfx->opaqueTransparentBlit(_preBuffer, _currentSprite.xPos, _currentSprite.yPos, _currentSprite.width, _currentSprite.height, _currentSprite.image, 0, 0, 0, _currentSprite.redTrans, _currentSprite.greenTrans, _currentSprite.blueTrans);
// Update the screen
_vm->_gfx->blit(_preBuffer, _rect.left, _rect.top);
if (_useScenePaint)
_currentScene->gdiPaint(this);
}
}
void SceneViewWindow::onTimer(uint timer) {
// Check first to see if this is the demo's sound timer
if (timer == _demoSoundTimer) {
// If no sound is playing, restart the ambient
if (!_vm->_sound->isAmbientSoundPlaying() && !_vm->_sound->isSoundEffectPlaying(_demoSoundEffectHandle)) {
// Reset the sound effect handle
_demoSoundEffectHandle = -1;
startDemoAmbientSound();
}
return;
}
SoundManager *sound = _vm->_sound; // Take a copy in case we die while in the timer
sound->timerCallback();
if (_paused)
return;
if (_asyncMovie)
asynchronousAnimationTimerCallback();
if (_currentScene && !_infoWindowDisplayed && !_bioChipWindowDisplayed && !_burnedLetterDisplayed)
_currentScene->timerCallback(this);
sound->timerCallback();
}
bool SceneViewWindow::onSetCursor(uint message) {
// Check the scene cursor callback function to see if we need to change the cursor
int newCursor = (int)kCursorArrow;
if (_currentScene)
newCursor = _currentScene->specifyCursor(this, _curMousePos);
// If the locate button is enabled, follow different logic
if (_globalFlags.bcLocateEnabled == 1) {
if (_curCursor >= 0 || (newCursor < 0 && newCursor != _curCursor)) {
// If the new cursor is less than zero, use it, otherwise use the default locate cursor
if (newCursor < 0) {
if (newCursor == -2)
_curCursor = (int)kCursorLocateB;
else
_curCursor = (int)kCursorLocateA;
} else {
_curCursor = (int)kCursorLocateA;
}
}
} else {
_curCursor = newCursor;
}
_vm->_gfx->setCursor((Cursor)_curCursor);
return true;
}
void SceneViewWindow::onEnable(bool enable) {
// If we're enabling, clear out the message queue of mouse messages
_vm->removeMouseMessages(this);
}
bool SceneViewWindow::resetNavigationArrows() {
if (!_currentScene)
return false;
if (_globalFlags.bcCloakingEnabled != 1)
((GameUIWindow *)_parent)->_navArrowWindow->updateAllArrows(_currentScene->_staticData);
return true;
}
int SceneViewWindow::draggingItem(int itemID, const Common::Point &location, int itemFlags) {
if (!_currentScene)
return 0;
return _currentScene->draggingItem(this, itemID, location, itemFlags);
}
int SceneViewWindow::droppedItem(int itemID, const Common::Point &location, int itemFlags) {
if (!_currentScene)
return 0;
return _currentScene->droppedItem(this, itemID, location, itemFlags);
}
bool SceneViewWindow::updatePrebufferWithSprite(Sprite &spriteData) {
if (_currentSprite.image != spriteData.image && _currentSprite.image != 0) {
_currentSprite.image->free();
delete _currentSprite.image;
}
_currentSprite = spriteData;
invalidateWindow(false);
return true;
}
bool SceneViewWindow::changeSpriteStatus(bool status) {
bool prevStatus = _useSprite;
_useSprite = status;
return prevStatus;
}
bool SceneViewWindow::resetCursor() {
_vm->_gfx->setCursor((Cursor)_curCursor);
return true;
}
bool SceneViewWindow::displayLiveText(const Common::String &text, bool notifyUser) {
if (((GameUIWindow *)_parent)->_liveTextWindow)
return ((GameUIWindow *)_parent)->_liveTextWindow->updateLiveText(text, notifyUser);
return false;
}
bool SceneViewWindow::displayTranslationText(const Common::String &text) {
if (((GameUIWindow *)_parent)->_liveTextWindow)
return ((GameUIWindow *)_parent)->_liveTextWindow->updateTranslationText(text);
return false;
}
Common::Array<AnimEvent> SceneViewWindow::getAnimationDatabase(int timeZone, int environment) {
Common::SeekableReadStream *stream = _vm->getAnimData(_vm->computeAnimDBResourceID(timeZone, environment));
stream->readUint16LE();
Common::Array<AnimEvent> animEvents;
while (stream->pos() < stream->size()) {
AnimEvent animEvent;
animEvent.animationID = stream->readSint16LE();
animEvent.fileNameID = stream->readSint16LE();
animEvent.audioStreamCount = stream->readSint16LE();
animEvent.startFrame = stream->readSint32LE();
animEvent.frameCount = stream->readSint32LE();
animEvents.push_back(animEvent);
}
delete stream;
return animEvents;
}
Common::Array<AIComment> SceneViewWindow::getAICommentDatabase(int timeZone, int environment) {
Common::SeekableReadStream *stream = _vm->getAIData(_vm->computeAIDBResourceID(timeZone, environment));
Common::Array<AIComment> comments;
if (!stream)
return comments;
uint16 count = stream->readUint16LE();
for (uint16 i = 0; i < count; i++) {
AIComment comment;
comment.location.timeZone = stream->readSint16LE();
comment.location.environment = stream->readSint16LE();
comment.location.node = stream->readSint16LE();
comment.location.facing = stream->readSint16LE();
comment.location.orientation = stream->readSint16LE();
comment.location.depth = stream->readSint16LE();
comment.commentID = stream->readUint16LE();
comment.commentFlags = stream->readUint16LE();
comment.dependencyFlagOffsetA = stream->readUint16LE();
comment.dependencyValueA = stream->readUint16LE();
comment.dependencyFlagOffsetB = stream->readUint16LE();
comment.dependencyValueB = stream->readUint16LE();
comment.statusFlagOffset = stream->readUint16LE();
comments.push_back(comment);
}
delete stream;
return comments;
}
void SceneViewWindow::startDemoAmbientSound() {
assert(_currentScene);
if (_currentScene->_staticData.location.environment == 5)
_vm->_sound->setAmbientSound("CASTLE/CGBSSNG.WAV", false, 127);
else
_vm->_sound->setAmbientSound("CASTLE/CGMBSNG.WAV", false, 127);
}
} // End of namespace Buried