Files
scummvm-cursorfix/engines/pegasus/neighborhood/norad/delta/globegame.cpp
2026-02-02 04:50:13 +01:00

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