aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hoops2011-10-20 17:35:25 -0400
committerMatthew Hoops2011-10-20 17:35:25 -0400
commitd6dee9ff1880cd8c0badbf55d6e50ec67d43031a (patch)
tree6af473e75326a4e0a9be3d551be35547e13cef31
parent422ff6c4c11bf09cfaa557c146b2f2d869c2cc3e (diff)
downloadscummvm-rg350-d6dee9ff1880cd8c0badbf55d6e50ec67d43031a.tar.gz
scummvm-rg350-d6dee9ff1880cd8c0badbf55d6e50ec67d43031a.tar.bz2
scummvm-rg350-d6dee9ff1880cd8c0badbf55d6e50ec67d43031a.zip
PEGASUS: Add the base Norad code
(Common stuff between both Norad Alpha and Norad Delta)
-rw-r--r--engines/pegasus/module.mk6
-rwxr-xr-xengines/pegasus/movie.cpp15
-rwxr-xr-xengines/pegasus/neighborhood/norad/norad.cpp284
-rwxr-xr-xengines/pegasus/neighborhood/norad/norad.h485
-rwxr-xr-xengines/pegasus/neighborhood/norad/noradelevator.cpp130
-rwxr-xr-xengines/pegasus/neighborhood/norad/noradelevator.h67
-rwxr-xr-xengines/pegasus/neighborhood/norad/pressuredoor.cpp543
-rwxr-xr-xengines/pegasus/neighborhood/norad/pressuredoor.h93
-rwxr-xr-xengines/pegasus/neighborhood/norad/pressuretracker.cpp86
-rwxr-xr-xengines/pegasus/neighborhood/norad/pressuretracker.h69
-rwxr-xr-xengines/pegasus/neighborhood/norad/subcontrolroom.cpp1174
-rwxr-xr-xengines/pegasus/neighborhood/norad/subcontrolroom.h133
-rwxr-xr-xengines/pegasus/neighborhood/norad/subplatform.cpp205
-rwxr-xr-xengines/pegasus/neighborhood/norad/subplatform.h63
14 files changed, 3351 insertions, 2 deletions
diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk
index a371aef716..58c9dc99d7 100644
--- a/engines/pegasus/module.mk
+++ b/engines/pegasus/module.mk
@@ -59,6 +59,12 @@ MODULE_OBJS = \
neighborhood/caldoria/caldoriamirror.o \
neighborhood/mars/reactor.o \
neighborhood/mars/mars.o \
+ neighborhood/norad/norad.o \
+ neighborhood/norad/noradelevator.o \
+ neighborhood/norad/pressuredoor.o \
+ neighborhood/norad/pressuretracker.o \
+ neighborhood/norad/subcontrolroom.o \
+ neighborhood/norad/subplatform.o \
neighborhood/prehistoric/prehistoric.o \
neighborhood/tsa/fulltsa.o \
neighborhood/tsa/tinytsa.o \
diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp
index 75790ed897..9551248ccc 100755
--- a/engines/pegasus/movie.cpp
+++ b/engines/pegasus/movie.cpp
@@ -57,8 +57,19 @@ void Movie::initFromMovieFile(const Common::String &fileName, bool transparent)
releaseMovie();
_video = new Video::QuickTimeDecoder();
- if (!_video->loadFile(fileName))
- error("Could not load video '%s'", fileName.c_str());
+ if (!_video->loadFile(fileName)) {
+ // Replace any colon with an underscore, since only Mac OS X
+ // supports that. See PegasusEngine::detectOpeningClosingDirectory()
+ // for more info.
+ Common::String newName(fileName);
+ if (newName.contains(':'))
+ for (uint i = 0; i < newName.size(); i++)
+ if (newName[i] == ':')
+ newName.setChar(i, '_');
+
+ if (!_video->loadFile(newName))
+ error("Could not load video '%s'", fileName.c_str());
+ }
_video->pauseVideo(true);
diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp
new file mode 100755
index 0000000000..931d854f3a
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/norad.cpp
@@ -0,0 +1,284 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/energymonitor.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/noradelevator.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/subplatform.h"
+
+namespace Pegasus {
+
+const tNotificationFlags kDoneWithPressureDoorNotification = 1;
+
+const tNotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification;
+
+// This class handles everything that Norad Alpha and Delta have in common, such as
+// oxygen mask usage, the elevator and the pressure doors.
+
+Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, tNeighborhoodID id) :
+ Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) {
+ _elevatorUpSpotID = kNoHotSpotID;
+ _elevatorDownSpotID = kNoHotSpotID;
+ _elevatorUpRoomID = kNoHotSpotID;
+ _elevatorDownRoomID = kNoHotSpotID;
+
+ _subRoomEntryRoom1 = kNoRoomID;
+ _subRoomEntryDir1 = kNoDirection;
+ _subRoomEntryRoom2 = kNoRoomID;
+ _subRoomEntryDir2 = kNoDirection;
+ _upperPressureDoorRoom = kNoRoomID;
+ _lowerPressureDoorRoom = kNoRoomID;
+
+ _upperPressureDoorUpSpotID = kNoHotSpotID;
+ _upperPressureDoorDownSpotID = kNoHotSpotID;
+ _upperPressureDoorAbortSpotID = kNoHotSpotID;
+
+ _lowerPressureDoorUpSpotID = kNoHotSpotID;
+ _lowerPressureDoorDownSpotID = kNoHotSpotID;
+ _lowerPressureDoorAbortSpotID = kNoHotSpotID;
+
+ _pressureSoundIn = 0xffffffff;
+ _pressureSoundOut = 0xffffffff;
+ _equalizeSoundIn = 0xffffffff;
+ _equalizeSoundOut = 0xffffffff;
+ _accessDeniedIn = 0xffffffff;
+ _accessDeniedOut = 0xffffffff;
+
+ _platformRoom = kNoRoomID;
+ _subControlRoom = kNoRoomID;
+
+ _doneWithPressureDoor = false;
+
+ _noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags);
+}
+
+GameInteraction *Norad::makeInteraction(const tInteractionID interactionID) {
+ PressureDoor *pressureDoor;
+ SubControlRoom *subControl;
+
+ switch (interactionID) {
+ case kNoradElevatorInteractionID:
+ return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID);
+ case kNoradPressureDoorInteractionID:
+ if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
+ pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID,
+ _upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
+ else
+ pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID,
+ _lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
+
+ if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot())
+ pressureDoor->playAgainstRobot();
+
+ return pressureDoor;
+ case kNoradSubControlRoomInteractionID:
+ subControl = new SubControlRoom(this);
+
+ if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot())
+ subControl->playAgainstRobot();
+
+ return subControl;
+ case kNoradSubPlatformInteractionID:
+ return new SubPlatform(this);
+ default:
+ return 0;
+ }
+}
+
+void Norad::flushGameState() {
+ g_energyMonitor->saveCurrentEnergyValue();
+}
+
+void Norad::start() {
+ setUpAirMask();
+ Neighborhood::start();
+}
+
+void Norad::activateHotspots() {
+ Neighborhood::activateHotspots();
+
+ tRoomID room = GameState.getCurrentRoom();
+ if (room == _elevatorUpRoomID)
+ _neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID);
+ else if (room == _elevatorDownRoomID)
+ _neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID);
+}
+
+void Norad::arriveAt(const tRoomID room, const tDirectionConstant direction) {
+ Neighborhood::arriveAt(room, direction);
+
+ if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID)
+ arriveAtNoradElevator();
+ else if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
+ arriveAtUpperPressureDoorRoom();
+ else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom)
+ arriveAtLowerPressureDoorRoom();
+ else if (GameState.getCurrentRoom() == _platformRoom)
+ arriveAtSubPlatformRoom();
+ else if (GameState.getCurrentRoom() == _subControlRoom)
+ arriveAtSubControlRoom();
+
+ if (_doneWithPressureDoor) {
+ _doneWithPressureDoor = false;
+ openDoor();
+ }
+}
+
+void Norad::arriveAtNoradElevator() {
+ if (_currentInteraction)
+ _currentInteraction->startOverInteraction();
+ else
+ newInteraction(kNoradElevatorInteractionID);
+}
+
+void Norad::arriveAtUpperPressureDoorRoom() {
+ newInteraction(kNoradPressureDoorInteractionID);
+}
+
+void Norad::arriveAtLowerPressureDoorRoom() {
+ newInteraction(kNoradPressureDoorInteractionID);
+}
+
+void Norad::arriveAtSubPlatformRoom() {
+ newInteraction(kNoradSubPlatformInteractionID);
+}
+
+void Norad::arriveAtSubControlRoom() {
+ newInteraction(kNoradSubControlRoomInteractionID);
+}
+
+int16 Norad::getStaticCompassAngle(const tRoomID room, const tDirectionConstant dir) {
+ int16 result = Neighborhood::getStaticCompassAngle(room, dir);
+
+ if (room == _elevatorUpRoomID || room == _elevatorDownRoomID)
+ result += kElevatorCompassAngle;
+ else if (room == _platformRoom)
+ result += kSubPlatformCompassAngle;
+ else if (room == _subControlRoom)
+ result += kSubControlCompassAngle;
+
+ return result;
+}
+
+tCanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
+ if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) ||
+ (GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) &&
+ GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure)
+ return kCantOpenBadPressure;
+
+ return Neighborhood::canOpenDoor(entry);
+}
+
+void Norad::cantOpenDoor(tCanOpenDoorReason reason) {
+ if (reason == kCantOpenBadPressure)
+ playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
+ else
+ playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
+}
+
+void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
+ if (GameState.getCurrentRoom() == _elevatorUpRoomID) {
+ if (exitEntry.exitRoom != _elevatorDownRoomID)
+ newInteraction(kNoInteractionID);
+ } else if (GameState.getCurrentRoom() == _elevatorDownRoomID) {
+ if (exitEntry.exitRoom != _elevatorUpRoomID)
+ newInteraction(kNoInteractionID);
+ } else {
+ newInteraction(kNoInteractionID);
+ }
+
+ Neighborhood::startExitMovie(exitEntry);
+}
+
+void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
+ newInteraction(kNoInteractionID);
+ Neighborhood::startZoomMovie(zoomEntry);
+}
+
+void Norad::upButton(const Input &input) {
+ if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID)
+ Neighborhood::upButton(input);
+}
+
+void Norad::setUpAirMask() {
+ _airMaskCallBack.setNotification(&_neighborhoodNotification);
+ _airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes);
+ _airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag);
+ _neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag);
+ _airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _airMaskTimer.setScale(1);
+ _airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit);
+ checkAirMask();
+}
+
+void Norad::checkAirMask() {
+ if (g_airMask && g_airMask->isAirFilterOn()) {
+ _airMaskTimer.stop();
+ } else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) {
+ _airMaskTimer.setTime(0);
+ _airMaskTimer.start();
+ }
+
+ loadAmbientLoops();
+}
+
+void Norad::receiveNotification(Notification *notification, const tNotificationFlags flags) {
+ if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0)
+ ((PegasusEngine *)g_engine)->die(kDeathGassedInNorad);
+
+ Neighborhood::receiveNotification(notification, flags);
+
+ if (notification == &_noradNotification) {
+ // Must be kDoneWithPressureDoorNotification...
+ Input scratch;
+ _doneWithPressureDoor = true;
+ downButton(scratch);
+ }
+}
+
+uint16 Norad::getDateResID() const {
+ return kDate2112ID;
+}
+
+Common::String Norad::getBriefingMovie() {
+ return "Images/AI/Norad/XNO";
+}
+
+void Norad::pickedUpItem(Item *item) {
+ Neighborhood::pickedUpItem(item);
+ g_AIArea->checkMiddleArea();
+}
+
+void Norad::doneWithPressureDoor() {
+ _noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h
new file mode 100755
index 0000000000..4b4473cd44
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/norad.h
@@ -0,0 +1,485 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
+
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+const tCanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1;
+
+const tNotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1;
+
+const uint16 kNoradWarningVolume = 0x100 / 3;
+const uint16 kNoradSuckWindVolume = 0x100 / 2;
+
+const int16 kElevatorCompassAngle = -40;
+const int16 kSubPlatformCompassAngle = 45;
+const int16 kSubControlCompassAngle = -10;
+
+// Norad interactions.
+
+const tInteractionID kNoradGlobeGameInteractionID = 0;
+const tInteractionID kNoradECRMonitorInteractionID = 1;
+const tInteractionID kNoradFillingStationInteractionID = 2;
+const tInteractionID kNoradElevatorInteractionID = 3;
+const tInteractionID kNoradPressureDoorInteractionID = 4;
+const tInteractionID kNoradSubControlRoomInteractionID = 5;
+const tInteractionID kNoradSubPlatformInteractionID = 6;
+
+/////////////////////////////////////////////
+//
+// Norad Alpha
+
+const tCoordType kECRSlideShowLeft = kNavAreaLeft + 78;
+const tCoordType kECRSlideShowTop = kNavAreaTop + 1;
+
+const tCoordType kECRPanLeft = kNavAreaLeft + 78 + 5;
+const tCoordType kECRPanTop = kNavAreaTop + 1 + 4;
+const tCoordType kECRPanRight = kECRPanLeft + 213;
+const tCoordType kECRPanBottom = kECRPanTop + 241;
+
+const tCoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332;
+const tCoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127;
+
+const tCoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0;
+const tCoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0;
+
+const tCoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240;
+const tCoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12;
+
+const tCoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98;
+const tCoordType kNoradUpperLevelsTop = kNavAreaTop + 31;
+
+const tCoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114;
+const tCoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8;
+
+const tCoordType kNoradUpperUpLeft = kNavAreaLeft + 361;
+const tCoordType kNoradUpperUpTop = kNavAreaTop + 32;
+
+const tCoordType kNoradUpperDownLeft = kNavAreaLeft + 367;
+const tCoordType kNoradUpperDownTop = kNavAreaTop + 66;
+
+const tCoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74;
+const tCoordType kNoradLowerLevelsTop = kNavAreaTop + 157;
+
+const tCoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144;
+const tCoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9;
+
+const tCoordType kNoradLowerUpLeft = kNavAreaLeft + 380;
+const tCoordType kNoradLowerUpTop = kNavAreaTop + 164;
+
+const tCoordType kNoradLowerDownLeft = kNavAreaLeft + 388;
+const tCoordType kNoradLowerDownTop = kNavAreaTop + 212;
+
+const tCoordType kNoradPlatformLeft = kNavAreaLeft + 36;
+const tCoordType kNoradPlatformTop = kNavAreaTop + 87;
+
+const tCoordType kNoradSubControlLeft = kNavAreaLeft + 0;
+const tCoordType kNoradSubControlTop = kNavAreaTop + 84;
+
+const tCoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106;
+const tCoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86;
+
+const tCoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66;
+const tCoordType kNoradSubControlDownTop = kNoradSubControlTop + 106;
+
+const tCoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83;
+const tCoordType kNoradSubControlRightTop = kNoradSubControlTop + 90;
+
+const tCoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56;
+const tCoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91;
+
+const tCoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66;
+const tCoordType kNoradSubControlUpTop = kNoradSubControlTop + 81;
+
+const tCoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29;
+const tCoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88;
+
+const tCoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0;
+const tCoordType kNoradSubControlCWTop = kNoradSubControlTop + 89;
+
+const tCoordType kNoradClawMonitorLeft = kNavAreaLeft + 288;
+const tCoordType kNoradClawMonitorTop = kNavAreaTop + 97;
+
+const tCoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179;
+const tCoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82;
+
+const tCoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130;
+const tCoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73;
+
+const tCoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110;
+const tCoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26;
+
+const tCoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21;
+const tCoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49;
+
+/////////////////////////////////////////////
+//
+// Norad Delta
+
+const tCoordType kGlobeMonitorLeft = kNavAreaLeft + 360;
+const tCoordType kGlobeMonitorTop = kNavAreaTop + 144;
+
+const tCoordType kGlobeLeft = kNavAreaLeft + 172;
+const tCoordType kGlobeTop = kNavAreaTop;
+
+const tCoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186;
+const tCoordType kGlobeCircleLeftTop = kNavAreaTop + 41;
+
+const tCoordType kGlobeCircleRightLeft = kNavAreaLeft + 321;
+const tCoordType kGlobeCircleRightTop = kNavAreaTop + 41;
+
+const tCoordType kGlobeCircleUpLeft = kNavAreaLeft + 220;
+const tCoordType kGlobeCircleUpTop = kNavAreaTop + 7;
+
+const tCoordType kGlobeCircleDownLeft = kNavAreaLeft + 220;
+const tCoordType kGlobeCircleDownTop = kNavAreaTop + 142;
+
+const tCoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207;
+const tCoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28;
+
+const tCoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307;
+const tCoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28;
+
+const tCoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207;
+const tCoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128;
+
+const tCoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307;
+const tCoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128;
+
+const tCoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182;
+const tCoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60;
+
+const tCoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331;
+const tCoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60;
+
+const tCoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239;
+const tCoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3;
+
+const tCoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239;
+const tCoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152;
+
+const tCoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368;
+const tCoordType kGlobeUpperNamesTop = kNavAreaTop + 188;
+
+const tCoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368;
+const tCoordType kGlobeLowerNamesTop = kNavAreaTop + 212;
+
+const tCoordType kGlobeCountdownLeft = kNavAreaLeft + 478;
+const tCoordType kGlobeCountdownTop = kNavAreaTop + 164;
+
+// Norad Alpha display IDs.
+
+const tDisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID;
+const tDisplayElementID kECRPanID = kECRSlideShowMovieID + 1;
+const tDisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1;
+const tDisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1;
+const tDisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1;
+const tDisplayElementID kN01RightSideID = kN01LeftSideID + 1;
+const tDisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1;
+const tDisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1;
+const tDisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1;
+const tDisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1;
+const tDisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1;
+const tDisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1;
+const tDisplayElementID kClawMonitorID = kSubControlMonitorID + 1;
+const tDisplayElementID kSubControlPinchID = kClawMonitorID + 1;
+const tDisplayElementID kSubControlDownID = kSubControlPinchID + 1;
+const tDisplayElementID kSubControlRightID = kSubControlDownID + 1;
+const tDisplayElementID kSubControlLeftID = kSubControlRightID + 1;
+const tDisplayElementID kSubControlUpID = kSubControlLeftID + 1;
+const tDisplayElementID kSubControlCCWID = kSubControlUpID + 1;
+const tDisplayElementID kSubControlCWID = kSubControlCCWID + 1;
+const tDisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1;
+
+// Norad Delta display IDs.
+
+const tDisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID;
+const tDisplayElementID kGlobeMovieID = kGlobeMonitorID + 14;
+const tDisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1;
+const tDisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1;
+const tDisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1;
+const tDisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1;
+const tDisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1;
+const tDisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1;
+const tDisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1;
+const tDisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1;
+const tDisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1;
+const tDisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1;
+const tDisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1;
+const tDisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1;
+const tDisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1;
+const tDisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1;
+const tDisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1;
+
+// Norad Alpha:
+
+const tDisplayOrder kECRMonitorOrder = kMonitorLayer;
+const tDisplayOrder kECRPanOrder = kECRMonitorOrder + 1;
+
+const tDisplayOrder kN01LeftSideOrder = kMonitorLayer;
+const tDisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1;
+
+const tDisplayOrder kElevatorControlsOrder = kMonitorLayer;
+
+const tDisplayOrder kPressureLevelsOrder = kMonitorLayer;
+const tDisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1;
+const tDisplayOrder kPressureUpOrder = kPressureTypeOrder + 1;
+const tDisplayOrder kPressureDownOrder = kPressureUpOrder + 1;
+
+const tDisplayOrder kPlatformOrder = kMonitorLayer;
+
+const tDisplayOrder kSubControlOrder = kMonitorLayer;
+const tDisplayOrder kClawMonitorOrder = kSubControlOrder + 1;
+const tDisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1;
+const tDisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1;
+const tDisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1;
+const tDisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1;
+const tDisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1;
+const tDisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1;
+const tDisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1;
+const tDisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1;
+
+// Norad Delta:
+
+const tDisplayOrder kGlobeMonitorLayer = kMonitorLayer;
+const tDisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1;
+const tDisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1;
+const tDisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1;
+const tDisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1;
+const tDisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1;
+const tDisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1;
+
+
+// Norad Alpha Extra sequence IDs.
+
+const tExtraID kNoradArriveFromTSA = 0;
+const tExtraID kNorad01RobotTaunt = 1;
+const tExtraID kNorad01ZoomInWithGasCanister = 2;
+const tExtraID kN01WGasCanister = 3;
+const tExtraID kNorad01ZoomOutWithGasCanister = 4;
+const tExtraID kN01WZEmptyLit = 5;
+const tExtraID kN01WZGasCanisterDim = 6;
+const tExtraID kN01WZGasCanisterLit = 7;
+const tExtraID kN01WZArgonCanisterDim = 8;
+const tExtraID kN01WZArgonCanisterLit = 9;
+const tExtraID kN01WZAirMaskDim = 10;
+const tExtraID kN01WZAirMaskLit = 11;
+const tExtraID kN01WZNitrogenCanisterDim = 12;
+const tExtraID kN01WZNitrogenCanisterLit = 13;
+const tExtraID kNorad04EastDeath = 14;
+const tExtraID kNorad19PrepSub = 15;
+const tExtraID kNorad19ExitToSub = 16;
+const tExtraID kNorad22SouthIntro = 17;
+const tExtraID kNorad22SouthReply = 18;
+const tExtraID kNorad22SouthFinish = 19;
+const tExtraID kN22ClawFromAToB = 20;
+const tExtraID kN22ClawALoop = 21;
+const tExtraID kN22ClawAPinch = 22;
+const tExtraID kN22ClawACounterclockwise = 23;
+const tExtraID kN22ClawAClockwise = 24;
+const tExtraID kN22ClawFromBToA = 25;
+const tExtraID kN22ClawFromBToC = 26;
+const tExtraID kN22ClawFromBToD = 27;
+const tExtraID kN22ClawBLoop = 28;
+const tExtraID kN22ClawBPinch = 29;
+const tExtraID kN22ClawBCounterclockwise = 30;
+const tExtraID kN22ClawBClockwise = 31;
+const tExtraID kN22ClawFromCToB = 32;
+const tExtraID kN22ClawCLoop = 33;
+const tExtraID kN22ClawCPinch = 34;
+const tExtraID kN22ClawCCounterclockwise = 35;
+const tExtraID kN22ClawCClockwise = 36;
+const tExtraID kN22ClawFromDToB = 37;
+const tExtraID kN22ClawDLoop = 38;
+const tExtraID kN22ClawDPinch = 39;
+const tExtraID kN22ClawDCounterclockwise = 40;
+const tExtraID kN22ClawDClockwise = 41;
+
+// Norad Delta Extra sequence IDs.
+
+const tExtraID kArriveFromSubChase = 0;
+const tExtraID kN59ZoomWithRobot = 1;
+const tExtraID kN59RobotApproaches = 2;
+const tExtraID kN59RobotPunchLoop = 3;
+const tExtraID kN59PlayerWins1 = 4;
+const tExtraID kN59PlayerWins2 = 5;
+const tExtraID kN59RobotWins = 6;
+const tExtraID kN59RobotHeadOpens = 7;
+const tExtraID kN59Biochips111 = 8;
+const tExtraID kN59Biochips011 = 9;
+const tExtraID kN59Biochips101 = 10;
+const tExtraID kN59Biochips001 = 11;
+const tExtraID kN59Biochips110 = 12;
+const tExtraID kN59Biochips010 = 13;
+const tExtraID kN59Biochips100 = 14;
+const tExtraID kN59Biochips000 = 15;
+const tExtraID kN59RobotDisappears = 16;
+const tExtraID kN60ClawFromAToB = 17;
+const tExtraID kN60ClawALoop = 18;
+const tExtraID kN60ClawAPinch = 19;
+const tExtraID kN60ClawACounterclockwise = 20;
+const tExtraID kN60ClawAClockwise = 21;
+const tExtraID kN60ClawFromBToA = 22;
+const tExtraID kN60ClawFromBToC = 23;
+const tExtraID kN60ClawFromBToD = 24;
+const tExtraID kN60ClawBLoop = 25;
+const tExtraID kN60ClawBPinch = 26;
+const tExtraID kN60ClawBCounterclockwise = 27;
+const tExtraID kN60ClawBClockwise = 28;
+const tExtraID kN60ClawFromCToB = 29;
+const tExtraID kN60ClawCLoop = 30;
+const tExtraID kN60ClawCPinch = 31;
+const tExtraID kN60ClawCCounterclockwise = 32;
+const tExtraID kN60ClawCClockwise = 33;
+const tExtraID kN60ClawFromDToB = 34;
+const tExtraID kN60ClawDLoop = 35;
+const tExtraID kN60ClawDPinch = 36;
+const tExtraID kN60ClawDCounterclockwise = 37;
+const tExtraID kN60ClawDClockwise = 38;
+const tExtraID kN60RobotApproaches = 39;
+const tExtraID kN60FirstMistake = 40;
+const tExtraID kN60ArmActivated = 41;
+const tExtraID kN60SecondMistake = 42;
+const tExtraID kN60ArmToPositionB = 43;
+const tExtraID kN60ThirdMistake = 44;
+const tExtraID kN60ArmGrabsRobot = 45;
+const tExtraID kN60FourthMistake = 46;
+const tExtraID kN60ArmCarriesRobotToPositionA = 47;
+const tExtraID kN60PlayerFollowsRobotToDoor = 48;
+const tExtraID kN60RobotHeadOpens = 49;
+const tExtraID kN60Biochips111 = 50;
+const tExtraID kN60Biochips011 = 51;
+const tExtraID kN60Biochips101 = 52;
+const tExtraID kN60Biochips001 = 53;
+const tExtraID kN60Biochips110 = 54;
+const tExtraID kN60Biochips010 = 55;
+const tExtraID kN60Biochips100 = 56;
+const tExtraID kN60Biochips000 = 57;
+const tExtraID kN60RobotDisappears = 58;
+const tExtraID kNoradDeltaRetinalScanBad = 59;
+const tExtraID kNoradDeltaRetinalScanGood = 60;
+const tExtraID kN79BrightView = 61;
+
+const TimeScale kNoradAlphaMovieScale = 600;
+const TimeScale kNoradAlphaFramesPerSecond = 15;
+const TimeScale kNoradAlphaFrameDuration = 40;
+
+const tRoomID kNorad59West = 23;
+const tRoomID kNorad60West = 25;
+const tHotSpotID kNorad19ActivateMonitorSpotID = 5031;
+
+// This is the code common to both Norad Alpha and Norad Delta
+
+class Norad : public Neighborhood {
+public:
+ Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const tNeighborhoodID);
+ virtual ~Norad() {}
+
+ void flushGameState();
+
+ virtual void start();
+
+ virtual void getClawInfo(tHotSpotID &outSpotID, tHotSpotID &prepSpotID,
+ tHotSpotID &clawControlSpotID, tHotSpotID &pinchClawSpotID,
+ tHotSpotID &moveClawDownSpotID, tHotSpotID &moveClawRightSpotID,
+ tHotSpotID &moveClawLeftSpotID,tHotSpotID &moveClawUpSpotID,
+ tHotSpotID &clawCCWSpotID, tHotSpotID &clawCWSpotID, uint32 &, uint32 *&) = 0;
+ void checkAirMask();
+
+ virtual uint16 getDateResID() const;
+
+ virtual GameInteraction *makeInteraction(const tInteractionID);
+
+ Common::String getBriefingMovie();
+
+ void pickedUpItem(Item *);
+
+ virtual void playClawMonitorIntro() {}
+
+ void doneWithPressureDoor();
+
+protected:
+ tCanOpenDoorReason canOpenDoor(DoorTable::Entry &);
+ void cantOpenDoor(tCanOpenDoorReason);
+ int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant);
+ virtual void startExitMovie(const ExitTable::Entry &);
+ void startZoomMovie(const ZoomTable::Entry &);
+ virtual void upButton(const Input &);
+ virtual void activateHotspots();
+
+ virtual void arriveAt(const tRoomID, const tDirectionConstant);
+ virtual void arriveAtNoradElevator();
+ virtual void arriveAtUpperPressureDoorRoom();
+ virtual void arriveAtLowerPressureDoorRoom();
+ virtual void arriveAtSubPlatformRoom();
+ virtual void arriveAtSubControlRoom();
+ void setUpAirMask();
+ virtual void receiveNotification(Notification *, const tNotificationFlags);
+ virtual bool playingAgainstRobot() { return false; }
+
+ Notification _noradNotification;
+ bool _doneWithPressureDoor;
+
+ tRoomID _elevatorUpRoomID;
+ tRoomID _elevatorDownRoomID;
+ tHotSpotID _elevatorUpSpotID;
+ tHotSpotID _elevatorDownSpotID;
+
+ TimeBase _airMaskTimer;
+ NotificationCallBack _airMaskCallBack;
+
+ tRoomID _subRoomEntryRoom1;
+ tDirectionConstant _subRoomEntryDir1;
+ tRoomID _subRoomEntryRoom2;
+ tDirectionConstant _subRoomEntryDir2;
+ tRoomID _upperPressureDoorRoom;
+ tRoomID _lowerPressureDoorRoom;
+
+ tHotSpotID _upperPressureDoorUpSpotID;
+ tHotSpotID _upperPressureDoorDownSpotID;
+ tHotSpotID _upperPressureDoorAbortSpotID;
+
+ tHotSpotID _lowerPressureDoorUpSpotID;
+ tHotSpotID _lowerPressureDoorDownSpotID;
+ tHotSpotID _lowerPressureDoorAbortSpotID;
+
+ TimeValue _pressureSoundIn;
+ TimeValue _pressureSoundOut;
+ TimeValue _equalizeSoundIn;
+ TimeValue _equalizeSoundOut;
+ TimeValue _accessDeniedIn;
+ TimeValue _accessDeniedOut;
+
+ tRoomID _platformRoom;
+ tRoomID _subControlRoom;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/noradelevator.cpp b/engines/pegasus/neighborhood/norad/noradelevator.cpp
new file mode 100755
index 0000000000..98b3d2949e
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/noradelevator.cpp
@@ -0,0 +1,130 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/noradelevator.h"
+
+namespace Pegasus {
+
+// Norad elevator PICTs:
+
+const tResIDType kElevatorLabelID = 200;
+const tResIDType kElevatorButtonsID = 201;
+const tResIDType kElevatorDownOnID = 202;
+const tResIDType kElevatorUpOnID = 203;
+
+NoradElevator::NoradElevator(Neighborhood *handler, const tRoomID upRoom, const tRoomID downRoom,
+ const tHotSpotID upHotspot, const tHotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler),
+ _elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, ((PegasusEngine *)g_engine)) {
+ _timerExpired = false;
+ _upRoom = upRoom;
+ _downRoom = downRoom;
+ _upHotspot = upHotspot;
+ _downHotspot = downHotspot;
+}
+
+void NoradElevator::openInteraction() {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorLabelID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorButtonsID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorDownOnID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorUpOnID, true);
+ _elevatorControls.addFrame(frame, 0, 0);
+
+ _elevatorControls.setCurrentFrameIndex(0);
+ _elevatorControls.setDisplayOrder(kElevatorControlsOrder);
+
+ Common::Rect r;
+ frame->getSurfaceBounds(r);
+ r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop);
+
+ _elevatorControls.setBounds(r);
+ _elevatorControls.startDisplaying();
+ _elevatorControls.show();
+}
+
+void NoradElevator::initInteraction() {
+ _elevatorTimer.setScale(2);
+ _elevatorTimer.setSegment(0, 1);
+ _elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes);
+ _elevatorCallBack.setCallBackFlag(1);
+ _elevatorCallBack.setNotification(&_elevatorNotification);
+ _elevatorNotification.notifyMe(this, 1, 1);
+ _elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _elevatorTimer.start();
+}
+
+void NoradElevator::closeInteraction() {
+ _elevatorControls.stopDisplaying();
+ _elevatorControls.discardFrames();
+ _elevatorCallBack.releaseCallBack();
+}
+
+void NoradElevator::resetInteraction() {
+ _elevatorControls.setCurrentFrameIndex(1);
+}
+
+void NoradElevator::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ if (_timerExpired) {
+ if (GameState.getCurrentRoom() == _upRoom)
+ g_allHotspots.activateOneHotspot(_downHotspot);
+ else if (GameState.getCurrentRoom() == _downRoom)
+ g_allHotspots.activateOneHotspot(_upHotspot);
+ }
+}
+
+void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) {
+ tHotSpotID id = spot->getObjectID();
+
+ if (id == _upHotspot || id == _downHotspot) {
+ g_neighborhood->moveForward();
+ if (id == _downHotspot)
+ _elevatorControls.setCurrentFrameIndex(2);
+ else
+ _elevatorControls.setCurrentFrameIndex(3);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void NoradElevator::receiveNotification(Notification *, const tNotificationFlags) {
+ _elevatorControls.setCurrentFrameIndex(1);
+ _timerExpired = true;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/noradelevator.h b/engines/pegasus/neighborhood/norad/noradelevator.h
new file mode 100755
index 0000000000..58dc8abf39
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/noradelevator.h
@@ -0,0 +1,67 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+#include "pegasus/surface.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class Neighborhood;
+
+class NoradElevator : public GameInteraction, private NotificationReceiver {
+public:
+ NoradElevator(Neighborhood *, const tRoomID, const tRoomID, const tHotSpotID, const tHotSpotID);
+ virtual ~NoradElevator() {}
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+ virtual void resetInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification*, const tNotificationFlags);
+
+ tRoomID _upRoom;
+ tRoomID _downRoom;
+ tHotSpotID _upHotspot;
+ tHotSpotID _downHotspot;
+ Sprite _elevatorControls;
+ TimeBase _elevatorTimer;
+ NotificationCallBack _elevatorCallBack;
+ Notification _elevatorNotification;
+ bool _timerExpired;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp
new file mode 100755
index 0000000000..066914c6db
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp
@@ -0,0 +1,543 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+
+namespace Pegasus {
+
+const TimeValue kLevelsSplashStart = 0;
+const TimeValue kLevelsSplashStop = 1;
+const TimeValue kPressureBase = 1;
+
+const TimeValue kDoorSealedTime = 0;
+const TimeValue kEqualizeTime = 1;
+const TimeValue kMaxPressureLoopStart = 2;
+const TimeValue kMaxPressureLoopStop = 3;
+const TimeValue kOpeningDoorLoopStart = 3;
+const TimeValue kOpeningDoorLoopStop = 4;
+const TimeValue kIncreasingPressureTime = 4;
+const TimeValue kDecreasingPressureTime = 5;
+const TimeValue kCautionLoopStart = 6;
+const TimeValue kCautionLoopStop = 7;
+
+const tNotificationFlags kSplashFinished = 1;
+const tNotificationFlags kPressureDroppingFlag = kSplashFinished << 1;
+
+const tNotificationFlags kPressureNotificationFlags = kSplashFinished |
+ kPressureDroppingFlag;
+
+const tNotificationFlags kDoorJumpsUpFlag = 1;
+const tNotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1;
+const tNotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1;
+
+const tNotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag |
+ kDoorJumpsBackFlag |
+ kDoorCrushedFlag;
+
+enum {
+ kPlayingRobotApproaching,
+ kRobotPunching,
+ kRobotComingThrough,
+ kRobotDying,
+ kRobotDead
+};
+
+const short kMaxPunches = 5;
+
+enum {
+ kPlayingSplash,
+ kPlayingPressureMessage,
+ kPlayingEqualizeMessage,
+ kWaitingForPlayer,
+ kPlayingDoneMessage,
+ kGameOver
+};
+
+// Pressure values range from 0 to 11.
+const short kMinPressure = 0;
+const short kMaxPressure = 11;
+
+const TimeScale kNavTimeScale = 600;
+const TimeValue kNavFrameRate = 15;
+const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate;
+
+const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame;
+const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame;
+const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame;
+
+// Pressure door PICTs:
+
+const tResIDType kUpperPressureUpOffPICTID = 400;
+const tResIDType kUpperPressureUpOnPICTID = 401;
+const tResIDType kUpperPressureDownOffPICTID = 402;
+const tResIDType kUpperPressureDownOnPICTID = 403;
+
+const tResIDType kLowerPressureUpOffPICTID = 404;
+const tResIDType kLowerPressureUpOnPICTID = 405;
+const tResIDType kLowerPressureDownOffPICTID = 406;
+const tResIDType kLowerPressureDownOnPICTID = 407;
+
+PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const tHotSpotID upSpotID, const tHotSpotID downSpotID,
+ const tHotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn,
+ TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler),
+ _levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID),
+ _downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, ((PegasusEngine *)g_engine)),
+ _doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, ((PegasusEngine *)g_engine)) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+ _upHotspotID = upSpotID;
+ _downHotspotID = downSpotID;
+ _outHotspotID = outSpotID;
+ _pressureSoundIn = pressureSoundIn;
+ _pressureSoundOut = pressureSoundOut;
+ _equalizeSoundIn = equalizeSoundIn;
+ _equalizeSoundOut = equalizeSoundOut;
+ _playingAgainstRobot = false;
+ _isUpperDoor = isUpperDoor;
+}
+
+void PressureDoor::openInteraction() {
+ if (_isUpperDoor) {
+ _levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie");
+ _levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop);
+ } else {
+ _levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie");
+ _levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop);
+ }
+
+ _levelsScale = _levelsMovie.getScale();
+ _levelsMovie.setDisplayOrder(kPressureLevelsOrder);
+ _levelsMovie.startDisplaying();
+ _levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale);
+ _levelsMovie.setTime(kLevelsSplashStart * _levelsScale);
+ _levelsMovie.show();
+
+ _pressureCallBack.setNotification(&_pressureNotification);
+ _pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes);
+ _pressureCallBack.setCallBackFlag(kSplashFinished);
+ _pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags);
+
+ if (_isUpperDoor) {
+ _typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie");
+ _typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop);
+ } else {
+ _typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie");
+ _typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop);
+ }
+
+ _typeScale = _typeMovie.getScale();
+ _typeMovie.setDisplayOrder(kPressureTypeOrder);
+ _typeMovie.startDisplaying();
+ _typeMovie.setTime(kDoorSealedTime * _typeScale);
+
+ SpriteFrame *frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID);
+ _upButton.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID);
+ _upButton.addFrame(frame, 0, 0);
+
+ _upButton.setCurrentFrameIndex(0);
+ _upButton.setDisplayOrder(kPressureUpOrder);
+
+ Common::Rect r;
+ frame->getSurfaceBounds(r);
+ if (_isUpperDoor)
+ r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop);
+ else
+ r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop);
+
+ _upButton.setBounds(r);
+ _upButton.startDisplaying();
+ _upButton.show();
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID);
+ _downButton.addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ if (_isUpperDoor)
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID);
+ else
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID);
+ _downButton.addFrame(frame, 0, 0);
+
+ _downButton.setCurrentFrameIndex(0);
+ _downButton.setDisplayOrder(kPressureDownOrder);
+
+ frame->getSurfaceBounds(r);
+ if (_isUpperDoor)
+ r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop);
+ else
+ r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop);
+
+ _downButton.setBounds(r);
+ _downButton.startDisplaying();
+ _downButton.show();
+
+ _utilityCallBack.setNotification(&_utilityNotification);
+ _utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime);
+ _utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags);
+ _utilityTimer.setMasterTimeBase(getOwner()->getNavMovie());
+
+ if (_playingAgainstRobot)
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag |
+ kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag);
+ else
+ _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag,
+ kDelayCompletedFlag | kSpotSoundCompletedFlag);
+
+ _gameState = kPlayingSplash;
+}
+
+void PressureDoor::initInteraction() {
+ _levelsMovie.start();
+
+ if (_playingAgainstRobot) {
+ ExtraTable::Entry entry;
+ _owner->getExtraEntry(kN59RobotApproaches, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kApproachPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ _utilityTimer.setTime(entry.movieStart);
+ _owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput);
+ _utilityTimer.start();
+ _robotState = kPlayingRobotApproaching;
+ }
+
+ // TODO: MoviesTask call -- needed?
+}
+
+void PressureDoor::closeInteraction() {
+ _pressureNotification.cancelNotification(this);
+ _pressureCallBack.releaseCallBack();
+ _utilityNotification.cancelNotification(this);
+ _utilityCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void PressureDoor::playAgainstRobot() {
+ _playingAgainstRobot = true;
+}
+
+void PressureDoor::receiveNotification(Notification *notification, const tNotificationFlags flags) {
+ Neighborhood *owner = getOwner();
+
+ if (notification == _neighborhoodNotification) {
+ if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) {
+ ExtraTable::Entry entry;
+
+ switch (_robotState) {
+ case kPlayingRobotApproaching:
+ _utilityTimer.stop();
+ if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
+ owner->getExtraEntry(kN59PlayerWins1, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kLoopPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
+ _utilityTimer.start();
+ _robotState = kRobotDying;
+ } else {
+ owner->getExtraEntry(kN59RobotPunchLoop, entry);
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _punchInTime = kLoopPunchInTime + entry.movieStart;
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag);
+ _utilityTimer.start();
+ _robotState = kRobotPunching;
+ _punchCount = 1;
+ }
+ break;
+ case kRobotPunching:
+ if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
+ owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
+ _robotState = kRobotDying;
+ } else if (++_punchCount >= kMaxPunches) {
+ _robotState = kRobotComingThrough;
+ owner->getExtraEntry(kN59RobotWins, entry);
+ _utilityTimer.stop();
+ _utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
+ _utilityTimer.setTime(entry.movieStart);
+ _utilityCallBack.cancelCallBack();
+ _utilityCallBack.setCallBackFlag(kDoorCrushedFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale);
+ owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput);
+ _utilityTimer.start();
+ } else {
+ _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
+ owner->scheduleNavCallBack(kExtraCompletedFlag);
+ }
+ break;
+ case kRobotComingThrough:
+ g_system->delayMillis(2 * 1000);
+ ((PegasusEngine *)g_engine)->die(kDeathRobotThroughNoradDoor);
+ break;
+ case kRobotDying:
+ _robotState = kRobotDead;
+ _levelsMovie.stop();
+ _levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale,
+ (GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _pressureCallBack.setCallBackFlag(kPressureDroppingFlag);
+ _pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+ _typeMovie.stop();
+ _typeMovie.setSegment(0, _typeMovie.getDuration());
+ _typeMovie.setTime(kDecreasingPressureTime * _typeScale);
+ _typeMovie.show();
+ _downButton.show();
+ _downButton.setCurrentFrameIndex(1);
+ _gameState = kGameOver;
+ allowInput(false);
+ _levelsMovie.setRate(Common::Rational(0x5555, 0x10000) - 1); // Should match door tracker.
+ break;
+ case kRobotDead:
+ allowInput(true);
+ // TODO
+ //((NoradDelta *)owner)->playerBeatRobotWithDoor();
+ owner->requestDeleteCurrentInteraction();
+ break;
+ }
+ }
+
+ if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) {
+ switch (_gameState) {
+ case kPlayingPressureMessage:
+ _typeMovie.setTime(kEqualizeTime * _typeScale);
+ owner->requestDelay(1, 5, kFilterNoInput, 0);
+ owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0);
+ owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingEqualizeMessage;
+ break;
+ case kPlayingEqualizeMessage:
+ _gameState = kWaitingForPlayer;
+ stopChangingPressure();
+ break;
+ case kPlayingDoneMessage:
+ _gameState = kWaitingForPlayer;
+ _typeMovie.stop();
+ _typeMovie.setFlags(0);
+ _typeMovie.hide();
+ if (!_playingAgainstRobot)
+ ((Norad *)_owner)->doneWithPressureDoor();
+ break;
+ }
+ }
+ } else if (notification == &_pressureNotification) {
+ switch (flags) {
+ case kSplashFinished:
+ _levelsMovie.stop();
+ _levelsMovie.setSegment(0, _levelsMovie.getDuration());
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+
+ if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) {
+ _typeMovie.show();
+ owner->requestDelay(1, 5, kFilterNoInput, 0);
+ owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0);
+ owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingPressureMessage;
+ } else {
+ _gameState = kWaitingForPlayer;
+ }
+ break;
+ case kPressureDroppingFlag:
+ _levelsMovie.stop();
+ _levelsMovie.hide();
+ _typeMovie.stop();
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ }
+ } else if (notification == &_utilityNotification) {
+ switch (flags) {
+ case kDoorJumpsUpFlag:
+ _utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag);
+ _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale);
+ _levelsMovie.hide();
+ _typePunched = _typeMovie.isVisible();
+ if (_typePunched == true)
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ break;
+ case kDoorJumpsBackFlag:
+ _levelsMovie.show();
+ _upButton.show();
+ _downButton.show();
+ if (_typePunched)
+ _typeMovie.show();
+ break;
+ case kDoorCrushedFlag:
+ _levelsMovie.hide();
+ _typeMovie.hide();
+ _upButton.hide();
+ _downButton.hide();
+ break;
+ }
+ }
+}
+
+void PressureDoor::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kWaitingForPlayer:
+ g_allHotspots.activateOneHotspot(_upHotspotID);
+ g_allHotspots.activateOneHotspot(_downHotspotID);
+ if (!_playingAgainstRobot)
+ g_allHotspots.activateOneHotspot(_outHotspotID);
+ break;
+ default:
+ break;
+ }
+}
+
+void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) {
+ tHotSpotID id = spot->getObjectID();
+
+ if (id == _upHotspotID || id == _downHotspotID) {
+ if (id == _upHotspotID)
+ _doorTracker.setTrackParameters(spot, &_upButton);
+ else
+ _doorTracker.setTrackParameters(spot, &_downButton);
+
+ _doorTracker.startTracking(input);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void PressureDoor::incrementPressure(const tHotSpotID id) {
+ _typeMovie.stop();
+ _typeMovie.setSegment(0, _typeMovie.getDuration());
+ _typeMovie.setFlags(0);
+
+ if (id == _upHotspotID) {
+ if (GameState.getNoradSubRoomPressure() < kMaxPressure) {
+ GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1);
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _typeMovie.setTime(kIncreasingPressureTime * _typeScale);
+ _typeMovie.show();
+ g_AIArea->checkMiddleArea();
+ } else {
+ _typeMovie.hide();
+ }
+ } else if (id == _downHotspotID) {
+ if (GameState.getNoradSubRoomPressure() > kMinPressure) {
+ GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1);
+ _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
+ _typeMovie.setTime(kDecreasingPressureTime * _typeScale);
+ _typeMovie.show();
+ g_AIArea->checkMiddleArea();
+ } else {
+ _typeMovie.hide();
+ }
+ }
+}
+
+void PressureDoor::stopChangingPressure() {
+ Neighborhood *owner;
+
+ switch (GameState.getNoradSubRoomPressure()) {
+ case 11:
+ _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ break;
+ case 10:
+ _typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ break;
+ case kNormalSubRoomPressure:
+ owner = getOwner();
+ _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _gameState = kPlayingDoneMessage;
+ owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _typeMovie.start();
+ break;
+ default:
+ _typeMovie.hide();
+ break;
+ }
+}
+
+bool PressureDoor::canSolve() {
+ if (_playingAgainstRobot)
+ return GameState.getNoradSubRoomPressure() < 11;
+
+ return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure;
+}
+
+void PressureDoor::doSolve() {
+ if (_playingAgainstRobot) {
+ GameState.setNoradSubRoomPressure(11);
+ _levelsMovie.setTime((11 + kPressureBase) * _levelsScale);
+ _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ _typeMovie.start();
+ g_AIArea->checkMiddleArea();
+ } else {
+ GameState.setNoradSubRoomPressure(kNormalSubRoomPressure);
+ _levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale);
+ _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
+ _typeMovie.setFlags(kLoopTimeBase);
+ _typeMovie.show();
+ Neighborhood *owner = getOwner();
+ owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingDoneMessage;
+ _typeMovie.start();
+ g_AIArea->checkMiddleArea();
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.h b/engines/pegasus/neighborhood/norad/pressuredoor.h
new file mode 100755
index 0000000000..94868bdd8c
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuredoor.h
@@ -0,0 +1,93 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/neighborhood/norad/pressuretracker.h"
+
+namespace Pegasus {
+
+const short kNormalSubRoomPressure = 2;
+
+class PressureDoor : public GameInteraction, public NotificationReceiver {
+public:
+ PressureDoor(Neighborhood *, bool isUpperDoor, const tHotSpotID, const tHotSpotID,
+ const tHotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut,
+ TimeValue equalizeSoundIn, TimeValue equalizeSoundOut);
+ virtual ~PressureDoor() {}
+
+ void incrementPressure(const tHotSpotID);
+ void stopChangingPressure();
+
+ void playAgainstRobot();
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const tNotificationFlags);
+
+ Movie _levelsMovie;
+ TimeScale _levelsScale;
+ Movie _typeMovie;
+ TimeScale _typeScale;
+ Sprite _upButton;
+ Sprite _downButton;
+ Notification _pressureNotification;
+ NotificationCallBack _pressureCallBack;
+ Notification *_neighborhoodNotification;
+ int _gameState;
+ tHotSpotID _upHotspotID;
+ tHotSpotID _downHotspotID;
+ tHotSpotID _outHotspotID;
+ PressureTracker _doorTracker;
+ TimeValue _pressureSoundIn;
+ TimeValue _pressureSoundOut;
+ TimeValue _equalizeSoundIn;
+ TimeValue _equalizeSoundOut;
+ bool _isUpperDoor;
+
+ bool _playingAgainstRobot, _typePunched;
+ int _robotState, _punchCount;
+ TimeBase _utilityTimer;
+ Notification _utilityNotification;
+ NotificationCallBack _utilityCallBack;
+ TimeValue _punchInTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.cpp b/engines/pegasus/neighborhood/norad/pressuretracker.cpp
new file mode 100755
index 0000000000..60521a4a98
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuretracker.cpp
@@ -0,0 +1,86 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/hotspot.h"
+#include "pegasus/neighborhood/norad/pressuredoor.h"
+#include "pegasus/neighborhood/norad/pressuretracker.h"
+
+namespace Pegasus {
+
+PressureTracker::PressureTracker(PressureDoor *pressureDoor) {
+ _pressureDoor = pressureDoor;
+ _trackSpot = 0;
+ _trackTime = 0;
+}
+
+void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) {
+ _trackSpot = trackSpot;
+ _trackButton = trackButton;
+ _trackTime = 0;
+}
+
+void PressureTracker::activateHotspots() {
+ Tracker::activateHotspots();
+
+ if (_trackSpot)
+ g_allHotspots.activateOneHotspot(_trackSpot->getObjectID());
+}
+
+// For click-hold dragging.
+bool PressureTracker::stopTrackingInput(const Input &input) {
+ return !JMPPPInput::isPressingInput(input);
+}
+
+void PressureTracker::continueTracking(const Input &input) {
+ Common::Point where;
+ input.getInputLocation(where);
+
+ if (g_allHotspots.findHotspot(where) == _trackSpot) {
+ trackPressure();
+ _trackButton->setCurrentFrameIndex(1);
+ } else {
+ _trackButton->setCurrentFrameIndex(0);
+ }
+}
+
+void PressureTracker::startTracking(const Input &input) {
+ Tracker::startTracking(input);
+ trackPressure();
+}
+
+void PressureTracker::stopTracking(const Input &input) {
+ _trackButton->setCurrentFrameIndex(0);
+ _pressureDoor->stopChangingPressure();
+ Tracker::stopTracking(input);
+}
+
+void PressureTracker::trackPressure() {
+ if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) {
+ _pressureDoor->incrementPressure(_trackSpot->getObjectID());
+ _trackTime = g_system->getMillis();
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.h b/engines/pegasus/neighborhood/norad/pressuretracker.h
new file mode 100755
index 0000000000..39ce1c1a8c
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/pressuretracker.h
@@ -0,0 +1,69 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
+
+#include "pegasus/input.h"
+
+namespace Pegasus {
+
+// This class assumes that the globe movie is built at 15 frames per second with a
+// time scale of 600, yielding 40 time unit per frame.
+
+typedef enum {
+ kTrackPressureUp,
+ kTrackPressureDown
+} tPressureTrackDirection;
+
+const int kPressureDoorTrackInterval = 45;
+
+class PressureDoor;
+class Sprite;
+
+class PressureTracker : public Tracker {
+public:
+ PressureTracker(PressureDoor *);
+ virtual ~PressureTracker() {}
+
+ void setTrackParameters(const Hotspot *, Sprite *);
+ void continueTracking(const Input &);
+ void startTracking(const Input &);
+ void stopTracking(const Input &);
+ void activateHotspots();
+ bool stopTrackingInput(const Input &);
+
+protected:
+ void trackPressure();
+
+ PressureDoor *_pressureDoor;
+ const Hotspot *_trackSpot;
+ Sprite *_trackButton;
+ long _trackTime;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
new file mode 100755
index 0000000000..7a01d32211
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
@@ -0,0 +1,1174 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+
+namespace Pegasus {
+
+// Right Monitor times
+
+const TimeValue kAlphaClawSplashStart = 0;
+const TimeValue kAlphaClawSplashStop = 4000;
+
+const TimeValue kDeltaClawSplashStart = 4000;
+const TimeValue kDeltaClawSplashStop = 8000;
+
+const TimeValue kClawAtATime = 8000;
+const TimeValue kClawAtAPinchedTime = 8600;
+const TimeValue kClawAtATurnedTime = 9200;
+const TimeValue kClawAtAWithRobotPinchedTime = 9800;
+
+const TimeValue kClawAtBTime = 10400;
+const TimeValue kClawAtBPinchedTime = 11000;
+const TimeValue kClawAtBTurnedTime = 11600;
+const TimeValue kClawAtBWithRobotTime = 12200;
+const TimeValue kClawAtBWithRobotPinchedTime = 12800;
+
+const TimeValue kClawAtCTime = 13400;
+const TimeValue kClawAtCPinchedTime = 14000;
+const TimeValue kClawAtCTurnedTime = 14600;
+
+const TimeValue kClawAtDTime = 15200;
+const TimeValue kClawAtDPinchedTime = 15800;
+const TimeValue kClawAtDTurnedTime = 16400;
+
+const TimeValue kAToBStart = 17000;
+const TimeValue kAToBStop = 18680;
+const TimeValue kAPinchStart = 18680;
+const TimeValue kAPinchStop = 20200;
+const TimeValue kACCWStart = 20200;
+const TimeValue kACCWStop = 21600;
+const TimeValue kACWStart = 21600;
+const TimeValue kACWStop = 23000;
+
+const TimeValue kBToAStart = 23000;
+const TimeValue kBToAStop = 24680;
+const TimeValue kBToCStart = 24680;
+const TimeValue kBToCStop = 26520;
+const TimeValue kBToDStart = 26520;
+const TimeValue kBToDStop = 28320;
+const TimeValue kBPinchStart = 28320;
+const TimeValue kBPinchStop = 29680;
+const TimeValue kBCCWStart = 29680;
+const TimeValue kBCCWStop = 31200;
+const TimeValue kBCWStart = 31200;
+const TimeValue kBCWStop = 32720;
+
+const TimeValue kCToBStart = 32720;
+const TimeValue kCToBStop = 34560;
+const TimeValue kCPinchStart = 34560;
+const TimeValue kCPinchStop = 36400;
+const TimeValue kCCCWStart = 36400;
+const TimeValue kCCCWStop = 37840;
+const TimeValue kCCWStart = 37840;
+const TimeValue kCCWStop = 39280;
+
+const TimeValue kDToBStart = 39280;
+const TimeValue kDToBStop = 41080;
+const TimeValue kDPinchStart = 41080;
+const TimeValue kDPinchStop = 42600;
+const TimeValue kDCCWStart = 42600;
+const TimeValue kDCCWStop = 44000;
+const TimeValue kDCWStart = 44000;
+const TimeValue kDCWStop = 45400;
+
+const TimeValue kRobotApproachStart = 45400;
+const TimeValue kRobotApproachStop = 56800;
+
+const TimeValue kCToBWithRobotStart = 56800;
+const TimeValue kCToBWithRobotStop = 58600;
+
+const TimeValue kBPinchWithRobotStart = 58600;
+const TimeValue kBPinchWithRobotStop = 60400;
+const TimeValue kBToAWithRobotStart = 60400;
+const TimeValue kBToAWithRobotStop = 62240;
+
+// As usual, times here are in seconds.
+
+// Left monitor times.
+
+const TimeValue kAlphaSplashStart = 0;
+const TimeValue kAlphaSplashStop = 2;
+
+const TimeValue kMainMenuTime = 2;
+const TimeValue kLaunchPrepRolloverTime = 3;
+const TimeValue kLaunchPrepHighlightStart = 4;
+const TimeValue kLaunchPrepHighlightStop = 5;
+const TimeValue kClawControlRolloverTime = 5;
+const TimeValue kClawControlHighlightStart = 6;
+const TimeValue kClawControlHighlightStop = 7;
+
+const TimeValue kAlphaLaunchPrepStart = 7;
+const TimeValue kAlphaLaunchPrepStop = 17;
+
+const TimeValue kClawMenuStart = 17;
+const TimeValue kClawMenuStop = 18;
+
+const TimeValue kClawMenuTime = 18;
+
+const TimeValue kDeltaSplashStart = 19;
+const TimeValue kDeltaSplashStop = 21;
+
+const TimeValue kDeltaLaunchPrepStart = 21;
+const TimeValue kDeltaLaunchPrepStop = 30;
+
+// Right monitor times.
+
+const tNotificationFlags kAlphaSplashFinished = 1;
+const tNotificationFlags kAlphaPrepFinished = kAlphaSplashFinished << 1;
+const tNotificationFlags kPrepHighlightFinished = kAlphaPrepFinished << 1;
+const tNotificationFlags kClawHighlightFinished = kPrepHighlightFinished << 1;
+const tNotificationFlags kClawMenuFinished = kClawHighlightFinished << 1;
+const tNotificationFlags kDeltaSplashFinished = kClawMenuFinished << 1;
+const tNotificationFlags kDeltaPrepFinished = kDeltaSplashFinished << 1;
+
+const tNotificationFlags kSubControlNotificationFlags = kAlphaSplashFinished |
+ kAlphaPrepFinished |
+ kPrepHighlightFinished |
+ kClawHighlightFinished |
+ kClawMenuFinished |
+ kDeltaSplashFinished |
+ kDeltaPrepFinished;
+
+const tNotificationFlags kOneSecondOfMoveFinished = 1;
+
+const tNotificationFlags kGreenBallNotificationFlags = kOneSecondOfMoveFinished;
+
+enum {
+ kButtonDimFrame,
+ kButtonActiveFrame,
+ kButtonHighlightedFrame
+};
+
+enum {
+ kAlphaSplash,
+ kAlphaMainMenu,
+ kDeltaSplash,
+ kDeltaMainMenu,
+ kClawMenu,
+ kPlayingHighlight,
+ kPuttingClawAway
+};
+
+// The owning neighborhood must provide an array of longs which hold the various
+// extra IDs for moving the claw around. In addition, the owner must tell the sub
+// control room interaction what position the claw starts out in (which is also the
+// position the claw must be in before leaving).
+
+// Standard array indices:
+enum {
+ kClawFromAToBIndex,
+ kClawALoopIndex,
+ kClawAPinchIndex,
+ kClawACounterclockwiseIndex,
+ kClawAClockwiseIndex,
+ kClawFromBToAIndex,
+ kClawFromBToCIndex,
+ kClawFromBToDIndex,
+ kClawBLoopIndex,
+ kClawBPinchIndex,
+ kClawBCounterclockwiseIndex,
+ kClawBClockwiseIndex,
+ kClawFromCToBIndex,
+ kClawCLoopIndex,
+ kClawCPinchIndex,
+ kClawCCounterclockwiseIndex,
+ kClawCClockwiseIndex,
+ kClawFromDToBIndex,
+ kClawDLoopIndex,
+ kClawDPinchIndex,
+ kClawDCounterclockwiseIndex,
+ kClawDClockwiseIndex
+};
+
+// Action indices for s_clawStateTable:
+// Can also be used as indices into _buttons (except for kNoActionIndex and kLoopActionIndex).
+enum {
+ kNoActionIndex = -1,
+ kPinchActionIndex = 0,
+ kMoveDownActionIndex,
+ kMoveRightActionIndex,
+ kMoveLeftActionIndex,
+ kMoveUpActionIndex,
+ kCCWActionIndex,
+ kCWActionIndex,
+ kLoopActionIndex
+};
+
+/*
+ _currentAction and _nextAction:
+
+ At any time, _currentAction contains an action index (defined above). The current
+ action index is what the claw is doing right now. If the player presses a button
+ before the current action completes, _nextAction saves the new action and input
+ is disabled. This has the effect of implementing a queue of commands for the claw
+ that can save at most one extra command.
+
+ The general strategy for using _currentAction and _nextAction are:
+ -- If the player presses a claw button and _currentAction is kNoActionIndex,
+ do the command immediately and set _currentAction accordingly.
+ -- If the player presses a claw button and _currentAction is not kNoActionIndex,
+ save the appropriate action index in _nextAction.
+ -- When a command animation completes, set _nextAction to kNoActionIndex, then
+ check if _nextAction has a command waiting in it. If so, play the appriate
+ animation, copy _nextAction into _currentAction and set _nextAction to
+ kNoActionIndex.
+ -- If the player tries to get up, disable input (including all claw buttons) until
+ the player rises. Then, if the claw is in its original position, play the
+ animation of the player rising.
+ -- If the claw needs to be put back, play the first move required to put the
+ claw back by setting _currentAction and playing the appropriate animation.
+ Leave _nextAction alone. When the animation, completes, check to see if the
+ claw is in its original position or not. If so, complete the player rising
+ sequence by playing the rising animation. If not, repeat this whole step.
+
+ Using this general strategy allows the use of a single function,
+ DispatchClawAction, which can both cause the claw to perform a command and saving
+ the next command in _nextAction.
+*/
+
+// Array indexed by [claw position] [action]
+// array yields an index into the neighborhood's extra id table for claw animation or -1.
+static const int s_clawStateTable[4][8] = {
+ {
+ kClawAPinchIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawFromAToBIndex,
+ kNoActionIndex,
+ kClawACounterclockwiseIndex,
+ kClawAClockwiseIndex,
+ kClawALoopIndex
+ },
+ {
+ kClawBPinchIndex,
+ kNoActionIndex,
+ kClawFromBToAIndex,
+ kClawFromBToDIndex,
+ kClawFromBToCIndex,
+ kClawBCounterclockwiseIndex,
+ kClawBClockwiseIndex,
+ kClawBLoopIndex
+ },
+ {
+ kClawCPinchIndex,
+ kClawFromCToBIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawCCounterclockwiseIndex,
+ kClawCClockwiseIndex,
+ kClawCLoopIndex
+ },
+ {
+ kClawDPinchIndex,
+ kNoActionIndex,
+ kClawFromDToBIndex,
+ kNoActionIndex,
+ kNoActionIndex,
+ kClawDCounterclockwiseIndex,
+ kClawDClockwiseIndex,
+ kClawDLoopIndex
+ }
+};
+
+// Move directions for s_clawMovieTable:
+enum {
+ kMoveClawDown,
+ kMoveClawRight,
+ kMoveClawLeft,
+ kMoveClawUp
+};
+
+const int kClawNowhere = -1;
+
+// Array indexed by [claw position] [move direction]
+// array yields new claw position or -1.
+static const int s_clawMovieTable[4][4] = {
+ {
+ kClawNowhere,
+ kClawNowhere,
+ kClawAtB,
+ kClawNowhere
+ },
+ {
+ kClawNowhere,
+ kClawAtA,
+ kClawAtD,
+ kClawAtC
+ },
+ {
+ kClawAtB,
+ kClawNowhere,
+ kClawNowhere,
+ kClawNowhere
+ },
+ {
+ kClawNowhere,
+ kClawAtB,
+ kClawNowhere,
+ kClawNowhere
+ }
+};
+
+// Indexed by claw action index, claw position, plus 0 for start, 1 for stop.
+// (Never indexed with kLoopActionIndex.)
+static const TimeValue s_clawMonitorTable[7][4][2] = {
+ {
+ { kAPinchStart, kAPinchStop },
+ { kBPinchStart, kBPinchStop },
+ { kCPinchStart, kCPinchStop },
+ { kDPinchStart, kDPinchStop }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff },
+ { kCToBStart, kCToBStop },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { kBToAStart, kBToAStop },
+ { 0xffffffff, 0xffffffff },
+ { kDToBStart, kDToBStop }
+ },
+ {
+ { kAToBStart, kAToBStop },
+ { kBToDStart, kBToDStop },
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { 0xffffffff, 0xffffffff },
+ { kBToCStart, kBToCStop },
+ { 0xffffffff, 0xffffffff },
+ { 0xffffffff, 0xffffffff }
+ },
+ {
+ { kACCWStart, kACCWStop },
+ { kBCCWStart, kBCCWStop },
+ { kCCCWStart, kCCCWStop },
+ { kDCCWStart, kDCCWStop }
+ },
+ {
+ { kACWStart, kACWStop },
+ { kBCWStart, kBCWStop },
+ { kCCWStart, kCCWStop },
+ { kDCWStart, kDCWStop }
+ }
+};
+
+// Frame indices for the green ball sprite.
+enum {
+ kGreenBallAtA,
+ kGreenBallAtAWithClaw,
+ kGreenBallAtAWithClawAndRobot,
+ kGreenBallAtB,
+ kGreenBallAtBWithClaw,
+ kGreenBallAtBWithClawAndRobot,
+ kGreenBallAtCArmAtA,
+ kGreenBallAtCArmAtB,
+ kGreenBallAtCArmAtD,
+ kGreenBallAtCWithClaw,
+ kGreenBallAtD,
+ kGreenBallAtDWithClaw,
+ kNumClawGreenBalls
+};
+
+// State constants for _robotState.
+enum {
+ kNoRobot,
+ kRobotApproaching,
+ kPunchingOnce,
+ kPunchingTwice,
+ kPunchingThrice,
+ kCarriedToDoor,
+ kPlayerWon,
+ kRobotWon
+};
+
+// Sub Control Room button PICTs:
+
+const tResIDType kSubControlButtonBaseID = 500;
+const tResIDType kClawMonitorGreenBallBaseID = 600;
+
+// Constructor
+SubControlRoom::SubControlRoom(Neighborhood *handler) : GameInteraction(kNoradSubControlRoomInteractionID, handler),
+ _subControlMovie(kSubControlMonitorID), _subControlNotification(kSubControlNotificationID, (PegasusEngine *)g_engine),
+ _clawMonitorMovie(kClawMonitorID), _pinchButton(kSubControlPinchID), _downButton(kSubControlDownID),
+ _rightButton(kSubControlRightID), _leftButton(kSubControlLeftID), _upButton(kSubControlUpID),
+ _ccwButton(kSubControlCCWID), _cwButton(kSubControlCWID), _greenBall(kClawMonitorGreenBallID),
+ _greenBallNotification(kNoradGreenBallNotificationID, (PegasusEngine *)g_engine) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+ _playingAgainstRobot = false;
+ _robotState = kNoRobot;
+}
+
+void SubControlRoom::playAgainstRobot() {
+ _playingAgainstRobot = true;
+}
+
+void SubControlRoom::openInteraction() {
+ _currentAction = kNoActionIndex;
+ _nextAction = kNoActionIndex;
+
+ Norad *owner = (Norad *)getOwner();
+ owner->getClawInfo(_outSpotID, _prepSpotID, _clawControlSpotID, _clawButtonSpotIDs[0],
+ _clawButtonSpotIDs[1], _clawButtonSpotIDs[2], _clawButtonSpotIDs[3],
+ _clawButtonSpotIDs[4], _clawButtonSpotIDs[5], _clawButtonSpotIDs[6],
+ _clawStartPosition, _clawExtraIDs);
+
+ _clawPosition = _clawStartPosition;
+ _clawNextPosition = _clawPosition;
+ _subControlMovie.initFromMovieFile("Images/Norad Alpha/N22 Left Monitor Movie");
+ _subControlMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+ _subControlMovie.moveElementTo(kNoradSubControlLeft, kNoradSubControlTop);
+ _subControlScale = _subControlMovie.getScale();
+ _subControlMovie.setDisplayOrder(kSubControlOrder);
+ _subControlMovie.startDisplaying();
+ _subControlCallBack.setNotification(&_subControlNotification);
+ _subControlCallBack.initCallBack(&_subControlMovie, kCallBackAtExtremes);
+
+ _clawMonitorMovie.initFromMovieFile("Images/Norad Alpha/N22:N60 Right Monitor");
+ _clawMonitorMovie.moveElementTo(kNoradClawMonitorLeft, kNoradClawMonitorTop);
+ _clawMonitorMovie.setDisplayOrder(kClawMonitorOrder);
+ _clawMonitorMovie.startDisplaying();
+ _clawMonitorCallBack.setNotification(&_subControlNotification);
+ _clawMonitorCallBack.initCallBack(&_clawMonitorMovie, kCallBackAtExtremes);
+
+ _subControlNotification.notifyMe(this, kSubControlNotificationFlags, kSubControlNotificationFlags);
+
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+
+ _buttons[0] = &_pinchButton;
+ _buttons[1] = &_downButton;
+ _buttons[2] = &_rightButton;
+ _buttons[3] = &_leftButton;
+ _buttons[4] = &_upButton;
+ _buttons[5] = &_ccwButton;
+ _buttons[6] = &_cwButton;
+
+ _pinchButton.setDisplayOrder(kSubControlPinchOrder);
+ _downButton.setDisplayOrder(kSubControlDownOrder);
+ _rightButton.setDisplayOrder(kSubControlRightOrder);
+ _leftButton.setDisplayOrder(kSubControlLeftOrder);
+ _upButton.setDisplayOrder(kSubControlUpOrder);
+ _ccwButton.setDisplayOrder(kSubControlCCWOrder);
+ _cwButton.setDisplayOrder(kSubControlCWOrder);
+
+ for (int i = 0; i < kNumClawButtons; i++) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 1, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 2, true);
+ _buttons[i]->addFrame(frame, 0, 0);
+
+ _buttons[i]->setCurrentFrameIndex(0);
+ _buttons[i]->startDisplaying();
+ }
+
+ _pinchButton.moveElementTo(kNoradSubControlPinchLeft, kNoradSubControlPinchTop);
+ _downButton.moveElementTo(kNoradSubControlDownLeft, kNoradSubControlDownTop);
+ _rightButton.moveElementTo(kNoradSubControlRightLeft, kNoradSubControlRightTop);
+ _leftButton.moveElementTo(kNoradSubControlLeftLeft, kNoradSubControlLeftTop);
+ _upButton.moveElementTo(kNoradSubControlUpLeft, kNoradSubControlUpTop);
+ _ccwButton.moveElementTo(kNoradSubControlCCWLeft, kNoradSubControlCCWTop);
+ _cwButton.moveElementTo(kNoradSubControlCWLeft, kNoradSubControlCWTop);
+
+ _greenBall.setDisplayOrder(kClawMonitorGreenBallOrder);
+
+ for (int i = 0; i < kNumClawGreenBalls; i++) {
+ SpriteFrame *frame = new SpriteFrame();
+ frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kClawMonitorGreenBallBaseID + i);
+ _greenBall.addFrame(frame, 0, 0);
+ }
+
+ _greenBall.setCurrentFrameIndex(0);
+ _greenBall.startDisplaying();
+
+ _greenBallTimer.setScale(owner->getNavMovie()->getScale());
+ _greenBallCallBack.setNotification(&_greenBallNotification);
+ _greenBallCallBack.initCallBack(&_greenBallTimer, kCallBackAtExtremes);
+ _greenBallCallBack.setCallBackFlag(kOneSecondOfMoveFinished);
+ _greenBallNotification.notifyMe(this, kGreenBallNotificationFlags, kGreenBallNotificationFlags);
+
+ _subControlMovie.show();
+ _clawMonitorMovie.show();
+}
+
+void SubControlRoom::initInteraction() {
+ if (GameState.getNoradSubPrepState() == kSubDamaged) {
+ playControlMonitorSection(kDeltaSplashStart * _subControlScale, kDeltaSplashStop * _subControlScale,
+ 0, kDeltaSplash, false);
+ playClawMonitorSection(kDeltaClawSplashStart, kDeltaClawSplashStop, kDeltaSplashFinished, _gameState, false);
+ } else {
+ playControlMonitorSection(kAlphaSplashStart * _subControlScale, kAlphaSplashStop * _subControlScale,
+ 0, kAlphaSplash, false);
+ playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false);
+ }
+
+ // TODO: MoviesTask call. Needed?
+
+ GameState.setScoringPlayedWithClaw(true);
+}
+
+void SubControlRoom::closeInteraction() {
+ _subControlNotification.cancelNotification(this);
+ _subControlCallBack.releaseCallBack();
+ _greenBallNotification.cancelNotification(this);
+ _greenBallCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void SubControlRoom::setSoundFXLevel(const uint16 fxLevel) {
+ _subControlMovie.setVolume(fxLevel);
+}
+
+void SubControlRoom::receiveNotification(Notification *notification, const tNotificationFlags flags) {
+ Norad *owner = (Norad *)getOwner();
+
+ if (notification == &_subControlNotification) {
+ switch (flags) {
+ case kAlphaSplashFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true);
+ break;
+ case kPrepHighlightFinished:
+ if (GameState.getNoradSubPrepState() == kSubDamaged)
+ playControlMonitorSection(kDeltaLaunchPrepStart * _subControlScale,
+ kDeltaLaunchPrepStop * _subControlScale, kDeltaPrepFinished, _gameState, false);
+ else
+ playControlMonitorSection(kAlphaLaunchPrepStart * _subControlScale,
+ kAlphaLaunchPrepStop * _subControlScale, kAlphaPrepFinished, _gameState, false);
+ break;
+ case kAlphaPrepFinished:
+ GameState.setNoradSubPrepState(kSubPrepped);
+ GameState.setScoringPreppedSub(true);
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true);
+ break;
+ case kClawHighlightFinished:
+ playControlMonitorSection(kClawMenuStart * _subControlScale, kClawMenuStop * _subControlScale,
+ kClawMenuFinished, _gameState, false);
+ break;
+ case kClawMenuFinished:
+ owner->playClawMonitorIntro();
+ showButtons();
+ setControlMonitorToTime(kClawMenuTime * _subControlScale, kClawMenu, true);
+
+ if (!_playingAgainstRobot) {
+ updateClawMonitor();
+ owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]);
+ }
+ break;
+ case kDeltaSplashFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true);
+
+ if (_playingAgainstRobot) {
+ _robotState = kRobotApproaching;
+ playClawMonitorSection(kRobotApproachStart, kRobotApproachStop, 0, _gameState, true);
+ owner->startExtraSequence(kN60RobotApproaches, kExtraCompletedFlag, kFilterAllInput);
+ }
+ break;
+ case kDeltaPrepFinished:
+ setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true);
+ break;
+ }
+ } else if (notification == &_greenBallNotification) {
+ if (_robotState == kRobotWon) {
+ // We are using the green ball notification to hide stuff when the robot comes through
+ // the glass.
+ hideEverything();
+ } else {
+ // We are now midway through a move, time to update the claw's position and the green
+ // ball.
+ _clawPosition = _clawNextPosition;
+ updateClawMonitor();
+ updateGreenBall();
+ }
+ } else if (notification == _neighborhoodNotification) {
+ _currentAction = kNoActionIndex;
+ if (_playingAgainstRobot) {
+ switch (_robotState) {
+ case kRobotApproaching:
+ if (_gameState == kClawMenu) {
+ _robotState = kPunchingOnce;
+ dispatchClawAction(kNoActionIndex);
+ } else {
+ robotKillsPlayer(kN60FirstMistake, owner);
+ }
+ break;
+ case kPunchingOnce:
+ if (_nextAction == kMoveDownActionIndex) {
+ _robotState = kPunchingTwice;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60SecondMistake, owner);
+ }
+ break;
+ case kPunchingTwice:
+ if (_nextAction == kPinchActionIndex) {
+ _robotState = kPunchingThrice;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60ThirdMistake, owner);
+ }
+ break;
+ case kPunchingThrice:
+ if (_nextAction == kMoveRightActionIndex) {
+ _robotState = kCarriedToDoor;
+ performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner);
+ } else {
+ robotKillsPlayer(kN60FourthMistake, owner);
+ }
+ break;
+ case kCarriedToDoor:
+ hideEverything();
+ _robotState = kPlayerWon;
+ owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kPlayerWon:
+ // TODO
+ //((NoradDelta *)owner)->playerBeatRobotWithClaw();
+ owner->requestDeleteCurrentInteraction();
+ break;
+ case kRobotWon:
+ g_system->delayMillis(2 * 1000); // 120 ticks
+ ((PegasusEngine *)g_engine)->die(kDeathRobotSubControlRoom);
+ break;
+ }
+ } else {
+ if (_gameState == kPuttingClawAway && _nextAction == kNoActionIndex) {
+ if (_clawPosition == _clawStartPosition) {
+ Input scratch;
+ GameInteraction::clickInHotspot(scratch, g_allHotspots.findHotspotByID(_outSpotID));
+ } else {
+ switch (_clawPosition) {
+ case kClawAtA:
+ dispatchClawAction(kMoveLeftActionIndex);
+ break;
+ case kClawAtB:
+ if (_clawStartPosition == kClawAtD) // Norad Alpha
+ dispatchClawAction(kMoveLeftActionIndex);
+ else if (_clawStartPosition == kClawAtC) // Norad Delta
+ dispatchClawAction(kMoveUpActionIndex);
+ break;
+ case kClawAtC:
+ dispatchClawAction(kMoveDownActionIndex);
+ break;
+ case kClawAtD:
+ dispatchClawAction(kMoveRightActionIndex);
+ break;
+ }
+ }
+ } else {
+ dispatchClawAction(_nextAction);
+ }
+ }
+ }
+}
+
+void SubControlRoom::hideEverything() {
+ hideButtons();
+ _subControlMovie.hide();
+ _clawMonitorMovie.hide();
+ _greenBall.hide();
+}
+
+void SubControlRoom::robotKillsPlayer(const uint32 extraID, Neighborhood *owner) {
+ _robotState = kRobotWon;
+ owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput);
+ _greenBallTimer.stop();
+ _greenBallTimer.setSegment(0, 32 * _greenBallTimer.getScale() / 15);
+ _greenBallTimer.setTime(0);
+ _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _greenBallTimer.start();
+}
+
+void SubControlRoom::activateHotspots() {
+ if (_robotState == kRobotWon || _robotState == kPlayerWon)
+ return;
+
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kAlphaMainMenu:
+ case kDeltaMainMenu:
+ g_allHotspots.activateOneHotspot(_prepSpotID);
+ g_allHotspots.activateOneHotspot(_clawControlSpotID);
+ break;
+ case kClawMenu:
+ // This could be called during a move, so use _clawNextPosition.
+ if (_playingAgainstRobot) {
+ g_allHotspots.deactivateOneHotspot(_outSpotID);
+ if (_robotState != kRobotApproaching && _nextAction == kNoActionIndex)
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex)
+ g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]);
+ } else if (_nextAction == kNoActionIndex) {
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex)
+ g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void SubControlRoom::showButtons() {
+ if (_playingAgainstRobot && _robotState == kRobotApproaching) {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ } else if (_nextAction != kNoActionIndex) {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ if (i == _currentAction || i == _nextAction)
+ _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame);
+ else
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ } else {
+ for (int i = 0; i < kNumClawButtons; i++) {
+ _buttons[i]->show();
+ if (i == _currentAction)
+ _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame);
+ else if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex &&
+ _gameState != kPuttingClawAway) // this could be called during a move, so check _clawNextPosition
+ _buttons[i]->setCurrentFrameIndex(kButtonActiveFrame);
+ else
+ _buttons[i]->setCurrentFrameIndex(kButtonDimFrame);
+ }
+ }
+}
+
+void SubControlRoom::hideButtons() {
+ for (int i = 0; i < kNumClawButtons; i++)
+ _buttons[i]->hide();
+}
+
+int SubControlRoom::findActionIndex(tHotSpotID id) {
+ for (int i = 0; i < kNumClawButtons; i++)
+ if (id == _clawButtonSpotIDs[i])
+ return i;
+
+ return kNoActionIndex;
+}
+
+void SubControlRoom::clickInHotspot(const Input &input, const Hotspot *spot) {
+ tHotSpotID clickedID = spot->getObjectID();
+ int actionIndex = findActionIndex(clickedID);
+
+ if (actionIndex != kNoActionIndex) {
+ dispatchClawAction(actionIndex);
+ } else if (clickedID == _prepSpotID) {
+ playControlMonitorSection(kLaunchPrepHighlightStart * _subControlScale,
+ kLaunchPrepHighlightStop * _subControlScale,
+ kPrepHighlightFinished, kPlayingHighlight, false);
+ } else if (clickedID == _clawControlSpotID) {
+ playControlMonitorSection(kClawControlHighlightStart * _subControlScale,
+ kClawControlHighlightStop * _subControlScale,
+ kClawHighlightFinished, kPlayingHighlight, false);
+ } else if (clickedID == _outSpotID) {
+ _gameState = kPuttingClawAway;
+
+ if (_currentAction == kNoActionIndex)
+ if (_clawPosition == _clawStartPosition) {
+ GameInteraction::clickInHotspot(input, spot);
+ } else {
+ switch (_clawPosition) {
+ case kClawAtA:
+ dispatchClawAction(kMoveLeftActionIndex);
+ break;
+ case kClawAtB:
+ if (_clawStartPosition == kClawAtD) // Norad Alpha
+ dispatchClawAction(kMoveLeftActionIndex);
+ else if (_clawStartPosition == kClawAtC) // Norad Delta
+ dispatchClawAction(kMoveUpActionIndex);
+ break;
+ case kClawAtC:
+ dispatchClawAction(kMoveDownActionIndex);
+ break;
+ case kClawAtD:
+ dispatchClawAction(kMoveRightActionIndex);
+ break;
+ }
+ }
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+void SubControlRoom::dispatchClawAction(const int newAction) {
+ Neighborhood *owner = getOwner();
+
+ if (newAction == kNoActionIndex) {
+ _currentAction = kNoActionIndex;
+ _nextAction = kNoActionIndex;
+ showButtons();
+ updateGreenBall();
+
+ if (_playingAgainstRobot)
+ owner->startExtraSequence(kN60ArmActivated, kExtraCompletedFlag, kFilterAllInput);
+ else
+ owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]);
+ } else {
+ if (_currentAction == kNoActionIndex) {
+ if (_playingAgainstRobot) {
+ _nextAction = newAction;
+ showButtons();
+ updateGreenBall();
+ } else {
+ performActionImmediately(newAction, _clawExtraIDs[s_clawStateTable[_clawPosition][newAction]], owner);
+ }
+ } else if (_nextAction == kNoActionIndex) {
+ _nextAction = newAction;
+ showButtons();
+ updateGreenBall();
+ }
+ }
+}
+
+void SubControlRoom::performActionImmediately(const int action, const uint32 extraID, Neighborhood *owner) {
+ _currentAction = action;
+ _nextAction = kNoActionIndex;
+ ExtraTable::Entry entry;
+
+ switch (action) {
+ case kMoveDownActionIndex:
+ case kMoveRightActionIndex:
+ case kMoveLeftActionIndex:
+ case kMoveUpActionIndex:
+ // Set up green ball callback.
+ owner->getExtraEntry(extraID, entry);
+ _greenBallTimer.stop();
+ _greenBallTimer.setSegment(entry.movieStart, entry.movieStart + owner->getNavMovie()->getScale());
+ _greenBallTimer.setTime(entry.movieStart);
+ _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ // Start move.
+ _greenBallTimer.start();
+ break;
+ }
+
+ if (_playingAgainstRobot) {
+ switch (_robotState) {
+ case kPunchingTwice:
+ owner->startExtraSequence(kN60ArmToPositionB, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kPunchingThrice:
+ owner->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ case kCarriedToDoor:
+ owner->startExtraSequence(kN60ArmCarriesRobotToPositionA, kExtraCompletedFlag, kFilterAllInput);
+ break;
+ }
+ } else {
+ owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput);
+ }
+
+ switch (action) {
+ case kMoveDownActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawDown];
+ break;
+ case kMoveRightActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawRight];
+ break;
+ case kMoveLeftActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawLeft];
+ break;
+ case kMoveUpActionIndex:
+ _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawUp];
+ break;
+ case kLoopActionIndex:
+ // Do nothing.
+ break;
+ default:
+ playClawMonitorSection(s_clawMonitorTable[action][_clawPosition][0],
+ s_clawMonitorTable[action][_clawPosition][1], 0, _gameState, true);
+ break;
+ }
+
+ showButtons();
+ updateGreenBall();
+}
+
+void SubControlRoom::setControlMonitorToTime(const TimeValue newTime, const int newState, const bool shouldAllowInput) {
+ _subControlMovie.stop();
+ _subControlMovie.setSegment(0, _subControlMovie.getDuration());
+ _subControlMovie.setTime(newTime);
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+}
+
+void SubControlRoom::playControlMonitorSection(const TimeValue in, const TimeValue out, const tNotificationFlags flags,
+ const int newState, const bool shouldAllowInput) {
+ _subControlMovie.stop();
+ _subControlMovie.setSegment(in, out);
+ _subControlMovie.setTime(in);
+
+ if (flags != 0) {
+ _subControlCallBack.setCallBackFlag(flags);
+ _subControlCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+ _subControlMovie.start();
+}
+
+void SubControlRoom::updateClawMonitor() {
+ switch (_clawPosition) {
+ case kClawAtA:
+ setClawMonitorToTime(kClawAtATime);
+ break;
+ case kClawAtB:
+ setClawMonitorToTime(kClawAtBTime);
+ break;
+ case kClawAtC:
+ setClawMonitorToTime(kClawAtCTime);
+ break;
+ case kClawAtD:
+ setClawMonitorToTime(kClawAtDTime);
+ break;
+ }
+}
+
+void SubControlRoom::setClawMonitorToTime(const TimeValue newTime) {
+ _clawMonitorMovie.stop();
+ _clawMonitorMovie.setSegment(0, _clawMonitorMovie.getDuration());
+ _clawMonitorMovie.setTime(newTime);
+}
+
+void SubControlRoom::playClawMonitorSection(const TimeValue in, const TimeValue out, const tNotificationFlags flags,
+ const int newState, const bool shouldAllowInput) {
+ _clawMonitorMovie.stop();
+ _clawMonitorMovie.setSegment(in, out);
+ _clawMonitorMovie.setTime(in);
+
+ if (flags != 0) {
+ _clawMonitorCallBack.setCallBackFlag(flags);
+ _clawMonitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ }
+
+ _gameState = newState;
+ allowInput(shouldAllowInput);
+ _clawMonitorMovie.start();
+}
+
+void SubControlRoom::updateGreenBall() {
+ switch (_currentAction) {
+ case kMoveDownActionIndex:
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ break;
+ case kMoveRightActionIndex:
+ if (_clawNextPosition == kClawAtA) {
+ switch (_nextAction) {
+ case kMoveLeftActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToA();
+ break;
+ }
+ } else {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ }
+ break;
+ case kMoveLeftActionIndex:
+ if (_clawNextPosition == kClawAtB) {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToA();
+ break;
+ case kMoveLeftActionIndex:
+ moveGreenBallToD();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ moveGreenBallToB();
+ break;
+ }
+ } else {
+ switch (_nextAction) {
+ case kMoveRightActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToD();
+ break;
+ }
+ }
+ break;
+ case kMoveUpActionIndex:
+ switch (_nextAction) {
+ case kMoveDownActionIndex:
+ moveGreenBallToB();
+ break;
+ default:
+ moveGreenBallToC();
+ break;
+ }
+ break;
+ default:
+ switch (_nextAction) {
+ case kMoveDownActionIndex:
+ moveGreenBallToB();
+ break;
+ case kMoveRightActionIndex:
+ if (_clawPosition == kClawAtB)
+ moveGreenBallToA();
+ else
+ moveGreenBallToB();
+ break;
+ case kMoveLeftActionIndex:
+ if (_clawPosition == kClawAtB)
+ moveGreenBallToD();
+ else
+ moveGreenBallToB();
+ break;
+ case kMoveUpActionIndex:
+ moveGreenBallToC();
+ break;
+ default:
+ _greenBall.hide();
+ break;
+ }
+ break;
+ }
+}
+
+void SubControlRoom::moveGreenBallToA() {
+ if (_clawPosition == kClawAtA) {
+ if (_playingAgainstRobot)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClawAndRobot);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClaw);
+ } else {
+ _greenBall.setCurrentFrameIndex(kGreenBallAtA);
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtALeft, kNoradGreenBallAtATop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToB() {
+ if (_clawPosition == kClawAtB) {
+ if (_playingAgainstRobot)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClawAndRobot);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClaw);
+ } else {
+ _greenBall.setCurrentFrameIndex(kGreenBallAtB);
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtBLeft, kNoradGreenBallAtBTop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToC() {
+ switch (_clawPosition) {
+ case kClawAtA:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtA);
+ break;
+ case kClawAtB:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtB);
+ break;
+ case kClawAtC:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCWithClaw);
+ break;
+ case kClawAtD:
+ _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtD);
+ break;
+ }
+
+ _greenBall.moveElementTo(kNoradGreenBallAtCLeft, kNoradGreenBallAtCTop);
+ _greenBall.show();
+}
+
+void SubControlRoom::moveGreenBallToD() {
+ if (_clawPosition == kClawAtD)
+ _greenBall.setCurrentFrameIndex(kGreenBallAtDWithClaw);
+ else
+ _greenBall.setCurrentFrameIndex(kGreenBallAtD);
+
+ _greenBall.moveElementTo(kNoradGreenBallAtDLeft, kNoradGreenBallAtDTop);
+ _greenBall.show();
+}
+
+bool SubControlRoom::canSolve() {
+ return _playingAgainstRobot && _robotState < kCarriedToDoor;
+}
+
+void SubControlRoom::doSolve() {
+ _robotState = kCarriedToDoor;
+ hideEverything();
+ getOwner()->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput);
+}
+
+tInputBits SubControlRoom::getInputFilter() {
+ if (_playingAgainstRobot)
+ return GameInteraction::getInputFilter() & ~kFilterDownButtonAny;
+
+ return GameInteraction::getInputFilter();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h
new file mode 100755
index 0000000000..4a6889f82c
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h
@@ -0,0 +1,133 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+const uint32 kClawAtA = 0;
+const uint32 kClawAtB = 1;
+const uint32 kClawAtC = 2;
+const uint32 kClawAtD = 3;
+
+const int kNumClawButtons = 7;
+
+class Norad;
+
+class SubControlRoom : public GameInteraction, public NotificationReceiver {
+public:
+ SubControlRoom(Neighborhood *);
+ virtual ~SubControlRoom() {}
+
+ void playAgainstRobot();
+
+ virtual void setSoundFXLevel(const uint16);
+
+ bool canSolve();
+ void doSolve();
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const tNotificationFlags);
+
+ void robotKillsPlayer(const uint32, Neighborhood *);
+ tInputBits getInputFilter();
+
+ int findActionIndex(tHotSpotID);
+ void dispatchClawAction(const int);
+ void performActionImmediately(const int, const uint32, Neighborhood *);
+
+ void hideEverything();
+ void showButtons();
+ void hideButtons();
+
+ void updateGreenBall();
+ void moveGreenBallToA();
+ void moveGreenBallToB();
+ void moveGreenBallToC();
+ void moveGreenBallToD();
+
+ void setControlMonitorToTime(const TimeValue, const int, const bool);
+ void playControlMonitorSection(const TimeValue, const TimeValue, const tNotificationFlags,
+ const int, const bool);
+
+ void updateClawMonitor();
+ void setClawMonitorToTime(const TimeValue);
+ void playClawMonitorSection(const TimeValue, const TimeValue, const tNotificationFlags,
+ const int, const bool);
+
+ Movie _subControlMovie;
+ TimeScale _subControlScale;
+ Notification _subControlNotification;
+ NotificationCallBack _subControlCallBack;
+ Movie _clawMonitorMovie;
+ NotificationCallBack _clawMonitorCallBack;
+ int _gameState;
+ uint32 _clawStartPosition;
+ uint32 _clawPosition;
+ uint32 _clawNextPosition;
+ uint32 *_clawExtraIDs;
+
+ int _currentAction;
+ int _nextAction;
+
+ Sprite *_buttons[kNumClawButtons];
+ Sprite _pinchButton;
+ Sprite _downButton;
+ Sprite _rightButton;
+ Sprite _leftButton;
+ Sprite _upButton;
+ Sprite _ccwButton;
+ Sprite _cwButton;
+
+ Sprite _greenBall;
+ TimeBase _greenBallTimer;
+ Notification _greenBallNotification;
+ NotificationCallBack _greenBallCallBack;
+
+ tHotSpotID _outSpotID;
+ tHotSpotID _prepSpotID;
+ tHotSpotID _clawControlSpotID;
+ tHotSpotID _clawButtonSpotIDs[kNumClawButtons];
+
+ Notification *_neighborhoodNotification;
+
+ bool _playingAgainstRobot;
+ int _robotState;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp
new file mode 100755
index 0000000000..0d4827b910
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subplatform.cpp
@@ -0,0 +1,205 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "pegasus/gamestate.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/neighborhood/norad/norad.h"
+#include "pegasus/neighborhood/norad/subplatform.h"
+
+namespace Pegasus {
+
+// As usual, times here are in seconds.
+
+const TimeValue kNormalSplashStart = 0;
+const TimeValue kNormalSplashStop = 5;
+
+const TimeValue kPrepSubStart = 5;
+const TimeValue kPrepSubStop = 15;
+
+const TimeValue kPrepIncompleteStart = 15;
+const TimeValue kPrepIncompleteStop = 19;
+
+const TimeValue kDamagedStart = 19;
+const TimeValue kDamagedStop = 28;
+
+const tNotificationFlags kNormalSplashFinished = 1;
+const tNotificationFlags kPrepSubFinished = kNormalSplashFinished << 1;
+const tNotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1;
+const tNotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1;
+
+const tNotificationFlags kPlatformNotificationFlags = kNormalSplashFinished |
+ kPrepSubFinished |
+ kPrepIncompleteFinished |
+ kDamagedFinished;
+
+const uint16 kSubPreppedBit = (1 << 0);
+const uint16 kWaitingForPlayerBit = (1 << 1);
+
+SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler),
+ _platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, (PegasusEngine *)g_engine) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+}
+
+void SubPlatform::openInteraction() {
+ _stateBits = 0;
+
+ // TODO: These next two lines seem unused?
+ if (GameState.getNoradSubPrepState() == kSubPrepped)
+ _stateBits |= kSubPreppedBit;
+
+ _stateBits |= kWaitingForPlayerBit;
+ _platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie");
+ _platformMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+ _platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop);
+ _platformScale = _platformMovie.getScale();
+ _platformMovie.setDisplayOrder(kPlatformOrder);
+ _platformMovie.startDisplaying();
+ _platformCallBack.setNotification(&_platformNotification);
+ _platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes);
+
+ _platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags);
+}
+
+void SubPlatform::initInteraction() {
+ _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
+}
+
+void SubPlatform::closeInteraction() {
+ _platformNotification.cancelNotification(this);
+ _platformCallBack.releaseCallBack();
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void SubPlatform::setSoundFXLevel(const uint16 fxLevel) {
+ _platformMovie.setVolume(fxLevel);
+}
+
+void SubPlatform::receiveNotification(Notification *notification, const tNotificationFlags flags) {
+ FaderMoveSpec loop1Spec, loop2Spec;
+ ExtraTable::Entry entry;
+
+ Norad *owner = (Norad *)getOwner();
+
+ if (notification == &_platformNotification) {
+ switch (flags) {
+ case kNormalSplashFinished:
+ _platformMovie.stop();
+ switch (GameState.getNoradSubPrepState()) {
+ case kSubNotPrepped:
+ _platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale);
+ _platformMovie.setTime(kPrepIncompleteStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kPrepIncompleteFinished);
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _platformMovie.start();
+ break;
+ case kSubPrepped:
+ _platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale);
+ _platformMovie.setTime(kPrepSubStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kPrepSubFinished);
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput);
+ _platformMovie.start();
+ break;
+ case kSubDamaged:
+ // Shouldn't happen.
+ break;
+ }
+ break;
+ case kPrepSubFinished:
+ _platformMovie.stop();
+ _platformMovie.stopDisplaying();
+
+ owner->getExtraEntry(kNorad19ExitToSub, entry);
+
+ loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume,
+ entry.movieEnd - entry.movieStart, 0);
+ loop1Spec.insertFaderKnot(4560, kNoradWarningVolume);
+ loop1Spec.insertFaderKnot(5080, 0);
+
+ loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume,
+ entry.movieEnd - entry.movieStart, 0);
+ loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume);
+ loop1Spec.insertFaderKnot(5080, 0);
+
+ owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput);
+
+ owner->startLoop1Fader(loop1Spec);
+ owner->startLoop2Fader(loop2Spec);
+ break;
+ case kPrepIncompleteFinished:
+ // TODO
+ //((NoradAlpha *)owner)->setSubPrepFailed(true);
+ g_AIArea->checkMiddleArea();
+ // Fall through...
+ case kDamagedFinished:
+ _platformMovie.stop();
+ _platformMovie.hide();
+ _stateBits |= kWaitingForPlayerBit;
+ allowInput(true);
+ break;
+ }
+ } else if (notification == _neighborhoodNotification) {
+ allowInput(true);
+ ((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection);
+ GameState.setScoringEnteredSub(true);
+ }
+}
+
+void SubPlatform::activateHotspots() {
+ if (_stateBits & kWaitingForPlayerBit)
+ g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID);
+
+ GameInteraction::activateHotspots();
+}
+
+void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) {
+ if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) {
+ if (GameState.getNoradSubPrepState() == kSubDamaged) {
+ _platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale);
+ _platformMovie.setTime(kDamagedStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kDamagedFinished);
+ } else {
+ _platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale);
+ _platformMovie.setTime(kNormalSplashStart * _platformScale);
+ _platformCallBack.setCallBackFlag(kNormalSplashFinished);
+ }
+
+ _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _platformMovie.show();
+ _platformMovie.start();
+
+ // TODO: MoviesTask call? I don't think it's needed
+
+ _stateBits &= ~kWaitingForPlayerBit;
+
+ allowInput(false);
+ } else {
+ GameInteraction::clickInHotspot(input, spot);
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/subplatform.h b/engines/pegasus/neighborhood/norad/subplatform.h
new file mode 100755
index 0000000000..ae35cceb8f
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/subplatform.h
@@ -0,0 +1,63 @@
+/* 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-1997 Presto Studios, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+#include "pegasus/timers.h"
+
+namespace Pegasus {
+
+class SubPlatform : public GameInteraction, public NotificationReceiver {
+public:
+ SubPlatform(Neighborhood *);
+ virtual ~SubPlatform() {}
+
+ virtual void setSoundFXLevel(const uint16);
+
+protected:
+ virtual void openInteraction();
+ virtual void initInteraction();
+ virtual void closeInteraction();
+
+ virtual void activateHotspots();
+ virtual void clickInHotspot(const Input &, const Hotspot *);
+
+ virtual void receiveNotification(Notification *, const tNotificationFlags);
+
+ Movie _platformMovie;
+ TimeScale _platformScale;
+ Notification _platformNotification;
+ NotificationCallBack _platformCallBack;
+ Notification *_neighborhoodNotification;
+ uint16 _stateBits;
+};
+
+} // End of namespace Pegasus
+
+#endif