/* 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 3 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, see . * */ #include "buried/biochip_right.h" #include "buried/buried.h" #include "buried/gameui.h" #include "buried/graphics.h" #include "buried/invdata.h" #include "buried/inventory_window.h" #include "buried/navarrow.h" #include "buried/resources.h" #include "buried/scene_view.h" #include "buried/sound.h" #include "buried/environ/scene_base.h" #include "buried/environ/scene_common.h" #include "common/system.h" #include "graphics/font.h" namespace Buried { enum { GC_AIHW_STARTING_VALUE = 100, GC_AI_OT_WALK_DECREMENT = 2, GC_AI_OT_TURN_DECREMENT = 1, GC_AI_OT_WAIT_DECREMENT = 1, GC_AI_OT_WAIT_TIME_PERIOD = 10000 }; class BaseOxygenTimer : public SceneBase { public: BaseOxygenTimer(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int preExitRoom(Window *viewWindow, const Location &priorLocation) override; int timerCallback(Window *viewWindow) override; protected: uint32 _entryStartTime; int _deathID; bool _jumped; }; BaseOxygenTimer::BaseOxygenTimer(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _deathID = 41; _jumped = false; _entryStartTime = g_system->getMillis(); } int BaseOxygenTimer::postEnterRoom(Window *viewWindow, const Location &priorLocation) { _entryStartTime = g_system->getMillis(); return SC_TRUE; } int BaseOxygenTimer::preExitRoom(Window *viewWindow, const Location &newLocation) { // NOTE: v1.01 used 25% as the low threshold instead of ~14.2% if (newLocation.timeZone == -2) { _jumped = true; return SC_TRUE; } int currentValue = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 0) { if (_staticData.location.node != newLocation.node) { if (currentValue <= GC_AI_OT_WALK_DECREMENT) { if (newLocation.timeZone != -2) ((SceneViewWindow *)viewWindow)->showDeathScene(_deathID); return SC_DEATH; } else { currentValue -= GC_AI_OT_WALK_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 7 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 7) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } } else { if (currentValue <= GC_AI_OT_TURN_DECREMENT) { if (newLocation.timeZone != -2) ((SceneViewWindow *)viewWindow)->showDeathScene(_deathID); return SC_DEATH; } else { currentValue -= GC_AI_OT_TURN_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 7 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 7) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } } } return SC_TRUE; } int BaseOxygenTimer::timerCallback(Window *viewWindow) { // NOTE: Earlier versions (1.01) used 25% as the low threshold instead of // ~14.2% if (_jumped) return SC_TRUE; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 0) { if ((g_system->getMillis() - _entryStartTime) >= GC_AI_OT_WAIT_TIME_PERIOD) { int currentValue = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer; if (currentValue <= GC_AI_OT_WAIT_DECREMENT) { ((SceneViewWindow *)viewWindow)->showDeathScene(_deathID); return SC_DEATH; } else { currentValue -= GC_AI_OT_WAIT_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 7 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 7) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } _entryStartTime = g_system->getMillis(); } } return SC_TRUE; } class SpaceDoorTimer : public BaseOxygenTimer { public: SpaceDoorTimer(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left = -1, int top = -1, int right = -1, int bottom = -1, int openFrame = -1, int closedFrame = -1, int depth = -1, int transitionType = -1, int transitionData = -1, int transitionStartFrame = -1, int transitionLength = -1); int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: bool _clicked; Common::Rect _clickable; DestinationScene _destData; int _openFrame; int _closedFrame; }; SpaceDoorTimer::SpaceDoorTimer(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left, int top, int right, int bottom, int openFrame, int closedFrame, int depth, int transitionType, int transitionData, int transitionStartFrame, int transitionLength) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _clicked = false; _openFrame = openFrame; _closedFrame = closedFrame; _clickable = Common::Rect(left, top, right, bottom); _destData.destinationScene = _staticData.location; _destData.destinationScene.depth = depth; _destData.transitionType = transitionType; _destData.transitionData = transitionData; _destData.transitionStartFrame = transitionStartFrame; _destData.transitionLength = transitionLength; } int SpaceDoorTimer::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) { _clicked = true; return SC_TRUE; } return SC_FALSE; } int SpaceDoorTimer::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clicked) { // If we are facing the habitat wing death door in walkthrough mode, // keep it locked. if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1 && _staticData.location.timeZone == 6 && _staticData.location.environment == 1 && _staticData.location.node == 3 && _staticData.location.facing == 1 && _staticData.location.orientation == 2 && _staticData.location.depth == 0) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13)); _clicked = false; return SC_TRUE; } // If we are facing the scanning room door and we have Arthur, automatically recall // to the future apartment if (_staticData.location.timeZone == 6 && _staticData.location.environment == 3 && _staticData.location.node == 9 && _staticData.location.facing == 0 && _staticData.location.orientation == 0 && _staticData.location.depth == 0 && ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) { ((SceneViewWindow *)viewWindow)->timeSuitJump(4); return SC_TRUE; } // Change the still frame to the new one if (_openFrame >= 0) { _staticData.navFrameIndex = _openFrame; viewWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AI_LOCK.BTA"); // Broken in 1.01 } ((SceneViewWindow *)viewWindow)->moveToDestination(_destData); _clicked = false; return SC_TRUE; } return SC_FALSE; } int SpaceDoorTimer::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { // If we are in walkthrough mode and are at the death door in the habitat wing, // don't allow you to open the door. if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1 && _staticData.location.timeZone == 6 && _staticData.location.environment == 1 && _staticData.location.node == 3 && _staticData.location.facing == 1 && _staticData.location.orientation == 2 && _staticData.location.depth == 0) { return kCursorArrow; } if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class UseCheeseGirlPropellant : public BaseOxygenTimer { public: UseCheeseGirlPropellant(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; int droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; private: Common::Rect _badPos; }; UseCheeseGirlPropellant::UseCheeseGirlPropellant(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _deathID = 40; _badPos = Common::Rect(144, 0, 288, 189); } int UseCheeseGirlPropellant::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // Display text message about zero-g and no propulsion and oxygen level ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_AI_IS_JUMP_IN_TEXT)); return SC_TRUE; } int UseCheeseGirlPropellant::draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (itemID == kItemCheeseGirl && !_badPos.contains(pointLocation)) return 1; return 0; } int UseCheeseGirlPropellant::droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (itemID == kItemCheeseGirl) { if (_badPos.contains(pointLocation)) { DestinationScene destData; destData.destinationScene.timeZone = 6; destData.destinationScene.environment = 10; destData.destinationScene.node = 1; destData.destinationScene.facing = 0; destData.destinationScene.orientation = 0; destData.destinationScene.depth = 0; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); } else { DestinationScene destData; destData.destinationScene.timeZone = 6; destData.destinationScene.environment = 1; destData.destinationScene.node = 1; destData.destinationScene.facing = 1; destData.destinationScene.orientation = 2; destData.destinationScene.depth = 0; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); } return SIC_ACCEPT; } return SIC_REJECT; } class PlayArthurOffsetTimed : public BaseOxygenTimer { public: PlayArthurOffsetTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int stingerVolume = 127, int firstStingerFileID = -1, int lastStingerFileID = -1, int stingerDelay = 1); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; private: int _stingerVolume; int _firstStingerFileID; int _lastStingerFileID; int _stingerDelay; //int _timerFlagOffset; }; PlayArthurOffsetTimed::PlayArthurOffsetTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int stingerVolume, int firstStingerFileID, int lastStingerFileID, int stingerDelay) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _stingerVolume = stingerVolume; _firstStingerFileID = firstStingerFileID; _lastStingerFileID = lastStingerFileID; _stingerDelay = stingerDelay; } int PlayArthurOffsetTimed::postEnterRoom(Window *viewWindow, const Location &priorLocation) { SceneViewWindow *sceneView = ((SceneViewWindow *)viewWindow); GlobalFlags &globalFlags = sceneView->getGlobalFlags(); if ((priorLocation.node != _staticData.location.node || priorLocation.environment != _staticData.location.environment)) { byte effectID = globalFlags.aiHWStingerChannelID; if (!_vm->_sound->isSoundEffectPlaying(effectID - 1)) { int lastStinger = globalFlags.aiHWStingerID + 1; if ((lastStinger % _stingerDelay) == 0) { if (lastStinger <= (_lastStingerFileID - _firstStingerFileID) * _stingerDelay) { int fileNameIndex = _vm->computeFileNameResourceID(_staticData.location.timeZone, _staticData.location.environment, _firstStingerFileID + lastStinger / _stingerDelay - 1); byte newStingerID = 0; if (((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) { newStingerID = _vm->_sound->playSoundEffect(_vm->getFilePath(fileNameIndex), _stingerVolume / 2, false, true) + 1; byte &lastArthurComment = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiHWLastCommentPlayed; if ((lastStinger / 2) != 0 && lastArthurComment < 4) { lastArthurComment++; _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 10, lastArthurComment + 5), 128, false, true); } } else { newStingerID = _vm->_sound->playSoundEffect(_vm->getFilePath(fileNameIndex), _stingerVolume, false, true) + 1; } globalFlags.aiHWStingerChannelID = newStingerID; globalFlags.aiHWStingerID = lastStinger; } } else { globalFlags.aiHWStingerChannelID = 0xFF; globalFlags.aiHWStingerID = lastStinger; } } } return SC_TRUE; } class HabitatWingIceteroidDoor : public BaseOxygenTimer { public: HabitatWingIceteroidDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _doorHandle; }; HabitatWingIceteroidDoor::HabitatWingIceteroidDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _doorHandle = Common::Rect(122, 48, 246, 172); } int HabitatWingIceteroidDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_doorHandle.contains(pointLocation)) { if (((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) { if (((SceneViewWindow * )viewWindow)->getGlobalFlags().aiHWIceDoorUnlocked == 0) { // Move to depth one (door open) DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 1; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 6; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); // Add the explosive charge to your inventory ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->addItem(kItemExplosiveCharge); // Set the door unlocked flag ((SceneViewWindow * )viewWindow)->getGlobalFlags().aiHWIceDoorUnlocked = 1; } else { // Move to depth one (door open) DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 1; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 3; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); } } else { // Play the closed door video clip ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(7); } return SC_TRUE; } return SC_FALSE; } int HabitatWingIceteroidDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_doorHandle.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class IceteroidPodTimed : public BaseOxygenTimer { public: IceteroidPodTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left = -1, int top = -1, int right = -1, int bottom = -1, int animID = -1, int timeZone = -1, int environment = -1, int node = -1, int facing = -1, int orientation = -1, int depth = -1); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _engageButton; DestinationScene _clickDestination; }; IceteroidPodTimed::IceteroidPodTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left, int top, int right, int bottom, int animID, int timeZone, int environment, int node, int facing, int orientation, int depth) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _engageButton = Common::Rect(left, top, right, bottom); _clickDestination.destinationScene = Location(timeZone, environment, node, facing, orientation, depth); _clickDestination.transitionType = TRANSITION_VIDEO; _clickDestination.transitionData = animID; _clickDestination.transitionStartFrame = -1; _clickDestination.transitionLength = -1; } int IceteroidPodTimed::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_engageButton.contains(pointLocation)) // Make it so! ((SceneViewWindow *)viewWindow)->moveToDestination(_clickDestination); return SC_FALSE; } int IceteroidPodTimed::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_engageButton.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class IceteroidElevatorExtremeControls : public BaseOxygenTimer { public: IceteroidElevatorExtremeControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int upTimeZone = -1, int upEnvironment = -1, int upNode = -1, int upFacing = -1, int upOrientation = -1, int upDepth = -1, int upAnimID = -1, int downTimeZone = -1, int downEnvironment = -1, int downNode = -1, int downFacing = -1, int downOrientation = -1, int downDepth = -1, int downAnimID = -1); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _up, _down; DestinationScene _upDestination; DestinationScene _downDestination; }; IceteroidElevatorExtremeControls::IceteroidElevatorExtremeControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int upTimeZone, int upEnvironment, int upNode, int upFacing, int upOrientation, int upDepth, int upAnimID, int downTimeZone, int downEnvironment, int downNode, int downFacing, int downOrientation, int downDepth, int downAnimID) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _up = Common::Rect(192, 123, 212, 143); _down = Common::Rect(192, 148, 212, 168); _upDestination.destinationScene = Location(upTimeZone, upEnvironment, upNode, upFacing, upOrientation, upDepth); _upDestination.transitionType = TRANSITION_VIDEO; _upDestination.transitionData = upAnimID; _upDestination.transitionStartFrame = -1; _upDestination.transitionLength = -1; _downDestination.destinationScene = Location(downTimeZone, downEnvironment, downNode, downFacing, downOrientation, downDepth); _downDestination.transitionType = TRANSITION_VIDEO; _downDestination.transitionData = downAnimID; _downDestination.transitionStartFrame = -1; _downDestination.transitionLength = -1; } int IceteroidElevatorExtremeControls::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_up.contains(pointLocation) && _upDestination.transitionData >= 0) { ((SceneViewWindow *)viewWindow)->moveToDestination(_upDestination); return SC_TRUE; } if (_down.contains(pointLocation) && _downDestination.transitionData >= 0) { ((SceneViewWindow *)viewWindow)->moveToDestination(_downDestination); return SC_TRUE; } return SC_FALSE; } int IceteroidElevatorExtremeControls::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_up.contains(pointLocation) && _upDestination.transitionData >= 0) return kCursorFinger; if (_down.contains(pointLocation) && _downDestination.transitionData >= 0) return kCursorFinger; return kCursorArrow; } class IceteroidZoomInMineControls : public BaseOxygenTimer { public: IceteroidZoomInMineControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _controls; }; IceteroidZoomInMineControls::IceteroidZoomInMineControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _controls = Common::Rect(152, 76, 180, 114); } int IceteroidZoomInMineControls::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) { DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 1; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 8; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } return SC_FALSE; } int IceteroidZoomInMineControls::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) return kCursorMagnifyingGlass; return kCursorArrow; } class IceteroidMineControls : public BaseOxygenTimer { public: IceteroidMineControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _mineButton, _makeOxygenButton; }; IceteroidMineControls::IceteroidMineControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _mineButton = Common::Rect(190, 40, 204, 128); _makeOxygenButton = Common::Rect(205, 40, 220, 128); } int IceteroidMineControls::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_mineButton.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined < 255) { TempCursorChange cursorChange(kCursorWait); // Update the amount of ice mined ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined++; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICUsedMiningControls = 1; // Transition to another scene, using the video clip DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.facing = 2; destinationData.destinationScene.orientation = 2; destinationData.destinationScene.depth = 0; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 10; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } if (_makeOxygenButton.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined > 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves < 255) { int currentStillFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 108; viewWindow->invalidateWindow(false); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICProcessedOxygen = 1; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined--; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves++; // Play the conversion audio file _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128); // Reset the frame _staticData.navFrameIndex = currentStillFrame; viewWindow->invalidateWindow(false); return SC_TRUE; } // Move back to the far view DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 0; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 9; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } int IceteroidMineControls::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_mineButton.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined < 255) return kCursorFinger; if (_makeOxygenButton.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiIceMined > 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves < 255) return kCursorFinger; return kCursorPutDown; } class IceteroidZoomInDispenser : public BaseOxygenTimer { public: IceteroidZoomInDispenser(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _controls; }; IceteroidZoomInDispenser::IceteroidZoomInDispenser(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _controls = Common::Rect(90, 0, 350, 189); } int IceteroidZoomInDispenser::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) { DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 1; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 11; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } return SC_FALSE; } int IceteroidZoomInDispenser::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) return kCursorMagnifyingGlass; return kCursorArrow; } class IceteroidDispenserControls : public BaseOxygenTimer { public: IceteroidDispenserControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int preExitRoom(Window *viewWindow, const Location &priorLocation) override; int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; int draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; int droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; private: Common::Rect _oxygenHandle; Common::Rect _fillHandle; Common::Rect _dropZone; }; IceteroidDispenserControls::IceteroidDispenserControls(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _oxygenHandle = Common::Rect(290, 42, 410, 128); _fillHandle = Common::Rect(0, 36, 102, 132); _dropZone = Common::Rect(0, 136, 148, 189); if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) _staticData.navFrameIndex = 110; } int IceteroidDispenserControls::preExitRoom(Window *viewWindow, const Location &priorLocation) { // Add the canister back into the inventory if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) { _staticData.navFrameIndex = 109; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle == 1) ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->addItem(kItemWaterCanEmpty); else ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->addItem(kItemWaterCanFull); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle = 0; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); } return BaseOxygenTimer::preExitRoom(viewWindow, priorLocation); } int IceteroidDispenserControls::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_dropZone.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) { int itemID = (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle == 1) ? kItemWaterCanEmpty : kItemWaterCanFull; // Reset present flag and change the frame of the background _staticData.navFrameIndex = 109; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle = 0; // Start dragging Common::Point ptInventoryWindow = viewWindow->convertPointToGlobal(pointLocation); ptInventoryWindow = ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->convertPointToLocal(ptInventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->startDraggingNewItem(itemID, ptInventoryWindow); // Update the biochips ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); return SC_TRUE; } return SC_FALSE; } int IceteroidDispenserControls::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_oxygenHandle.contains(pointLocation)) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves > 0 || ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICProcessedOxygen == 1) { TempCursorChange cursorChange(kCursorWait); // Reset the flags if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICProcessedOxygen == 0) ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves--; // Set the use flag ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICRefilledOxygen = 1; // Play the refill animation ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(18); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = GC_AIHW_STARTING_VALUE; // Place a message in the window Common::String text; if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0)) text = _vm->getString(IDS_AI_IC_OXYGEN_REFILLED); else text = "Emergency oxygen reserves refilled."; ((SceneViewWindow *)viewWindow)->displayLiveText(text, false); } else { // Play voiceover informing the player the oxygen needs to be refilled _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 12)); } return SC_TRUE; } if (_fillHandle.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) { TempCursorChange cursorChange(kCursorWait); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle = 2; ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(17); return SC_TRUE; } DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 0; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 12; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } int IceteroidDispenserControls::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { // Water canister if (_dropZone.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) return kCursorOpenHand; // Oxygen handle if (_oxygenHandle.contains(pointLocation)) return kCursorFinger; // Fill handle if (_fillHandle.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle > 0) return kCursorFinger; return kCursorPutDown; } int IceteroidDispenserControls::draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if ((itemID == kItemWaterCanFull || itemID == kItemWaterCanEmpty) && _dropZone.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle == 0) return 1; return 0; } int IceteroidDispenserControls::droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if ((itemID == kItemWaterCanFull || itemID == kItemWaterCanEmpty) && _dropZone.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle == 0) { _staticData.navFrameIndex = 110; viewWindow->invalidateWindow(false); if (itemID == kItemWaterCanEmpty) ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle = 1; else ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICWaterInFillHandle = 2; return SIC_ACCEPT; } return SIC_REJECT; } class PlaySoundExitingForward : public BaseOxygenTimer { public: PlaySoundExitingForward(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundFileNameID); int postExitRoom(Window *viewWindow, const Location &newLocation) override; private: int _soundFileNameID; }; PlaySoundExitingForward::PlaySoundExitingForward(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundFileNameID) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _soundFileNameID = soundFileNameID; } int PlaySoundExitingForward::postExitRoom(Window *viewWindow, const Location &newLocation) { if (_soundFileNameID >= 0 && _staticData.location.timeZone == newLocation.timeZone && _staticData.location.node != newLocation.node) _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, _soundFileNameID), 128, false, true); return SC_TRUE; } class TakeWaterCanister : public BaseOxygenTimer { public: TakeWaterCanister(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _canister; }; TakeWaterCanister::TakeWaterCanister(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _canister = Common::Rect(232, 76, 376, 134); // If the canister has not been taken, change the still if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICTakenWaterCanister == 0) _staticData.navFrameIndex = 111; } int TakeWaterCanister::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_canister.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICTakenWaterCanister == 0) { // Set the frame _staticData.navFrameIndex = 51; // Walkthrough mode skips the filling the canister puzzle and gets it filled already ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICTakenWaterCanister = 1; int itemID = (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1) ? kItemWaterCanFull : kItemWaterCanEmpty; // Start dragging Common::Point ptInventoryWindow = viewWindow->convertPointToGlobal(pointLocation); ptInventoryWindow = ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->convertPointToLocal(ptInventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->startDraggingNewItem(itemID, ptInventoryWindow); // Update the biochips ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); return SC_TRUE; } return SC_FALSE; } int TakeWaterCanister::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_canister.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICTakenWaterCanister == 0) return kCursorOpenHand; return kCursorArrow; } class ScienceWingZoomIntoPanel : public BaseOxygenTimer { public: ScienceWingZoomIntoPanel(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: int _cursorID; Common::Rect _clickRegion; DestinationScene _clickDestination; }; ScienceWingZoomIntoPanel::ScienceWingZoomIntoPanel(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _clickRegion = Common::Rect(282, 6, 390, 189); _cursorID = kCursorMagnifyingGlass; _clickDestination.destinationScene = _staticData.location; _clickDestination.destinationScene.depth = 1; _clickDestination.transitionType = TRANSITION_VIDEO; _clickDestination.transitionData = 1; _clickDestination.transitionStartFrame = -1; _clickDestination.transitionLength = -1; } int ScienceWingZoomIntoPanel::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) ((SceneViewWindow *)viewWindow)->moveToDestination(_clickDestination); return SC_FALSE; } int ScienceWingZoomIntoPanel::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) return _cursorID; return kCursorArrow; } class ScienceWingPanelInterface : public BaseOxygenTimer { public: ScienceWingPanelInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); ~ScienceWingPanelInterface(); void preDestructor() override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; int gdiPaint(Window *viewWindow) override; private: Common::Rect _stationRegions[15]; int _currentSelection; int _currentTextIndex; int _lineHeight; Graphics::Font *_textFont; Common::Rect _leftTextRegion; Common::Rect _rightTextRegion; }; ScienceWingPanelInterface::ScienceWingPanelInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _currentSelection = -1; _currentTextIndex = -1; _stationRegions[0] = Common::Rect(265, 110, 286, 135); _stationRegions[1] = Common::Rect(102, 45, 180, 134); _stationRegions[2] = Common::Rect(195, 106, 216, 133); _stationRegions[3] = Common::Rect(268, 72, 283, 87); _stationRegions[4] = Common::Rect(221, 46, 236, 74); _stationRegions[5] = Common::Rect(290, 72, 317, 108); _stationRegions[6] = Common::Rect(264, 55, 288, 67); _stationRegions[7] = Common::Rect(194, 74, 266, 84); _stationRegions[8] = Common::Rect(198, 62, 214, 74); _stationRegions[9] = Common::Rect(221, 106, 236, 134); _stationRegions[10] = Common::Rect(245, 46, 260, 74); _stationRegions[11] = Common::Rect(245, 106, 260, 134); _stationRegions[12] = Common::Rect(266, 92, 290, 109); _stationRegions[13] = Common::Rect(194, 96, 264, 106); _stationRegions[14] = Common::Rect(180, 85, 194, 94); _leftTextRegion = Common::Rect(83, 144, 211, 170); _rightTextRegion = Common::Rect(228, 144, 356, 170); _lineHeight = _vm->getLanguage() == Common::JA_JPN ? 10 : 13; _textFont = _vm->_gfx->createFont(_lineHeight); } ScienceWingPanelInterface::~ScienceWingPanelInterface() { preDestructor(); } void ScienceWingPanelInterface::preDestructor() { delete _textFont; _textFont = nullptr; } int ScienceWingPanelInterface::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { byte &oxygenReserves = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves; if (_currentSelection == 2) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 0 && (_stationRegions[_currentSelection].contains(pointLocation) || _rightTextRegion.contains(pointLocation))) { if (oxygenReserves > 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiICProcessedOxygen != 0) { // Decrement reserves flag oxygenReserves--; // Set the machine room to pressurized ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized = 1; // Display pressurizing message viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_PRES_ENV_TEXT; // Play sound file _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128); // Display pressurized text viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; } else { // Not enough oxygen reserves viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_INSUF_OXYGEN; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSWAttemptedPresMR = 1; return SC_TRUE; } } } // Check against the hotspots for (int i = 0; i < 15; i++) { if (_stationRegions[i].contains(pointLocation) && _currentSelection != i) { switch (i) { case 0: _staticData.navFrameIndex = 60; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 1: _staticData.navFrameIndex = 61; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ZERO_PRES_ENV; return SC_TRUE; case 2: _staticData.navFrameIndex = 62; viewWindow->invalidateWindow(false); _currentSelection = i; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 1) _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; else _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES; return SC_TRUE; case 3: _staticData.navFrameIndex = 63; viewWindow->invalidateWindow(false); _currentSelection = i; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 1) _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; else _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_OBST; return SC_TRUE; case 4: _staticData.navFrameIndex = 64; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 5: _staticData.navFrameIndex = 65; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 6: _staticData.navFrameIndex = 66; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 7: _staticData.navFrameIndex = 67; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 8: _staticData.navFrameIndex = 68; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 9: _staticData.navFrameIndex = 69; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 10: _staticData.navFrameIndex = 70; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 11: _staticData.navFrameIndex = 71; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 12: _staticData.navFrameIndex = 72; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 13: case 14: _staticData.navFrameIndex = 73; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; } } } // By default, return to depth zero (zoomed out) DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 0; destData.transitionType = TRANSITION_NONE; destData.transitionData = -1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } int ScienceWingPanelInterface::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { for (int i = 0; i < 15; i++) if (_stationRegions[i].contains(pointLocation)) return kCursorFinger; return kCursorPutDown; } int ScienceWingPanelInterface::gdiPaint(Window *viewWindow) { if (_currentSelection >= 0) { uint32 color = _vm->_gfx->getColor(208, 144, 24); Common::String location = _vm->getString(IDS_AI_PRES_PANEL_DESC_BASE + _currentSelection); if (_currentSelection == 2) location += _vm->getString(IDS_AI_PRES_PANEL_DESC_BASE + 19); Common::Rect absoluteRect = viewWindow->getAbsoluteRect(); Common::Rect rect(_leftTextRegion); rect.translate(absoluteRect.left, absoluteRect.top); _vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, location, rect.left, rect.top, rect.width(), rect.height(), color, _lineHeight, kTextAlignCenter, true); if (_currentTextIndex >= 0) { rect = _rightTextRegion; rect.translate(absoluteRect.left, absoluteRect.top); _vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, _vm->getString(_currentTextIndex), rect.left, rect.top, rect.width(), rect.height(), color, _lineHeight, kTextAlignCenter, true); } } return SC_FALSE; } class ScienceWingMachineRoomDoor : public BaseOxygenTimer { public: ScienceWingMachineRoomDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: int _cursorID; Common::Rect _clickRegion; DestinationScene _clickDestination; }; ScienceWingMachineRoomDoor::ScienceWingMachineRoomDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _clickRegion = Common::Rect(162, 54, 250, 142); _cursorID = kCursorFinger; _clickDestination.destinationScene = Location(6, 8, 0, 0, 1, 0); _clickDestination.transitionType = TRANSITION_VIDEO; _clickDestination.transitionData = (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 0) ? 2 : 3; _clickDestination.transitionStartFrame = -1; _clickDestination.transitionLength = -1; } int ScienceWingMachineRoomDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) ((SceneViewWindow *)viewWindow)->moveToDestination(_clickDestination); return SC_FALSE; } int ScienceWingMachineRoomDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) return _cursorID; return 0; } class ScienceWingStingersTimed : public BaseOxygenTimer { public: ScienceWingStingersTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; }; ScienceWingStingersTimed::ScienceWingStingersTimed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { } int ScienceWingStingersTimed::postEnterRoom(Window *viewWindow, const Location &priorLocation) { BaseOxygenTimer::postEnterRoom(viewWindow, priorLocation); byte effectID = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSWStingerChannelID; if (!_vm->_sound->isSoundEffectPlaying(effectID - 1)) { byte &lastStinger = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSWStingerID; byte newStingerID = _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, lastStinger + 7)) + 1; lastStinger++; if (lastStinger > 3) lastStinger = 0; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSWStingerChannelID = newStingerID; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSWStingerID = lastStinger; } return SC_TRUE; } class NexusDoor : public BaseOxygenTimer { public: NexusDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: //uint32 _entryStartTime; Common::Rect _door; //bool _jumped; }; NexusDoor::NexusDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _door = Common::Rect(148, 30, 328, 192); } int NexusDoor::postEnterRoom(Window *viewWindow, const Location &priorLocation) { BaseOxygenTimer::postEnterRoom(viewWindow, priorLocation); if (priorLocation.environment != _staticData.location.environment || priorLocation.timeZone != _staticData.location.timeZone) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = GC_AIHW_STARTING_VALUE; ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_AI_ENTERING_PRES_ENV_TEXT)); } return SC_TRUE; } int NexusDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_door.contains(pointLocation)) { DestinationScene destData; destData.destinationScene = Location(6, 5, 1, 0, 1, 0); destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } return SC_FALSE; } int NexusDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_door.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class NexusPuzzle : public SceneBase { public: NexusPuzzle(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int gdiPaint(Window *viewWindow) override; int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _lights[7]; int _data[7]; bool _resetMessage; }; NexusPuzzle::NexusPuzzle(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _data[0] = 2; _data[1] = 2; _data[2] = 2; _data[3] = 0; _data[4] = 1; _data[5] = 1; _data[6] = 1; _lights[0] = Common::Rect(209, 39, 225, 47); _lights[1] = Common::Rect(209, 52, 225, 63); _lights[2] = Common::Rect(209, 71, 225, 84); _lights[3] = Common::Rect(209, 90, 225, 106); _lights[4] = Common::Rect(209, 110, 225, 123); _lights[5] = Common::Rect(209, 126, 225, 137); _lights[6] = Common::Rect(209, 140, 225, 148); _resetMessage = false; } int NexusPuzzle::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // If we came from outside (node zero), display the atmosphere message if (priorLocation.node == 0) { ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_AI_ENTERING_PRES_ENV_TEXT)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = GC_AIHW_STARTING_VALUE; } // Check to see if we heard the brain comment before if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiNXPlayedBrainComment == 0) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiNXPlayedBrainComment = 1; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 0) { // Play a synchronous comment here to introduce the player to the puzzle _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 9)); } else { // Play a synchronous comment to introduce what is about to happen _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 8)); // Play the Farnstein voiceover _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 14)); // Move to the next node, playing the Arthur retrieval movie DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.node = 2; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); } } return SC_TRUE; } int NexusPuzzle::gdiPaint(Window *viewWindow) { // Puzzle is only in adventure mode if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1) return SC_REPAINT; uint32 green = _vm->_gfx->getColor(0, 255, 0); uint32 red = _vm->_gfx->getColor(255, 0, 0); Common::Rect absoluteRect = viewWindow->getAbsoluteRect(); for (int i = 0; i < 7; i++) { if (_data[i] != 0) { uint32 color = (_data[i] == 1) ? green : red; Common::Rect rect(_lights[i]); rect.translate(absoluteRect.left, absoluteRect.top); rect.left++; rect.top++; _vm->_gfx->drawEllipse(rect, color); } } return SC_REPAINT; } int NexusPuzzle::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { // Puzzle is only in adventure mode if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1) return SC_FALSE; // Reset the live text, if the puzzle was reset if (_resetMessage) { ((SceneViewWindow *)viewWindow)->displayLiveText(""); _resetMessage = false; } // Check to see if we clicked on any of the colored circles, and if a jump is allowed for (int i = 0; i < 7; i++) { if (_lights[i].contains(pointLocation) && _data[i] != 0) { if (_data[i] == 1) { // Can we move up one? if (i > 0) { if (_data[i - 1] == 0) { _data[i - 1] = _data[i]; _data[i] = 0; viewWindow->invalidateWindow(false); } else if (i > 1 && _data[i - 2] == 0) { _data[i - 2] = _data[i]; _data[i] = 0; viewWindow->invalidateWindow(false); } } } else { if (i < 6) { if (_data[i + 1] == 0) { _data[i + 1] = _data[i]; _data[i] = 0; viewWindow->invalidateWindow(false); } else if (i < 5 && _data[i + 2] == 0) { _data[i + 2] = _data[i]; _data[i] = 0; viewWindow->invalidateWindow(false); } } } // Check to see if we completed the puzzle if (_data[0] == 1 && _data[1] == 1 && _data[2] == 1 && _data[3] == 0 && _data[4] == 2 && _data[5] == 2 && _data[6] == 2) { // Play the Farnstein voiceover _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 14)); // Move to the next node, playing the Arthur retrieval movie DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.node = 2; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } // Check to see that we can still make valid moves bool validMove = false; for (int j = 0; j < 7; j++) { if (_data[j] == 1) { if (j > 1 && _data[j - 2] == 0) { validMove = true; break; } if (j > 0 && _data[j - 1] == 0) { validMove = true; break; } } else if (_data[j] == 2) { if (j < 5 && _data[j + 2] == 0) { validMove = true; break; } if (j < 6 && _data[j + 1] == 0) { validMove = true; break; } } } if (!validMove) { // Reset the puzzle _data[0] = 2; _data[1] = 2; _data[2] = 2; _data[3] = 0; _data[4] = 1; _data[5] = 1; _data[6] = 1; viewWindow->invalidateWindow(false); Common::String text; if (_vm->getVersion() >= MAKEVERSION(1, 0, 4, 0)) text = _vm->getString(IDS_AI_NX_CODE_RESET_MESSAGE); else text = "Unable to complete in current state. Resetting code lock."; ((SceneViewWindow *)viewWindow)->displayLiveText(text); _resetMessage = true; } return SC_TRUE; } } return SC_FALSE; } int NexusPuzzle::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { // Puzzle is only in adventure mode if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1) return kCursorArrow; for (int i = 0; i < 7; i++) if (_lights[i].contains(pointLocation) && _data[i] != 0) // In the liiiiiight return kCursorFinger; return kCursorArrow; } class NexusEnd : public SceneBase { public: NexusEnd(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; }; NexusEnd::NexusEnd(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { } int NexusEnd::postEnterRoom(Window *viewWindow, const Location &priorLocation) { if (!((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) { // Congrats, you have Arthur! // Swap and activate the chips ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->removeItem(kItemBioChipBlank); ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->addItem(kItemBioChipAI); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->changeCurrentBioChip(kItemBioChipAI); // Play Arthur's comments TempCursorChange cursorChange(kCursorWait); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->_forceComment = true; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 10)); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->_forceComment = false; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->_forceHelp = true; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 11)); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->_forceHelp = false; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13)); } return SC_TRUE; } class HabitatWingLockedDoor : public BaseOxygenTimer { public: HabitatWingLockedDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int newFrameID = -1, int beepSoundID = -1, int voSoundID = -1, int left = 0, int top = 0, int right = 0, int bottom = 0); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: int _newFrameID; Common::Rect _clickRegion; int _beepSoundID; int _voSoundID; }; HabitatWingLockedDoor::HabitatWingLockedDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int newFrameID, int beepSoundID, int voSoundID, int left, int top, int right, int bottom) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _clickRegion = Common::Rect(left, top, right, bottom); _newFrameID = newFrameID; _beepSoundID = beepSoundID; _voSoundID = voSoundID; } int HabitatWingLockedDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) { int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = _newFrameID; viewWindow->invalidateWindow(false); if (_beepSoundID != -1) _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, _beepSoundID)); if (_voSoundID != -1) _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, _voSoundID)); _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); return SC_TRUE; } return SC_FALSE; } int HabitatWingLockedDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class BaseOxygenTimerInSpace : public BaseOxygenTimer { public: BaseOxygenTimerInSpace(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); }; BaseOxygenTimerInSpace::BaseOxygenTimerInSpace(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimer(vm, viewWindow, sceneStaticData, priorLocation) { _deathID = 40; } class BaseOxygenTimerCapacitance : public SceneBase { public: BaseOxygenTimerCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int preExitRoom(Window *viewWindow, const Location &priorLocation) override; int timerCallback(Window *viewWindow) override; protected: uint32 _entryStartTime; bool _jumped; }; BaseOxygenTimerCapacitance::BaseOxygenTimerCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _jumped = false; _entryStartTime = g_system->getMillis(); } int BaseOxygenTimerCapacitance::postEnterRoom(Window *viewWindow, const Location &priorLocation) { _entryStartTime = g_system->getMillis(); return SC_TRUE; } int BaseOxygenTimerCapacitance::preExitRoom(Window *viewWindow, const Location &newLocation) { // This does the 25% warning, unlike BaseOxygenTimer if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 0) { if (newLocation.timeZone == -2) { _jumped = true; return SC_TRUE; } int currentValue = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer; if (_staticData.location.node != newLocation.node) { if (currentValue <= GC_AI_OT_WALK_DECREMENT) { if (newLocation.timeZone != -2) ((SceneViewWindow *)viewWindow)->showDeathScene(41); return SC_DEATH; } else { currentValue -= GC_AI_OT_WALK_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 4 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 4) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } } else { if (currentValue <= GC_AI_OT_TURN_DECREMENT) { if (newLocation.timeZone != -2) ((SceneViewWindow *)viewWindow)->showDeathScene(41); return SC_DEATH; } else { currentValue -= GC_AI_OT_TURN_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 4 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 4) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } } } return SC_TRUE; } int BaseOxygenTimerCapacitance::timerCallback(Window *viewWindow) { // This does the 25% warning, unlike BaseOxygenTimer if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 0) { if (_jumped) return SC_TRUE; if ((g_system->getMillis() - _entryStartTime) >= GC_AI_OT_WAIT_TIME_PERIOD) { int currentValue = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer; if (currentValue <= GC_AI_OT_WAIT_DECREMENT) { ((SceneViewWindow *)viewWindow)->showDeathScene(41); return SC_DEATH; } else { currentValue -= GC_AI_OT_WAIT_DECREMENT; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = currentValue; if (currentValue < GC_AIHW_STARTING_VALUE / 4 || (currentValue % (GC_AIHW_STARTING_VALUE / 10)) == 0) { if (currentValue < GC_AIHW_STARTING_VALUE / 4) { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_LOW); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } else { Common::String oxygenMessage = _vm->getString(IDS_AI_OXY_LEVEL_TEXT_TEMPLATE_NORM); assert(!oxygenMessage.empty()); oxygenMessage = Common::String::format(oxygenMessage.c_str(), currentValue * 100 / GC_AIHW_STARTING_VALUE); ((SceneViewWindow *)viewWindow)->displayLiveText(oxygenMessage); } } } _entryStartTime = g_system->getMillis(); } } return SC_TRUE; } class CapacitanceToHabitatDoorClosed : public BaseOxygenTimerCapacitance { public: CapacitanceToHabitatDoorClosed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; int draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; int droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; private: Common::Rect _metalBar; Common::Rect _door; }; CapacitanceToHabitatDoorClosed::CapacitanceToHabitatDoorClosed(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 1) _staticData.navFrameIndex = 7; else _staticData.navFrameIndex = 55; _metalBar = Common::Rect(184, 146, 264, 184); _door = Common::Rect(132, 14, 312, 180); } int CapacitanceToHabitatDoorClosed::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_metalBar.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) { _staticData.navFrameIndex = 7; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar = 1; Common::Point ptInventoryWindow = viewWindow->convertPointToWindow(pointLocation, ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->startDraggingNewItem(kItemMetalBar, ptInventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); return SC_TRUE; } return SC_FALSE; } int CapacitanceToHabitatDoorClosed::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_door.contains(pointLocation)) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) { _staticData.navFrameIndex = 96; viewWindow->invalidateWindow(false); // Wait for a second (why?) uint32 startTime = g_system->getMillis(); while (!_vm->shouldQuit() && g_system->getMillis() < startTime + 1000) { _vm->yield(nullptr, -1); _vm->_sound->timerCallback(); } DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 1; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 1; destData.transitionStartFrame = -1; destData.transitionLength = -1; // Move to the final destination ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } else { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 0) { _staticData.navFrameIndex = 97; viewWindow->invalidateWindow(false); // Wait for a second (why?) uint32 startTime = g_system->getMillis(); while (!_vm->shouldQuit() && g_system->getMillis() < startTime + 1000) { _vm->yield(nullptr, -1); _vm->_sound->timerCallback(); } DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 1; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 2; destData.transitionStartFrame = -1; destData.transitionLength = -1; // Move to the final destination ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } else { int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 121; viewWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment - 1, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment - 1, 13)); _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); return SC_TRUE; } } } return SC_FALSE; } int CapacitanceToHabitatDoorClosed::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_metalBar.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) return kCursorOpenHand; if (_door.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } int CapacitanceToHabitatDoorClosed::draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (itemID == kItemMetalBar && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 1) return 1; return 0; } int CapacitanceToHabitatDoorClosed::droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (pointLocation.x == -1 && pointLocation.y == -1) return SIC_REJECT; return SIC_REJECT; } class CapacitanceToHabitatDoorOpen : public BaseOxygenTimerCapacitance { public: CapacitanceToHabitatDoorOpen(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postExitRoom(Window *viewWindow, const Location &newLocation) override; int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; int draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; int droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) override; private: Common::Rect _metalBar; }; CapacitanceToHabitatDoorOpen::CapacitanceToHabitatDoorOpen(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 1) { _staticData.navFrameIndex = 101; _staticData.destForward.transitionStartFrame = 0; _staticData.destForward.transitionLength = 28; } else { _staticData.navFrameIndex = 100; _staticData.destForward.transitionStartFrame = 53; _staticData.destForward.transitionLength = 28; } _metalBar = Common::Rect(184, 146, 264, 184); } int CapacitanceToHabitatDoorOpen::postExitRoom(Window *viewWindow, const Location &newLocation) { // Play the door closing sound if (_staticData.location.timeZone == newLocation.timeZone) _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 14), 128, false, true); return SC_TRUE; } int CapacitanceToHabitatDoorOpen::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_metalBar.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) { _staticData.navFrameIndex = 101; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar = 1; _staticData.destForward.transitionStartFrame = 0; _staticData.destForward.transitionLength = 28; Common::Point ptInventoryWindow = viewWindow->convertPointToWindow(pointLocation, ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->startDraggingNewItem(kItemMetalBar, ptInventoryWindow); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); return SC_TRUE; } return SC_FALSE; } int CapacitanceToHabitatDoorOpen::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_metalBar.contains(pointLocation) && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) return kCursorOpenHand; return kCursorArrow; } int CapacitanceToHabitatDoorOpen::draggingItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (itemID == kItemMetalBar && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 1) return 1; return 0; } int CapacitanceToHabitatDoorOpen::droppedItem(Window *viewWindow, int itemID, const Common::Point &pointLocation, int itemFlags) { if (pointLocation.x == -1 && pointLocation.y == -1) return SIC_REJECT; if (itemID == kItemMetalBar && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 1) { _staticData.navFrameIndex = 100; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar = 0; viewWindow->invalidateWindow(false); _staticData.destForward.transitionStartFrame = 53; _staticData.destForward.transitionLength = 28; ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); return SIC_ACCEPT; } return SIC_REJECT; } class CapacitancePanelInterface : public BaseOxygenTimerCapacitance { public: CapacitancePanelInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); ~CapacitancePanelInterface(); void preDestructor() override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; int gdiPaint(Window *viewWindow) override; private: Common::Rect _stationRegions[15]; int _currentSelection; int _currentTextIndex; int _lineHeight; Graphics::Font *_textFont; Common::Rect _leftTextRegion; Common::Rect _rightTextRegion; }; CapacitancePanelInterface::CapacitancePanelInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { _currentSelection = -1; _currentTextIndex = -1; _stationRegions[0] = Common::Rect(265, 110, 286, 135); _stationRegions[1] = Common::Rect(102, 45, 180, 134); _stationRegions[2] = Common::Rect(195, 106, 216, 133); _stationRegions[3] = Common::Rect(268, 72, 283, 87); _stationRegions[4] = Common::Rect(221, 46, 236, 74); _stationRegions[5] = Common::Rect(290, 72, 317, 108); _stationRegions[6] = Common::Rect(264, 55, 288, 67); _stationRegions[7] = Common::Rect(194, 74, 266, 84); _stationRegions[8] = Common::Rect(198, 62, 214, 74); _stationRegions[9] = Common::Rect(221, 106, 236, 134); _stationRegions[10] = Common::Rect(245, 46, 260, 74); _stationRegions[11] = Common::Rect(245, 106, 260, 134); _stationRegions[12] = Common::Rect(266, 92, 290, 109); _stationRegions[13] = Common::Rect(194, 96, 264, 106); _stationRegions[14] = Common::Rect(180, 85, 194, 94); _leftTextRegion = Common::Rect(83, 144, 211, 170); _rightTextRegion = Common::Rect(228, 144, 356, 170); _lineHeight = _vm->getLanguage() == Common::JA_JPN ? 10 : 13; _textFont = _vm->_gfx->createFont(_lineHeight); } CapacitancePanelInterface::~CapacitancePanelInterface() { preDestructor(); } void CapacitancePanelInterface::preDestructor() { delete _textFont; _textFont = nullptr; } int CapacitancePanelInterface::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { byte &oxygenReserves = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenReserves; if (_currentSelection == 2) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 0 && (_stationRegions[_currentSelection].contains(pointLocation) || _rightTextRegion.contains(pointLocation))) { if (oxygenReserves > 0) { // Decrement reserves flag oxygenReserves--; // Set the machine room to pressurized ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized = 1; // Display pressurizing message viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_PRES_ENV_TEXT; // Play sound file _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128); // Display pressurized text viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; } else { // Not enough oxygen reserves viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_INSUF_OXYGEN; return SC_TRUE; } } } else if (_currentSelection == 3) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 0 && (_stationRegions[_currentSelection].contains(pointLocation) || _rightTextRegion.contains(pointLocation))) { if (oxygenReserves > 0) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRGrabbedMetalBar == 0) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurizedAttempted == 0) { // Display pressurizing message viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_PRES_ENV_TEXT; // Play sound file _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128); // Display bulkhead message viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; // Play Mom audio // (Is this an Alien reference?) _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 11)); // Update attempt flag ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurizedAttempted = 1; } } else { // Decrement reserves flag oxygenReserves--; // Set the capacitance array to pressurized ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized = 1; // Display pressurizing message viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_PRES_ENV_TEXT; // Play sound file _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128); // Display pressurized text viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; // Display oxygen text in the message window ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_AI_ENTERING_PRES_ENV_TEXT)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = GC_AIHW_STARTING_VALUE; } return SC_TRUE; } else { // Not enough oxygen reserves viewWindow->invalidateWindow(false); _currentTextIndex = IDS_AI_PRES_PANEL_INSUF_OXYGEN; return SC_TRUE; } } } // Check against the hotspots for (int i = 0; i < 15; i++) { if (_stationRegions[i].contains(pointLocation) && _currentSelection != i) { switch (i) { case 0: _staticData.navFrameIndex = 107; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 1: _staticData.navFrameIndex = 108; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ZERO_PRES_ENV; return SC_TRUE; case 2: _staticData.navFrameIndex = 109; viewWindow->invalidateWindow(false); _currentSelection = i; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 1) _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; else _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES; return SC_TRUE; case 3: _staticData.navFrameIndex = 110; viewWindow->invalidateWindow(false); _currentSelection = i; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 1) _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; else _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES; return SC_TRUE; case 4: _staticData.navFrameIndex = 111; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 5: _staticData.navFrameIndex = 112; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 6: _staticData.navFrameIndex = 113; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 7: _staticData.navFrameIndex = 114; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 8: _staticData.navFrameIndex = 115; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 9: _staticData.navFrameIndex = 116; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 10: _staticData.navFrameIndex = 117; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 11: _staticData.navFrameIndex = 118; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; case 12: _staticData.navFrameIndex = 119; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_PRES_TEXT; return SC_TRUE; case 13: case 14: _staticData.navFrameIndex = 120; viewWindow->invalidateWindow(false); _currentSelection = i; _currentTextIndex = IDS_AI_PRES_PANEL_ENV_DEPRES_BREACH; return SC_TRUE; } } } // By default, return to depth zero (zoomed out) DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 0; destData.transitionType = TRANSITION_NONE; destData.transitionData = -1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } int CapacitancePanelInterface::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { for (int i = 0; i < 15; i++) if (_stationRegions[i].contains(pointLocation)) return kCursorFinger; return kCursorPutDown; } int CapacitancePanelInterface::gdiPaint(Window *viewWindow) { if (_currentSelection >= 0) { uint32 color = _vm->_gfx->getColor(208, 144, 24); Common::String location = _vm->getString(IDS_AI_PRES_PANEL_DESC_BASE + _currentSelection); if (_currentSelection == 3) location += _vm->getString(IDS_AI_PRES_PANEL_DESC_BASE + 19); Common::Rect absoluteRect = viewWindow->getAbsoluteRect(); Common::Rect rect(_leftTextRegion); rect.translate(absoluteRect.left, absoluteRect.top); _vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, location, rect.left, rect.top, rect.width(), rect.height(), color, _lineHeight, kTextAlignCenter, true); if (_currentTextIndex >= 0) { rect = _rightTextRegion; rect.translate(absoluteRect.left, absoluteRect.top); _vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, _vm->getString(_currentTextIndex), rect.left, rect.top, rect.width(), rect.height(), color, _lineHeight, kTextAlignCenter, true); } } return SC_FALSE; } class PlayArthurOffsetCapacitance : public BaseOxygenTimerCapacitance { public: PlayArthurOffsetCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int stingerVolume = 127, int firstStingerFileID = -1, int lastStingerFileID = -1, int stingerDelay = 1, int newStill = -1, int newNavStart = -1, int newNavLength = -1); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; private: int _stingerVolume; int _firstStingerFileID; int _lastStingerFileID; int _stingerDelay; }; PlayArthurOffsetCapacitance::PlayArthurOffsetCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int stingerVolume, int firstStingerFileID, int lastStingerFileID, int stingerDelay, int newStill, int newNavStart, int newNavLength) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { SceneViewWindow *sceneView = ((SceneViewWindow *)viewWindow); GlobalFlags &globalFlags = sceneView->getGlobalFlags(); _stingerVolume = stingerVolume; _firstStingerFileID = firstStingerFileID; _lastStingerFileID = lastStingerFileID; _stingerDelay = stingerDelay; if (globalFlags.aiCRGrabbedMetalBar == 0) { // This is completely wrong. //if (newStill >= 0) // _staticData.navFrameIndex; if (newNavStart >= 0) _staticData.destForward.transitionStartFrame = newNavStart; if (newNavLength >= 0) _staticData.destForward.transitionLength = newNavLength; } } int PlayArthurOffsetCapacitance::postEnterRoom(Window *viewWindow, const Location &priorLocation) { SceneViewWindow *sceneView = ((SceneViewWindow *)viewWindow); GlobalFlags &globalFlags = sceneView->getGlobalFlags(); BaseOxygenTimerCapacitance::postEnterRoom(viewWindow, priorLocation); byte effectID = globalFlags.aiCRStingerChannelID; if (!_vm->_sound->isSoundEffectPlaying(effectID - 1)) { byte lastStinger = globalFlags.aiCRStingerID + 1; if ((lastStinger % _stingerDelay) == 0) { if (lastStinger < (_lastStingerFileID - _firstStingerFileID) * _stingerDelay) { int fileNameIndex = _vm->computeFileNameResourceID(_staticData.location.timeZone, _staticData.location.environment, _firstStingerFileID + (lastStinger / _stingerDelay) - 1); if (((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI) && (lastStinger / _stingerDelay) < 3) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(fileNameIndex)); // Play an Arthur comment if we have the chip switch (lastStinger / _stingerDelay) { case 0: _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AICR_C01.BTA", 127); break; case 1: _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AICR_C02.BTA", 127); break; case 2: _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AICR_C03.BTA", 127); break; } // Update the global flags globalFlags.aiCRStingerID = lastStinger; } else { byte newStingerID = _vm->_sound->playSoundEffect(_vm->getFilePath(fileNameIndex), _stingerVolume, false, true) + 1; // Update the global flags globalFlags.aiCRStingerChannelID = newStingerID; globalFlags.aiCRStingerID = lastStinger; } } } else { globalFlags.aiCRStingerChannelID = 0xFF; globalFlags.aiCRStingerID = lastStinger; } } return SC_TRUE; } class ClickChangeSceneCapacitance : public BaseOxygenTimerCapacitance { public: ClickChangeSceneCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left = -1, int top = -1, int right = -1, int bottom = -1, int cursorID = 0, int timeZone = -1, int environment = -1, int node = -1, int facing = -1, int orientation = -1, int depth = -1, int transitionType = -1, int transitionData = -1, int transitionStartFrame = -1, int transitionLength = -1); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: int _cursorID; Common::Rect _clickRegion; DestinationScene _clickDestination; }; ClickChangeSceneCapacitance::ClickChangeSceneCapacitance(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left, int top, int right, int bottom, int cursorID, int timeZone, int environment, int node, int facing, int orientation, int depth, int transitionType, int transitionData, int transitionStartFrame, int transitionLength) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { _clickRegion = Common::Rect(left, top, right, bottom); _cursorID = cursorID; _clickDestination.destinationScene = Location(timeZone, environment, node, facing, orientation, depth); _clickDestination.transitionType = transitionType; _clickDestination.transitionData = transitionData; _clickDestination.transitionStartFrame = transitionStartFrame; _clickDestination.transitionLength = transitionLength; } int ClickChangeSceneCapacitance::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) ((SceneViewWindow *)viewWindow)->moveToDestination(_clickDestination); return SC_FALSE; } int ClickChangeSceneCapacitance::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) return _cursorID; return kCursorArrow; } class CapacitanceDockingBayDoor : public BaseOxygenTimerCapacitance { public: CapacitanceDockingBayDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _door; }; CapacitanceDockingBayDoor::CapacitanceDockingBayDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : BaseOxygenTimerCapacitance(vm, viewWindow, sceneStaticData, priorLocation) { _door = Common::Rect(160, 54, 276, 168); } int CapacitanceDockingBayDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_door.contains(pointLocation)) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiCRPressurized == 1) { _staticData.navFrameIndex = 98; viewWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AI_LOCK.BTA"); // Wait a second? uint32 startTime = g_system->getMillis(); while (!_vm->shouldQuit() && g_system->getMillis() < startTime + 1000) { _vm->yield(nullptr, -1); _vm->_sound->timerCallback(); } DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 1; destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; // Move to the final destination ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } else { int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 99; viewWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment - 1, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment - 1, 13)); _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); return SC_TRUE; } } return SC_FALSE; } int CapacitanceDockingBayDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_door.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ScanningRoomEntryScan : public SceneBase { public: ScanningRoomEntryScan(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int postExitRoom(Window *viewWindow, const Location &newLocation) override; int timerCallback(Window *viewWindow) override; private: DestinationScene _savedForwardData; }; ScanningRoomEntryScan::ScanningRoomEntryScan(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _savedForwardData = _staticData.destForward; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardInitialSpeech == 0) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0) { if (_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); else ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; } if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 3) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); } int ScanningRoomEntryScan::postEnterRoom(Window *viewWindow, const Location &priorLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardInitialSpeech == 0) { // Play the scanning movie ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(7); // Set the flag ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardInitialSpeech = 1; // Start the initial monologue byte channel = _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 12), 127, false, true) + 1; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = channel; } return SC_TRUE; } int ScanningRoomEntryScan::postExitRoom(Window *viewWindow, const Location &newLocation) { if (newLocation.timeZone == 6 && newLocation.environment == 4 && newLocation.node != 3 && newLocation.node != 0 && _staticData.location.timeZone == newLocation.timeZone) { _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128, false, true); } return SC_TRUE; } int ScanningRoomEntryScan::timerCallback(Window *viewWindow) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0 && !_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) { _staticData.destForward = _savedForwardData; ((GameUIWindow *)viewWindow->getParent())->_navArrowWindow->updateAllArrows(_staticData); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; } return SC_TRUE; } class ScanningRoomWalkWarning : public SceneBase { public: ScanningRoomWalkWarning(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postExitRoom(Window *viewWindow, const Location &newLocation) override; int timerCallback(Window *viewWindow) override; private: DestinationScene _savedForwardData; }; ScanningRoomWalkWarning::ScanningRoomWalkWarning(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _savedForwardData = _staticData.destForward; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0) { if (_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); else ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; } } int ScanningRoomWalkWarning::postExitRoom(Window *viewWindow, const Location &newLocation) { if (newLocation.timeZone == 6 && newLocation.environment == 4 && newLocation.node != 3 && newLocation.node != 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCMoveCenterWarning == 0) { if (_staticData.location.timeZone == newLocation.timeZone) _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 13), 128, false, true); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCMoveCenterWarning = 1; } return SC_TRUE; } int ScanningRoomWalkWarning::timerCallback(Window *viewWindow) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0 && !_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) { _staticData.destForward = _savedForwardData; ((GameUIWindow *)viewWindow->getParent())->_navArrowWindow->updateAllArrows(_staticData); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; } return SC_TRUE; } class ScanningRoomDockingBayDoor : public SceneBase { public: ScanningRoomDockingBayDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int timerCallback(Window *viewWindow) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: bool _audioEnded; Common::Rect _doorRegion; }; ScanningRoomDockingBayDoor::ScanningRoomDockingBayDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _audioEnded = true; _doorRegion = Common::Rect(152, 34, 266, 148); if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0) { if (!_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; _audioEnded = true; } else { _audioEnded = false; } } } int ScanningRoomDockingBayDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_audioEnded && _doorRegion.contains(pointLocation)) { // Change the still frame int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 46; viewWindow->invalidateWindow(false); // Play the beep and voiceovers _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 13)); // Reset the frame _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); // Play Arthur's voiceover if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCDBDoorWarning == 0) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 11), 127); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCDBDoorWarning = 1; } } return SC_FALSE; } int ScanningRoomDockingBayDoor::timerCallback(Window *viewWindow) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel > 0 && !_vm->_sound->isSoundEffectPlaying(((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel - 1)) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCInitialAudioChannel = 0; _audioEnded = true; } return SC_TRUE; } int ScanningRoomDockingBayDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_audioEnded && _doorRegion.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ScanningRoomScienceWingDoor : public SceneBase { public: ScanningRoomScienceWingDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _doorRegion; }; ScanningRoomScienceWingDoor::ScanningRoomScienceWingDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _doorRegion = Common::Rect(152, 34, 266, 148); } int ScanningRoomScienceWingDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_doorRegion.contains(pointLocation)) { // Change the still frame int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 44; viewWindow->invalidateWindow(false); // Play the beep and voiceovers _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 13)); // Reset the frame _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); } return SC_FALSE; } int ScanningRoomScienceWingDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_doorRegion.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ArthurScanningRoomConversation : public SceneBase { public: ArthurScanningRoomConversation(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _yes; Common::Rect _no; }; ArthurScanningRoomConversation::ArthurScanningRoomConversation(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _yes = Common::Rect(152, 54, 284, 124); _no = Common::Rect(194, 128, 244, 152); } int ArthurScanningRoomConversation::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // If this is the initial entry, play Arthur's comment if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 0) { ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(9); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus = 1; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger = 0; } _staticData.cycleStartFrame = 0; _staticData.cycleFrameCount = 20; _staticData.navFrameIndex = 37; viewWindow->invalidateWindow(false); return SC_TRUE; } int ArthurScanningRoomConversation::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_yes.contains(pointLocation)) { switch (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus) { case 1: // Proceed with scan, then ask about downloading into biochips ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(8); _staticData.navFrameIndex = 36; ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(10); _staticData.navFrameIndex = 37; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus = 2; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger = 0; viewWindow->invalidateWindow(false); // Original doesn't do this, but I can't see how it works otherwise return SC_TRUE; case 2: { // Proceed with downloading ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(11); _staticData.navFrameIndex = 36; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus = 3; // Move the player back and play the instructions for the door DestinationScene destData; destData.destinationScene = Location(6, 4, 1, 2, 1, 0); destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } } } else if (_no.contains(pointLocation)) { switch (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus) { case 1: { // No-go on the scan, so drop the player back and play the rejection sound file if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger == 0) { _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 9), 128, false, true); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger = 1; } DestinationScene destData; destData.destinationScene = Location(6, 4, 1, 2, 1, 0); destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } case 2: { // No-go on the download, so drop the player back and play the rejection sound file if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger == 0) { _vm->_sound->playSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 10), 128, false, true); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCPlayedNoStinger = 1; } DestinationScene destData; destData.destinationScene = Location(6, 4, 1, 2, 1, 0); destData.transitionType = TRANSITION_VIDEO; destData.transitionData = 0; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } } } return SC_FALSE; } int ArthurScanningRoomConversation::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_yes.contains(pointLocation) || _no.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ScanningRoomNexusDoorNormalFacing : public SceneBase { public: ScanningRoomNexusDoorNormalFacing(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _clickable; }; ScanningRoomNexusDoorNormalFacing::ScanningRoomNexusDoorNormalFacing(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clickable = Common::Rect(162, 67, 284, 189); } int ScanningRoomNexusDoorNormalFacing::postEnterRoom(Window *viewWindow, const Location &priorLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorComment == 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 3) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 8)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorComment = 1; } return SC_TRUE; } int ScanningRoomNexusDoorNormalFacing::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) { // Change the still frame int oldFrame = _staticData.navFrameIndex; _staticData.navFrameIndex = 43; viewWindow->invalidateWindow(false); // Play the beep and voiceovers _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 12)); _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, 1, 13)); // Reset the frame _staticData.navFrameIndex = oldFrame; viewWindow->invalidateWindow(false); return SC_TRUE; } return SC_FALSE; } int ScanningRoomNexusDoorNormalFacing::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ScanningRoomNexusDoorZoomInCodePad : public SceneBase { public: ScanningRoomNexusDoorZoomInCodePad(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _controls; }; ScanningRoomNexusDoorZoomInCodePad::ScanningRoomNexusDoorZoomInCodePad(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _controls = Common::Rect(160, 50, 282, 140); } int ScanningRoomNexusDoorZoomInCodePad::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) { DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 1; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 1; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } return SC_FALSE; } int ScanningRoomNexusDoorZoomInCodePad::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_controls.contains(pointLocation)) return kCursorMagnifyingGlass; return kCursorArrow; } class ScanningRoomNexusDoorCodePad : public SceneBase { public: ScanningRoomNexusDoorCodePad(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); ~ScanningRoomNexusDoorCodePad(); void preDestructor() override; int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int onCharacter(Window *viewWindow, const Common::KeyState &character) override; int gdiPaint(Window *viewWindow) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _numbers[10]; Common::String _entries; Graphics::Font *_textFont; int _lineHeight; Common::Rect _display; }; ScanningRoomNexusDoorCodePad::ScanningRoomNexusDoorCodePad(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _numbers[0] = Common::Rect(200, 129, 229, 146); _numbers[1] = Common::Rect(165, 63, 194, 80); _numbers[2] = Common::Rect(200, 63, 229, 80); _numbers[3] = Common::Rect(235, 63, 264, 80); _numbers[4] = Common::Rect(165, 85, 194, 102); _numbers[5] = Common::Rect(200, 85, 229, 102); _numbers[6] = Common::Rect(235, 85, 264, 102); _numbers[7] = Common::Rect(165, 107, 194, 124); _numbers[8] = Common::Rect(200, 107, 229, 124); _numbers[9] = Common::Rect(235, 107, 264, 124); _display = Common::Rect(166, 40, 262, 58); _lineHeight = _vm->getLanguage() == Common::JA_JPN ? 12 : 14; _textFont = _vm->_gfx->createFont(_lineHeight); } ScanningRoomNexusDoorCodePad::~ScanningRoomNexusDoorCodePad() { preDestructor(); } void ScanningRoomNexusDoorCodePad::preDestructor() { delete _textFont; _textFont = nullptr; } int ScanningRoomNexusDoorCodePad::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // Play Arthur's comment, if applicable if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorCode == 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 3) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 7)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorCode = 1; } return SC_TRUE; } int ScanningRoomNexusDoorCodePad::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { for (int i = 0; i < 10; i++) { if (_numbers[i].contains(pointLocation)) { if (_entries.size() < 5) { // Append _entries += (char)('0' + i); viewWindow->invalidateWindow(false); if (_entries == "32770") { // If the answer is correct, move to the depth with the open hatch movie DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 2; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 3; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); } return SC_TRUE; } else { // Reset _entries = (char)('0' + i); viewWindow->invalidateWindow(false); return SC_TRUE; } } } DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 0; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 2; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } int ScanningRoomNexusDoorCodePad::onCharacter(Window *viewWindow, const Common::KeyState &character) { if (character.keycode >= Common::KEYCODE_0 && character.keycode <= Common::KEYCODE_9) { char c = (char)('0' + character.keycode - Common::KEYCODE_0); if (_entries.size() < 5) { // Append _entries += c; viewWindow->invalidateWindow(false); if (_entries == "32770") { // If the answer is correct, move to the depth with the open hatch movie DestinationScene destinationData; destinationData.destinationScene = _staticData.location; destinationData.destinationScene.depth = 2; destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 3; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); } return SC_TRUE; } else { // Reset _entries = c; viewWindow->invalidateWindow(false); return SC_TRUE; } } if ((character.keycode == Common::KEYCODE_BACKSPACE || character.keycode == Common::KEYCODE_DELETE) && !_entries.empty()) { // Delete last character _entries.deleteLastChar(); viewWindow->invalidateWindow(false); return SC_TRUE; } return SC_FALSE; } int ScanningRoomNexusDoorCodePad::gdiPaint(Window *viewWindow) { if (!_entries.empty()) { uint32 textColor = _vm->_gfx->getColor(208, 144, 24); Common::Rect absoluteRect = viewWindow->getAbsoluteRect(); Common::Rect rect(_display); rect.translate(absoluteRect.left, absoluteRect.top); _vm->_gfx->renderText(_vm->_gfx->getScreen(), _textFont, _entries, rect.left, rect.top, rect.width(), rect.height(), textColor, _lineHeight, kTextAlignLeft, true); } return SC_REPAINT; } int ScanningRoomNexusDoorCodePad::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { for (int i = 0; i < 10; i++) if (_numbers[i].contains(pointLocation)) return kCursorFinger; return kCursorPutDown; } class ScanningRoomNexusDoorPullHandle : public SceneBase { public: ScanningRoomNexusDoorPullHandle(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _handle; }; ScanningRoomNexusDoorPullHandle::ScanningRoomNexusDoorPullHandle(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _handle = Common::Rect(186, 44, 276, 154); } int ScanningRoomNexusDoorPullHandle::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // Play Arthur's comment, if applicable if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorCode == 0 && ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 3) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, 7)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCHeardNexusDoorCode = 1; } return SC_TRUE; } int ScanningRoomNexusDoorPullHandle::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_handle.contains(pointLocation)) { DestinationScene destinationData; destinationData.destinationScene = Location(6, 5, 0, 0, 1, 0); destinationData.transitionType = TRANSITION_VIDEO; destinationData.transitionData = 4; destinationData.transitionStartFrame = -1; destinationData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destinationData); return SC_TRUE; } return SC_FALSE; } int ScanningRoomNexusDoorPullHandle::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_handle.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class MachineRoomExitDoor : public SceneBase { public: MachineRoomExitDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _clickable; DestinationScene _destData; }; MachineRoomExitDoor::MachineRoomExitDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clickable = Common::Rect(138, 0, 338, 189); _destData.destinationScene = Location(6, 7, 1, 3, 1, 0); _destData.transitionType = TRANSITION_VIDEO; _destData.transitionData = 4; _destData.transitionStartFrame = -1; _destData.transitionLength = -1; } int MachineRoomExitDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) { ((SceneViewWindow *)viewWindow)->moveToDestination(_destData); return SC_TRUE; } return SC_FALSE; } int MachineRoomExitDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class MachineRoomTamperedSculpture : public SceneBase { public: MachineRoomTamperedSculpture(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int locateAttempted(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _clickable; }; MachineRoomTamperedSculpture::MachineRoomTamperedSculpture(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clickable = Common::Rect(184, 54, 274, 142); } int MachineRoomTamperedSculpture::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRCorrectFreqSet == 2) { // Play the morph movie ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(1); // Attempt to add it to the biochip if (((SceneViewWindow *)viewWindow)->addNumberToGlobalFlagTable(AI_EVIDENCE_SCULPTURE)) ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_MBT_EVIDENCE_RIPPLE_DOCUMENTED)); else ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_MBT_EVIDENCE_ALREADY_ACQUIRED)); // Turn off evidence capture ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->disableEvidenceCapture(); // Set the scoring flag ((SceneViewWindow *)viewWindow)->getGlobalFlags().scoreFoundSculptureDiagram = 1; // Update the AI chip and check for spontaneous comments if (((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) ((SceneViewWindow *)viewWindow)->playAIComment(_staticData.location, AI_COMMENT_TYPE_SPONTANEOUS); ((GameUIWindow *)viewWindow->getParent())->_bioChipRightWindow->sceneChanged(); } else { // Play the normal morphing animation ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(0); } return SC_TRUE; } return SC_FALSE; } int MachineRoomTamperedSculpture::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // If we have not yet captured it, set the anachronism message if (!((SceneViewWindow *)viewWindow)->isNumberInGlobalFlagTable(AI_EVIDENCE_SCULPTURE)) ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_MBT_EVIDENCE_PRESENT)); return SC_TRUE; } int MachineRoomTamperedSculpture::locateAttempted(Window *viewWindow, const Common::Point &pointLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().bcLocateEnabled == 1 && _clickable.contains(pointLocation) && !((SceneViewWindow *)viewWindow)->isNumberInGlobalFlagTable(AI_EVIDENCE_SCULPTURE)) { ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_MBT_EVIDENCE_MUST_BE_REVEALED)); // All will be reveaaaaaled return SC_TRUE; } return SC_FALSE; } int MachineRoomTamperedSculpture::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().bcLocateEnabled == 1) { if (_clickable.contains(pointLocation)) return -2; return -1; } if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class MachineRoomHarmonicsInterface : public SceneBase { public: MachineRoomHarmonicsInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _testButton; Common::Rect _turnRight; Common::Rect _turnLeft; int _currentSelection; bool _tested; }; MachineRoomHarmonicsInterface::MachineRoomHarmonicsInterface(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _testButton = Common::Rect(122, 90, 160, 118); _turnRight = Common::Rect(128, 27, 173, 48); _turnLeft = Common::Rect(128, 53, 173, 80); _currentSelection = ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRCorrectFreqSet; _tested = false; switch (_currentSelection) { case 0: _staticData.navFrameIndex = 105; break; case 1: _staticData.navFrameIndex = 107; break; case 2: _staticData.navFrameIndex = 109; break; case 3: _staticData.navFrameIndex = 111; break; case 4: _staticData.navFrameIndex = 113; break; case 5: _staticData.navFrameIndex = 115; break; case 6: _staticData.navFrameIndex = 116; break; case 7: _staticData.navFrameIndex = 118; break; } } int MachineRoomHarmonicsInterface::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_turnRight.contains(pointLocation)) { _staticData.navFrameIndex = 103; viewWindow->invalidateWindow(false); // TODO: Delay _staticData.navFrameIndex = 104; viewWindow->invalidateWindow(false); // TODO: Delay _currentSelection++; if (_currentSelection > 7) _currentSelection = 0; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRCorrectFreqSet = _currentSelection; _tested = false; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRUsedHarmonicsInterface = 1; switch (_currentSelection) { case 0: _staticData.navFrameIndex = 105; break; case 1: _staticData.navFrameIndex = 107; break; case 2: _staticData.navFrameIndex = 109; break; case 3: _staticData.navFrameIndex = 111; break; case 4: _staticData.navFrameIndex = 113; break; case 5: _staticData.navFrameIndex = 115; break; case 6: _staticData.navFrameIndex = 116; break; case 7: _staticData.navFrameIndex = 118; break; } viewWindow->invalidateWindow(false); return SC_TRUE; } if (_turnLeft.contains(pointLocation)) { _staticData.navFrameIndex = 104; viewWindow->invalidateWindow(false); // TODO: Delay _staticData.navFrameIndex = 103; viewWindow->invalidateWindow(false); // TODO: Delay _currentSelection--; if (_currentSelection < 0) _currentSelection = 7; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRCorrectFreqSet = _currentSelection; _tested = false; ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRUsedHarmonicsInterface = 1; switch (_currentSelection) { case 0: _staticData.navFrameIndex = 105; break; case 1: _staticData.navFrameIndex = 107; break; case 2: _staticData.navFrameIndex = 109; break; case 3: _staticData.navFrameIndex = 111; break; case 4: _staticData.navFrameIndex = 113; break; case 5: _staticData.navFrameIndex = 115; break; case 6: _staticData.navFrameIndex = 116; break; case 7: _staticData.navFrameIndex = 118; break; } viewWindow->invalidateWindow(false); return SC_TRUE; } if (_testButton.contains(pointLocation) && _currentSelection != 5 && !_tested) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRUsedHarmonicsInterface = 1; // Play the proper sound effect int fileID = -1; switch (_currentSelection) { case 0: fileID = 6; break; case 1: fileID = 7; break; case 2: fileID = 8; break; case 3: fileID = 9; break; case 4: fileID = 10; break; case 6: fileID = 11; break; case 7: fileID = 12; break; } _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, fileID), 128); // Increment the frame to display the results _staticData.navFrameIndex++; viewWindow->invalidateWindow(false); _tested = true; // Set the score flag if (_currentSelection == 2) ((SceneViewWindow *)viewWindow)->getGlobalFlags().scoreResearchMorphSculpture = 1; return SC_TRUE; } // Return the player to the original position (depth zero) DestinationScene destData; destData.destinationScene = _staticData.location; destData.destinationScene.depth = 0; destData.transitionType = TRANSITION_NONE; destData.transitionData = -1; destData.transitionStartFrame = -1; destData.transitionLength = -1; ((SceneViewWindow *)viewWindow)->moveToDestination(destData); return SC_TRUE; } int MachineRoomHarmonicsInterface::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_turnRight.contains(pointLocation)) return kCursorArrowRight; if (_turnLeft.contains(pointLocation)) return kCursorArrowRight; if (_testButton.contains(pointLocation) && _currentSelection != 5 && !_tested) return kCursorFinger; return kCursorPutDown; } class MachineRoomHarmonicsZoomIn : public SceneBase { public: MachineRoomHarmonicsZoomIn(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _clickRegion; DestinationScene _clickDestination; }; MachineRoomHarmonicsZoomIn::MachineRoomHarmonicsZoomIn(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clickRegion = Common::Rect(90, 60, 178, 134); _clickDestination.destinationScene = _staticData.location; _clickDestination.destinationScene.depth = 1; _clickDestination.transitionType = TRANSITION_NONE; _clickDestination.transitionData = -1; _clickDestination.transitionStartFrame = -1; _clickDestination.transitionLength = -1; } int MachineRoomHarmonicsZoomIn::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) ((SceneViewWindow *)viewWindow)->moveToDestination(_clickDestination); return SC_FALSE; } int MachineRoomHarmonicsZoomIn::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickRegion.contains(pointLocation)) return kCursorMagnifyingGlass; return kCursorArrow; } class SpaceDoor : public SceneBase { public: SpaceDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left = -1, int top = -1, int right = -1, int bottom = -1, int openFrame = -1, int closedFrame = -1, int depth = -1, int transitionType = -1, int transitionData = -1, int transitionStartFrame = -1, int transitionLength = -1); int mouseDown(Window *viewWindow, const Common::Point &pointLocation) override; int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: bool _clicked; Common::Rect _clickable; DestinationScene _destData; int _openFrame; int _closedFrame; }; SpaceDoor::SpaceDoor(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left, int top, int right, int bottom, int openFrame, int closedFrame, int depth, int transitionType, int transitionData, int transitionStartFrame, int transitionLength) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clicked = false; _openFrame = openFrame; _closedFrame = closedFrame; _clickable = Common::Rect(left, top, right, bottom); _destData.destinationScene = _staticData.location; _destData.destinationScene.depth = depth; _destData.transitionType = transitionType; _destData.transitionData = transitionData; _destData.transitionStartFrame = transitionStartFrame; _destData.transitionLength = transitionLength; } int SpaceDoor::mouseDown(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) { _clicked = true; return SC_TRUE; } return SC_FALSE; } int SpaceDoor::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clicked) { // If we are facing the scanning room door and we have Arthur, automatically recall // to the future apartment if (_staticData.location.timeZone == 6 && _staticData.location.environment == 3 && _staticData.location.node == 9 && _staticData.location.facing == 0 && _staticData.location.orientation == 0 && _staticData.location.depth == 0 && ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemBioChipAI)) { ((SceneViewWindow *)viewWindow)->timeSuitJump(4); return SC_TRUE; } // Change the still frame to the new one if (_openFrame >= 0) { _staticData.navFrameIndex = _openFrame; viewWindow->invalidateWindow(false); _vm->_sound->playSynchronousSoundEffect("BITDATA/AILAB/AI_LOCK.BTA"); } ((SceneViewWindow *)viewWindow)->moveToDestination(_destData); _clicked = false; return SC_TRUE; } return SC_FALSE; } int SpaceDoor::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } class ScanningRoomNexusDoorToGlobe : public SceneBase { public: ScanningRoomNexusDoorToGlobe(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation); }; ScanningRoomNexusDoorToGlobe::ScanningRoomNexusDoorToGlobe(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiSCConversationStatus == 3) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); } class MachineRoomEntry : public SceneBase { public: MachineRoomEntry(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundID = -1); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; int timerCallback(Window *viewWindow) override; private: int _soundID; bool _die; }; MachineRoomEntry::MachineRoomEntry(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundID) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _soundID = soundID; _die = false; } int MachineRoomEntry::postEnterRoom(Window *viewWindow, const Location &priorLocation) { // If the machine room is not pressurized, flag ourselves for death if (((SceneViewWindow *)viewWindow)->getGlobalFlags().aiMRPressurized == 0) { _die = true; ((SceneViewWindow *)viewWindow)->_disableArthur = true; return SC_TRUE; } // Otherwise, notify the player they have entered a pressurized room and their oxygen has replenished if (_staticData.location.node != priorLocation.node) { ((SceneViewWindow *)viewWindow)->displayLiveText(_vm->getString(IDS_AI_ENTERING_PRES_ENV_TEXT)); ((SceneViewWindow *)viewWindow)->getGlobalFlags().aiOxygenTimer = GC_AIHW_STARTING_VALUE; } return SC_TRUE; } int MachineRoomEntry::timerCallback(Window *viewWindow) { // If we have not pressurized the room, kill the player if (_die) { ((SceneViewWindow *)viewWindow)->showDeathScene(41); return SC_DEATH; } return SC_TRUE; } class DockingBayPlaySoundEntering : public SceneBase { public: DockingBayPlaySoundEntering(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundFileNameID = -1); int postEnterRoom(Window *viewWindow, const Location &priorLocation) override; private: int _soundFileNameID; }; DockingBayPlaySoundEntering::DockingBayPlaySoundEntering(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int soundFileNameID) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _soundFileNameID = soundFileNameID; if (((SceneViewWindow *)viewWindow)->getGlobalFlags().generalWalkthroughMode == 1) _staticData.destForward.destinationScene = Location(-1, -1, -1, -1, -1, -1); } int DockingBayPlaySoundEntering::postEnterRoom(Window *viewWindow, const Location &priorLocation) { SceneViewWindow *sceneView = ((SceneViewWindow *)viewWindow); GlobalFlags &globalFlags = sceneView->getGlobalFlags(); if (globalFlags.aiDBPlayedMomComment == 0) { _vm->_sound->playSynchronousSoundEffect(_vm->getFilePath(_staticData.location.timeZone, _staticData.location.environment, _soundFileNameID)); globalFlags.aiDBPlayedMomComment = 1; } return SC_TRUE; } class MachineRoomPlayAnim : public SceneBase { public: MachineRoomPlayAnim(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left = -1, int top = -1, int right = -1, int bottom = -1, int animID = -1); int mouseUp(Window *viewWindow, const Common::Point &pointLocation) override; int specifyCursor(Window *viewWindow, const Common::Point &pointLocation) override; private: Common::Rect _clickable; int _animID; }; MachineRoomPlayAnim::MachineRoomPlayAnim(BuriedEngine *vm, Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation, int left, int top, int right, int bottom, int animID) : SceneBase(vm, viewWindow, sceneStaticData, priorLocation) { _clickable = Common::Rect(left, top, right, bottom); _animID = animID; } int MachineRoomPlayAnim::mouseUp(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation) && _animID >= 0) { ((SceneViewWindow *)viewWindow)->playSynchronousAnimation(_animID); return SC_TRUE; } return SC_FALSE; } int MachineRoomPlayAnim::specifyCursor(Window *viewWindow, const Common::Point &pointLocation) { if (_clickable.contains(pointLocation)) return kCursorFinger; return kCursorArrow; } bool SceneViewWindow::initializeAILabTimeZoneAndEnvironment(Window *viewWindow, int environment) { if (environment == -1) { GlobalFlags &flags = ((SceneViewWindow *)viewWindow)->getGlobalFlags(); flags.aiHWStingerID = 0; flags.aiHWStingerChannelID = 0; flags.aiCRStingerID = 0; flags.aiCRStingerChannelID = 0; flags.aiDBStingerID = 0; flags.aiDBStingerChannelID = 0; flags.aiOxygenTimer = kAIHWStartingValue; flags.aiCRPressurized = flags.generalWalkthroughMode; flags.aiCRPressurizedAttempted = 0; flags.aiMRPressurized = flags.generalWalkthroughMode; flags.aiIceMined = 0; flags.aiOxygenReserves = 1; flags.aiSCHeardInitialSpeech = 0; flags.aiMRCorrectFreqSet = 4; flags.aiSCConversationStatus = 0; flags.aiSCHeardNexusDoorComment = 0; flags.aiSCHeardNexusDoorCode = 0; flags.aiNXPlayedBrainComment = 0; flags.aiDBPlayedSecondArthur = 0; flags.aiDBPlayedThirdArthur = 0; flags.aiDBPlayedFourthArthur = 0; flags.aiCRGrabbedMetalBar = ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemMetalBar) ? 1 : 0; flags.aiICGrabbedWaterCanister = (((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemWaterCanEmpty) || ((GameUIWindow *)viewWindow->getParent())->_inventoryWindow->isItemInInventory(kItemWaterCanFull)) ? 1 : 0; } else if (environment == 1) { ((SceneViewWindow *)viewWindow)->getGlobalFlags().scoreEnteredSpaceStation = 1; } return true; } bool SceneViewWindow::startAILabAmbient(int oldTimeZone, int oldEnvironment, int environment, bool fade) { _vm->_sound->setAmbientSound(_vm->getFilePath(6, environment, SF_AMBIENT), fade, 64); return true; } bool SceneViewWindow::checkCustomSpaceStationAICommentDependencies(const Location &commentLocation, const AIComment &commentData) { switch (commentData.dependencyFlagOffsetB) { case 1: // After failing to pressurize from SW panel interface, before using mining controls return _globalFlags.aiSWAttemptedPresMR == 1 && _globalFlags.aiICUsedMiningControls == 1; case 2: // Never used oxygen before return _globalFlags.aiICRefilledOxygen == 0; case 3: // If no water canister is in our inventory return !((GameUIWindow *)getParent())->_inventoryWindow->isItemInInventory(kItemWaterCanFull) && !((GameUIWindow *)getParent())->_inventoryWindow->isItemInInventory(kItemWaterCanEmpty); case 4: // Have not used pressurization interface return _globalFlags.aiSWAttemptedPresMR == 0; case 5: // If tried biomass, not enough reserve oxygen return _globalFlags.aiSWAttemptedPresMR == 1 && _globalFlags.aiOxygenReserves == 0; case 6: // If tried biomass, not enough reserve, has not run mining sequence return _globalFlags.aiSWAttemptedPresMR == 1 && _globalFlags.aiOxygenReserves == 0 && _globalFlags.aiICUsedMiningControls == 0; case 7: // If tried biomass, not enough reserve, has run mining sequence, has not run processing sequence return _globalFlags.aiSWAttemptedPresMR == 1 && _globalFlags.aiOxygenReserves == 0 && _globalFlags.aiICUsedMiningControls == 1 && _globalFlags.aiICProcessedOxygen == 0; case 8: // If we have not pressurized the machine room return _globalFlags.aiMRPressurized == 0; case 9: // If we have not revealed the diagram return _globalFlags.scoreFoundSculptureDiagram == 0; case 10: // If we have not revealed the diagram or used the harmonics interface return _globalFlags.scoreFoundSculptureDiagram == 0 && _globalFlags.aiMRUsedHarmonicsInterface == 0; case 11: // After we have recorded evidence return _globalFlags.scoreFoundSculptureDiagram == 1; case 12: // Before using mining control, after having tried to pressurize biomass room // clone2727: This was mistakenly cut out of the original return _globalFlags.aiICUsedMiningControls == 0 && _globalFlags.aiSWAttemptedPresMR == 1; } return false; } SceneBase *SceneViewWindow::constructAILabSceneObject(Window *viewWindow, const LocationStaticData &sceneStaticData, const Location &priorLocation) { SceneViewWindow *sceneView = ((SceneViewWindow *)viewWindow); GlobalFlags &globalFlags = sceneView->getGlobalFlags(); // Special scene for the trial version if (_vm->isTrial()) return new TrialRecallScene(_vm, viewWindow, sceneStaticData, priorLocation); switch (sceneStaticData.classID) { case 0: // Default scene break; case 1: return new UseCheeseGirlPropellant(_vm, viewWindow, sceneStaticData, priorLocation); case 3: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 172, 46, 262, 136, 87, -1, 1, TRANSITION_VIDEO, 2, -1, -1); case 4: return new PlayArthurOffsetTimed(_vm, viewWindow, sceneStaticData, priorLocation, 127, 4, 10, 1); // 1.01 uses a delay of 2, clone2727 likes that better case 5: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 144, 30, 268, 152, 88, -1, 1, TRANSITION_VIDEO, 4, -1, -1); case 6: return new PlaySoundExitingFromScene(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 7: return new HabitatWingLockedDoor(_vm, viewWindow, sceneStaticData, priorLocation, 99, 12, 13, 166, 32, 286, 182); case 8: return new HabitatWingLockedDoor(_vm, viewWindow, sceneStaticData, priorLocation, 100, 12, 13, 130, 48, 290, 189); case 9: return new HabitatWingIceteroidDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 11: return new BaseOxygenTimer(_vm, viewWindow, sceneStaticData, priorLocation); case 12: return new BaseOxygenTimerInSpace(_vm, viewWindow, sceneStaticData, priorLocation); case 20: return new PlayArthurOffsetCapacitance(_vm, viewWindow, sceneStaticData, priorLocation, 127, 4, 11, 1); case 21: return new CapacitanceToHabitatDoorClosed(_vm, viewWindow, sceneStaticData, priorLocation); case 22: return new CapacitanceToHabitatDoorOpen(_vm, viewWindow, sceneStaticData, priorLocation); case 23: return new ClickChangeSceneCapacitance(_vm, viewWindow, sceneStaticData, priorLocation, 122, 32, 310, 140, kCursorMagnifyingGlass, 6, 2, 3, 0, 1, 1, TRANSITION_VIDEO, 3, -1, -1); case 24: return new CapacitancePanelInterface(_vm, viewWindow, sceneStaticData, priorLocation); case 25: return new CapacitanceDockingBayDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 26: return new PlaySoundExitingFromScene(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 27: return new PlayArthurOffsetCapacitance(_vm, viewWindow, sceneStaticData, priorLocation, 127, 4, 11, 1, 73, 320, 40); case 28: return new PlayArthurOffsetCapacitance(_vm, viewWindow, sceneStaticData, priorLocation, 127, 4, 11, 1, 66, 241, 25); case 30: return new PlaySoundEnteringScene(_vm, viewWindow, sceneStaticData, priorLocation, 5, globalFlags.aiDBPlayedFirstArthur); case 31: return new SpaceDoor(_vm, viewWindow, sceneStaticData, priorLocation, 174, 70, 256, 152, 166, -1, 1, TRANSITION_VIDEO, 0, -1, -1); case 32: return new PlaySoundExitingFromScene(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 33: return new SpaceDoor(_vm, viewWindow, sceneStaticData, priorLocation, 185, 42, 253, 110, 167, -1, 1, TRANSITION_VIDEO, 1, -1, -1); case 35: return new DockingBayPlaySoundEntering(_vm, viewWindow, sceneStaticData, priorLocation, 4); case 36: return new PlaySoundEnteringScene(_vm, viewWindow, sceneStaticData, priorLocation, 6, globalFlags.aiDBPlayedSecondArthur); case 37: return new PlaySoundEnteringScene(_vm, viewWindow, sceneStaticData, priorLocation, 7, globalFlags.aiDBPlayedThirdArthur); case 38: return new PlaySoundEnteringScene(_vm, viewWindow, sceneStaticData, priorLocation, 8, globalFlags.aiDBPlayedFourthArthur); case 39: return new DisableForwardMovement(_vm, viewWindow, sceneStaticData, priorLocation, 1); case 40: return new ScanningRoomEntryScan(_vm, viewWindow, sceneStaticData, priorLocation); case 41: return new ScanningRoomWalkWarning(_vm, viewWindow, sceneStaticData, priorLocation); case 42: return new ScanningRoomDockingBayDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 43: return new ScanningRoomScienceWingDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 44: return new ArthurScanningRoomConversation(_vm, viewWindow, sceneStaticData, priorLocation); case 45: return new ScanningRoomNexusDoorNormalFacing(_vm, viewWindow, sceneStaticData, priorLocation); case 46: return new ScanningRoomNexusDoorZoomInCodePad(_vm, viewWindow, sceneStaticData, priorLocation); case 47: return new ScanningRoomNexusDoorCodePad(_vm, viewWindow, sceneStaticData, priorLocation); case 48: return new ScanningRoomNexusDoorPullHandle(_vm, viewWindow, sceneStaticData, priorLocation); case 49: return new ScanningRoomNexusDoorToGlobe(_vm, viewWindow, sceneStaticData, priorLocation); case 50: return new IceteroidPodTimed(_vm, viewWindow, sceneStaticData, priorLocation, 174, 96, 246, 118, 1, 6, 6, 1, 0, 1, 0); case 51: return new IceteroidPodTimed(_vm, viewWindow, sceneStaticData, priorLocation, 174, 96, 246, 118, 3, 6, 6, 0, 0, 1, 0); case 52: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 164, 40, 276, 140, -1, -1, 1, TRANSITION_VIDEO, 0, -1, -1); case 53: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 164, 40, 276, 140, -1, -1, 1, TRANSITION_VIDEO, 2, -1, -1); case 54: return new PlaySoundExitingFromSceneDeux(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 55: return new IceteroidElevatorExtremeControls(_vm, viewWindow, sceneStaticData, priorLocation, 6, 6, 6, 0, 1, 0, 6); case 56: return new IceteroidElevatorExtremeControls(_vm, viewWindow, sceneStaticData, priorLocation, 6, 6, 3, 0, 1, 0, 5, 6, 6, 2, 0, 1, 0, 7); case 57: return new IceteroidElevatorExtremeControls(_vm, viewWindow, sceneStaticData, priorLocation, -1, -1, -1, -1, -1, -1, -1, 6, 6, 6, 0, 1, 0, 4); case 58: return new IceteroidZoomInMineControls(_vm, viewWindow, sceneStaticData, priorLocation); case 59: return new IceteroidMineControls(_vm, viewWindow, sceneStaticData, priorLocation); case 60: return new BaseOxygenTimer(_vm, viewWindow, sceneStaticData, priorLocation); case 61: return new IceteroidZoomInDispenser(_vm, viewWindow, sceneStaticData, priorLocation); case 62: return new IceteroidDispenserControls(_vm, viewWindow, sceneStaticData, priorLocation); case 63: return new IceteroidPodTimed(_vm, viewWindow, sceneStaticData, priorLocation, 174, 96, 246, 118, 14, 6, 6, 5, 0, 1, 0); case 64: return new IceteroidPodTimed(_vm, viewWindow, sceneStaticData, priorLocation, 174, 96, 246, 118, 15, 6, 6, 4, 0, 1, 0); case 65: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 164, 26, 268, 124, -1, -1, 1, TRANSITION_VIDEO, 13, -1, -1); case 66: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 164, 26, 268, 124, -1, -1, 1, TRANSITION_VIDEO, 16, -1, -1); case 67: return new TakeWaterCanister(_vm, viewWindow, sceneStaticData, priorLocation); case 68: return new PlaySoundExitingFromSceneDeux(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 69: return new PlaySoundExitingForward(_vm, viewWindow, sceneStaticData, priorLocation, 14); case 70: return new SpaceDoorTimer(_vm, viewWindow, sceneStaticData, priorLocation, 92, 92, 212, 189, 48, -1, 1, TRANSITION_VIDEO, 0, -1, -1); case 71: return new ScienceWingZoomIntoPanel(_vm, viewWindow, sceneStaticData, priorLocation); case 72: return new ScienceWingPanelInterface(_vm, viewWindow, sceneStaticData, priorLocation); case 73: return new ScienceWingMachineRoomDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 74: return new ScienceWingStingersTimed(_vm, viewWindow, sceneStaticData, priorLocation); case 75: return new HabitatWingLockedDoor(_vm, viewWindow, sceneStaticData, priorLocation, 51, 4, 5, 146, 0, 396, 84); case 80: // Scene exists, but is just the default one break; case 81: return new MachineRoomExitDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 82: return new MachineRoomPlayAnim(_vm, viewWindow, sceneStaticData, priorLocation, 156, 30, 251, 125, 2); case 83: return new MachineRoomPlayAnim(_vm, viewWindow, sceneStaticData, priorLocation, 184, 38, 272, 126, 3); case 84: return new MachineRoomTamperedSculpture(_vm, viewWindow, sceneStaticData, priorLocation); case 85: return new MachineRoomHarmonicsInterface(_vm, viewWindow, sceneStaticData, priorLocation); case 86: return new MachineRoomHarmonicsZoomIn(_vm, viewWindow, sceneStaticData, priorLocation); case 87: return new MachineRoomEntry(_vm, viewWindow, sceneStaticData, priorLocation); case 90: return new NexusDoor(_vm, viewWindow, sceneStaticData, priorLocation); case 91: return new NexusPuzzle(_vm, viewWindow, sceneStaticData, priorLocation); case 92: return new NexusEnd(_vm, viewWindow, sceneStaticData, priorLocation); case 93: return new BaseOxygenTimer(_vm, viewWindow, sceneStaticData, priorLocation); case 100: return new TakeWaterCanister(_vm, viewWindow, sceneStaticData, priorLocation); default: warning("Unknown AI lab scene object %d", sceneStaticData.classID); break; } return new SceneBase(_vm, viewWindow, sceneStaticData, priorLocation); } } // End of namespace Buried