1271 lines
42 KiB
C++
1271 lines
42 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1995-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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "pegasus/cursor.h"
|
|
#include "pegasus/pegasus.h"
|
|
#include "pegasus/items/biochips/arthurchip.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;
|
|
_trackSpot = nullptr;
|
|
_trackTime = -1;
|
|
_trackDirection = kTrackDown;
|
|
}
|
|
|
|
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);
|
|
|
|
// Clip new time so we don't go past the end of the segment
|
|
if (newTime >= start + kDurationPerRow)
|
|
newTime = start + kDurationPerRow - 1;
|
|
|
|
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);
|
|
|
|
// Clip new time so we don't go past the end of the segment
|
|
if (newTime >= start + kDurationPerRow)
|
|
newTime = start + kDurationPerRow - 1;
|
|
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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(g_vm->_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, Philippines
|
|
{ -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 kGlobeRobot1Finished = kGlobeSplash1Finished << 1;
|
|
static const NotificationFlags kGlobeRobot2Finished = kGlobeRobot1Finished << 1;
|
|
static const NotificationFlags kGlobeRobot3Finished = kGlobeRobot2Finished << 1;
|
|
static const NotificationFlags kGlobeRobot4Finished = kGlobeRobot3Finished << 1;
|
|
static const NotificationFlags kGlobeRobot5Finished = kGlobeRobot4Finished << 1;
|
|
static const NotificationFlags kGlobeRobot6Finished = kGlobeRobot5Finished << 1;
|
|
static const NotificationFlags kGlobeTimerExpired = kGlobeRobot6Finished << 1;
|
|
static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1;
|
|
|
|
static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished |
|
|
kGlobeRobot1Finished |
|
|
kGlobeRobot2Finished |
|
|
kGlobeRobot3Finished |
|
|
kGlobeRobot4Finished |
|
|
kGlobeRobot5Finished |
|
|
kGlobeRobot6Finished |
|
|
kGlobeTimerExpired |
|
|
kMaxDeactivatedFinished;
|
|
|
|
enum {
|
|
kSplash1End = 4,
|
|
kSplash2End = 5,
|
|
kSplash3Start = 8,
|
|
kSplash3Stop = 9,
|
|
kSplash4Start = 9,
|
|
kSplash4Stop = 10,
|
|
kNewLaunchSiloTime = 10,
|
|
kSiloDeactivatedTime = 11,
|
|
kMissileLaunchedTime = 12,
|
|
kMaxDeactivatedStart = 13,
|
|
kMaxDeactivatedStop = 23,
|
|
|
|
kGamePlaying = 1,
|
|
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),
|
|
_robotMovie(kGlobeRobotID), _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID),
|
|
_upperNamesMovie(kGlobeUpperNamesID), _lowerNamesMovie(kGlobeLowerNamesID),
|
|
_globeNotification(kNoradGlobeNotificationID, g_vm), _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::setSoundFXLevel(const uint16 fxLevel) {
|
|
_monitorMovie.setVolume(fxLevel);
|
|
}
|
|
|
|
void GlobeGame::openInteraction() {
|
|
if (g_vm->isDVD()) {
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor1");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.setDisplayOrder(kGlobeMonitorLayer);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
|
|
_robotCallBack.setNotification(&_globeNotification);
|
|
_robotCallBack.initCallBack(&_robotMovie, kCallBackAtExtremes);
|
|
_robotCallBack.setCallBackFlag(kGlobeRobot1Finished);
|
|
_robotCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
}
|
|
|
|
_monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor");
|
|
_monitorMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_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(g_vm->_resFork, kGlobeCircleLeftPICTID, true);
|
|
_globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop);
|
|
_globeCircleLeft.setDisplayOrder(kGlobeCircleLayer);
|
|
_globeCircleLeft.startDisplaying();
|
|
|
|
_globeCircleRight.initFromPICTResource(g_vm->_resFork, kGlobeCircleRightPICTID, true);
|
|
_globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop);
|
|
_globeCircleRight.setDisplayOrder(kGlobeCircleLayer);
|
|
_globeCircleRight.startDisplaying();
|
|
|
|
_globeCircleUp.initFromPICTResource(g_vm->_resFork, kGlobeCircleUpPICTID, true);
|
|
_globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop);
|
|
_globeCircleUp.setDisplayOrder(kGlobeCircleLayer);
|
|
_globeCircleUp.startDisplaying();
|
|
|
|
_globeCircleDown.initFromPICTResource(g_vm->_resFork, kGlobeCircleDownPICTID, true);
|
|
_globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop);
|
|
_globeCircleDown.setDisplayOrder(kGlobeCircleLayer);
|
|
_globeCircleDown.startDisplaying();
|
|
|
|
_motionHighlightLeft.initFromPICTResource(g_vm->_resFork, kMotionHiliteLeftPICTID, true);
|
|
_motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop);
|
|
_motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer);
|
|
_motionHighlightLeft.startDisplaying();
|
|
|
|
_motionHighlightRight.initFromPICTResource(g_vm->_resFork, kMotionHiliteRightPICTID, true);
|
|
_motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop);
|
|
_motionHighlightRight.setDisplayOrder(kGlobeCircleLayer);
|
|
_motionHighlightRight.startDisplaying();
|
|
|
|
_motionHighlightUp.initFromPICTResource(g_vm->_resFork, kMotionHiliteUpPICTID, true);
|
|
_motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop);
|
|
_motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer);
|
|
_motionHighlightUp.startDisplaying();
|
|
|
|
_motionHighlightDown.initFromPICTResource(g_vm->_resFork, kMotionHiliteDownPICTID, true);
|
|
_motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop);
|
|
_motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer);
|
|
_motionHighlightDown.startDisplaying();
|
|
|
|
_targetHighlightUpperLeft.initFromPICTResource(g_vm->_resFork, kTargetUpperLeftPICTID, true);
|
|
_targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop);
|
|
_targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer);
|
|
_targetHighlightUpperLeft.startDisplaying();
|
|
|
|
_targetHighlightUpperRight.initFromPICTResource(g_vm->_resFork, kTargetUpperRightPICTID, true);
|
|
_targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop);
|
|
_targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer);
|
|
_targetHighlightUpperRight.startDisplaying();
|
|
|
|
_targetHighlightLowerLeft.initFromPICTResource(g_vm->_resFork, kTargetLowerLeftPICTID, true);
|
|
_targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop);
|
|
_targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer);
|
|
_targetHighlightLowerLeft.startDisplaying();
|
|
|
|
_targetHighlightLowerRight.initFromPICTResource(g_vm->_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() {
|
|
if (g_vm->isDVD())
|
|
_robotMovie.start();
|
|
_monitorMovie.start();
|
|
_monitorMovie.redrawMovieWorld();
|
|
}
|
|
|
|
void GlobeGame::closeInteraction() {
|
|
if (g_vm->isDVD()) {
|
|
_robotMovie.stop();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
_robotCallBack.releaseCallBack();
|
|
}
|
|
|
|
_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:
|
|
if (!g_vm->isDVD()) {
|
|
_monitorMovie.stop();
|
|
_monitorMovie.setSegment(0, _monitorMovie.getDuration());
|
|
_monitorMovie.setTime(kSplash2End * scale - 1);
|
|
_monitorMovie.redrawMovieWorld();
|
|
_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);
|
|
_monitorMovie.redrawMovieWorld();
|
|
_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;
|
|
default:
|
|
break;
|
|
}
|
|
// fall through
|
|
// FIXME: fall through intentional?
|
|
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:
|
|
if (!g_vm->isDVD()) {
|
|
_owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut,
|
|
kFilterNoInput, kSpotSoundCompletedFlag);
|
|
} else {
|
|
_robotMovie.hide();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor2");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
_robotMovie.start();
|
|
|
|
_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!g_vm->isDVD()) {
|
|
_owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput,
|
|
kSpotSoundCompletedFlag);
|
|
} else {
|
|
_robotMovie.hide();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor3");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
_robotMovie.start();
|
|
|
|
_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
|
|
}
|
|
break;
|
|
case 7:
|
|
if (!g_vm->isDVD()) {
|
|
_owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput,
|
|
kSpotSoundCompletedFlag);
|
|
} else {
|
|
_robotMovie.hide();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor4");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
_robotMovie.start();
|
|
|
|
_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
|
|
}
|
|
break;
|
|
case 9:
|
|
if (!g_vm->isDVD()) {
|
|
_owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut,
|
|
kFilterNoInput, kSpotSoundCompletedFlag);
|
|
} else {
|
|
_robotMovie.hide();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor5");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
_robotMovie.start();
|
|
|
|
_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
|
|
}
|
|
break;
|
|
default:
|
|
_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut,
|
|
kFilterNoInput, kSpotSoundCompletedFlag);
|
|
_monitorMovie.setTime(kNewLaunchSiloTime * scale);
|
|
_monitorMovie.redrawMovieWorld();
|
|
_gameState = kPlayingNewSilo1;
|
|
break;
|
|
}
|
|
break;
|
|
case kRobotTaunting:
|
|
_owner->requestDelay(1, 2, 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:
|
|
_monitorMovie.stop();
|
|
_monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale);
|
|
_monitorMovie.setFlags(kLoopTimeBase);
|
|
_monitorMovie.start();
|
|
if (!g_vm->isDVD()) {
|
|
_owner->getExtraEntry(kN79BrightView, entry);
|
|
_owner->showViewFrame(entry.movieStart);
|
|
_owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0);
|
|
_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
|
|
_gameState = kPlayingRobotIntro;
|
|
}
|
|
break;
|
|
case kGlobeRobot1Finished:
|
|
if (g_vm->isDVD()) {
|
|
_owner->getExtraEntry(kN79BrightView, entry);
|
|
_monitorMovie.stop();
|
|
_monitorMovie.setSegment(0, _monitorMovie.getDuration());
|
|
_monitorMovie.setTime(kSplash2End * scale - 1);
|
|
_monitorMovie.redrawMovieWorld();
|
|
_monitorMovie.setFlags(0);
|
|
|
|
_owner->showViewFrame(entry.movieStart);
|
|
_owner->requestDelay(1, 2, kFilterNoInput, 0);
|
|
_owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
|
|
kFilterNoInput, kSpotSoundCompletedFlag);
|
|
|
|
_gameState = kPlayingStrikeAuthorized;
|
|
}
|
|
break;
|
|
case kGlobeTimerExpired:
|
|
// Missile launched, player loses.
|
|
_upperNamesMovie.hide();
|
|
_lowerNamesMovie.hide();
|
|
_countdown.hide();
|
|
_monitorMovie.setTime(kMissileLaunchedTime * scale);
|
|
_monitorMovie.redrawMovieWorld();
|
|
_owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag);
|
|
_gameState = kPlayerLost1;
|
|
break;
|
|
case kMaxDeactivatedFinished:
|
|
_monitorMovie.stop();
|
|
_monitorMovie.setSegment(0, _monitorMovie.getDuration());
|
|
if (g_arthurChip)
|
|
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurNoradFinishedGlobeGame);
|
|
if (!g_vm->isDVD()) {
|
|
_owner->requestDelay(1, 2, kFilterNoInput, 0);
|
|
_owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0);
|
|
_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
|
|
} else {
|
|
_robotMovie.hide();
|
|
_robotMovie.stopDisplaying();
|
|
_robotMovie.releaseMovie();
|
|
|
|
_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor6");
|
|
_robotMovie.setVolume(g_vm->getSoundFXLevel());
|
|
_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
_robotMovie.setDisplayOrder(kGlobeCountdownLayer + 1);
|
|
_robotMovie.startDisplaying();
|
|
_robotMovie.show();
|
|
_robotMovie.start();
|
|
|
|
_owner->requestDelay(_robotMovie.getDuration(), 0, 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 (g_vm->_cursor->isVisible() && spot != nullptr &&
|
|
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) {
|
|
Movie movie(kNoDisplayElement);
|
|
Input movieInput;
|
|
|
|
if (g_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
|
|
g_vm->_cursor->hide();
|
|
|
|
movie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor7");
|
|
movie.setVolume(g_vm->getSoundFXLevel());
|
|
movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
|
|
movie.setDisplayOrder(kGlobeCountdownLayer + 1);
|
|
movie.startDisplaying();
|
|
movie.show();
|
|
movie.start();
|
|
|
|
while (movie.isRunning() && !g_vm->shouldQuit()) {
|
|
InputDevice.getInput(movieInput, kFilterNoInput);
|
|
|
|
g_vm->checkCallBacks();
|
|
g_vm->refreshDisplay();
|
|
g_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
if (g_vm->shouldQuit())
|
|
return;
|
|
|
|
movie.hide();
|
|
movie.stopDisplaying();
|
|
movie.releaseMovie();
|
|
|
|
g_vm->_cursor->hideUntilMoved();
|
|
} else {
|
|
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);
|
|
|
|
// This sound was left out of the original.
|
|
_owner->requestSpotSound(kAllSilosDeactivatedIn, kAllSilosDeactivatedOut,
|
|
kFilterNoInput, kSpotSoundCompletedFlag);
|
|
|
|
_gameState = kPlayerWon1;
|
|
} else {
|
|
_owner->requestDelay(1, 2, 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?
|
|
if (g_arthurChip)
|
|
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB38", kArthurNoradSelectedIncorrectSilo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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() + (kSiloDeactivatedOut - kSiloDeactivatedIn), kMaxDeactivatedStop * _monitorMovie.getScale());
|
|
_monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale() + (kSiloDeactivatedOut - kSiloDeactivatedIn));
|
|
_monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
|
|
_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
_monitorMovie.start();
|
|
_owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag);
|
|
_owner->requestSpotSound(kAllSilosDeactivatedIn, kAllSilosDeactivatedOut, kFilterNoInput, kSpotSoundCompletedFlag);
|
|
_gameState = kPlayerWon1;
|
|
}
|
|
|
|
} // End of namespace Pegasus
|