aboutsummaryrefslogtreecommitdiff
path: root/engines/pegasus/neighborhood/norad/delta
diff options
context:
space:
mode:
Diffstat (limited to 'engines/pegasus/neighborhood/norad/delta')
-rw-r--r--engines/pegasus/neighborhood/norad/delta/globegame.cpp1062
-rw-r--r--engines/pegasus/neighborhood/norad/delta/globegame.h169
-rw-r--r--engines/pegasus/neighborhood/norad/delta/noraddelta.cpp869
-rw-r--r--engines/pegasus/neighborhood/norad/delta/noraddelta.h117
4 files changed, 2217 insertions, 0 deletions
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
new file mode 100644
index 0000000000..7d4c1c5f8f
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
@@ -0,0 +1,1062 @@
+/* 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/cursor.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/delta/globegame.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+static const TimeValue kDurationPerFrame = 600 / 15;
+static const TimeValue kDurationPerRow = kNumLongSlices * kDurationPerFrame;
+static const short kVerticalDuration = 16;
+
+GlobeTracker::GlobeTracker(Movie *globeMovie, Picture *leftHighlight, Picture *rightHighlight,
+ Picture *upHighlight, Picture *downHighlight) {
+ _globeMovie = globeMovie;
+ _leftHighlight = leftHighlight;
+ _rightHighlight = rightHighlight;
+ _upHighlight = upHighlight;
+ _downHighlight = downHighlight;
+}
+
+void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirection direction) {
+ _trackSpot = trackSpot;
+ _trackDirection = direction;
+
+ TimeValue time, newTime, start;
+
+ switch (_trackDirection) {
+ case kTrackLeft:
+ time = _globeMovie->getTime();
+
+ if (((time / kDurationPerRow) & 1) == 0) {
+ start = (time / kDurationPerRow + 1) * kDurationPerRow;
+ newTime = start + kDurationPerRow - time % kDurationPerRow;
+ } else {
+ start = (time / kDurationPerRow) * kDurationPerRow;
+ newTime = time;
+ }
+
+ _globeMovie->setSegment(start, start + kDurationPerRow);
+
+ if (newTime != time) {
+ _globeMovie->setTime(newTime);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _globeMovie->setFlags(kLoopTimeBase);
+ break;
+ case kTrackRight:
+ time = _globeMovie->getTime();
+
+ if (((time / kDurationPerRow) & 1) == 0) {
+ start = (time / kDurationPerRow) * kDurationPerRow;
+ newTime = time;
+ } else {
+ start = (time / kDurationPerRow - 1) * kDurationPerRow;
+ newTime = start + kDurationPerRow - time % kDurationPerRow;
+ }
+
+ _globeMovie->setSegment(start, start + kDurationPerRow);
+
+ if (newTime != time) {
+ _globeMovie->setTime(newTime);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _globeMovie->setFlags(kLoopTimeBase);
+ break;
+ case kTrackUp:
+ case kTrackDown:
+ _globeMovie->setSegment(0, _globeMovie->getDuration());
+ _globeMovie->setFlags(0);
+ break;
+ }
+}
+
+void GlobeTracker::activateHotspots() {
+ Tracker::activateHotspots();
+
+ if (_trackSpot)
+ g_allHotspots.activateOneHotspot(_trackSpot->getObjectID());
+}
+
+bool GlobeTracker::stopTrackingInput(const Input &input) {
+ return !JMPPPInput::isPressingInput(input);
+}
+
+void GlobeTracker::continueTracking(const Input &input) {
+ Common::Point where;
+ input.getInputLocation(where);
+
+ if (g_allHotspots.findHotspot(where) == _trackSpot)
+ trackGlobeMovie();
+ else
+ stopGlobeMovie();
+}
+
+void GlobeTracker::startTracking(const Input &input) {
+ Tracker::startTracking(input);
+ trackGlobeMovie();
+}
+
+void GlobeTracker::stopTracking(const Input &input) {
+ Tracker::stopTracking(input);
+ stopGlobeMovie();
+}
+
+void GlobeTracker::trackGlobeMovie() {
+ TimeValue time;
+
+ switch (_trackDirection) {
+ case kTrackLeft:
+ if (!_globeMovie->isRunning())
+ _globeMovie->start();
+
+ _leftHighlight->show();
+ break;
+ case kTrackRight:
+ if (!_globeMovie->isRunning())
+ _globeMovie->start();
+
+ _rightHighlight->show();
+ break;
+ case kTrackUp:
+ time = _globeMovie->getTime();
+
+ if (_trackTime == 0) {
+ _trackTime = tickCount();
+ } else if ((int)time - (int)kDurationPerRow * 2 >= 0 && (int)tickCount() >= _trackTime + kVerticalDuration) {
+ _trackTime = tickCount();
+ _globeMovie->setTime(time - kDurationPerRow * 2);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _upHighlight->show();
+ break;
+ case kTrackDown:
+ time = _globeMovie->getTime();
+
+ if (_trackTime == 0) {
+ _trackTime = tickCount();
+ } else if (time + kDurationPerRow * 2 < _globeMovie->getDuration() && (int)tickCount() >= _trackTime + kVerticalDuration) {
+ _trackTime = tickCount();
+ _globeMovie->setTime(time + kDurationPerRow * 2);
+ _globeMovie->redrawMovieWorld();
+ }
+
+ _downHighlight->show();
+ break;
+ }
+}
+
+void GlobeTracker::stopGlobeMovie() {
+ switch (_trackDirection) {
+ case kTrackLeft:
+ _leftHighlight->hide();
+ _globeMovie->stop();
+ break;
+ case kTrackRight:
+ _rightHighlight->hide();
+ _globeMovie->stop();
+ break;
+ case kTrackUp:
+ _upHighlight->hide();
+ _trackTime = tickCount() - kVerticalDuration;
+ break;
+ case kTrackDown:
+ _downHighlight->hide();
+ _trackTime = tickCount() - kVerticalDuration;
+ break;
+ }
+}
+
+// Globe game PICTs:
+static const ResIDType kGlobeCircleLeftPICTID = 300;
+static const ResIDType kGlobeCircleRightPICTID = 301;
+static const ResIDType kGlobeCircleUpPICTID = 302;
+static const ResIDType kGlobeCircleDownPICTID = 303;
+static const ResIDType kTargetUpperLeftPICTID = 304;
+static const ResIDType kTargetUpperRightPICTID = 305;
+static const ResIDType kTargetLowerLeftPICTID = 306;
+static const ResIDType kTargetLowerRightPICTID = 307;
+static const ResIDType kMotionHiliteLeftPICTID = 308;
+static const ResIDType kMotionHiliteRightPICTID = 309;
+static const ResIDType kMotionHiliteUpPICTID = 310;
+static const ResIDType kMotionHiliteDownPICTID = 311;
+
+static const ResIDType kGlobeCountdownDigitsID = 350;
+
+static const int kGlobeCountdownWidth = 28;
+static const int kGlobeCountdownHeight = 12;
+static const int kGlobeCountdownOffset1 = 12;
+static const int kGlobeCountdownOffset2 = 20;
+
+GlobeCountdown::GlobeCountdown(const DisplayElementID id) : IdlerAnimation(id) {
+ _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCountdownDigitsID);
+
+ Common::Rect r;
+ _digits.getSurfaceBounds(r);
+ _digitOffset = r.width() / 10;
+ setScale(1);
+ sizeElement(kGlobeCountdownWidth, kGlobeCountdownHeight);
+}
+
+void GlobeCountdown::setDisplayOrder(const DisplayOrder order) {
+ IdlerAnimation::setDisplayOrder(order);
+}
+
+void GlobeCountdown::show() {
+ IdlerAnimation::show();
+}
+
+void GlobeCountdown::hide() {
+ IdlerAnimation::hide();
+}
+
+void GlobeCountdown::moveElementTo(const CoordType x, const CoordType y) {
+ IdlerAnimation::moveElementTo(x, y);
+}
+
+void GlobeCountdown::setCountdownTime(const int numSeconds) {
+ stop();
+ setSegment(0, numSeconds);
+ setTime(numSeconds);
+}
+
+void GlobeCountdown::startCountdown() {
+ setRate(-1);
+}
+
+void GlobeCountdown::stopCountdown() {
+ stop();
+}
+
+void GlobeCountdown::draw(const Common::Rect &) {
+ Common::Rect r1;
+ _digits.getSurfaceBounds(r1);
+ r1.right = r1.left + _digitOffset;
+ Common::Rect r2 = r1;
+ TimeValue time = getTime();
+
+ Common::Rect bounds;
+ getBounds(bounds);
+
+ if (time > 60 * 9 + 59) {
+ r2.moveTo(bounds.left, bounds.top);
+ r1.moveTo(9 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top);
+ r1.moveTo(5 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top);
+ r1.moveTo(9 * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+ } else {
+ r2.moveTo(bounds.left, bounds.top);
+ r1.moveTo((time / 60) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ time %= 60;
+ r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top);
+ r1.moveTo((time / 10) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+
+ r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top);
+ r1.moveTo((time % 10) * _digitOffset, 0);
+ _digits.copyToCurrentPort(r1, r2);
+ }
+}
+
+const int16 GlobeGame::_siloCoords[kNumAllSilos][2] = {
+ { 60, -151 }, // Anchorage, Alaska
+ { 6, 39 }, // Addis Ababa, Ethiopia
+ { -22, 44 }, // Antaro, Madagascar
+ { 30, -83 }, // Atlanta, Georgia
+ { -41, 173 }, // Auckland, New Zealand
+ { 39, -78 }, // Baltimore, Maryland
+ { 11, 101 }, // Bangkok, Thailand
+ { 2, -75 }, // Bogota, Colombia
+ { 46, 4 }, // Bonn, Germany
+ { 51, -7 }, // Dublin, Ireland
+ { 28, -1 }, // El Menia, Algeria
+ { 67, -111 }, // Ellesmere, Canada
+ { 43, -107 }, // Glasgow, Montana
+ { 61, -48 }, // Godthab, Greenland
+ { 19, -157 }, // Honolulu, Hawaii
+ { 6, 5 }, // Ibadan, Nigeria
+ { -29, 26 }, // Johannesburg, South Africa
+ { 46, 92 }, // Kobdo, Mongolia
+ { -15, -63 }, // La Paz, Bolivia
+ { -35, -61 }, // La Plata, Argentina
+ { -9, -76 }, // Lima, Peru
+ { 38, -4 }, // Madrid, Spain
+ { -8, -51 }, // Manaus, Brazil
+ { 13, 120 }, // Manila, Phillipines
+ { -35, 143 }, // Melbourne, Australia
+ { 60, -161 }, // Nome, Alaska
+ { -7, 142 }, // Papua, New Guinea
+ { -32, 117 }, // Perth, West Australia
+ { 34, -114 }, // Phoenix, Arizona
+ { 18, -71 }, // Port-Au-Prince, Haiti
+ { 42, -121 }, // Portland, Oregon
+ { 61, -20 }, // Reykjavik, Iceland
+ { -22, -46 }, // Rio de Janeiro
+ { 27, -101 }, // San Antonio, Texas
+ { 34, 126 }, // Seoul, Korea
+ { 37, -87 }, // Saint Louis, Missouri
+ { 60, 30 }, // Saint Petersberg, Russia
+ { 56, 12 }, // Stockholm, Sweden
+ { 51, 105 }, // Svortalsk, Siberia
+ { 36, -96 } // Tulsa, Oklahoma
+};
+
+const int16 GlobeGame::_targetSilo[kNumTargetSilos] = {
+ 14, 9, 1, 33, 6, 8, 34, 31, 38, 21
+};
+
+const short GlobeGame::_timeLimit[kNumTargetSilos] = {
+ 120, 110, 100, 90, 80, 70, 60, 50, 40, 30
+};
+
+const TimeValue GlobeGame::_siloName[kNumTargetSilos][2] = {
+ { kHonoluluIn, kHonoluluOut },
+ { kDublinIn, kDublinOut },
+ { kAddisAbabaIn, kAddisAbabaOut },
+ { kSanAntonioIn, kSanAntonioOut },
+ { kBangkokIn, kBangkokOut },
+ { kBonnIn, kBonnOut },
+ { kSeoulIn, kSeoulOut },
+ { kReykjavikIn, kReykjavikOut },
+ { kSvortalskIn, kSvortalskOut },
+ { kMadridIn, kMadridOut }
+};
+
+// From globe room models
+
+static const GlobeGame::Point3D kCameraLocation = { 0.53f, 4.4f, -0.86f };
+static const GlobeGame::Point3D kGlobeCenter = { -31.5f, 8.0f, 0.0f };
+static const float kGlobeRadius = 8.25f;
+static const int16 kDegreesPerLongSlice = 360 / kNumLongSlices;
+static const int16 kDegreesPerLatSlice = 25;
+static const int16 kLongOrigin = -95;
+
+// Other constants.
+
+static const float kTanFieldOfView = 0.7082373180482f;
+static const float kPicturePlaneDistance = 10.0f; // Completely arbitrary.
+static const int16 kLatError = 2;
+static const int16 kLongError = 2;
+static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15;
+
+static const TimeValue kTimePerGlobeFrame = 40;
+
+static const NotificationFlags kGlobeSplash1Finished = 1;
+static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1;
+static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1;
+
+static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished |
+ kGlobeTimerExpired |
+ kMaxDeactivatedFinished;
+
+static const int16 kSplash1End = 4;
+static const int16 kSplash2End = 5;
+static const int16 kSplash3Start = 8;
+static const int16 kSplash3Stop = 9;
+static const int16 kSplash4Start = 9;
+static const int16 kSplash4Stop = 10;
+static const int16 kNewLaunchSiloTime = 10;
+static const int16 kSiloDeactivatedTime = 11;
+static const int16 kMissileLaunchedTime = 12;
+static const int16 kMaxDeactivatedStart = 13;
+static const int16 kMaxDeactivatedStop = 23;
+
+static const int16 kGamePlaying = 1;
+static const int16 kGameOver = 2;
+
+enum {
+ kGameIntro,
+ kPlayingRobotIntro,
+ kPlayingStrikeAuthorized,
+ kPlayingPrimaryTarget,
+ kPlayingNewSilo1,
+ kPlayingNewSilo2,
+ kPlayingNewSilo3,
+ kPlayingTime,
+ kPlayingInstructions,
+ kWaitingForPlayer,
+ kSiloDeactivated,
+ kRobotTaunting,
+ kDelayingPlayer,
+ kPlayerWon1,
+ kPlayerWon2,
+ kPlayerLost1
+};
+
+// TODO: Use ScummVM equivalent
+static const float kPI = 3.1415926535f;
+
+float degreesToRadians(float angle) {
+ return (angle * kPI) / 180;
+}
+
+float radiansToDegrees(float angle) {
+ return (angle * 180) / kPI;
+}
+
+GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler),
+ _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID),
+ _lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine),
+ _globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID),
+ _globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID),
+ _motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID),
+ _motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID),
+ _targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID),
+ _targetHighlightLowerLeft(kTargetHiliteLowerLeftID), _targetHighlightLowerRight(kTargetHiliteLowerRightID),
+ _globeTracker(&_globeMovie, &_motionHighlightLeft, &_motionHighlightRight, &_motionHighlightUp,
+ &_motionHighlightDown), _countdown(kGlobeCountdownID) {
+ _neighborhoodNotification = handler->getNeighborhoodNotification();
+}
+
+void GlobeGame::openInteraction() {
+ _monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor");
+ _monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop);
+ _monitorMovie.setDisplayOrder(kGlobeMonitorLayer);
+ _monitorMovie.startDisplaying();
+ _monitorMovie.setSegment(0, kSplash1End * _monitorMovie.getScale());
+ _monitorMovie.show();
+
+ _monitorCallBack.setNotification(&_globeNotification);
+ _monitorCallBack.initCallBack(&_monitorMovie, kCallBackAtExtremes);
+ _monitorCallBack.setCallBackFlag(kGlobeSplash1Finished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+ _upperNamesMovie.initFromMovieFile("Images/Norad Delta/Upper Names");
+ _upperNamesMovie.moveElementTo(kGlobeUpperNamesLeft, kGlobeUpperNamesTop);
+ _upperNamesMovie.setDisplayOrder(kGlobeUpperNamesLayer);
+ _upperNamesMovie.startDisplaying();
+
+ _lowerNamesMovie.initFromMovieFile("Images/Norad Delta/Lower Names");
+ _lowerNamesMovie.moveElementTo(kGlobeLowerNamesLeft, kGlobeLowerNamesTop);
+ _lowerNamesMovie.setDisplayOrder(kGlobeLowerNamesLayer);
+ _lowerNamesMovie.startDisplaying();
+
+ _globeMovie.initFromMovieFile("Images/Norad Delta/Spinning Globe");
+ _globeMovie.moveElementTo(kGlobeLeft, kGlobeTop);
+ _globeMovie.setDisplayOrder(kGlobeMovieLayer);
+ _globeMovie.startDisplaying();
+ _globeMovie.setTime(kGlobeMovieStartTime);
+ _globeMovie.redrawMovieWorld();
+
+ _globeCircleLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleLeftPICTID, true);
+ _globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop);
+ _globeCircleLeft.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleLeft.startDisplaying();
+
+ _globeCircleRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleRightPICTID, true);
+ _globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop);
+ _globeCircleRight.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleRight.startDisplaying();
+
+ _globeCircleUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleUpPICTID, true);
+ _globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop);
+ _globeCircleUp.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleUp.startDisplaying();
+
+ _globeCircleDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleDownPICTID, true);
+ _globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop);
+ _globeCircleDown.setDisplayOrder(kGlobeCircleLayer);
+ _globeCircleDown.startDisplaying();
+
+ _motionHighlightLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteLeftPICTID, true);
+ _motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop);
+ _motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightLeft.startDisplaying();
+
+ _motionHighlightRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteRightPICTID, true);
+ _motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop);
+ _motionHighlightRight.setDisplayOrder(kGlobeCircleLayer);
+ _motionHighlightRight.startDisplaying();
+
+ _motionHighlightUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteUpPICTID, true);
+ _motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop);
+ _motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightUp.startDisplaying();
+
+ _motionHighlightDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteDownPICTID, true);
+ _motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop);
+ _motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer);
+ _motionHighlightDown.startDisplaying();
+
+ _targetHighlightUpperLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperLeftPICTID, true);
+ _targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop);
+ _targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightUpperLeft.startDisplaying();
+
+ _targetHighlightUpperRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperRightPICTID, true);
+ _targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop);
+ _targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightUpperRight.startDisplaying();
+
+ _targetHighlightLowerLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerLeftPICTID, true);
+ _targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop);
+ _targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightLowerLeft.startDisplaying();
+
+ _targetHighlightLowerRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerRightPICTID, true);
+ _targetHighlightLowerRight.moveElementTo(kGlobeLowerRightHiliteLeft, kGlobeLowerRightHiliteTop);
+ _targetHighlightLowerRight.setDisplayOrder(kGlobeHilitesLayer);
+ _targetHighlightLowerRight.startDisplaying();
+
+ _countdown.setDisplayOrder(kGlobeCountdownLayer);
+ _countdown.moveElementTo(kGlobeCountdownLeft, kGlobeCountdownTop);
+ _countdown.startDisplaying();
+ _countdown.setCountdownTime(_timeLimit[0]);
+
+ _countdownCallBack.setNotification(&_globeNotification);
+ _countdownCallBack.initCallBack(&_countdown, kCallBackAtExtremes);
+ _countdownCallBack.setCallBackFlag(kGlobeTimerExpired);
+ _countdownCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
+
+ _globeNotification.notifyMe(this, kGlobeNotificationFlags, kGlobeNotificationFlags);
+
+ _gameState = kGameIntro;
+ _currentSiloIndex = 0;
+ _playedInstructions = false;
+
+ _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag);
+}
+
+void GlobeGame::initInteraction() {
+ _monitorMovie.start();
+ _monitorMovie.redrawMovieWorld();
+}
+
+void GlobeGame::closeInteraction() {
+ _monitorMovie.stop();
+ _monitorMovie.stopDisplaying();
+ _monitorMovie.releaseMovie();
+ _monitorCallBack.releaseCallBack();
+
+ _globeMovie.stop();
+ _globeMovie.stopDisplaying();
+ _globeMovie.releaseMovie();
+ _globeNotification.cancelNotification(this);
+
+ _upperNamesMovie.stop();
+ _upperNamesMovie.stopDisplaying();
+ _upperNamesMovie.releaseMovie();
+
+ _lowerNamesMovie.stop();
+ _lowerNamesMovie.stopDisplaying();
+ _lowerNamesMovie.releaseMovie();
+
+ _countdown.hide();
+ _countdown.stopDisplaying();
+ _countdownCallBack.releaseCallBack();
+
+ _globeCircleLeft.stopDisplaying();
+ _globeCircleLeft.deallocateSurface();
+ _globeCircleRight.stopDisplaying();
+ _globeCircleRight.deallocateSurface();
+ _globeCircleUp.stopDisplaying();
+ _globeCircleUp.deallocateSurface();
+ _globeCircleDown.stopDisplaying();
+ _globeCircleDown.deallocateSurface();
+
+ _motionHighlightLeft.stopDisplaying();
+ _motionHighlightLeft.deallocateSurface();
+ _motionHighlightRight.stopDisplaying();
+ _motionHighlightRight.deallocateSurface();
+ _motionHighlightUp.stopDisplaying();
+ _motionHighlightUp.deallocateSurface();
+ _motionHighlightDown.stopDisplaying();
+ _motionHighlightDown.deallocateSurface();
+
+ _targetHighlightUpperLeft.stopDisplaying();
+ _targetHighlightUpperLeft.deallocateSurface();
+ _targetHighlightUpperRight.stopDisplaying();
+ _targetHighlightUpperRight.deallocateSurface();
+ _targetHighlightLowerLeft.stopDisplaying();
+ _targetHighlightLowerLeft.deallocateSurface();
+ _targetHighlightLowerRight.stopDisplaying();
+ _targetHighlightLowerRight.deallocateSurface();
+
+ _neighborhoodNotification->cancelNotification(this);
+}
+
+void GlobeGame::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ TimeScale scale = _monitorMovie.getScale();
+
+ if (notification == _neighborhoodNotification) {
+ switch (_gameState) {
+ case kPlayingRobotIntro:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _monitorMovie.setTime(kSplash2End * scale - 1);
+ _monitorMovie.setFlags(0);
+
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayingStrikeAuthorized;
+ break;
+ case kPlayingStrikeAuthorized:
+ _monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale);
+ _monitorMovie.setTime(kSplash3Start * scale);
+ _monitorMovie.redrawMovieWorld();
+
+ _owner->requestDelay(1, 3, kFilterNoInput, 0);
+ _owner->requestSpotSound(kPrimaryTargetIn, kPrimaryTargetOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _monitorMovie.start();
+ _gameState = kPlayingPrimaryTarget;
+ break;
+ case kPlayingPrimaryTarget:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ _gameState = kPlayingNewSilo1;
+ break;
+ case kPlayingNewSilo1:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _owner->requestDelay(1, 3, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingNewSilo2;
+ break;
+ case kPlayingNewSilo2:
+ _upperNamesMovie.show();
+ _upperNamesMovie.setTime(_currentSiloIndex * _upperNamesMovie.getScale());
+ _upperNamesMovie.redrawMovieWorld();
+ _monitorMovie.setTime(kSplash4Stop * scale - 1);
+ _monitorMovie.redrawMovieWorld();
+ _owner->requestSpotSound(_siloName[_currentSiloIndex][0], _siloName[_currentSiloIndex][1], kFilterNoInput, 0);
+ _owner->requestDelay(1, 3, kFilterNoInput, 0);
+ _owner->requestSpotSound(kLaunchToProceedIn, kLaunchToProceedOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingNewSilo3;
+ break;
+ case kPlayingNewSilo3:
+ _countdown.stopCountdown();
+ _countdown.setCountdownTime(_timeLimit[_currentSiloIndex]);
+ _countdown.show();
+ _gameState = kPlayingTime;
+
+ if (_timeLimit[_currentSiloIndex] >= 120)
+ _owner->requestSpotSound(kTwoMinutesIn, kTwoMinutesOut, kFilterNoInput, 0);
+ else if (_timeLimit[_currentSiloIndex] >= 60)
+ _owner->requestSpotSound(kOneMinuteIn, kOneMinuteOut, kFilterNoInput, 0);
+
+ switch (_timeLimit[_currentSiloIndex] % 60) {
+ case 0:
+ _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
+ break;
+ case 10:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTenSecondsIn, kTenSecondsOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 20:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTwentySecondsIn, kTwentySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 30:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kThirtySecondsIn, kThirtySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 40:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kFortySecondsIn, kFortySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 50:
+ _owner->requestDelay(1, 5, kFilterNoInput, 0);
+ _owner->requestSpotSound(kFiftySecondsIn, kFiftySecondsOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ }
+ case kPlayingTime:
+ _gameState = kPlayingInstructions;
+ _globeMovie.show();
+ _globeCircleLeft.show();
+ _globeCircleRight.show();
+ _globeCircleUp.show();
+ _globeCircleDown.show();
+
+ if (_playedInstructions) {
+ receiveNotification(notification, flags);
+ } else {
+ _owner->requestSpotSound(kToDeactivateIn, kToDeactivateOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ _playedInstructions = true;
+ }
+ break;
+ case kPlayingInstructions:
+ _gameState = kWaitingForPlayer;
+ _countdown.startCountdown();
+ break;
+ case kSiloDeactivated:
+ _gameState = kRobotTaunting;
+
+ switch (_currentSiloIndex) {
+ case 3:
+ _owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ case 5:
+ _owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 7:
+ _owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput,
+ kSpotSoundCompletedFlag);
+ break;
+ case 9:
+ _owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ break;
+ default:
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kPlayingNewSilo1;
+ break;
+ }
+ break;
+ case kRobotTaunting:
+ _owner->requestDelay(1, 1, kFilterNoInput, 0);
+ _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _monitorMovie.setTime(kNewLaunchSiloTime * scale);
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kPlayingNewSilo1;
+ break;
+ case kDelayingPlayer:
+ _gameState = kWaitingForPlayer;
+ break;
+ case kPlayerLost1:
+ _owner->recallToTSAFailure();
+ break;
+ case kPlayerWon2:
+ ((NoradDelta *)_owner)->finishedGlobeGame();
+ _owner->requestDeleteCurrentInteraction();
+ break;
+ default:
+ break;
+ }
+ } else if (notification == &_globeNotification) {
+ ExtraTable::Entry entry;
+
+ switch (flags) {
+ case kGlobeSplash1Finished:
+ _owner->getExtraEntry(kN79BrightView, entry);
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale);
+ _monitorMovie.setFlags(kLoopTimeBase);
+ _monitorMovie.start();
+ _owner->showViewFrame(entry.movieStart);
+ _owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayingRobotIntro;
+ break;
+ case kGlobeTimerExpired:
+ // Missile launched, player loses.
+ _owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerLost1;
+ break;
+ case kMaxDeactivatedFinished:
+ _monitorMovie.stop();
+ _monitorMovie.setSegment(0, _monitorMovie.getDuration());
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0);
+ _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kPlayerWon2;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Prevent the player from getting up until the game is over.
+
+void GlobeGame::handleInput(const Input &input, const Hotspot *cursorSpot) {
+ Common::Point where;
+ input.getInputLocation(where);
+ Hotspot *spot = g_allHotspots.findHotspot(where);
+
+ if (((PegasusEngine *)g_engine)->_cursor->isVisible() && spot != 0 &&
+ spot->getObjectID() == kNorad79SiloAreaSpotID && findClickedSilo(input) != -1) {
+ _targetHighlightUpperLeft.show();
+ _targetHighlightUpperRight.show();
+ _targetHighlightLowerLeft.show();
+ _targetHighlightLowerRight.show();
+ } else {
+ _targetHighlightUpperLeft.hide();
+ _targetHighlightUpperRight.hide();
+ _targetHighlightLowerLeft.hide();
+ _targetHighlightLowerRight.hide();
+ }
+
+ // Interrupt certain inputs to prevent player from switching modes.
+ InputHandler::handleInput(input, cursorSpot);
+}
+
+int16 GlobeGame::findClickedSilo(const Input &input) {
+ Common::Point screenPoint;
+ input.getInputLocation(screenPoint);
+ screenPoint.x -= kNavAreaLeft;
+ screenPoint.y -= kNavAreaTop;
+
+ Line3D ray;
+ screenPointTo3DPoint(screenPoint.x, screenPoint.y, ray.pt2);
+ ray.pt1 = kCameraLocation;
+
+ Point3D globePoint;
+ if (lineHitsGlobe(ray, globePoint)) {
+ int16 latOrigin, longOrigin, latitude, longitude;
+ globeMovieFrameToOrigin(_globeMovie.getTime() / kTimePerGlobeFrame, latOrigin, longOrigin);
+ globePointToLatLong(globePoint, latOrigin, longOrigin, latitude, longitude);
+
+ for (int16 i = 0; i < kNumAllSilos; i++)
+ if (_siloCoords[i][0] >= latitude - kLatError && _siloCoords[i][0] <= latitude + kLatError &&
+ _siloCoords[i][1] >= longitude - kLongError && _siloCoords[i][1] <= longitude + kLongError)
+ return i;
+ }
+
+ return -1;
+}
+
+void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDirection trackDirection) {
+ _globeTracker.setTrackParameters(spot, trackDirection);
+ _globeTracker.startTracking(input);
+}
+
+void GlobeGame::clickGlobe(const Input &input) {
+ int16 newSilo = findClickedSilo(input);
+
+ if (newSilo != -1) {
+ _targetHighlightUpperLeft.hide();
+ _targetHighlightUpperRight.hide();
+ _targetHighlightLowerLeft.hide();
+ _targetHighlightLowerRight.hide();
+ _lowerNamesMovie.show();
+ _lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale());
+ _lowerNamesMovie.redrawMovieWorld();
+ _owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0);
+
+ if (newSilo == _targetSilo[_currentSiloIndex]) {
+ _currentSiloIndex++;
+ _countdown.stopCountdown();
+ _owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0);
+
+ if (_currentSiloIndex == kNumTargetSilos) {
+ // Player won.
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(),
+ kMaxDeactivatedStop * _monitorMovie.getScale());
+ _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
+ _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _monitorMovie.start();
+ _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut,
+ kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerWon1;
+ } else {
+ _owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale());
+ _monitorMovie.redrawMovieWorld();
+ _gameState = kSiloDeactivated;
+ }
+ } else {
+ _owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag);
+ _gameState = kDelayingPlayer;
+ // Play "incorrect" sound?
+ }
+ }
+}
+
+void GlobeGame::clickInHotspot(const Input &input, const Hotspot *spot) {
+ switch (spot->getObjectID()) {
+ case kNorad79SpinLeftSpotID:
+ spinGlobe(input, spot, kTrackLeft);
+ break;
+ case kNorad79SpinRightSpotID:
+ spinGlobe(input, spot, kTrackRight);
+ break;
+ case kNorad79SpinUpSpotID:
+ spinGlobe(input, spot, kTrackUp);
+ break;
+ case kNorad79SpinDownSpotID:
+ spinGlobe(input, spot, kTrackDown);
+ break;
+ case kNorad79SiloAreaSpotID:
+ clickGlobe(input);
+ break;
+ default:
+ GameInteraction::clickInHotspot(input, spot);
+ break;
+ }
+}
+
+void GlobeGame::activateHotspots() {
+ GameInteraction::activateHotspots();
+
+ switch (_gameState) {
+ case kWaitingForPlayer:
+ g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinLeftSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinRightSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinUpSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SpinDownSpotID);
+ g_allHotspots.activateOneHotspot(kNorad79SiloAreaSpotID);
+ break;
+ default:
+ g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID);
+ break;
+ }
+}
+
+void GlobeGame::globeMovieFrameToOrigin(int16 frameNum, int16 &latOrigin, int16 &longOrigin) {
+ latOrigin = kDegreesPerLatSlice * 2 - (frameNum / (kNumLongSlices * 2)) * kDegreesPerLatSlice;
+ frameNum %= kNumLongSlices * 2;
+
+ if (frameNum >= kNumLongSlices)
+ longOrigin = kLongOrigin + (kNumLongSlices * 2 - 1 - frameNum) * kDegreesPerLongSlice;
+ else
+ longOrigin = kLongOrigin + frameNum * kDegreesPerLongSlice;
+
+ if (longOrigin > 180)
+ longOrigin -= 360;
+}
+
+void GlobeGame::globePointToLatLong(const GlobeGame::Point3D &pt, int16 latOrigin, int16 longOrigin,
+ int16 &latitude, int16 &longitude) {
+ Point3D scratch = pt;
+
+ // Translate globe center to origin.
+ scratch.x -= kGlobeCenter.x;
+ scratch.y -= kGlobeCenter.y;
+ scratch.z -= kGlobeCenter.z;
+
+ // Rotate around z axis latOrigin degrees to bring equator parallel with XZ plane
+ float theta = degreesToRadians(latOrigin);
+ float s = sin(theta);
+ float c = cos(theta);
+ float x = scratch.x * c - scratch.y * s;
+ float y = scratch.y * c + scratch.x * s;
+ scratch.x = x;
+ scratch.y = y;
+
+ // Calculate latitude
+ latitude = (int16)radiansToDegrees(asin(scratch.y / kGlobeRadius));
+
+ // Rotate around y axis longOrigin degrees to bring longitude 0 to positive X axis
+ theta = degreesToRadians(longOrigin);
+ s = sin(theta);
+ c = cos(theta);
+ x = scratch.x * c - scratch.z * s;
+ float z = scratch.z * c + scratch.x * s;
+ scratch.x = x;
+ scratch.z = z;
+
+ // Calculate longitude
+ longitude = (int16)radiansToDegrees(acos(scratch.x / sqrt(scratch.x * scratch.x + scratch.z * scratch.z)));
+
+ if (scratch.z < 0)
+ longitude = -longitude;
+}
+
+// h, v in [0, 511][0, 255]
+// Looking down negative x axis.
+void GlobeGame::screenPointTo3DPoint(int16 h, int16 v, GlobeGame::Point3D &pt) {
+ pt.x = kCameraLocation.x - kPicturePlaneDistance;
+ pt.y = kCameraLocation.y + (128 - v) * kPicturePlaneDistance * kTanFieldOfView / 256;
+ pt.z = kCameraLocation.z + (h - 256) * kPicturePlaneDistance * kTanFieldOfView / 256;
+}
+
+// Fundamentals of Three-Dimensional Graphics, by Alan Watt
+// pp. 163-164
+bool GlobeGame::lineHitsGlobe(const GlobeGame::Line3D &line, GlobeGame::Point3D &pt) {
+ float i = line.pt2.x - line.pt1.x;
+ float j = line.pt2.y - line.pt1.y;
+ float k = line.pt2.z - line.pt1.z;
+ float a = i * i + j * j + k * k;
+ float b = 2 * i * (line.pt1.x - kGlobeCenter.x) + 2 * j * (line.pt1.y - kGlobeCenter.y) +
+ 2 * k * (line.pt1.z - kGlobeCenter.z);
+ float c = kGlobeCenter.x * kGlobeCenter.x + kGlobeCenter.y * kGlobeCenter.y +
+ kGlobeCenter.z * kGlobeCenter.z + line.pt1.x * line.pt1.x + line.pt1.y * line.pt1.y +
+ line.pt1.z * line.pt1.z + -2 * (kGlobeCenter.x * line.pt1.x + kGlobeCenter.y * line.pt1.y +
+ kGlobeCenter.z * line.pt1.z) - kGlobeRadius * kGlobeRadius;
+
+ // Solve quadratic equation of a, b, c.
+ float t = b * b - 4 * a * c;
+
+ if (t >= 0.0f) {
+ // Return smaller root, which corresponds to closest intersection point.
+ t = (-b - sqrt(t)) / (2 * a);
+ pt.x = i * t + line.pt1.x;
+ pt.y = j * t + line.pt1.y;
+ pt.z = k * t + line.pt1.z;
+ return true;
+ }
+
+ return false;
+}
+
+bool GlobeGame::canSolve() {
+ return _gameState != kPlayerWon1 && _gameState != kPlayerWon2 && _gameState != kPlayerLost1;
+}
+
+void GlobeGame::doSolve() {
+ _owner->requestDelay(1, 2, kFilterNoInput, 0);
+ _upperNamesMovie.hide();
+ _lowerNamesMovie.hide();
+ _countdown.hide();
+ _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), kMaxDeactivatedStop * _monitorMovie.getScale());
+ _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
+ _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
+ _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+ _monitorMovie.start();
+ _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+ _gameState = kPlayerWon1;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h
new file mode 100644
index 0000000000..9c31a931fc
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.h
@@ -0,0 +1,169 @@
+/* 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_DELTA_GLOBEGAME_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H
+
+#include "pegasus/interaction.h"
+#include "pegasus/movie.h"
+#include "pegasus/notification.h"
+
+namespace Pegasus {
+
+enum GlobeTrackDirection {
+ kTrackLeft,
+ kTrackRight,
+ kTrackUp,
+ kTrackDown
+};
+
+// 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.
+
+class GlobeTracker : public Tracker {
+public:
+ GlobeTracker(Movie *, Picture *, Picture *, Picture *, Picture *);
+ virtual ~GlobeTracker() {}
+
+ void setTrackParameters(const Hotspot *, GlobeTrackDirection);
+ void continueTracking(const Input &);
+ void startTracking(const Input &);
+ void stopTracking(const Input &);
+ void activateHotspots();
+ bool stopTrackingInput(const Input &);
+
+protected:
+ void trackGlobeMovie();
+ void stopGlobeMovie();
+
+ Movie *_globeMovie;
+ Picture *_leftHighlight;
+ Picture *_rightHighlight;
+ Picture *_upHighlight;
+ Picture *_downHighlight;
+ const Hotspot *_trackSpot;
+ int _trackTime;
+ GlobeTrackDirection _trackDirection;
+};
+
+class GlobeCountdown : public IdlerAnimation {
+public:
+ GlobeCountdown(const DisplayElementID);
+ virtual ~GlobeCountdown() {}
+
+ void setCountdownTime(const int);
+ void startCountdown();
+ void stopCountdown();
+
+ void setDisplayOrder(const DisplayOrder);
+ void show();
+ void hide();
+ void moveElementTo(const CoordType, const CoordType);
+
+ void draw(const Common::Rect &);
+
+protected:
+ Surface _digits;
+ int16 _digitOffset;
+};
+
+static const int16 kNumAllSilos = 40;
+static const int16 kNumTargetSilos = 10;
+static const int16 kNumLongSlices = 72;
+
+class GlobeGame : public GameInteraction, public NotificationReceiver {
+public:
+ GlobeGame(Neighborhood *);
+ virtual ~GlobeGame() {}
+
+ void handleInput(const Input &, const Hotspot *);
+ void clickInHotspot(const Input &, const Hotspot *);
+ void activateHotspots();
+
+ bool canSolve();
+ void doSolve();
+
+ struct Point3D {
+ float x, y, z;
+ };
+
+ struct Line3D {
+ Point3D pt1, pt2;
+ };
+
+protected:
+ // Latitude (-90 - 90) and longitude (-180 - 180)
+ static const int16 _siloCoords[kNumAllSilos][2];
+
+ static const int16 _targetSilo[kNumTargetSilos];
+ static const int16 _timeLimit[kNumTargetSilos];
+ static const TimeValue _siloName[kNumTargetSilos][2];
+
+ void openInteraction();
+ void initInteraction();
+ void closeInteraction();
+
+ void receiveNotification(Notification *, const NotificationFlags);
+
+ void spinGlobe(const Input &, const Hotspot *, GlobeTrackDirection);
+ void clickGlobe(const Input &);
+
+ int16 findClickedSilo(const Input &);
+
+ void globeMovieFrameToOrigin(int16, int16 &, int16 &);
+ void globePointToLatLong(const Point3D &, int16, int16, int16 &, int16 &);
+ void screenPointTo3DPoint(int16, int16, Point3D &);
+ bool lineHitsGlobe(const Line3D &, Point3D &);
+
+ Movie _monitorMovie;
+ Movie _globeMovie;
+ Movie _upperNamesMovie;
+ Movie _lowerNamesMovie;
+ Notification _globeNotification;
+ NotificationCallBack _monitorCallBack;
+ GlobeTracker _globeTracker;
+ Picture _globeCircleLeft;
+ Picture _globeCircleRight;
+ Picture _globeCircleUp;
+ Picture _globeCircleDown;
+ Picture _motionHighlightLeft;
+ Picture _motionHighlightRight;
+ Picture _motionHighlightUp;
+ Picture _motionHighlightDown;
+ Picture _targetHighlightUpperLeft;
+ Picture _targetHighlightUpperRight;
+ Picture _targetHighlightLowerLeft;
+ Picture _targetHighlightLowerRight;
+ GlobeCountdown _countdown;
+ NotificationCallBack _countdownCallBack;
+ int16 _gameState;
+ int16 _currentSiloIndex;
+ Notification *_neighborhoodNotification;
+ bool _playedInstructions;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
new file mode 100644
index 0000000000..2b208aab71
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
@@ -0,0 +1,869 @@
+/* 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/interface.h"
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/opticalchip.h"
+#include "pegasus/items/biochips/retscanchip.h"
+#include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/subcontrolroom.h"
+#include "pegasus/neighborhood/norad/delta/globegame.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+
+namespace Pegasus {
+
+const uint32 NoradDelta::_noradDeltaClawExtras[22] = {
+ kN60ClawFromAToB,
+ kN60ClawALoop,
+ kN60ClawAPinch,
+ kN60ClawACounterclockwise,
+ kN60ClawAClockwise,
+ kN60ClawFromBToA,
+ kN60ClawFromBToC,
+ kN60ClawFromBToD,
+ kN60ClawBLoop,
+ kN60ClawBPinch,
+ kN60ClawBCounterclockwise,
+ kN60ClawBClockwise,
+ kN60ClawFromCToB,
+ kN60ClawCLoop,
+ kN60ClawCPinch,
+ kN60ClawCCounterclockwise,
+ kN60ClawCClockwise,
+ kN60ClawFromDToB,
+ kN60ClawDLoop,
+ kN60ClawDPinch,
+ kN60ClawDCounterclockwise,
+ kN60ClawDClockwise
+};
+
+NoradDelta::NoradDelta(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Delta", kNoradDeltaID) {
+ _elevatorUpRoomID = kNorad49South;
+ _elevatorDownRoomID = kNorad48South;
+ _elevatorUpSpotID = kNorad48ElevatorUpSpotID;
+ _elevatorDownSpotID = kNorad49ElevatorDownSpotID;
+
+ // Pressure door stuff.
+
+ _subRoomEntryRoom1 = kNorad50;
+ _subRoomEntryDir1 = kEast;
+ _subRoomEntryRoom2 = kNorad59;
+ _subRoomEntryDir2 = kWest;
+ _upperPressureDoorRoom = kNorad50East;
+ _lowerPressureDoorRoom = kNorad59West;
+
+ _upperPressureDoorUpSpotID = kDeltaUpperPressureDoorUpSpotID;
+ _upperPressureDoorDownSpotID = kDeltaUpperPressureDoorDownSpotID;
+ _upperPressureDoorAbortSpotID = kNorad50DoorOutSpotID;
+
+ _lowerPressureDoorUpSpotID = kDeltaLowerPressureDoorUpSpotID;
+ _lowerPressureDoorDownSpotID = kDeltaLowerPressureDoorDownSpotID;
+ _lowerPressureDoorAbortSpotID = kNorad59WestOutSpotID;
+
+ _pressureSoundIn = kPressureDoorIntro1In;
+ _pressureSoundOut = kPressureDoorIntro1Out;
+ _equalizeSoundIn = kPressureDoorIntro2In;
+ _equalizeSoundOut = kPressureDoorIntro2Out;
+ _accessDeniedIn = kDeltaAccessDeniedIn;
+ _accessDeniedOut = kDeltaAccessDeniedOut;
+
+ GameState.setNoradSubPrepState(kSubDamaged);
+
+ _subControlRoom = kNorad60West;
+}
+
+void NoradDelta::init() {
+ Norad::init();
+
+ // Little fix for the retinal scan zoom in spot...
+ Hotspot *hotspot = g_allHotspots.findHotspotByID(kNorad68WestSpotID);
+ hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
+
+ hotspot = g_allHotspots.findHotspotByID(kNorad79WestSpotID);
+ hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta59RobotShieldBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID);
+ hotspotEntry->hotspotItem = kShieldBiochip;
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta59RobotOpMemBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID);
+ hotspotEntry->hotspotItem = kOpticalBiochip;
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta59RobotRetinalBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID);
+ hotspotEntry->hotspotItem = kRetinalScanBiochip;
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta60RobotShieldBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID);
+ hotspotEntry->hotspotItem = kShieldBiochip;
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta60RobotOpMemBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID);
+ hotspotEntry->hotspotItem = kOpticalBiochip;
+
+ hotspot = g_allHotspots.findHotspotByID(kDelta60RobotRetinalBiochipSpotID);
+ hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
+ hotspotEntry = findHotspotEntry(kDelta60RobotRetinalBiochipSpotID);
+ hotspotEntry->hotspotItem = kRetinalScanBiochip;
+}
+
+void NoradDelta::start() {
+ if (g_energyMonitor) {
+ g_energyMonitor->stopEnergyDraining();
+ g_energyMonitor->restoreLastEnergyValue();
+ _vm->resetEnergyDeathReason();
+ g_energyMonitor->startEnergyDraining();
+ }
+
+ Norad::start();
+}
+
+void NoradDelta::setUpAIRules() {
+ Neighborhood::setUpAIRules();
+
+ if (g_AIArea) {
+ AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN07NE", false);
+ AILocationCondition *locCondition = new AILocationCondition(1);
+ locCondition->addLocation(MakeRoomView(kNorad68, kWest));
+ AIRule *rule = new AIRule(locCondition, messageAction);
+ g_AIArea->addAIRule(rule);
+ }
+}
+
+void NoradDelta::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
+ switch (entry.extra) {
+ case kArriveFromSubChase:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 20, entry.movieEnd, 90);
+ compassMove.insertFaderKnot(entry.movieStart + 25 * kNoradDeltaFrameDuration, 20);
+ compassMove.insertFaderKnot(entry.movieStart + 94 * kNoradDeltaFrameDuration, 45);
+ compassMove.insertFaderKnot(entry.movieStart + 101 * kNoradDeltaFrameDuration, 45);
+ compassMove.insertFaderKnot(entry.movieStart + 146 * kNoradDeltaFrameDuration, 90 + 15);
+ compassMove.insertFaderKnot(entry.movieStart + 189 * kNoradDeltaFrameDuration, 90 + 15);
+ compassMove.insertFaderKnot(entry.movieStart + 204 * kNoradDeltaFrameDuration, 90 + 30);
+ compassMove.insertFaderKnot(entry.movieStart + 214 * kNoradDeltaFrameDuration, 90 + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 222 * kNoradDeltaFrameDuration, 90 + 20);
+ compassMove.insertFaderKnot(entry.movieStart + 228 * kNoradDeltaFrameDuration, 90 + 10);
+ compassMove.insertFaderKnot(entry.movieStart + 245 * kNoradDeltaFrameDuration, 90 + 85);
+ compassMove.insertFaderKnot(entry.movieStart + 262 * kNoradDeltaFrameDuration, 90 + 70);
+ compassMove.insertFaderKnot(entry.movieStart + 273 * kNoradDeltaFrameDuration, 90 + 80);
+ compassMove.insertFaderKnot(entry.movieStart + 287 * kNoradDeltaFrameDuration, 90);
+ break;
+ case kN60PlayerFollowsRobotToDoor:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270 + kSubControlCompassAngle,
+ entry.movieEnd, 270 - 15);
+ compassMove.insertFaderKnot(entry.movieStart + 280, 270 + kSubControlCompassAngle);
+ compassMove.insertFaderKnot(entry.movieStart + 920, 360);
+ compassMove.insertFaderKnot(entry.movieStart + 1840, 360);
+ compassMove.insertFaderKnot(entry.movieStart + 2520, 270);
+ compassMove.insertFaderKnot(entry.movieStart + 3760, 270);
+ compassMove.insertFaderKnot(entry.movieStart + 4640, 270 + kSubControlCompassAngle);
+ break;
+ case kN59PlayerWins2:
+ compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270, entry.movieEnd, 280);
+ compassMove.insertFaderKnot(entry.movieEnd - 1000, 270);
+ default:
+ Norad::getExtraCompassMove(entry, compassMove);
+ break;
+ }
+}
+
+GameInteraction *NoradDelta::makeInteraction(const InteractionID interactionID) {
+ if (interactionID == kNoradGlobeGameInteractionID)
+ return new GlobeGame(this);
+
+ return Norad::makeInteraction(interactionID);
+}
+
+void NoradDelta::playClawMonitorIntro() {
+ playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut);
+}
+
+void NoradDelta::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) {
+ Norad::getExitEntry(room, direction, entry);
+
+ if (room == kNorad61 && direction == kSouth)
+ entry.movieStart += kNoradDeltaFrameDuration;
+}
+
+void NoradDelta::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) {
+ Norad::getZoomEntry(id, zoomEntry);
+
+ if (id == kNorad59WestSpotID && GameState.getNoradPlayedGlobeGame()) {
+ ExtraTable::Entry extraEntry;
+ getExtraEntry(kN59ZoomWithRobot, extraEntry);
+ zoomEntry.movieStart = extraEntry.movieStart;
+ zoomEntry.movieEnd = extraEntry.movieEnd;
+ }
+}
+
+void NoradDelta::loadAmbientLoops() {
+/*
+ Logic:
+
+ loop sound 1:
+ if room == kNorad79West
+ if player globe game
+ play kNoradGlobeLoop2SoundNum
+ else
+ play kNoradRedAlertLoopSoundNum
+ else if room >= kNorad78 && room <= kNorad79
+ play kNoradGlobeLoop2SoundNum
+ else if gassed,
+ if room >= kNorad41 && room <= kNorad49South
+ play kNoradNewSubLoopSoundNum, kNoradWarningVolume
+ else if room >= kNorad59 && room <= kNorad60West
+ play kNoradSubControlLoopSoundNum, kNoradWarningVolume
+ else
+ play kNoradWarningLoopSoundNum, kNoradWarningVolume
+ else
+ play nothing
+ loop sound 2:
+ if gassed and not wearing air mask
+ if room == kNorad54North
+ play breathing unmanned loop
+ else
+ play breathing
+ else
+ if room == kNorad54North
+ play unmanned loop
+ else
+ play nothing
+*/
+
+ if (GameState.getNoradArrivedFromSub()) {
+ RoomID room = GameState.getCurrentRoom();
+
+ if (room == kNorad79West) {
+ if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag))
+ loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF");
+ else
+ loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF");
+ } else if (room >= kNorad78 && room <= kNorad79) {
+ // clone2727 says: This looks like it should be loadLoopSound1...
+ loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF");
+ } else if (GameState.getNoradGassed()) {
+ if (room >= kNorad41 && room <= kNorad49South)
+ loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
+ else if (room >= kNorad59 && room <= kNorad60West)
+ loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
+ else
+ loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+ } else {
+ loadLoopSound1("");
+ }
+
+ if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) {
+ if (room == kNorad54North)
+ loadLoopSound2("Sounds/Norad/Breathing Typing.22K.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
+ } else {
+ if (room == kNorad54North)
+ loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2);
+ else
+ loadLoopSound2("");
+ }
+ } else {
+ // Start them off at zero...
+ if (GameState.getNoradGassed())
+ loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0);
+ if (!g_airMask->isAirFilterOn())
+ loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0);
+ }
+}
+
+void NoradDelta::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
+ switch (MakeRoomView(room, direction)) {
+ case MakeRoomView(kNorad41, kEast):
+ case MakeRoomView(kNorad49, kEast):
+ case MakeRoomView(kNorad49, kWest):
+ case MakeRoomView(kNorad61, kSouth):
+ case MakeRoomView(kNorad68, kEast):
+ case MakeRoomView(kNorad79, kWest):
+ makeContinuePoint();
+ break;
+ }
+}
+
+void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction) {
+ if (room != kNorad68)
+ GameState.setNoradRetScanGood(false);
+
+ Norad::arriveAt(room, direction);
+
+ FaderMoveSpec loop1Spec, loop2Spec;
+ ExtraTable::Entry entry;
+
+ switch (room) {
+ case kNorad41:
+ if (direction == kEast && !GameState.getNoradArrivedFromSub()) {
+ GameState.setNoradPlayedGlobeGame(false);
+
+ GameState.setNoradBeatRobotWithClaw(false);
+ GameState.setNoradBeatRobotWithDoor(false);
+ GameState.setNoradRetScanGood(false);
+
+ GameState.setScoringExitedSub(true);
+
+ getExtraEntry(kArriveFromSubChase, entry);
+
+ loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+ entry.movieStart, kNoradWarningVolume);
+ loop1Spec.insertFaderKnot(7320, 0);
+ loop1Spec.insertFaderKnot(7880, kNoradWarningVolume);
+
+ loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+ entry.movieStart, kNoradSuckWindVolume);
+ loop1Spec.insertFaderKnot(7320, 0);
+ loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume);
+
+ startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput);
+
+ startLoop1Fader(loop1Spec);
+ startLoop2Fader(loop2Spec);
+ }
+ break;
+ case kNorad54North:
+ GameState.setScoringSawRobotAt54North(true);
+ break;
+ case kNorad68:
+ if (GameState.getNoradRetScanGood())
+ openDoor();
+ break;
+ case kNorad68West:
+ arriveAtNorad68West();
+ break;
+ case kNorad79West:
+ arriveAtNorad79West();
+ break;
+ default:
+ break;
+ }
+}
+
+void NoradDelta::doorOpened() {
+ Norad::doorOpened();
+ GameState.setNoradRetScanGood(false);
+}
+
+void NoradDelta::arriveAtNorad68West() {
+ playSpotSoundSync(kHoldForRetinalIn, kHoldForRetinalOut);
+
+ BiochipItem *retScan = _vm->getCurrentBiochip();
+
+ if (retScan != 0 && retScan->getObjectID() == kRetinalScanBiochip) {
+ ((RetScanChip *)retScan)->searchForLaser();
+ succeedRetinalScan();
+ } else {
+ failRetinalScan();
+ }
+}
+
+void NoradDelta::arriveAtNorad79West() {
+ if (!GameState.getNoradPlayedGlobeGame())
+ newInteraction(kNoradGlobeGameInteractionID);
+}
+
+void NoradDelta::bumpIntoWall() {
+ requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0);
+ Neighborhood::bumpIntoWall();
+}
+
+void NoradDelta::failRetinalScan() {
+ startExtraSequence(kNoradDeltaRetinalScanBad, kExtraCompletedFlag, kFilterNoInput);
+}
+
+void NoradDelta::succeedRetinalScan() {
+ startExtraSequence(kNoradDeltaRetinalScanGood, kExtraCompletedFlag, kFilterNoInput);
+ GameState.setNoradRetScanGood(true);
+ GameState.setScoringUsedRetinalChip(true);
+}
+
+void NoradDelta::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &entry) {
+ Norad::getDoorEntry(room, direction, entry);
+
+ if (room == kNorad68 && direction == kWest && !GameState.getNoradRetScanGood())
+ entry.flags = kDoorPresentMask | kDoorLockedMask;
+}
+
+void NoradDelta::finishedGlobeGame() {
+ GameState.setNoradPlayedGlobeGame(true);
+ _privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true);
+ GameState.setScoringFinishedGlobeGame(true);
+ loadAmbientLoops();
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption);
+}
+
+bool NoradDelta::playingAgainstRobot() {
+ return GameState.getNoradPlayedGlobeGame();
+}
+
+void NoradDelta::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID,
+ HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID,
+ HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) {
+ outSpotID = kNorad60MonitorOutSpotID;
+ prepSpotID = kNorad60LaunchPrepSpotID;
+ clawControlSpotID = kNorad60ClawControlSpotID;
+ pinchClawSpotID = kNorad60ClawPinchSpotID;
+ moveClawDownSpotID = kNorad60ClawDownSpotID;
+ moveClawRightSpotID = kNorad60ClawRightSpotID;
+ moveClawLeftSpotID = kNorad60ClawLeftSpotID;
+ moveClawUpSpotID = kNorad60ClawUpSpotID;
+ clawCCWSpotID = kNorad60ClawCCWSpotID;
+ clawCWSpotID = kNorad60ClawCWSpotID;
+ clawPosition = kClawAtC;
+ clawExtraIDs = _noradDeltaClawExtras;
+}
+
+void NoradDelta::playerBeatRobotWithDoor() {
+ GameState.setNoradBeatRobotWithDoor(true);
+ updateViewFrame();
+ GameState.setScoringStoppedNoradRobot(true);
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+}
+
+void NoradDelta::playerBeatRobotWithClaw() {
+ GameState.setNoradBeatRobotWithClaw(true);
+ updateViewFrame();
+ GameState.setScoringStoppedNoradRobot(true);
+ GameState.setScoringNoradGandhi(true);
+ g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+}
+
+TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) {
+ ExtraTable::Entry entry;
+
+ if (room == kNorad41 && direction == kSouth && !GameState.getNoradArrivedFromSub()) {
+ getExtraEntry(kArriveFromSubChase, entry);
+ return entry.movieStart;
+ }
+
+ if (GameState.getNoradBeatRobotWithDoor()) {
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ uint32 extraID = kN59Biochips111;
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ extraID += 1;
+ if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ extraID += 2;
+ if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ extraID += 4;
+ getExtraEntry(extraID, entry);
+ return entry.movieStart;
+ }
+
+ getExtraEntry(kN59RobotHeadOpens, entry);
+ return entry.movieStart;
+ } else if (GameState.getNoradBeatRobotWithClaw()) {
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ uint32 extraID = kN60Biochips111;
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ extraID += 1;
+ if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ extraID += 2;
+ if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ extraID += 4;
+ getExtraEntry(extraID, entry);
+ return entry.movieStart;
+ }
+
+ getExtraEntry(kN60RobotHeadOpens, entry);
+ return entry.movieStart;
+ }
+
+ return Norad::getViewTime(room, direction);
+}
+
+void NoradDelta::openDoor() {
+ if (GameState.getCurrentRoom() == kNorad59 && GameState.getCurrentDirection() == kWest && GameState.getNoradPlayedGlobeGame()) {
+ Input scratch;
+ InputHandler::_inputHandler->clickInHotspot(scratch, g_allHotspots.findHotspotByID(kNorad59WestSpotID));
+ } else {
+ Norad::openDoor();
+ }
+}
+
+void NoradDelta::activateHotspots() {
+ Norad::activateHotspots();
+
+ if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) {
+ g_allHotspots.deactivateOneHotspot(kNorad59WestOutSpotID);
+
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta59RobotShieldBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
+ } else
+ g_allHotspots.activateOneHotspot(kDelta59RobotHeadSpotID);
+ } else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest &&
+ GameState.getNoradBeatRobotWithClaw()) {
+ g_allHotspots.deactivateOneHotspot(kNorad60MonitorOutSpotID);
+
+ if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
+ if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta60RobotShieldBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
+
+ if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
+ g_allHotspots.activateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
+ else
+ g_allHotspots.deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
+ } else {
+ g_allHotspots.activateOneHotspot(kDelta60RobotHeadSpotID);
+ }
+ } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) {
+ if (GameState.isCurrentDoorOpen())
+ g_allHotspots.deactivateOneHotspot(kNorad50DoorSpotID);
+ } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) {
+ if (GameState.isCurrentDoorOpen())
+ g_allHotspots.deactivateOneHotspot(kNorad59WestSpotID);
+ }
+}
+
+void NoradDelta::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+ switch (clickedSpot->getObjectID()) {
+ case kDelta59RobotHeadSpotID:
+ startExtraSequence(kN59RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ case kDelta60RobotHeadSpotID:
+ startExtraSequence(kN60RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput);
+ break;
+ default:
+ Norad::clickInHotspot(input, clickedSpot);
+ break;
+ }
+}
+
+void NoradDelta::receiveNotification(Notification *notification, const NotificationFlags flags) {
+ Norad::receiveNotification(notification, flags);
+
+ if ((flags & kExtraCompletedFlag) != 0) {
+ RetScanChip *retScan;
+ Input dummy;
+
+ switch (_lastExtra) {
+ case kArriveFromSubChase:
+ GameState.setNoradArrivedFromSub(true);
+ GameState.setCurrentRoom(kNoRoomID);
+ GameState.setCurrentDirection(kNoDirection);
+ arriveAt(kNorad41, kEast);
+ break;
+ case kN59RobotHeadOpens:
+ case kN60RobotHeadOpens:
+ _privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true);
+ break;
+ case kNoradDeltaRetinalScanBad:
+ retScan = (RetScanChip *)_vm->getCurrentBiochip();
+ retScan->setItemState(kNormalItem);
+ playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut);
+ downButton(dummy);
+ break;
+ case kNoradDeltaRetinalScanGood:
+ retScan = (RetScanChip *)_vm->getCurrentBiochip();
+ retScan->setItemState(kNormalItem);
+ downButton(dummy);
+ break;
+ case kN59RobotDisappears:
+ case kN60RobotDisappears:
+ recallToTSASuccess();
+ break;
+ }
+
+ _interruptionFilter = kFilterAllInput;
+ }
+
+ g_AIArea->checkMiddleArea();
+}
+
+void NoradDelta::pickedUpItem(Item *item) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kRetinalScanBiochip:
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ case kOpticalBiochip:
+ g_opticalChip->addPoseidon();
+ GameState.setScoringGotNoradOpMemChip();
+
+ if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) &&
+ _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) {
+ GameState.setNoradFinished(true);
+
+ if (GameState.getCurrentRoom() == kNorad59West)
+ startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ else
+ startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput);
+ }
+ break;
+ }
+
+ Norad::pickedUpItem(item);
+}
+
+void NoradDelta::takeItemFromRoom(Item *item) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, true);
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, true);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, true);
+ break;
+ }
+
+ Norad::takeItemFromRoom(item);
+}
+
+void NoradDelta::dropItemIntoRoom(Item *item, Hotspot *hotspot) {
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, false);
+ break;
+ case kOpticalBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, false);
+ break;
+ case kRetinalScanBiochip:
+ _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, false);
+ break;
+ }
+
+ Norad::dropItemIntoRoom(item, hotspot);
+}
+
+Hotspot *NoradDelta::getItemScreenSpot(Item *item, DisplayElement *element) {
+ HotSpotID id = kNoHotSpotID;
+
+ switch (item->getObjectID()) {
+ case kShieldBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotShieldBiochipSpotID;
+ else
+ id = kDelta60RobotShieldBiochipSpotID;
+ break;
+ case kOpticalBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotOpMemBiochipSpotID;
+ else
+ id = kDelta60RobotOpMemBiochipSpotID;
+ break;
+ case kRetinalScanBiochip:
+ if (GameState.getNoradBeatRobotWithDoor())
+ id = kDelta59RobotRetinalBiochipSpotID;
+ else
+ id = kDelta60RobotRetinalBiochipSpotID;
+ break;
+ }
+
+ if (id != kNoHotSpotID)
+ return g_allHotspots.findHotspotByID(id);
+
+ return Norad::getItemScreenSpot(item, element);
+}
+
+Common::String NoradDelta::getEnvScanMovie() {
+ return "Images/AI/Norad/XNE2";
+}
+
+uint NoradDelta::getNumHints() {
+ uint numHints = Neighborhood::getNumHints();
+
+ if (numHints == 0) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad60, kWest):
+ if (GameState.getNoradPlayedGlobeGame())
+ numHints = 2;
+ else
+ numHints = 1;
+ break;
+ case MakeRoomView(kNorad59, kNorth):
+ case MakeRoomView(kNorad59, kSouth):
+ case MakeRoomView(kNorad59, kEast):
+ case MakeRoomView(kNorad59, kWest):
+ case MakeRoomView(kNorad60, kNorth):
+ case MakeRoomView(kNorad60, kSouth):
+ case MakeRoomView(kNorad60, kEast):
+ if (GameState.getNoradPlayedGlobeGame())
+ numHints = 2;
+ break;
+ case MakeRoomView(kNorad68, kWest):
+ if (_vm->playerHasItemID(kRetinalScanBiochip)) {
+ BiochipItem *retScan = _vm->getCurrentBiochip();
+ if (retScan == 0 || retScan->getObjectID() != kRetinalScanBiochip)
+ numHints = 2;
+ } else if (!GameState.isCurrentDoorOpen()) {
+ numHints = 2;
+ }
+ break;
+ }
+ }
+
+ return numHints;
+}
+
+Common::String NoradDelta::getHintMovie(uint hintNum) {
+ Common::String movieName = Neighborhood::getHintMovie(hintNum);
+
+ if (movieName.empty()) {
+ switch (GameState.getCurrentRoomAndView()) {
+ case MakeRoomView(kNorad60, kWest):
+ if (GameState.getNoradPlayedGlobeGame()) {
+ if (hintNum == 1)
+ return "Images/AI/Norad/XN60WD2";
+
+ return "Images/AI/Norad/XN60WD3";
+ }
+
+ return "Images/AI/Globals/XGLOB1C";
+ case MakeRoomView(kNorad59, kNorth):
+ case MakeRoomView(kNorad59, kSouth):
+ case MakeRoomView(kNorad59, kEast):
+ case MakeRoomView(kNorad59, kWest):
+ case MakeRoomView(kNorad60, kNorth):
+ case MakeRoomView(kNorad60, kSouth):
+ case MakeRoomView(kNorad60, kEast):
+ if (hintNum == 1)
+ return "Images/AI/Norad/XN60WD2";
+
+ return "Images/AI/Norad/XN60WD3";
+ case MakeRoomView(kNorad68, kWest):
+ if (_vm->playerHasItemID(kRetinalScanBiochip)) {
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB1A";
+
+ return "Images/AI/Globals/XGLOB1C";
+ }
+
+ if (hintNum == 1)
+ return "Images/AI/Globals/XGLOB1B";
+
+ return "Images/AI/Globals/XGLOB3B";
+ }
+ }
+
+ return movieName;
+}
+
+void NoradDelta::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
+ switch (room) {
+ case kNorad47:
+ case kNorad48:
+ case kNorad41:
+ case kNorad42:
+ playSpotSoundSync(kDeltaElevatorDoorCloseIn, kDeltaElevatorDoorCloseOut);
+ break;
+ default:
+ playSpotSoundSync(kDeltaRegDoorCloseIn, kDeltaRegDoorCloseOut);
+ break;
+ }
+}
+
+bool NoradDelta::canSolve() {
+ if (Norad::canSolve())
+ return true;
+
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) {
+ BiochipItem *biochip = _vm->getCurrentBiochip();
+ if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip)
+ return true;
+ }
+
+ return false;
+}
+
+void NoradDelta::doSolve() {
+ Norad::doSolve();
+
+ if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) {
+ if (!_vm->playerHasItemID(kRetinalScanBiochip))
+ _vm->addItemToBiochips((BiochipItem *)g_allItems.findItemByID(kRetinalScanBiochip));
+
+ BiochipItem *biochip = _vm->getCurrentBiochip();
+ if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip && g_interface)
+ g_interface->setCurrentBiochipID(kRetinalScanBiochip);
+
+ Hotspot *spot = g_allHotspots.findHotspotByID(kNorad68WestSpotID);
+ Input scratch;
+ InputHandler::_inputHandler->clickInHotspot(scratch, spot);
+ }
+}
+
+Common::String NoradDelta::getSoundSpotsName() {
+ return "Sounds/Norad/Norad Delta Spots";
+}
+
+Common::String NoradDelta::getNavMovieName() {
+ return "Images/Norad Delta/Norad Delta.movie";
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
new file mode 100644
index 0000000000..11065f2c9d
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
@@ -0,0 +1,117 @@
+/* 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_DELTA_NORADDELTA_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H
+
+#include "pegasus/neighborhood/norad/norad.h"
+
+namespace Pegasus {
+
+class NoradDelta : public Norad {
+public:
+ NoradDelta(InputHandler *, PegasusEngine *);
+ virtual ~NoradDelta() {}
+
+ void init();
+
+ void start();
+
+ void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &);
+
+ void finishedGlobeGame();
+
+ virtual GameInteraction *makeInteraction(const InteractionID);
+
+ void playClawMonitorIntro();
+
+ virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID,
+ HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID,
+ HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID,
+ HotSpotID &clawCWSpotID, uint32 &, const uint32 *&);
+
+ void playerBeatRobotWithClaw();
+ void playerBeatRobotWithDoor();
+
+ void loadAmbientLoops();
+
+ void setUpAIRules();
+ Common::String getEnvScanMovie();
+ uint getNumHints();
+ Common::String getHintMovie(uint);
+ void closeDoorOffScreen(const RoomID, const DirectionConstant);
+
+ void checkContinuePoint(const RoomID, const DirectionConstant);
+
+ bool canSolve();
+ void doSolve();
+
+ void doorOpened();
+
+protected:
+ enum {
+ kNoradPrivateArrivedFromSubFlag,
+ kNoradPrivateFinishedGlobeGameFlag,
+ kNoradPrivateRobotHeadOpenFlag,
+ kNoradPrivateGotShieldChipFlag,
+ kNoradPrivateGotOpticalChipFlag,
+ kNoradPrivateGotRetScanChipFlag,
+ kNumNoradPrivateFlags
+ };
+
+ static const uint32 _noradDeltaClawExtras[22];
+
+ void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &);
+ void getZoomEntry(const HotSpotID, ZoomTable::Entry &);
+ virtual void arriveAt(const RoomID, const DirectionConstant);
+ void arriveAtNorad68West();
+ void arriveAtNorad79West();
+ TimeValue getViewTime(const RoomID, const DirectionConstant);
+ void openDoor();
+ void activateHotspots();
+ void clickInHotspot(const Input &, const Hotspot *);
+ void receiveNotification(Notification *, const NotificationFlags);
+ void pickedUpItem(Item *item);
+ void takeItemFromRoom(Item *item);
+ void dropItemIntoRoom(Item *item, Hotspot *);
+ Hotspot *getItemScreenSpot(Item *, DisplayElement *);
+
+ virtual bool playingAgainstRobot();
+
+ void failRetinalScan();
+ void succeedRetinalScan();
+ void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &);
+
+ void bumpIntoWall();
+
+ FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags;
+
+ Common::String getSoundSpotsName();
+ Common::String getNavMovieName();
+};
+
+} // End of namespace Pegasus
+
+#endif