Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
/* 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/pegasus.h"
#include "pegasus/items/biochips/arthurchip.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
namespace Pegasus {
static const NotificationFlags kECRSection1FinishedFlag = 1;
static const NotificationFlags kECRPanFinishedFlag = kECRSection1FinishedFlag << 1;
static const NotificationFlags kECRSection2FinishedFlag = kECRPanFinishedFlag << 1;
static const NotificationFlags kECRNotificationFlags = kECRSection1FinishedFlag |
kECRPanFinishedFlag |
kECRSection2FinishedFlag;
static const TimeValue kSection1Start = 0;
static const TimeValue kSection1Stop = 25;
static const TimeValue kPanStart = 0;
static const TimeValue kPanStop = 20;
static const TimeValue kSection2Start = 26;
static const TimeValue kSection2Stop = 1000;
// Seems to be a good value for a 20 second pan.
enum {
kPanPixelsPerFrame = 8
};
// Interesting times are in seconds.
static const TimeValue s_ECRInterestingTimes[] = {
0, 1, 2, 10, 25, 26, 56, 64, 72, 80, 88, 94, 102, 108, 116, 999
};
// Index into s_ECRInterestingTimes of interesting time before security pan.
static const int kBeforePanTime = 3;
// Index into s_ECRInterestingTimes of interesting time after security pan.
static const int kAfterPanTime = 5;
NoradAlphaECRMonitor::NoradAlphaECRMonitor(Neighborhood *nextHandler) : GameInteraction(kNoradECRMonitorInteractionID, nextHandler),
_ecrSlideShowNotification(kNoradECRNotificationID, g_vm), _ecrMovie(kECRSlideShowMovieID),
_ecrPan(kECRPanID) {
}
void NoradAlphaECRMonitor::receiveNotification(Notification *, const NotificationFlags flags) {
if (flags & kECRSection1FinishedFlag)
ecrSection1Finished();
else if (flags & kECRPanFinishedFlag)
ecrPanFinished();
else if (flags & kECRSection2FinishedFlag)
ecrSection2Finished();
}
int NoradAlphaECRMonitor::findCurrentInterestingTime() {
TimeValue time = _ecrMovie.getTime();
TimeScale scale = _ecrMovie.getScale();
for (int i = ARRAYSIZE(s_ECRInterestingTimes) - 1; i >= 0; i--)
if (time >= s_ECRInterestingTimes[i] * scale)
return i;
return 0;
}
void NoradAlphaECRMonitor::skipToNextInterestingTime() {
if (_ecrMovie.isRunning()) {
int interestingTime = findCurrentInterestingTime();
_ecrMovie.setTime(s_ECRInterestingTimes[interestingTime + 1] * _ecrMovie.getScale());
_ecrMovie.redrawMovieWorld();
} else if (_ecrPan.isRunning()) {
_ecrPanCallBack.cancelCallBack();
ecrPanFinished();
}
}
void NoradAlphaECRMonitor::skipToPreviousInterestingTime() {
if (_ecrPan.isRunning()) {
_ecrPan.stop();
_ecrPan.stopDisplaying();
_ecrPanCallBack.cancelCallBack();
_ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag);
_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
TimeScale scale = _ecrMovie.getScale();
_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
_ecrMovie.setTime(s_ECRInterestingTimes[kBeforePanTime] * scale);
_ecrMovie.start();
} else {
int interestingTime = findCurrentInterestingTime();
if (interestingTime == kAfterPanTime) {
_ecrMovieCallBack.cancelCallBack();
TimeScale scale = _ecrMovie.getScale();
_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
_ecrMovie.setTime(kSection1Stop * scale);
ecrSection1Finished();
} else if (interestingTime == 0) {
_ecrMovie.setTime(kSection1Start * _ecrMovie.getScale());
_ecrMovie.redrawMovieWorld();
} else {
_ecrMovie.setTime(s_ECRInterestingTimes[interestingTime - 1] * _ecrMovie.getScale());
_ecrMovie.redrawMovieWorld();
}
}
}
void NoradAlphaECRMonitor::handleInput(const Input &input, const Hotspot *cursorSpot) {
if (isInteracting()) {
if (input.rightButtonDown())
skipToNextInterestingTime();
else if (input.leftButtonDown())
skipToPreviousInterestingTime();
else
InputHandler::handleInput(input, cursorSpot);
} else {
InputHandler::handleInput(input, cursorSpot);
}
}
void NoradAlphaECRMonitor::ecrSection1Finished() {
_ecrMovie.stop();
_ecrPanCallBack.setNotification(&_ecrSlideShowNotification);
_ecrPanCallBack.initCallBack(&_ecrPan, kCallBackAtExtremes);
_ecrPanCallBack.setCallBackFlag(kECRPanFinishedFlag);
_ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags);
_ecrPanCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_ecrPan.startDisplaying();
_ecrPan.show();
TimeScale scale = _ecrPan.getScale();
_ecrPan.setSegment(kPanStart * scale, kPanStop * scale);
_ecrPan.setTime(0);
_ecrPan.start();
}
void NoradAlphaECRMonitor::ecrPanFinished() {
_ecrPan.stop();
_ecrPan.stopDisplaying();
_ecrMovieCallBack.setCallBackFlag(kECRSection2FinishedFlag);
_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
TimeScale scale = _ecrMovie.getScale();
_ecrMovie.setSegment(kSection2Start * scale, kSection2Stop * scale);
_ecrMovie.start();
}
void NoradAlphaECRMonitor::ecrSection2Finished() {
_ecrMovie.stop();
_ecrMovie.stopDisplaying();
}
void NoradAlphaECRMonitor::openInteraction() {
// Initialize the security pan.
_ecrPan.initFromMovieFile("Images/Norad Alpha/Security Pan.pano");
_ecrPan.initMaskFromPICTFile("Images/Norad Alpha/Security Pan Mask");
_ecrPan.setBounds(Common::Rect(kECRPanLeft, kECRPanTop, kECRPanRight, kECRPanBottom));
_ecrPan.setDisplayOrder(kECRPanOrder);
_ecrPan.setScale(15); // 15 fps.
// Begin the lame ECR slide show.
// clone2727: I didn't say it :P
_ecrMovie.initFromMovieFile("Images/Norad Alpha/ECR Monitor Movie");
_ecrMovieCallBack.setNotification(&_ecrSlideShowNotification);
_ecrMovieCallBack.initCallBack(&_ecrMovie, kCallBackAtExtremes);
_ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag);
_ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags);
_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_ecrMovie.moveElementTo(kECRSlideShowLeft, kECRSlideShowTop);
_ecrMovie.setDisplayOrder(kECRMonitorOrder);
_ecrMovie.startDisplaying();
_ecrMovie.show();
_ecrMovie.redrawMovieWorld();
TimeScale scale = _ecrMovie.getScale();
_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1);
_ecrMovie.start();
}
void NoradAlphaECRMonitor::closeInteraction() {
_ecrMovieCallBack.releaseCallBack();
_ecrMovie.stop();
_ecrMovie.stopDisplaying();
_ecrMovie.releaseMovie();
_ecrMovieCallBack.releaseCallBack();
_ecrPanCallBack.releaseCallBack();
_ecrPan.stop();
_ecrPan.stopDisplaying();
_ecrPan.releasePanorama();
_ecrPanCallBack.releaseCallBack();
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB33", kArthurNoradAtSecurityMonitor);
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H
#include "pegasus/interaction.h"
#include "pegasus/notification.h"
#include "pegasus/neighborhood/norad/alpha/panoramascroll.h"
namespace Pegasus {
class NoradAlphaECRMonitor : public GameInteraction, public NotificationReceiver {
public:
NoradAlphaECRMonitor(Neighborhood *);
~NoradAlphaECRMonitor() override {}
void handleInput(const Input &, const Hotspot *) override;
protected:
void openInteraction() override;
void closeInteraction() override;
void receiveNotification(Notification *, const NotificationFlags) override;
void ecrSection1Finished();
void ecrPanFinished();
void ecrSection2Finished();
int findCurrentInterestingTime();
void skipToNextInterestingTime();
void skipToPreviousInterestingTime();
Notification _ecrSlideShowNotification;
Movie _ecrMovie;
NotificationCallBack _ecrMovieCallBack;
PanoramaScroll _ecrPan;
NotificationCallBack _ecrPanCallBack;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,459 @@
/* 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/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/items/biochips/arthurchip.h"
#include "pegasus/items/inventory/airmask.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/alpha/fillingstation.h"
#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
namespace Pegasus {
static const NotificationFlags kFSPowerUpFinishedFlag = 1;
static const NotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1;
static const NotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1;
static const NotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1;
static const NotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1;
static const NotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1;
static const NotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1;
static const NotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1;
static const NotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1;
static const NotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1;
static const NotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag |
kFSSplashFinishedFlag |
kFSIntakeWarningFinishedFlag |
kFSIntakeHiliteFinishedFlag |
kFSDispenseHiliteFinishedFlag |
kFSArHiliteFinishedFlag |
kFSCO2HiliteFinishedFlag |
kFSHeHiliteFinishedFlag |
kFSOHiliteFinishedFlag |
kFSNHiliteFinishedFlag;
static const int16 kNoState = 0;
static const int16 kMainMenu = 1;
static const int16 kWaitingForAttach = 2;
static const int16 kDispenseMenu = 3;
static const int16 kWaitingForDispense = 4;
// Dummy itemIDs
static const ItemID kCO2Item = 10000;
static const ItemID kHeItem = 10001;
// Interactive points.
enum {
kFSPowerUpStartStart = 0,
kFSPowerUpStartStop = 600,
kFSSplashStart = 600,
kFSSplashStop = 7800,
kFSSplashIntakeStart = 7800,
kFSSplashIntakeStop = 18600,
kFSMainMenu = 18600,
kFSIntakeHiliteStart = 19200,
kFSIntakeHiliteStop = 19800,
kFSDispenseHiliteStart = 19800,
kFSDispenseHiliteStop = 20400,
kFSDispenseMenu = 20400,
kFSArHiliteStart = 21000,
kFSArHiliteStop = 21600,
kFSArAttach = 21600,
kFSArFilledStart = 22200,
kFSArFilledStop = 25200,
kFSArIncompatibleStart = 25200,
kFSArIncompatibleStop = 30000,
kFSCO2HiliteStart = 30000,
kFSCO2HiliteStop = 30600,
kFSCO2Attach = 30600,
kFSCO2FilledStart = 31200,
kFSCO2FilledStop = 34200,
kFSCO2IncompatibleStart = 34200,
kFSCO2IncompatibleStop = 39000,
kFSHeHiliteStart = 39000,
kFSHeHiliteStop = 39600,
kFSHeAttach = 39600,
kFSHeFilledStart = 40200,
kFSHeFilledStop = 43200,
kFSHeIncompatibleStart = 43200,
kFSHeIncompatibleStop = 48000,
kFSOHiliteStart = 48000,
kFSOHiliteStop = 48600,
kFSOAttach = 48600,
kFSOFilledStart = 49200,
kFSOFilledStop = 52200,
kFSOIncompatibleStart = 52200,
kFSOIncompatibleStop = 57000,
kFSNHiliteStart = 57000,
kFSNHiliteStop = 57600,
kFSNAttach = 57600,
kFSNFilledStart = 58200,
kFSNFilledStop = 61200,
kFSNIncompatibleStart = 61200,
kFSNIncompatibleStop = 66000,
kFSIntakeMenu = 66000,
kFSIntakeInProgressStart = 66600,
kFSIntakeInProgressStop = 69600
};
NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner),
_rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, g_vm) {
_state = kNoState;
}
void NoradAlphaFillingStation::openInteraction() {
_rightSideMovie.initFromMovieFile("Images/Norad Alpha/N01W Right Side");
_rightSideMovie.moveElementTo(kNoradAlpha01RightSideLeft, kNoradAlpha01RightSideTop);
_rightSideMovie.setDisplayOrder(kN01RightSideOrder);
_rightSideMovie.startDisplaying();
_rightSideCallBack.setNotification(&_rightSideNotification);
_rightSideCallBack.initCallBack(&_rightSideMovie, kCallBackAtExtremes);
_rightSideCallBack.setCallBackFlag(kFSPowerUpFinishedFlag);
_rightSideNotification.notifyMe(this, kFSNotificationFlags, kFSNotificationFlags);
_rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_rightSideMovie.show();
_rightSideMovie.redrawMovieWorld();
_rightSideMovie.setSegment(kFSPowerUpStartStart, kFSPowerUpStartStop);
}
void NoradAlphaFillingStation::initInteraction() {
allowInput(false);
_rightSideMovie.setRate(2);
}
void NoradAlphaFillingStation::closeInteraction() {
_rightSideMovie.stop();
_rightSideMovie.stopDisplaying();
_rightSideMovie.releaseMovie();
_rightSideCallBack.releaseCallBack();
((NoradAlpha *)getOwner())->turnOffFillingStation();
}
void NoradAlphaFillingStation::setStaticState(TimeValue time, int16 state) {
_rightSideMovie.stop();
_rightSideMovie.setSegment(0, _rightSideMovie.getDuration());
_rightSideMovie.setTime(time);
_rightSideMovie.redrawMovieWorld();
_state = state;
allowInput(true);
}
void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, NotificationFlags flag, int16 state) {
_rightSideMovie.stop();
_rightSideMovie.setSegment(start, stop);
_rightSideMovie.setTime(start);
_rightSideCallBack.setCallBackFlag(flag);
_rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_state = state;
allowInput(false);
_rightSideMovie.setRate(2);
}
void NoradAlphaFillingStation::powerUpFinished() {
((NoradAlpha *)getOwner())->turnOnFillingStation();
setSegmentState(kFSSplashStart, kFSSplashStop, kFSSplashFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::splashFinished() {
if (GameState.getNoradGassed())
setSegmentState(kFSSplashIntakeStart, kFSSplashIntakeStop, kFSIntakeWarningFinishedFlag, kNoState);
else
intakeWarningFinished();
}
void NoradAlphaFillingStation::intakeWarningFinished() {
setStaticState(kFSMainMenu, kMainMenu);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA29", kArthurNoradSawIntakeWarning);
}
void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) {
if (numSeconds == 0) {
setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStop, kFSIntakeWarningFinishedFlag, kNoState);
Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
if (item->getObjectID() == kGasCanister) {
GameState.setNoradGassed(true);
((NoradAlpha *)getOwner())->checkAirMask();
getOwner()->restoreStriding(kNorad03, kEast, kAltNoradAlphaNormal);
}
} else {
setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStart + _rightSideMovie.getScale() * numSeconds,
kFSIntakeWarningFinishedFlag, kNoState);
}
}
void NoradAlphaFillingStation::intakeHighlightFinished() {
_rightSideMovie.stop();
if (GameState.getNoradGassed()) {
showIntakeInProgress(2);
} else {
Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
if (item)
showIntakeInProgress(0);
else
setStaticState(kFSIntakeMenu, kWaitingForAttach);
}
}
void NoradAlphaFillingStation::dispenseHighlightFinished() {
setStaticState(kFSDispenseMenu, kDispenseMenu);
}
void NoradAlphaFillingStation::dispenseGas() {
Item *item = ((NoradAlpha *)getOwner())->getFillingItem();
if (item) {
if (item->getObjectID() != _dispenseItemID)
switch (_dispenseItemID) {
case kArgonCanister:
setSegmentState(kFSArIncompatibleStart, kFSArIncompatibleStop,
kFSIntakeWarningFinishedFlag, kNoState);
break;
case kCO2Item:
setSegmentState(kFSCO2IncompatibleStart, kFSCO2IncompatibleStop,
kFSIntakeWarningFinishedFlag, kNoState);
break;
case kHeItem:
setSegmentState(kFSHeIncompatibleStart, kFSHeIncompatibleStop,
kFSIntakeWarningFinishedFlag, kNoState);
break;
case kAirMask:
setSegmentState(kFSOIncompatibleStart, kFSOIncompatibleStop,
kFSIntakeWarningFinishedFlag, kNoState);
break;
case kNitrogenCanister:
setSegmentState(kFSNIncompatibleStart, kFSNIncompatibleStop,
kFSIntakeWarningFinishedFlag, kNoState);
break;
default:
break;
}
else {
if (_dispenseItemID == kArgonCanister) {
setSegmentState(kFSArFilledStart, kFSArFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
item->setItemState(kArgonFull);
GameState.setScoringFilledArgonCanister(true);
} else if (_dispenseItemID == kAirMask) {
setSegmentState(kFSOFilledStart, kFSOFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
((AirMask *)item)->refillAirMask();
GameState.setScoringFilledOxygenCanister(true);
} else if (_dispenseItemID == kNitrogenCanister) {
setSegmentState(kFSNFilledStart, kFSNFilledStop, kFSIntakeWarningFinishedFlag, kNoState);
item->setItemState(kNitrogenFull);
}
}
} else {
switch (_dispenseItemID) {
case kArgonCanister:
setStaticState(kFSArAttach, kWaitingForDispense);
break;
case kCO2Item:
setStaticState(kFSCO2Attach, kWaitingForDispense);
break;
case kHeItem:
setStaticState(kFSHeAttach, kWaitingForDispense);
break;
case kAirMask:
setStaticState(kFSOAttach, kWaitingForDispense);
break;
case kNitrogenCanister:
setStaticState(kFSNAttach, kWaitingForDispense);
break;
default:
break;
}
}
}
void NoradAlphaFillingStation::ArHighlightFinished() {
_dispenseItemID = kArgonCanister;
dispenseGas();
}
void NoradAlphaFillingStation::CO2HighlightFinished() {
_dispenseItemID = kCO2Item;
dispenseGas();
}
void NoradAlphaFillingStation::HeHighlightFinished() {
_dispenseItemID = kHeItem;
dispenseGas();
}
void NoradAlphaFillingStation::OHighlightFinished() {
_dispenseItemID = kAirMask;
dispenseGas();
}
void NoradAlphaFillingStation::NHighlightFinished() {
_dispenseItemID = kNitrogenCanister;
dispenseGas();
}
void NoradAlphaFillingStation::receiveNotification(Notification *, const NotificationFlags flags) {
switch (flags) {
case kFSPowerUpFinishedFlag:
powerUpFinished();
break;
case kFSSplashFinishedFlag:
splashFinished();
break;
case kFSIntakeWarningFinishedFlag:
intakeWarningFinished();
break;
case kFSIntakeHiliteFinishedFlag:
intakeHighlightFinished();
break;
case kFSDispenseHiliteFinishedFlag:
dispenseHighlightFinished();
break;
case kFSArHiliteFinishedFlag:
ArHighlightFinished();
break;
case kFSCO2HiliteFinishedFlag:
CO2HighlightFinished();
break;
case kFSHeHiliteFinishedFlag:
HeHighlightFinished();
break;
case kFSOHiliteFinishedFlag:
OHighlightFinished();
break;
case kFSNHiliteFinishedFlag:
NHighlightFinished();
break;
default:
break;
}
}
void NoradAlphaFillingStation::handleInput(const Input &input, const Hotspot *cursorSpot) {
InputHandler::handleInput(input, cursorSpot);
}
void NoradAlphaFillingStation::clickInIntake() {
setSegmentState(kFSIntakeHiliteStart, kFSIntakeHiliteStop, kFSIntakeHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInDispense() {
setSegmentState(kFSDispenseHiliteStart, kFSDispenseHiliteStop, kFSDispenseHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInAr() {
setSegmentState(kFSArHiliteStart, kFSArHiliteStop, kFSArHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInCO2() {
setSegmentState(kFSCO2HiliteStart, kFSCO2HiliteStop, kFSCO2HiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInHe() {
setSegmentState(kFSHeHiliteStart, kFSHeHiliteStop, kFSHeHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInO() {
setSegmentState(kFSOHiliteStart, kFSOHiliteStop, kFSOHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInN() {
setSegmentState(kFSNHiliteStart, kFSNHiliteStop, kFSNHiliteFinishedFlag, kNoState);
}
void NoradAlphaFillingStation::clickInHotspot(const Input &input, const Hotspot *spot) {
GameInteraction::clickInHotspot(input, spot);
switch (spot->getObjectID()) {
case kNorad01IntakeSpotID:
clickInIntake();
break;
case kNorad01DispenseSpotID:
clickInDispense();
break;
case kNorad01ArSpotID:
clickInAr();
break;
case kNorad01CO2SpotID:
clickInCO2();
break;
case kNorad01HeSpotID:
clickInHe();
break;
case kNorad01OSpotID:
clickInO();
break;
case kNorad01NSpotID:
clickInN();
break;
default:
break;
}
}
void NoradAlphaFillingStation::activateHotspots() {
GameInteraction::activateHotspots();
switch (_state) {
case kMainMenu:
g_allHotspots.activateOneHotspot(kNorad01IntakeSpotID);
g_allHotspots.activateOneHotspot(kNorad01DispenseSpotID);
break;
case kDispenseMenu:
g_allHotspots.activateOneHotspot(kNorad01ArSpotID);
g_allHotspots.activateOneHotspot(kNorad01CO2SpotID);
g_allHotspots.activateOneHotspot(kNorad01HeSpotID);
g_allHotspots.activateOneHotspot(kNorad01OSpotID);
g_allHotspots.activateOneHotspot(kNorad01NSpotID);
break;
default:
break;
}
}
void NoradAlphaFillingStation::newFillingItem(Item *item) {
switch (_state) {
case kWaitingForAttach:
if (item)
showIntakeInProgress(0);
break;
case kWaitingForDispense:
dispenseGas();
break;
default:
break;
}
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,90 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H
#include "pegasus/interaction.h"
#include "pegasus/movie.h"
#include "pegasus/notification.h"
namespace Pegasus {
class Item;
class NoradAlphaFillingStation : public GameInteraction, public NotificationReceiver {
public:
NoradAlphaFillingStation(Neighborhood *);
~NoradAlphaFillingStation() override {}
void handleInput(const Input &, const Hotspot *) override;
void clickInHotspot(const Input &, const Hotspot *) override;
void activateHotspots() override;
void newFillingItem(Item *);
protected:
void receiveNotification(Notification *, const NotificationFlags) override;
void openInteraction() override;
void initInteraction() override;
void closeInteraction() override;
void powerUpFinished();
void splashFinished();
void intakeWarningFinished();
void intakeHighlightFinished();
void dispenseHighlightFinished();
void ArHighlightFinished();
void CO2HighlightFinished();
void HeHighlightFinished();
void OHighlightFinished();
void NHighlightFinished();
void showIntakeInProgress(uint16);
void clickInIntake();
void clickInDispense();
void clickInAr();
void clickInCO2();
void clickInHe();
void clickInO();
void clickInN();
void dispenseGas();
void setStaticState(TimeValue, int16);
void setSegmentState(TimeValue, TimeValue, NotificationFlags, int16);
Movie _rightSideMovie;
Notification _rightSideNotification;
NotificationCallBack _rightSideCallBack;
int16 _state;
ItemID _dispenseItemID;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,969 @@
/* 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/energymonitor.h"
#include "pegasus/gamestate.h"
#include "pegasus/interface.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/items/biochips/arthurchip.h"
#include "pegasus/items/inventory/airmask.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/subcontrolroom.h"
#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
#include "pegasus/neighborhood/norad/alpha/fillingstation.h"
#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
#include "pegasus/neighborhood/norad/alpha/subchase.h"
namespace Pegasus {
static const ExtraID kShowThermalScan = 1000;
static const HotSpotID kThermalScanHotSpotID = 10000;
const uint32 NoradAlpha::_noradAlphaClawExtras[22] = {
kN22ClawFromAToB,
kN22ClawALoop,
kN22ClawAPinch,
kN22ClawACounterclockwise,
kN22ClawAClockwise,
kN22ClawFromBToA,
kN22ClawFromBToC,
kN22ClawFromBToD,
kN22ClawBLoop,
kN22ClawBPinch,
kN22ClawBCounterclockwise,
kN22ClawBClockwise,
kN22ClawFromCToB,
kN22ClawCLoop,
kN22ClawCPinch,
kN22ClawCCounterclockwise,
kN22ClawCClockwise,
kN22ClawFromDToB,
kN22ClawDLoop,
kN22ClawDPinch,
kN22ClawDCounterclockwise,
kN22ClawDClockwise
};
NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner)
: Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID),
_thermalScanSpot(kThermalScanHotSpotID), _extraMovie(kNoDisplayElement) {
_elevatorUpRoomID = kNorad11South;
_elevatorDownRoomID = kNorad12South;
_elevatorUpSpotID = kNorad12ElevatorUpSpotID;
_elevatorDownSpotID = kNorad11ElevatorDownSpotID;
_subRoomEntryRoom1 = kNorad10;
_subRoomEntryDir1 = kEast;
_subRoomEntryRoom2 = kNorad21;
_subRoomEntryDir2 = kWest;
_upperPressureDoorRoom = kNorad10East;
_lowerPressureDoorRoom = kNorad21West;
_upperPressureDoorUpSpotID = kAlphaUpperPressureDoorUpSpotID;
_upperPressureDoorDownSpotID = kAlphaUpperPressureDoorDownSpotID;
_upperPressureDoorAbortSpotID = kNorad10EastOutSpotID;
_lowerPressureDoorUpSpotID = kAlphaLowerPressureDoorUpSpotID;
_lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID;
_lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID;
_pressureSoundIn = kAlphaPressureDoorIntro1In;
_pressureSoundOut = kAlphaPressureDoorIntro1Out;
_equalizeSoundIn = kAlphaPressureDoorIntro2In;
_equalizeSoundOut = kAlphaPressureDoorIntro2Out;
_accessDeniedIn = kAlphaAccessDeniedIn;
_accessDeniedOut = kAlphaAccessDeniedOut;
_platformRoom = kNorad19West;
_subControlRoom = kNorad22West;
_subPrepFailed = false;
_fillingStationItem = nullptr;
setIsItemTaken(kGasCanister);
}
NoradAlpha::~NoradAlpha() {
if (_vm->isDVD())
_vm->getAllHotspots().remove(&_thermalScanSpot);
}
void NoradAlpha::init() {
Norad::init();
_extraMovieCallBack.setNotification(&_neighborhoodNotification);
if (_vm->isDVD()) {
_thermalScanSpot.setArea(Common::Rect(216, 112, 336, 312));
_thermalScanSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
_vm->getAllHotspots().push_back(&_thermalScanSpot);
}
Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID);
hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID);
hotspotEntry->hotspotItem = kGasCanister;
hotspot = _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID);
hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID);
hotspotEntry->hotspotItem = kArgonCanister;
hotspot = _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID);
hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID);
hotspotEntry->hotspotItem = kNitrogenCanister;
hotspot = _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID);
hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
hotspotEntry = findHotspotEntry(kN01AirMaskSpotID);
hotspotEntry->hotspotItem = kAirMask;
hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasOutletSpotID);
hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag);
}
void NoradAlpha::start() {
if (g_energyMonitor) {
g_energyMonitor->stopEnergyDraining();
g_energyMonitor->restoreLastEnergyValue();
_vm->resetEnergyDeathReason();
g_energyMonitor->startEnergyDraining();
}
NeighborhoodID itemNeighborhood;
RoomID itemRoom;
DirectionConstant itemDirection;
Item *item = (Item *)_vm->getAllItems().findItemByID(kGasCanister);
item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
if (itemNeighborhood == getObjectID()) {
_fillingStationItem = item;
} else {
item = (Item *)_vm->getAllItems().findItemByID(kAirMask);
item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
if (itemNeighborhood == getObjectID()) {
_fillingStationItem = item;
} else {
item = (Item *)_vm->getAllItems().findItemByID(kNitrogenCanister);
item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
if (itemNeighborhood == getObjectID()) {
_fillingStationItem = item;
} else {
item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
if (itemNeighborhood == getObjectID())
_fillingStationItem = item;
else
_fillingStationItem = nullptr;
}
}
}
if (!GameState.getNoradGassed())
forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal);
GameState.setNoradArrivedFromSub(false);
Norad::start();
}
void NoradAlpha::setUpAIRules() {
Neighborhood::setUpAIRules();
if (g_AIArea) {
AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN01WD1", false);
AIHasItemCondition *hasGasCanisterCondition = new AIHasItemCondition(kGasCanister);
AIRule *rule = new AIRule(hasGasCanisterCondition, messageAction);
g_AIArea->addAIRule(rule);
}
}
bool NoradAlpha::okayToJump() {
bool result = Neighborhood::okayToJump();
if (!result)
playSpotSoundSync(kAlphaCantTransportIn, kAlphaCantTransportOut);
return result;
}
void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) {
if (entry.extra == kNorad19ExitToSub) {
compassMove.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, entry.movieStart, 270 + kSubPlatformCompassAngle,
entry.movieEnd, 90 + 20 + 360);
compassMove.insertFaderKnot(entry.movieStart + 10 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle);
compassMove.insertFaderKnot(entry.movieStart + 29 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20);
compassMove.insertFaderKnot(entry.movieStart + 52 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20);
compassMove.insertFaderKnot(entry.movieStart + 84 * kNoradAlphaFrameDuration, 360 + 90);
compassMove.insertFaderKnot(entry.movieStart + 198 * kNoradAlphaFrameDuration, 360 + 90);
compassMove.insertFaderKnot(entry.movieStart + 270 * kNoradAlphaFrameDuration, 360 + 90 + 15);
compassMove.insertFaderKnot(entry.movieStart + 280 * kNoradAlphaFrameDuration, 360 + 90 + 20);
} else {
Norad::getExtraCompassMove(entry, compassMove);
}
}
void NoradAlpha::playClawMonitorIntro() {
playSpotSoundSync(kAlphaLoadClawIntroIn, kAlphaLoadClawIntroOut);
}
GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) {
switch (interactionID) {
case kNoradECRMonitorInteractionID:
return new NoradAlphaECRMonitor(this);
case kNoradFillingStationInteractionID:
return new NoradAlphaFillingStation(this);
case kNoradSubChaseInteractionID:
return new SubChase(this);
default:
break;
}
return Norad::makeInteraction(interactionID);
}
void NoradAlpha::loadAmbientLoops() {
// clone2727 would like to point out that the following comment does not quite
// match the code logic below
/*
Logic:
loop sound 1:
if gassed,
play warning loop of some sort
else
play nothing
loop sound 2:
if gassed and not wearing air mask
if in ECR
play breathing water loop
else
play breathing
else
if in ECR
play water loop
if at N07 north
play unmanned loop
*/
if (!GameState.getNoradSeenTimeStream() || !g_interface || _vm->getEnergyDeathReason() == kDeathSubDestroyed)
return;
RoomID room = GameState.getCurrentRoom();
if (GameState.getNoradGassed()) {
if (room >= kNorad11 && room <= kNorad19West) {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", kNoradWarningVolume * 3);
else
loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
} else if (room >= kNorad21 && room <= kNorad22West) {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.32K.AIFF", kNoradWarningVolume * 3);
else
loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
} else {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/WARNING LOOP.32K.AIFF", kNoradWarningVolume);
else
loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
}
} else {
loadLoopSound1("");
}
if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) {
if (room >= kNorad01 && room <= kNorad01West) {
loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume);
} else if (room == kNorad02) {
if (GameState.isCurrentDoorOpen())
loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume);
else
loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
} else {
loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
}
} else {
if (room >= kNorad01 && room <= kNorad01West) {
loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2);
} else if (room == kNorad02) {
if (GameState.isCurrentDoorOpen())
loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2);
else
loadLoopSound2("");
} else {
loadLoopSound2("");
}
}
}
void NoradAlpha::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
switch (MakeRoomView(room, direction)) {
case MakeRoomView(kNorad02, kEast):
case MakeRoomView(kNorad06, kEast):
case MakeRoomView(kNorad11, kEast):
case MakeRoomView(kNorad15, kEast):
case MakeRoomView(kNorad19, kWest):
case MakeRoomView(kNorad21, kSouth):
makeContinuePoint();
break;
default:
break;
}
}
void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction) {
Norad::arriveAt(room, direction);
switch (GameState.getCurrentRoom()) {
case kNorad01:
arriveAtNorad01();
break;
case kNorad01East:
arriveAtNorad01East();
break;
case kNorad01West:
arriveAtNorad01West();
break;
case kNorad04:
arriveAtNorad04();
break;
case kNorad07:
if (_vm->isDVD() && GameState.getLastRoom() == kNorad06)
startExtraSequence(kShowThermalScan, kExtraCompletedFlag, kFilterNoInput);
break;
case kNorad07North:
GameState.setScoringSawUnconsciousOperator(true);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA70", kArthurNoradSawUnconsciousOperator);
break;
case kNorad11:
GameState.setScoringWentThroughPressureDoor(true);
break;
case kNorad22:
arriveAtNorad22();
break;
default:
break;
}
}
void NoradAlpha::arriveAtNorad01() {
Item *argonCanister, *nitrogenCanister;
switch (GameState.getCurrentDirection()) {
case kSouth:
if (!GameState.getNoradSeenTimeStream()) {
GameState.setNoradN22MessagePlayed(false);
requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput);
// You are no match for me, human.
requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput);
}
break;
case kEast:
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurNoradAtSecurityMonitor);
break;
case kWest:
if (GameState.getLastRoom() == kNorad01West) {
argonCanister = g_allItems.findItemByID(kArgonCanister);
nitrogenCanister = g_allItems.findItemByID(kNitrogenCanister);
if (((GameState.isTakenItemID(kArgonCanister) && argonCanister->getItemState() != kArgonFull) ||
(GameState.isTakenItemID(kNitrogenCanister) && nitrogenCanister->getItemState() != kNitrogenFull)) && g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA34", kArthurNoradDidntFillCanisters);
}
break;
}
}
void NoradAlpha::arriveAtNorad01East() {
GameState.setScoringSawSecurityMonitor(true);
newInteraction(kNoradECRMonitorInteractionID);
}
void NoradAlpha::arriveAtNorad01West() {
newInteraction(kNoradFillingStationInteractionID);
}
void NoradAlpha::arriveAtNorad04() {
if (GameState.getCurrentDirection() == kEast && !GameState.getNoradGassed())
playDeathExtra(kNorad04EastDeath, kDeathWokeUpNorad);
}
void NoradAlpha::arriveAtNorad22() {
if (!GameState.getNoradN22MessagePlayed() && GameState.getCurrentDirection() == kSouth) {
startExtraSequence(kNorad22SouthIntro, kExtraCompletedFlag, kFilterNoInput);
GameState.setNoradN22MessagePlayed(true);
}
}
void NoradAlpha::turnTo(const DirectionConstant direction) {
Norad::turnTo(direction);
if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01, kEast) && g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurNoradAtSecurityMonitor);
}
void NoradAlpha::bumpIntoWall() {
requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0);
Neighborhood::bumpIntoWall();
}
void NoradAlpha::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
TimeValue segmentStart = 0, segmentStop = 0;
bool loopSequence = false;
Common::Rect pushBounds;
NotificationFlags extraFlags;
switch (extraID) {
case kShowThermalScan:
_turnPush.getBounds(pushBounds);
switch (extraID) {
case kShowThermalScan:
_extraMovie.initFromMovieFile("Images/Norad Alpha/N07NS.movie");
segmentStart = 0;
segmentStop = _extraMovie.getDuration();
loopSequence = false;
break;
default:
break;
}
_lastExtra = extraID;
_turnPush.hide();
if (!loopSequence && g_AIArea)
g_AIArea->lockAIOut();
extraFlags = flags;
_interruptionFilter = interruptionFilter;
// Stop the nav movie before doing anything else
_navMovie.stop();
_navMovie.stopDisplaying();
_extraMovie.setVolume(_vm->getSoundFXLevel());
_extraMovie.moveElementTo(pushBounds.left, pushBounds.top);
_extraMovie.setDisplayOrder(kNavMovieOrder + 1);
_extraMovie.startDisplaying();
_extraMovie.show();
_extraMovie.setFlags(0);
_extraMovie.setSegment(segmentStart, segmentStop);
_extraMovie.setTime(segmentStart);
if (loopSequence)
_extraMovie.setFlags(kLoopTimeBase);
else
extraFlags |= kNeighborhoodMovieCompletedFlag;
_extraMovieCallBack.cancelCallBack();
_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
if (extraFlags != 0) {
_extraMovieCallBack.setCallBackFlag(extraFlags);
_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
}
_extraMovie.start();
break;
default:
Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
break;
}
}
void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) {
if ((flags & kExtraCompletedFlag) != 0) {
switch (_lastExtra) {
case kShowThermalScan:
_interruptionFilter = kFilterAllInput;
_extraMovie.stopDisplaying();
_extraMovie.releaseMovie();
_navMovie.startDisplaying();
break;
case kNorad19ExitToSub:
if (_vm->isDVD()) {
_interruptionFilter = kFilterAllInput;
_vm->_cursor->hide();
setNextHandler(_vm);
throwAwayInterface();
loadLoopSound1("");
newInteraction(kNoradSubChaseInteractionID);
GameState.setScoringEnteredSub(true);
}
break;
case kNoradArriveFromTSA:
GameState.setNoradSeenTimeStream(true);
loadAmbientLoops();
break;
case kNorad01RobotTaunt:
if (_vm->isChattyAI())
g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption);
_interruptionFilter = kFilterAllInput;
makeContinuePoint();
break;
default:
break;
}
}
Norad::receiveNotification(notification, flags);
if ((flags & kExtraCompletedFlag) != 0) {
switch (_lastExtra) {
case kNorad22SouthIntro:
loopExtraSequence(kNorad22SouthReply);
playSpotSoundSync(kN22ReplyIn, kN22ReplyOut);
startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput);
break;
case kNorad22SouthFinish:
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB29", kArthurNoradSawSubMessage);
_interruptionFilter = kFilterAllInput;
// Force ArriveAt to do its thing...
GameState.setCurrentRoom(kNorad21);
arriveAt(kNorad22, kSouth);
break;
case kN22ClawFromAToB:
case kN22ClawAPinch:
case kN22ClawACounterclockwise:
case kN22ClawAClockwise:
case kN22ClawFromBToA:
case kN22ClawFromBToC:
case kN22ClawFromBToD:
case kN22ClawBPinch:
case kN22ClawBCounterclockwise:
case kN22ClawBClockwise:
case kN22ClawFromCToB:
case kN22ClawCPinch:
case kN22ClawCCounterclockwise:
case kN22ClawCClockwise:
case kN22ClawFromDToB:
case kN22ClawDPinch:
case kN22ClawDCounterclockwise:
case kN22ClawDClockwise:
if (g_arthurChip) {
if (_vm->getRandomBit())
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA64", kArthurNoradPlayedWithClaw);
else
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA66", kArthurNoradPlayedWithClaw);
}
break;
default:
break;
}
}
g_AIArea->checkMiddleArea();
}
void NoradAlpha::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) {
Norad::getZoomEntry(spotID, entry);
ExtraTable::Entry extra;
if (spotID == kNorad01GasSpotID) {
if (_fillingStationItem) {
if (_fillingStationItem->getObjectID() == kGasCanister) {
getExtraEntry(kNorad01ZoomInWithGasCanister, extra);
entry.movieStart = extra.movieStart;
entry.movieEnd = extra.movieEnd;
} else {
entry.clear();
}
}
} else if (spotID == kNorad01GasOutSpotID) {
if (_fillingStationItem) {
if (_fillingStationItem->getObjectID() == kGasCanister) {
getExtraEntry(kNorad01ZoomOutWithGasCanister, extra);
entry.movieStart = extra.movieStart;
entry.movieEnd = extra.movieEnd;
} else {
entry.clear();
}
}
}
}
TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant direction) {
ExtraTable::Entry entry;
if (room == kNorad01 && direction == kSouth && !GameState.getNoradSeenTimeStream()) {
getExtraEntry(kNoradArriveFromTSA, entry);
return entry.movieStart;
}
if (room == kNorad01 && direction == kWest) {
if (!_fillingStationItem) {
return Norad::getViewTime(room, direction);
} else {
getExtraEntry(kN01WGasCanister, entry);
return entry.movieStart;
}
} else if (room == kNorad01West && direction == kWest) {
uint32 extraID = 0xffffffff;
if (_fillingStationItem) {
switch (_fillingStationItem->getObjectID()) {
case kArgonCanister:
if (GameState.getNoradFillingStationOn())
extraID = kN01WZArgonCanisterLit;
else
extraID = kN01WZArgonCanisterDim;
break;
case kGasCanister:
if (GameState.getNoradFillingStationOn())
extraID = kN01WZGasCanisterLit;
else
extraID = kN01WZGasCanisterDim;
break;
case kAirMask:
if (GameState.getNoradFillingStationOn())
extraID = kN01WZAirMaskLit;
else
extraID = kN01WZAirMaskDim;
break;
case kNitrogenCanister:
if (GameState.getNoradFillingStationOn())
extraID = kN01WZNitrogenCanisterLit;
else
extraID = kN01WZNitrogenCanisterDim;
break;
default:
// Should never happen.
break;
}
} else if (GameState.getNoradFillingStationOn()) {
extraID = kN01WZEmptyLit;
}
if (extraID == 0xffffffff) {
return Norad::getViewTime(room, direction);
} else {
getExtraEntry(extraID, entry);
return entry.movieStart;
}
}
return Norad::getViewTime(room, direction);
}
void NoradAlpha::setSoundFXLevel(const uint16 level) {
Neighborhood::setSoundFXLevel(level);
if (_extraMovie.isMovieValid())
_extraMovie.setVolume(level);
}
void NoradAlpha::turnOnFillingStation() {
if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) {
GameState.setNoradFillingStationOn(true);
updateViewFrame();
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA72", kArthurNoradSawFillingStation);
}
}
void NoradAlpha::turnOffFillingStation() {
if (GameState.getCurrentRoom() == kNorad01West && GameState.getNoradFillingStationOn()) {
GameState.setNoradFillingStationOn(false);
updateViewFrame();
}
}
void NoradAlpha::activateHotspots() {
Norad::activateHotspots();
switch (GameState.getCurrentRoomAndView()) {
case MakeRoomView(kNorad01West, kWest):
if (_vm->getDragType() == kDragInventoryUse) {
if (!_fillingStationItem) {
ItemID itemID = _vm->getDraggingItem()->getObjectID();
if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask ||
itemID == kNitrogenCanister)
_vm->getAllHotspots().activateOneHotspot(kN01GasOutletSpotID);
}
} else {
HotSpotID spotID;
if (_fillingStationItem) {
switch (_fillingStationItem->getObjectID()) {
case kArgonCanister:
spotID = kN01ArgonCanisterSpotID;
_vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
break;
case kGasCanister:
spotID = kN01GasCanisterSpotID;
break;
case kAirMask:
spotID = kN01AirMaskSpotID;
_vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
break;
case kNitrogenCanister:
spotID = kN01NitrogenCanisterSpotID;
_vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID);
break;
default:
// Should never happen.
spotID = kNoHotSpotID;
break;
}
_vm->getAllHotspots().activateOneHotspot(spotID);
}
}
break;
case MakeRoomView(kNorad07, kNorth):
if (_vm->isDVD())
_vm->getAllHotspots().activateOneHotspot(kThermalScanHotSpotID);
break;
case MakeRoomView(kNorad10, kEast):
if (GameState.isCurrentDoorOpen())
_vm->getAllHotspots().deactivateOneHotspot(kNorad10DoorSpotID);
break;
case MakeRoomView(kNorad21, kWest):
if (GameState.isCurrentDoorOpen())
_vm->getAllHotspots().deactivateOneHotspot(kNorad21WestSpotID);
break;
default:
break;
}
}
void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) {
Norad::clickInHotspot(input, cursorSpot);
if (_vm->getDragType() == kDragInventoryUse) {
if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01West, kWest)) {
Item *item = _vm->getDraggingItem();
if (item->getObjectID() == kAirMask || item->getObjectID() == kArgonCanister ||
item->getObjectID() == kNitrogenCanister || item->getObjectID() == kGasCanister) {
HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasOutletSpotID);
hotspotEntry->hotspotItem = item->getObjectID();
}
}
} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad07, kNorth) &&
cursorSpot->getObjectID() == kThermalScanHotSpotID) {
startExtraSequence(kShowThermalScan, kExtraCompletedFlag, kFilterNoInput);
}
}
void NoradAlpha::takeItemFromRoom(Item *item) {
if (GameState.getCurrentRoom() == kNorad01West) {
if (_fillingStationItem == item) {
_fillingStationItem = nullptr;
GameState.setNoradGassed(false);
checkAirMask();
((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(nullptr);
forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal);
}
}
Norad::takeItemFromRoom(item);
}
void NoradAlpha::dropItemIntoRoom(Item *item, Hotspot *droppedSpot) {
if (GameState.getCurrentRoom() == kNorad01West) {
if (!_fillingStationItem) {
_fillingStationItem = item;
((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(item);
}
}
Norad::dropItemIntoRoom(item, droppedSpot);
}
void NoradAlpha::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 = kNorad22MonitorOutSpotID;
prepSpotID = kNorad22LaunchPrepSpotID;
clawControlSpotID = kNorad22ClawControlSpotID;
pinchClawSpotID = kNorad22ClawPinchSpotID;
moveClawDownSpotID = kNorad22ClawDownSpotID;
moveClawRightSpotID = kNorad22ClawRightSpotID;
moveClawLeftSpotID = kNorad22ClawLeftSpotID;
moveClawUpSpotID = kNorad22ClawUpSpotID;
clawCCWSpotID = kNorad22ClawCCWSpotID;
clawCWSpotID = kNorad22ClawCWSpotID;
clawPosition = kClawAtD;
clawExtraIDs = _noradAlphaClawExtras;
}
Hotspot *NoradAlpha::getItemScreenSpot(Item *item, DisplayElement *element) {
switch (item->getObjectID()) {
case kGasCanister:
return _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID);
case kAirMask:
return _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID);
case kArgonCanister:
return _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID);
case kNitrogenCanister:
return _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID);
default:
break;
}
return Norad::getItemScreenSpot(item, element);
}
Common::Path NoradAlpha::getEnvScanMovie() {
Common::Path movieName = Neighborhood::getEnvScanMovie();
if (movieName.empty()) {
RoomID room = GameState.getCurrentRoom();
if (room >= kNorad01 && room <= kNorad01West)
return "Images/AI/Norad/XNE1";
else if ((room >= kNorad02 && room <= kNorad19West))
return "Images/AI/Norad/XNE2";
return "Images/AI/Norad/XNE3";
}
return movieName;
}
uint NoradAlpha::getNumHints() {
uint numHints = Neighborhood::getNumHints();
if (numHints == 0) {
switch (GameState.getCurrentRoomAndView()) {
case MakeRoomView(kNorad01, kNorth):
case MakeRoomView(kNorad01, kSouth):
case MakeRoomView(kNorad01, kEast):
case MakeRoomView(kNorad01, kWest):
case MakeRoomView(kNorad01East, kEast):
case MakeRoomView(kNorad01West, kWest):
if (GameState.getNoradGassed()) {
if (g_airMask->isAirFilterOn())
numHints = 0;
else
numHints = 3;
} else {
numHints = 2;
}
break;
case MakeRoomView(kNorad19West, kWest):
if (getSubPrepFailed() && GameState.getNoradSubPrepState() != kSubPrepped)
numHints = 1;
break;
case MakeRoomView(kNorad22, kWest):
numHints = 1;
break;
default:
break;
}
}
return numHints;
}
Common::Path NoradAlpha::getHintMovie(uint hintNum) {
Common::Path movieName = Neighborhood::getHintMovie(hintNum);
if (movieName.empty()) {
switch (GameState.getCurrentRoomAndView()) {
case MakeRoomView(kNorad01, kNorth):
case MakeRoomView(kNorad01, kSouth):
case MakeRoomView(kNorad01, kEast):
case MakeRoomView(kNorad01, kWest):
case MakeRoomView(kNorad01East, kEast):
case MakeRoomView(kNorad01West, kWest):
switch (hintNum) {
case 1:
if (GameState.getNoradGassed())
return "Images/AI/Norad/XN01SW";
return "Images/AI/Norad/XN01WD2";
case 2:
if (GameState.getNoradGassed()) {
if (_vm->playerHasItemID(kAirMask))
// Mask must not be on if we get here...
return "Images/AI/Globals/XGLOB1A";
return "Images/AI/Globals/XGLOB3D";
}
return "Images/AI/Globals/XGLOB5C";
case 3:
return "Images/AI/Norad/XN01SH";
default:
break;
}
break;
case MakeRoomView(kNorad19West, kWest):
return "Images/AI/Norad/XN19NH";
case MakeRoomView(kNorad22, kWest):
return "Images/AI/Globals/XGLOB1C";
default:
break;
}
}
return movieName;
}
void NoradAlpha::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
switch (room) {
case kNorad12:
case kNorad13:
case kNorad18:
case kNorad19:
playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut);
break;
default:
playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut);
break;
}
}
void NoradAlpha::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) {
if (room == kNorad01 && direction == kSouth)
spotEntry.clear();
else
Norad::findSpotEntry(room, direction, flags, spotEntry);
}
bool NoradAlpha::canSolve() {
return Norad::canSolve() || getHintMovie(1) == "Images/AI/Norad/XN01SW";
}
void NoradAlpha::doSolve() {
Norad::doSolve();
if (getHintMovie(1) == "Images/AI/Norad/XN01SW") {
_vm->addItemToInventory(g_airMask);
g_airMask->putMaskOn();
}
}
Common::Path NoradAlpha::getNavMovieName() {
return "Images/Norad Alpha/Norad Alpha.movie";
}
Common::Path NoradAlpha::getSoundSpotsName() {
return "Sounds/Norad/Norad Alpha Spots";
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,126 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H
#include "pegasus/neighborhood/norad/norad.h"
namespace Pegasus {
class Item;
class SubChase;
class NoradAlpha : public Norad {
friend class SubChase;
public:
NoradAlpha(InputHandler *, PegasusEngine *);
~NoradAlpha() override;
void init() override;
void start() override;
bool okayToJump() override;
void playClawMonitorIntro() override;
void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &) override;
void turnOnFillingStation();
void turnOffFillingStation();
Item *getFillingItem() { return _fillingStationItem; }
bool gasCanisterIntake();
void takeItemFromRoom(Item *) override;
void dropItemIntoRoom(Item *, Hotspot *) override;
GameInteraction *makeInteraction(const InteractionID) override;
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 *&) override;
void loadAmbientLoops() override;
Common::Path getEnvScanMovie() override;
uint getNumHints() override;
Common::Path getHintMovie(uint) override;
void setUpAIRules() override;
void setSubPrepFailed(bool value) { _subPrepFailed = value; }
bool getSubPrepFailed() { return _subPrepFailed; }
void closeDoorOffScreen(const RoomID, const DirectionConstant) override;
void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &) override;
void clickInHotspot(const Input &, const Hotspot *) override;
void checkContinuePoint(const RoomID, const DirectionConstant) override;
void setSoundFXLevel(const uint16) override;
bool canSolve() override;
void doSolve() override;
protected:
static const uint32 _noradAlphaClawExtras[22];
virtual void arriveAtNorad01();
virtual void arriveAtNorad01East();
virtual void arriveAtNorad01West();
virtual void arriveAtNorad04();
virtual void arriveAtNorad22();
void arriveAt(const RoomID, const DirectionConstant) override;
void turnTo(const DirectionConstant) override;
void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
void getZoomEntry(const HotSpotID, ZoomTable::Entry &) override;
TimeValue getViewTime(const RoomID, const DirectionConstant) override;
void receiveNotification(Notification *, const NotificationFlags) override;
void activateHotspots() override;
Hotspot *getItemScreenSpot(Item *, DisplayElement *) override;
void bumpIntoWall() override;
Hotspot _thermalScanSpot;
Movie _extraMovie;
NotificationCallBack _extraMovieCallBack;
Item *_fillingStationItem;
bool _subPrepFailed;
Common::Path getSoundSpotsName() override;
Common::Path getNavMovieName() override;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,238 @@
/* 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 "common/macresman.h"
#include "common/stream.h"
#include "pegasus/neighborhood/norad/alpha/panorama.h"
namespace Pegasus {
Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) {
blankFields();
}
Panorama::~Panorama() {
releasePanorama();
}
void Panorama::blankFields() {
_viewBounds = Common::Rect();
_drawBounds = Common::Rect();
_mask = nullptr;
_panoramaWidth = 0;
_panoramaHeight = 0;
_stripWidth = 0;
_stripLeft = -1;
_stripRight = -1;
}
void Panorama::releasePanorama() {
if (_panoramaMovie.isMovieValid()) {
_panoramaMovie.releaseMovie();
_panoramaWorld.deallocateSurface();
blankFields();
}
}
static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information.
static const uint16 kPanoramaResID = 128;
void Panorama::initFromMovieFile(const Common::Path &fileName) {
// First, we need the resource fork for other reasons -- PanI resource
Common::MacResManager *resFork = new Common::MacResManager();
if (!resFork->open(fileName) || !resFork->hasResFork())
error("Could not open the resource fork of '%s'", fileName.toString().c_str());
Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID);
if (!resource)
error("No panorama information in the resource fork of '%s'", fileName.toString().c_str());
_panoramaWidth = resource->readUint16BE();
_panoramaHeight = resource->readUint16BE();
_stripWidth = resource->readUint16BE();
delete resource;
delete resFork;
// Now we open the movie like normal
_panoramaMovie.initFromMovieFile(fileName);
}
void Panorama::setMask(Surface *mask) {
_mask = mask;
}
// If the panorama is not open, do nothing and return.
// Otherwise, set up the view bounds.
void Panorama::setViewBounds(const Common::Rect &newView) {
if (!isPanoramaOpen())
return;
if (newView.isEmpty())
return;
Common::Rect r = newView;
if (r.width() > _panoramaWidth) {
r.left = 0;
r.right = _panoramaWidth;
} else {
if (r.right > _panoramaWidth)
r.translate(_panoramaWidth - r.right, 0);
if (r.left < 0)
r.translate(-r.left, 0);
}
if (r.height() > _panoramaHeight) {
r.top = 0;
r.bottom = _panoramaHeight;
} else {
if (r.bottom > _panoramaHeight)
r.translate(0, _panoramaHeight - r.bottom);
if (r.top < 0)
r.translate(0, -r.top);
}
if (_viewBounds != r) {
CoordType stripLeft = 0;
if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) {
_panoramaWorld.deallocateSurface();
makeNewSurface(r);
} else {
CoordType stripRight;
calcStripRange(r, stripLeft, stripRight);
loadStrips(stripLeft, stripRight);
}
_viewBounds = r;
_drawBounds = r;
_drawBounds.translate(-stripLeft * _stripWidth, 0);
}
}
void Panorama::getViewBounds(Common::Rect &r) const {
r = _viewBounds;
}
void Panorama::getPanoramaBounds(Common::Rect &r) const {
r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight);
}
void Panorama::drawPanorama(const Common::Rect &destRect) {
if (_panoramaWorld.isSurfaceValid()) {
if (_mask)
_panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask);
else
_panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect);
}
}
// Make a new Surface big enough to show r, which is assumed to be a valid view bounds.
// Assumptions:
// r is a valid view bounds.
// _panoramaWorld is not allocated.
// _panoramaHeight, _stripWidth is correct.
// _panoramaMovie is allocated.
void Panorama::makeNewSurface(const Common::Rect& view) {
CoordType stripLeft, stripRight;
calcStripRange(view, stripLeft, stripRight);
Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight);
_panoramaWorld.allocateSurface(r);
_panoramaMovie.shareSurface(&_panoramaWorld);
loadStrips(stripLeft, stripRight);
}
// Assumes view is not empty.
void Panorama::calcStripRange(const Common::Rect &view, CoordType &stripLeft, CoordType &stripRight) {
stripLeft = view.left / _stripWidth;
stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth;
}
// Load in all needed strips to put range (stripLeft, stripRight) into the
// panorama's Surface. Try to optimize by saving any pixels already in the Surface.
// Assumptions:
// Surface is allocated and is big enough for maximum range of
// stripLeft and stripRight
void Panorama::loadStrips(CoordType stripLeft, CoordType stripRight) {
if (_stripLeft == -1) {
// Surface has just been allocated.
// Load in all strips.
for (CoordType i = stripLeft; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
_stripLeft = stripLeft;
_stripRight = stripRight;
} else if (stripLeft != _stripLeft) {
CoordType overlapLeft = MAX(stripLeft, _stripLeft);
CoordType overlapRight = MIN(stripRight, _stripRight);
if (overlapLeft <= overlapRight) {
Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0,
(overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight);
if (stripLeft < _stripLeft) {
Common::Rect bounds;
_panoramaWorld.getSurfaceBounds(bounds);
_panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight);
for (CoordType i = stripLeft; i < _stripLeft; i++)
loadOneStrip(i, stripLeft);
} else {
_panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight);
for (CoordType i = _stripRight + 1; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
}
} else {
// No overlap.
// Load everything.
for (CoordType i = stripLeft; i <= stripRight; i++)
loadOneStrip(i, stripLeft);
}
_stripLeft = stripLeft;
_stripRight = stripRight;
} else if (stripRight > _stripRight) {
// Need to add one or more strips.
for (CoordType i = _stripRight + 1; i <= stripRight; i++)
loadOneStrip(i, _stripLeft);
_stripRight = stripRight;
} else if (stripRight < _stripRight) {
// Need to chop off one strip.
_stripRight = stripRight;
}
}
void Panorama::loadOneStrip(CoordType stripToLoad, CoordType leftStrip) {
_panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0);
_panoramaMovie.setTime(stripToLoad, 1);
_panoramaMovie.redrawMovieWorld();
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,97 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H
#include "pegasus/movie.h"
namespace Pegasus {
/*
Panorama implements a wide image using a specially constructed movie file.
The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide.
The panorama bounds defines the entire panorama. The view bounds represents the
area on the panorama that is kept in memory.
The panorama bounds is also stored in the movie file; it cannot be changed. The
view bounds must always be a subset of the panorama bounds.
In actuality, the area kept in memory is at least as wide as the view bounds (but
may be wider to coincide with the width of the movies slices), and is as tall as
the panorama bounds. The view bounds is used by the drawPanorama function to draw
a piece of the panorama to the current screen.
The panorama movie is built at a time scale of 1, with each strip lasting for one
second, so that strip number corresponds exactly with the time value at which the
strip is stored.
TO USE:
Call one initFromMovieFile to open the movie. Then set up a view rect by
calling setViewBounds. Once these two functions have been called, drawPanorama
will draw the panorama.
*/
class Panorama {
public:
Panorama();
virtual ~Panorama();
void initFromMovieFile(const Common::Path &);
void releasePanorama();
bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); }
void setViewBounds(const Common::Rect &);
void getViewBounds(Common::Rect &) const;
void setMask(Surface *);
void getPanoramaBounds(Common::Rect &) const;
void drawPanorama(const Common::Rect &);
protected:
void blankFields();
void makeNewSurface(const Common::Rect &);
void calcStripRange(const Common::Rect &, CoordType &, CoordType &);
void loadStrips(CoordType, CoordType);
void loadOneStrip(CoordType, CoordType);
Movie _panoramaMovie;
Surface _panoramaWorld, *_mask;
Common::Rect _viewBounds;
Common::Rect _drawBounds;
CoordType _panoramaWidth, _panoramaHeight;
CoordType _stripWidth; // Pixels per strip.
CoordType _numStrips;
CoordType _stripLeft, _stripRight;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,90 @@
/* 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/neighborhood/norad/alpha/panoramascroll.h"
namespace Pegasus {
PanoramaScroll::PanoramaScroll(const DisplayElementID id) : IdlerAnimation(id) {
_boundsWidth = 0;
_totalWidth = 0;
}
void PanoramaScroll::initFromMovieFile(const Common::Path &fileName) {
_panorama.initFromMovieFile(fileName);
Common::Rect r;
_panorama.getPanoramaBounds(r);
_totalWidth = r.width();
}
void PanoramaScroll::initMaskFromPICTFile(const Common::Path &fileName) {
if (!_panorama.isPanoramaOpen())
return;
_mask.getImageFromPICTFile(fileName);
_panorama.setMask(&_mask);
}
void PanoramaScroll::releasePanorama() {
if (_panorama.isPanoramaOpen())
_panorama.releasePanorama();
_mask.deallocateSurface();
}
void PanoramaScroll::setBounds(const Common::Rect &r) {
Animation::setBounds(r);
_boundsWidth = r.width();
Common::Rect r2;
_panorama.getViewBounds(r2);
r2.right = r2.left + _boundsWidth;
r2.bottom = r2.top + r.height();
_panorama.setViewBounds(r2);
}
void PanoramaScroll::draw(const Common::Rect &) {
_panorama.drawPanorama(_bounds);
}
void PanoramaScroll::timeChanged(const TimeValue newTime) {
CoordType leftPixel = (_totalWidth - _boundsWidth) * newTime / getDuration();
Common::Rect r;
_panorama.getViewBounds(r);
if (leftPixel != r.left) {
_panorama.getViewBounds(r);
r.moveTo(leftPixel, 0);
_panorama.setViewBounds(r);
triggerRedraw();
}
}
void PanoramaScroll::getPanoramaBounds(Common::Rect &r) const {
_panorama.getPanoramaBounds(r);
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H
#include "pegasus/neighborhood/norad/alpha/panorama.h"
namespace Pegasus {
class PanoramaScroll : public IdlerAnimation {
public:
PanoramaScroll(const DisplayElementID);
~PanoramaScroll() override {}
void initFromMovieFile(const Common::Path &);
void initMaskFromPICTFile(const Common::Path &);
void releasePanorama();
bool isPanoramaOpen() { return _panorama.isPanoramaOpen(); }
void getPanoramaBounds(Common::Rect &) const;
void setBounds(const Common::Rect&) override;
void draw(const Common::Rect &) override;
protected:
void timeChanged(const TimeValue) override;
Panorama _panorama;
Surface _mask;
CoordType _totalWidth, _boundsWidth;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,458 @@
/* 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-2013 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/pegasus.h"
#include "pegasus/gamestate.h"
#include "pegasus/neighborhood/norad/alpha/subchase.h"
#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
#include "pegasus/neighborhood/norad/constants.h"
namespace Pegasus {
static const TimeScale kSubChaseScale = 600;
static const DisplayOrder kSubChaseOrder = 27000;
// Segment start and end points.
static const TimeValue kIntroStart = 0;
static const TimeValue kIntroEnd = 2400;
static const TimeValue kDialogStart = kIntroEnd;
static const TimeValue kDialogEnd = 20920;
static const TimeValue kBranch1Start = kDialogEnd;
static const TimeValue kBranch1End = 32120;
static const TimeValue kBranch2LeftStart = kBranch1End;
static const TimeValue kBranch2LeftEnd = 48080;
static const TimeValue kBranch3Start = kBranch2LeftEnd;
static const TimeValue kBranch3End = 61080;
static const TimeValue kBranch4Start = kBranch3End;
static const TimeValue kBranch4End = 84080;
static const TimeValue kBranch5Start = kBranch4End;
static const TimeValue kBranch5End = 94840;
static const TimeValue kBranch6Start = kBranch5End;
static const TimeValue kBranch6End = 106040;
static const TimeValue kBranch7LeftStart = kBranch6End;
static const TimeValue kBranch7LeftEnd = 118840;
static const TimeValue kExitStart = kBranch7LeftEnd;
static const TimeValue kExitEnd = 133200;
static const TimeValue kBranch2RightStart = 133200;
static const TimeValue kBranch2RightEnd = 149160;
static const TimeValue kBranch7RightStart = 168000;
static const TimeValue kBranch7RightEnd = 180800;
// Death start and end points.
static const TimeValue kDeath4Start = 149160;
static const TimeValue kDeath4End = 158040;
static const TimeValue kDeath5Start = kDeath4End;
static const TimeValue kDeath5End = 163760;
static const TimeValue kDeath6Start = kDeath5End;
static const TimeValue kDeath6End = 168000;
static const TimeValue kDeath7Start = 180800;
static const TimeValue kDeath7End = 187040;
// Chase state.
enum {
kSubDialog,
kSubBranch1,
kSubBranch2Left,
kSubBranch2Right,
kSubBranch3,
kSubBranch4,
kSubBranch5,
kSubBranch6,
kSubBranch7Left,
kSubBranch7Right,
kSubExit
};
void HintTimerEvent::fire() {
subChase->hintTimerExpired(*this);
}
void BlinkTimerEvent::fire() {
subChase->blinkTimerExpired(*this);
}
SubChase::SubChase(Neighborhood *handler) : ChaseInteraction(kNoradSubChaseInteractionID, handler,
kNoradSubChaseNotificationID, g_vm), _subMovie(kNoDisplayElement),
_hintPict(kNoDisplayElement), _blinkPict(kNoDisplayElement), _canSteerSub(true) {
}
void SubChase::setSoundFXLevel(const uint16 fxLevel) {
_subMovie.setVolume(fxLevel);
}
void SubChase::openInteraction() {
_subMovie.initFromMovieFile("Images/Norad Alpha/Sub Chase Movie");
_subMovie.setVolume(g_vm->getSoundFXLevel());
_subMovie.moveElementTo(0, 0);
_subMovie.setDisplayOrder(kSubChaseOrder);
_subMovie.startDisplaying();
_subMovie.show();
_subCallBack.setNotification(&_chaseNotification);
_subCallBack.initCallBack(&_subMovie, kCallBackAtExtremes);
ChaseInteraction::openInteraction();
_steerPict.setDisplayOrder(kSubChaseOrder + 1);
_steerPict.moveElementTo(kNoradSubSteerLeft, kNoradSubSteerTop);
_hintPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk1.pict", true);
_hintPict.setDisplayOrder(kSubChaseOrder + 1);
_hintPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop);
_blinkPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk0.pict", true);
_blinkPict.setDisplayOrder(kSubChaseOrder + 1);
_blinkPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop);
}
void SubChase::initInteraction() {
_canSteerSub = !GameState.getWalkthroughMode();
_owner->playMovieSegment(&_subMovie, kIntroStart, kIntroEnd);
_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
if (_canSteerSub) {
_steerPict.startDisplaying();
_hintPict.startDisplaying();
_blinkPict.startDisplaying();
startHintTimer(20000 - kDialogStart, kSubChaseScale, kStartedHint);
_subCallBack.setCallBackFlag(kChaseEnteredBranchZone);
_subMovie.setSegment(kDialogStart, kBranch1End - kDecisionTime);
} else {
_subCallBack.setCallBackFlag(kChaseFinished);
_subMovie.setSegment(kDialogStart, kExitEnd);
}
_subState = kSubDialog;
_subMovie.setTime(kDialogStart);
_subMovie.start();
ChaseInteraction::initInteraction();
}
void SubChase::closeInteraction() {
_subMovie.stop();
_subMovie.stopDisplaying();
_subMovie.releaseMovie();
_subCallBack.releaseCallBack();
_hintPict.hide();
_hintPict.deallocateSurface();
_blinkPict.hide();
_blinkPict.deallocateSurface();
ChaseInteraction::closeInteraction();
}
void SubChase::receiveNotification(Notification *notification, const NotificationFlags flags) {
if (notification == &_chaseNotification && flags == kChaseFinished) {
if (_subState != kSubDialog && _subState != kSubExit) {
// We died
((NoradAlpha *)_owner)->die(kDeathSubDestroyed);
} else {
_subMovie.stopDisplaying();
g_vm->_gfx->enableErase();
g_vm->_gfx->updateDisplay();
g_vm->_gfx->disableErase();
g_vm->jumpToNewEnvironment(kNoradDeltaID, kNorad41, kEast);
}
}
ChaseInteraction::receiveNotification(notification, flags);
}
void SubChase::handleInput(const Input &input, const Hotspot *cursorSpot) {
if (_subMovie.getTime() < kBranch1Start && input.anyInput()) {
if (_canSteerSub) {
if (!_blinkFuse.isFuseLit()) {
// If the hint is not blinking then it must not be running, so display it
_hintPict.show();
startBlinkTimer(10, 10, kEnteredBlinkState);
}
_subState = kSubBranch1;
startHintTimer(3000, kSubChaseScale, kEndedHint);
} else {
_subState = kSubExit;
}
_subMovie.setTime(kBranch1Start);
} else {
ChaseInteraction::handleInput(input, cursorSpot);
}
}
void SubChase::setUpBranch() {
TimeValue branchStart, branchEnd;
branchStart = 0;
branchEnd = 0;
switch (_subState) {
case kSubDialog:
case kSubBranch1:
branchStart = kBranch1End - kDecisionTime;
branchEnd = kBranch1End;
break;
case kSubBranch2Left:
branchStart = kBranch2LeftEnd - kDecisionTime;
branchEnd = kBranch2LeftEnd;
break;
case kSubBranch2Right:
branchStart = kBranch2RightEnd - kDecisionTime;
branchEnd = kBranch2RightEnd;
break;
case kSubBranch3:
branchStart = kBranch3End - kDecisionTime;
branchEnd = kBranch3End;
break;
case kSubBranch4:
branchStart = kBranch4End - kDecisionTime;
branchEnd = kBranch4End;
break;
case kSubBranch5:
branchStart = kBranch5End - kDecisionTime;
branchEnd = kBranch5End;
break;
case kSubBranch6:
branchStart = kBranch6End - kDecisionTime;
branchEnd = kBranch6End;
break;
case kSubBranch7Left:
branchStart = kBranch7LeftEnd - kDecisionTime;
branchEnd = kBranch7LeftEnd;
break;
case kSubBranch7Right:
branchStart = kBranch7RightEnd - kDecisionTime;
branchEnd = kBranch7RightEnd;
break;
default:
break;
}
_subMovie.setSegment(branchStart, branchEnd);
_subCallBack.setCallBackFlag(kChaseExitedBranchZone);
_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
}
void SubChase::branchLeft() {
TimeValue branchStart, branchEnd;
NotificationFlags flag;
branchStart = 0;
branchEnd = 0;
flag = 0;
switch (_subState) {
case kSubDialog:
case kSubBranch1:
branchStart = kBranch2LeftStart;
branchEnd = kBranch2LeftEnd;
// Don't show the controls hint when we approach the whale
// since the branch segments here are identical
flag = kChaseExitedBranchZone;
_subState = kSubBranch2Left;
break;
case kSubBranch2Left:
case kSubBranch2Right:
branchStart = kBranch3Start;
branchEnd = kBranch3End - kDecisionTime;
flag = kChaseEnteredBranchZone;
_subState = kSubBranch3;
break;
case kSubBranch3:
branchStart = kBranch4Start;
branchEnd = kBranch4End - kDecisionTime;
flag = kChaseEnteredBranchZone;
_subState = kSubBranch4;
break;
case kSubBranch4:
branchStart = kDeath5Start;
branchEnd = kDeath5End;
flag = kChaseFinished;
_subState = kSubBranch5;
break;
case kSubBranch5:
branchStart = kBranch6Start;
branchEnd = kBranch6End - kDecisionTime;
flag = kChaseEnteredBranchZone;
_subState = kSubBranch6;
break;
case kSubBranch6:
branchStart = kBranch7LeftStart;
branchEnd = kBranch7LeftEnd;
flag = kChaseExitedBranchZone;
_subState = kSubBranch7Left;
break;
case kSubBranch7Left:
case kSubBranch7Right:
branchStart = kExitStart;
branchEnd = kExitEnd;
flag = kChaseFinished;
_subState = kSubExit;
break;
default:
break;
}
_subMovie.setSegment(branchStart, branchEnd);
_subMovie.setTime(branchStart);
_subCallBack.setCallBackFlag(flag);
_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
}
void SubChase::branchRight() {
TimeValue branchStart, branchEnd;
NotificationFlags flag;
branchStart = 0;
branchEnd = 0;
flag = 0;
switch (_subState) {
case kSubDialog:
case kSubBranch1:
branchStart = kBranch2RightStart;
branchEnd = kBranch2RightEnd;
// Don't show the controls hint when we approach the whale
// since the branch segments here are identical
flag = kChaseExitedBranchZone;
_subState = kSubBranch2Right;
break;
case kSubBranch2Left:
case kSubBranch2Right:
branchStart = kBranch3Start;
branchEnd = kBranch3End - kDecisionTime;
flag = kChaseEnteredBranchZone;
_subState = kSubBranch3;
break;
case kSubBranch3:
branchStart = kDeath4Start;
branchEnd = kDeath4End;
flag = kChaseFinished;
_subState = kSubBranch4;
break;
case kSubBranch4:
branchStart = kBranch5Start;
branchEnd = kBranch5End - kDecisionTime;
flag = kChaseEnteredBranchZone;
_subState = kSubBranch5;
break;
case kSubBranch5:
branchStart = kDeath6Start;
branchEnd = kDeath6End;
flag = kChaseFinished;
_subState = kSubBranch6;
break;
case kSubBranch6:
if (g_vm->getRandomBit()) {
branchStart = kBranch7RightStart;
branchEnd = kBranch7RightEnd;
flag = kChaseExitedBranchZone;
} else {
branchStart = kDeath7Start;
branchEnd = kDeath7End;
flag = kChaseFinished;
}
_subState = kSubBranch7Right;
break;
case kSubBranch7Left:
case kSubBranch7Right:
branchStart = kExitStart;
branchEnd = kExitEnd;
flag = kChaseFinished;
_subState = kSubExit;
break;
default:
break;
}
_subMovie.setSegment(branchStart, branchEnd);
_subMovie.setTime(branchStart);
_subCallBack.setCallBackFlag(flag);
_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
}
void SubChase::dontBranch() {
if (g_vm->getRandomBit())
branchLeft();
else
branchRight();
}
void SubChase::startHintTimer(TimeValue time, TimeScale scale, HintTimerCode code) {
if (_canSteerSub) {
_hintFuse.primeFuse(time, scale);
_hintEvent.subChase = this;
_hintEvent.theEvent = code;
_hintFuse.setFunctor(new Common::Functor0Mem<void, HintTimerEvent>(&_hintEvent, &HintTimerEvent::fire));
_hintFuse.lightFuse();
}
}
void SubChase::hintTimerExpired(HintTimerEvent &event) {
switch (event.theEvent) {
case kStartedHint:
_hintPict.show();
startBlinkTimer(10, 10, kEnteredBlinkState);
startHintTimer(3920, kSubChaseScale, kEndedHint);
break;
case kEndedHint:
_hintPict.hide();
_blinkPict.hide();
_blinkFuse.stopFuse();
break;
default:
break;
}
}
void SubChase::startBlinkTimer(TimeValue time, TimeScale scale, BlinkTimerCode code) {
_blinkFuse.primeFuse(time, scale);
_blinkEvent.subChase = this;
_blinkEvent.theEvent = code;
_blinkFuse.setFunctor(new Common::Functor0Mem<void, BlinkTimerEvent>(&_blinkEvent, &BlinkTimerEvent::fire));
_blinkFuse.lightFuse();
}
void SubChase::blinkTimerExpired(BlinkTimerEvent &event) {
switch (event.theEvent) {
case kEnteredBlinkState:
_hintPict.hide();
_blinkPict.show();
startBlinkTimer(5, 10, kExitedBlinkState);
break;
case kExitedBlinkState:
_blinkPict.hide();
_hintPict.show();
startBlinkTimer(10, 10, kEnteredBlinkState);
break;
default:
break;
}
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,110 @@
/* 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-2013 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_SUBCHASE_H
#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_SUBCHASE_H
#include "pegasus/chase.h"
#include "pegasus/movie.h"
namespace Pegasus {
class NoradAlpha;
class SubChase;
enum HintTimerCode {
kStartedHint,
kEndedHint
};
struct HintTimerEvent {
SubChase *subChase;
HintTimerCode theEvent;
void fire();
};
enum BlinkTimerCode {
kEnteredBlinkState,
kExitedBlinkState
};
struct BlinkTimerEvent {
SubChase *subChase;
BlinkTimerCode theEvent;
void fire();
};
class SubChase : public ChaseInteraction {
friend class NoradAlpha;
friend struct HintTimerEvent;
friend struct BlinkTimerEvent;
public:
SubChase(Neighborhood *);
virtual ~SubChase() {}
void setSoundFXLevel(const uint16);
void handleInput(const Input &, const Hotspot *);
protected:
void openInteraction();
void initInteraction();
void closeInteraction();
void receiveNotification(Notification *, const NotificationFlags);
void startBranching();
void setUpBranch();
void branchLeft();
void branchRight();
void dontBranch();
void startHintTimer(TimeValue, TimeScale, HintTimerCode);
void hintTimerExpired(HintTimerEvent &);
void startBlinkTimer(TimeValue, TimeScale, BlinkTimerCode);
void blinkTimerExpired(BlinkTimerEvent &);
Movie _subMovie;
NotificationCallBack _subCallBack;
Picture _hintPict;
Picture _blinkPict;
FuseFunction _hintFuse;
FuseFunction _blinkFuse;
HintTimerEvent _hintEvent;
BlinkTimerEvent _blinkEvent;
short _subState;
bool _canSteerSub;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,762 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H
#define PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H
#include "pegasus/constants.h"
namespace Pegasus {
// Norad Alpha spot constants
static const TimeValue kAlphaBumpIntoWallIn = 0;
static const TimeValue kAlphaBumpIntoWallOut = 303;
static const TimeValue kAlphaAccessDeniedIn = 303;
static const TimeValue kAlphaAccessDeniedOut = 3045;
static const TimeValue kAlphaRegDoorCloseIn = 3045;
static const TimeValue kAlphaRegDoorCloseOut = 4476;
static const TimeValue kAlphaElevatorDoorCloseIn = 4476;
static const TimeValue kAlphaElevatorDoorCloseOut = 5071;
static const TimeValue kAlphaCantTransportIn = 5071;
static const TimeValue kAlphaCantTransportOut = 9348;
static const TimeValue kAlphaPressureDoorIntro1In = 9348;
static const TimeValue kAlphaPressureDoorIntro1Out = 11061;
static const TimeValue kAlphaPressureDoorIntro2In = 11061;
static const TimeValue kAlphaPressureDoorIntro2Out = 14098;
static const TimeValue kN22ReplyIn = 14098;
static const TimeValue kN22ReplyOut = 18442;
static const TimeValue kAlphaLoadClawIntroIn = 18442;
static const TimeValue kAlphaLoadClawIntroOut = 20698;
// Norad Delta spot constants
static const TimeValue kDeltaBumpIntoWallIn = 0;
static const TimeValue kDeltaBumpIntoWallOut = 303;
static const TimeValue kDeltaAccessDeniedIn = 303;
static const TimeValue kDeltaAccessDeniedOut = 3045;
static const TimeValue kDeltaRegDoorCloseIn = 3045;
static const TimeValue kDeltaRegDoorCloseOut = 4476;
static const TimeValue kDeltaElevatorDoorCloseIn = 4476;
static const TimeValue kDeltaElevatorDoorCloseOut = 5071;
static const TimeValue kPressureDoorIntro1In = 5071;
static const TimeValue kPressureDoorIntro1Out = 6784;
static const TimeValue kPressureDoorIntro2In = 6784;
static const TimeValue kPressureDoorIntro2Out = 9821;
static const TimeValue kLoadClawIntroIn = 9821;
static const TimeValue kLoadClawIntroOut = 12077;
static const TimeValue kHoldForRetinalIn = 12077;
static const TimeValue kHoldForRetinalOut = 14104;
static const TimeValue kRetinalScanFailedIn = 14104;
static const TimeValue kRetinalScanFailedOut = 17538;
static const TimeValue kAddisAbabaIn = 17538;
static const TimeValue kAddisAbabaOut = 19263;
static const TimeValue kBangkokIn = 19263;
static const TimeValue kBangkokOut = 20201;
static const TimeValue kBonnIn = 20201;
static const TimeValue kBonnOut = 20915;
static const TimeValue kDublinIn = 20915;
static const TimeValue kDublinOut = 21660;
static const TimeValue kHonoluluIn = 21660;
static const TimeValue kHonoluluOut = 22498;
static const TimeValue kMadridIn = 22498;
static const TimeValue kMadridOut = 23474;
static const TimeValue kReykjavikIn = 23474;
static const TimeValue kReykjavikOut = 24488;
static const TimeValue kSanAntonioIn = 24488;
static const TimeValue kSanAntonioOut = 25561;
static const TimeValue kSeoulIn = 25561;
static const TimeValue kSeoulOut = 26461;
static const TimeValue kSvortalskIn = 26461;
static const TimeValue kSvortalskOut = 27582;
static const TimeValue kSiloBeepIn = 27582;
static const TimeValue kSiloBeepOut = 27721;
static const TimeValue kAllSilosDeactivatedIn = 27721;
static const TimeValue kAllSilosDeactivatedOut = 28928;
static const TimeValue kGlobalLaunchOverrideIn = 28928;
static const TimeValue kGlobalLaunchOverrideOut = 30736;
static const TimeValue kLaunchSiloSelectedIn = 30736;
static const TimeValue kLaunchSiloSelectedOut = 31660;
static const TimeValue kLaunchToProceedIn = 31660;
static const TimeValue kLaunchToProceedOut = 32536;
static const TimeValue kMaximumDeactivationIn = 32536;
static const TimeValue kMaximumDeactivationOut = 34337;
static const TimeValue kMissileLaunchedIn = 34337;
static const TimeValue kMissileLaunchedOut = 35082;
static const TimeValue kNewLaunchSiloIn = 35082;
static const TimeValue kNewLaunchSiloOut = 36320;
static const TimeValue kStrikeAuthorizedIn = 36320;
static const TimeValue kStrikeAuthorizedOut = 37393;
static const TimeValue kPrimaryTargetIn = 37393;
static const TimeValue kPrimaryTargetOut = 38628;
static const TimeValue kSiloDeactivatedIn = 38628;
static const TimeValue kSiloDeactivatedOut = 39566;
static const TimeValue kStrikeCodeRejectedIn = 39566;
static const TimeValue kStrikeCodeRejectedOut = 41056;
static const TimeValue kToDeactivateIn = 41056;
static const TimeValue kToDeactivateOut = 46494;
static const TimeValue kTwoMinutesIn = 46494;
static const TimeValue kTwoMinutesOut = 47166;
static const TimeValue kOneMinuteIn = 47166;
static const TimeValue kOneMinuteOut = 47856;
static const TimeValue kFiftySecondsIn = 47856;
static const TimeValue kFiftySecondsOut = 48691;
static const TimeValue kFortySecondsIn = 48691;
static const TimeValue kFortySecondsOut = 49500;
static const TimeValue kThirtySecondsIn = 49500;
static const TimeValue kThirtySecondsOut = 50362;
static const TimeValue kTwentySecondsIn = 50362;
static const TimeValue kTwentySecondsOut = 51245;
static const TimeValue kTenSecondsIn = 51245;
static const TimeValue kTenSecondsOut = 52069;
static const TimeValue kGiveUpHumanIn = 52069;
static const TimeValue kGiveUpHumanOut = 55023;
static const TimeValue kIJustBrokeIn = 55023;
static const TimeValue kIJustBrokeOut = 59191;
static const TimeValue kTheOnlyGoodHumanIn = 59191;
static const TimeValue kTheOnlyGoodHumanOut = 62379;
static const TimeValue kYouAreRunningIn = 62379;
static const TimeValue kYouAreRunningOut = 64201;
static const TimeValue kYouCannotPossiblyIn = 64201;
static const TimeValue kYouCannotPossiblyOut = 65740;
static const TimeValue kYouWillFailIn = 65740;
static const TimeValue kYouWillFailOut = 67217;
static const CanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1;
static const NotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1;
static const uint16 kNoradWarningVolume = 0x100 / 3;
static const uint16 kNoradSuckWindVolume = 0x100 / 2;
static const int16 kElevatorCompassAngle = -40;
static const int16 kSubPlatformCompassAngle = 45;
static const int16 kSubControlCompassAngle = -10;
// Norad interactions.
static const InteractionID kNoradGlobeGameInteractionID = 0;
static const InteractionID kNoradECRMonitorInteractionID = 1;
static const InteractionID kNoradFillingStationInteractionID = 2;
static const InteractionID kNoradElevatorInteractionID = 3;
static const InteractionID kNoradPressureDoorInteractionID = 4;
static const InteractionID kNoradSubControlRoomInteractionID = 5;
static const InteractionID kNoradSubPlatformInteractionID = 6;
static const InteractionID kNoradSubChaseInteractionID = 7;
/////////////////////////////////////////////
//
// Norad Alpha
static const CoordType kECRSlideShowLeft = kNavAreaLeft + 78;
static const CoordType kECRSlideShowTop = kNavAreaTop + 1;
static const CoordType kECRPanLeft = kNavAreaLeft + 78 + 5;
static const CoordType kECRPanTop = kNavAreaTop + 1 + 4;
static const CoordType kECRPanRight = kECRPanLeft + 213;
static const CoordType kECRPanBottom = kECRPanTop + 241;
static const CoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332;
static const CoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127;
static const CoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0;
static const CoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0;
static const CoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240;
static const CoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12;
static const CoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98;
static const CoordType kNoradUpperLevelsTop = kNavAreaTop + 31;
static const CoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114;
static const CoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8;
static const CoordType kNoradUpperUpLeft = kNavAreaLeft + 361;
static const CoordType kNoradUpperUpTop = kNavAreaTop + 32;
static const CoordType kNoradUpperDownLeft = kNavAreaLeft + 367;
static const CoordType kNoradUpperDownTop = kNavAreaTop + 66;
static const CoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74;
static const CoordType kNoradLowerLevelsTop = kNavAreaTop + 157;
static const CoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144;
static const CoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9;
static const CoordType kNoradLowerUpLeft = kNavAreaLeft + 380;
static const CoordType kNoradLowerUpTop = kNavAreaTop + 164;
static const CoordType kNoradLowerDownLeft = kNavAreaLeft + 388;
static const CoordType kNoradLowerDownTop = kNavAreaTop + 212;
static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36;
static const CoordType kNoradPlatformTop = kNavAreaTop + 87;
static const CoordType kNoradSubSteerLeft = 288;
static const CoordType kNoradSubSteerTop = 28;
static const CoordType kNoradSubHintLeft = 480;
static const CoordType kNoradSubHintTop = 240;
static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0;
static const CoordType kNoradSubControlTop = kNavAreaTop + 84;
static const CoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106;
static const CoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86;
static const CoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66;
static const CoordType kNoradSubControlDownTop = kNoradSubControlTop + 106;
static const CoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83;
static const CoordType kNoradSubControlRightTop = kNoradSubControlTop + 90;
static const CoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56;
static const CoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91;
static const CoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66;
static const CoordType kNoradSubControlUpTop = kNoradSubControlTop + 81;
static const CoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29;
static const CoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88;
static const CoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0;
static const CoordType kNoradSubControlCWTop = kNoradSubControlTop + 89;
static const CoordType kNoradClawMonitorLeft = kNavAreaLeft + 288;
static const CoordType kNoradClawMonitorTop = kNavAreaTop + 97;
static const CoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179;
static const CoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82;
static const CoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130;
static const CoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73;
static const CoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110;
static const CoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26;
static const CoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21;
static const CoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49;
/////////////////////////////////////////////
//
// Norad Delta
static const CoordType kGlobeMonitorLeft = kNavAreaLeft + 360;
static const CoordType kGlobeMonitorTop = kNavAreaTop + 144;
static const CoordType kGlobeLeft = kNavAreaLeft + 172;
static const CoordType kGlobeTop = kNavAreaTop;
static const CoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186;
static const CoordType kGlobeCircleLeftTop = kNavAreaTop + 41;
static const CoordType kGlobeCircleRightLeft = kNavAreaLeft + 321;
static const CoordType kGlobeCircleRightTop = kNavAreaTop + 41;
static const CoordType kGlobeCircleUpLeft = kNavAreaLeft + 220;
static const CoordType kGlobeCircleUpTop = kNavAreaTop + 7;
static const CoordType kGlobeCircleDownLeft = kNavAreaLeft + 220;
static const CoordType kGlobeCircleDownTop = kNavAreaTop + 142;
static const CoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207;
static const CoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28;
static const CoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307;
static const CoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28;
static const CoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207;
static const CoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128;
static const CoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307;
static const CoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128;
static const CoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182;
static const CoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60;
static const CoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331;
static const CoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60;
static const CoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239;
static const CoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3;
static const CoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239;
static const CoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152;
static const CoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368;
static const CoordType kGlobeUpperNamesTop = kNavAreaTop + 188;
static const CoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368;
static const CoordType kGlobeLowerNamesTop = kNavAreaTop + 212;
static const CoordType kGlobeCountdownLeft = kNavAreaLeft + 478;
static const CoordType kGlobeCountdownTop = kNavAreaTop + 164;
// Norad Alpha display IDs.
static const DisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID;
static const DisplayElementID kECRPanID = kECRSlideShowMovieID + 1;
static const DisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1;
static const DisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1;
static const DisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1;
static const DisplayElementID kN01RightSideID = kN01LeftSideID + 1;
static const DisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1;
static const DisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1;
static const DisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1;
static const DisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1;
static const DisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1;
static const DisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1;
static const DisplayElementID kClawMonitorID = kSubControlMonitorID + 1;
static const DisplayElementID kSubControlPinchID = kClawMonitorID + 1;
static const DisplayElementID kSubControlDownID = kSubControlPinchID + 1;
static const DisplayElementID kSubControlRightID = kSubControlDownID + 1;
static const DisplayElementID kSubControlLeftID = kSubControlRightID + 1;
static const DisplayElementID kSubControlUpID = kSubControlLeftID + 1;
static const DisplayElementID kSubControlCCWID = kSubControlUpID + 1;
static const DisplayElementID kSubControlCWID = kSubControlCCWID + 1;
static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1;
// Norad Delta display IDs.
static const DisplayElementID kGlobeRobotID = kNeighborhoodDisplayID;
static const DisplayElementID kGlobeMonitorID = kGlobeRobotID + 1;
static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 1;
static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1;
static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1;
static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1;
static const DisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1;
static const DisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1;
static const DisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1;
static const DisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1;
static const DisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1;
static const DisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1;
static const DisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1;
static const DisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1;
static const DisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1;
static const DisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1;
static const DisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1;
static const DisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1;
// Norad Alpha:
static const DisplayOrder kECRMonitorOrder = kMonitorLayer;
static const DisplayOrder kECRPanOrder = kECRMonitorOrder + 1;
static const DisplayOrder kN01LeftSideOrder = kMonitorLayer;
static const DisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1;
static const DisplayOrder kElevatorControlsOrder = kMonitorLayer;
static const DisplayOrder kPressureLevelsOrder = kMonitorLayer;
static const DisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1;
static const DisplayOrder kPressureUpOrder = kPressureTypeOrder + 1;
static const DisplayOrder kPressureDownOrder = kPressureUpOrder + 1;
static const DisplayOrder kPlatformOrder = kMonitorLayer;
static const DisplayOrder kSubControlOrder = kMonitorLayer;
static const DisplayOrder kClawMonitorOrder = kSubControlOrder + 1;
static const DisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1;
static const DisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1;
static const DisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1;
static const DisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1;
static const DisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1;
static const DisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1;
static const DisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1;
static const DisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1;
// Norad Delta:
static const DisplayOrder kGlobeMonitorLayer = kMonitorLayer;
static const DisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1;
static const DisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1;
static const DisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1;
static const DisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1;
static const DisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1;
static const DisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1;
// Norad Alpha Tables
static const TimeScale kNoradAlphaMovieScale = 600;
static const TimeScale kNoradAlphaFramesPerSecond = 15;
static const TimeScale kNoradAlphaFrameDuration = 40;
// Alternate IDs.
static const AlternateID kAltNoradAlphaNormal = 0;
// Room IDs.
static const RoomID kNorad01 = 0;
static const RoomID kNorad01East = 1;
static const RoomID kNorad01West = 2;
static const RoomID kNorad02 = 3;
static const RoomID kNorad03 = 4;
static const RoomID kNorad04 = 5;
static const RoomID kNorad05 = 6;
static const RoomID kNorad06 = 7;
static const RoomID kNorad07 = 8;
static const RoomID kNorad07North = 9;
static const RoomID kNorad08 = 10;
static const RoomID kNorad09 = 11;
static const RoomID kNorad10 = 12;
static const RoomID kNorad10East = 13;
static const RoomID kNorad11 = 14;
static const RoomID kNorad11South = 15;
static const RoomID kNorad12 = 16;
static const RoomID kNorad12South = 17;
static const RoomID kNorad13 = 18;
static const RoomID kNorad14 = 19;
static const RoomID kNorad15 = 20;
static const RoomID kNorad16 = 21;
static const RoomID kNorad17 = 22;
static const RoomID kNorad18 = 23;
static const RoomID kNorad19 = 24;
static const RoomID kNorad19West = 25;
static const RoomID kNorad21 = 26;
static const RoomID kNorad21West = 27;
static const RoomID kNorad22 = 28;
static const RoomID kNorad22West = 29;
// Hot Spot Activation IDs.
// Hot Spot IDs.
static const HotSpotID kNorad01ECRSpotID = 5000;
static const HotSpotID kNorad01GasSpotID = 5001;
static const HotSpotID kNorad01ECROutSpotID = 5002;
static const HotSpotID kNorad01GasOutSpotID = 5003;
static const HotSpotID kNorad01MonitorSpotID = 5004;
static const HotSpotID kNorad01IntakeSpotID = 5005;
static const HotSpotID kNorad01DispenseSpotID = 5006;
static const HotSpotID kNorad01ArSpotID = 5007;
static const HotSpotID kNorad01CO2SpotID = 5008;
static const HotSpotID kNorad01HeSpotID = 5009;
static const HotSpotID kNorad01OSpotID = 5010;
static const HotSpotID kNorad01NSpotID = 5011;
static const HotSpotID kN01GasCanisterSpotID = 5012;
static const HotSpotID kN01ArgonCanisterSpotID = 5013;
static const HotSpotID kN01AirMaskSpotID = 5014;
static const HotSpotID kN01NitrogenCanisterSpotID = 5015;
static const HotSpotID kN01GasOutletSpotID = 5016;
static const HotSpotID kNorad07DoorSpotID = 5017;
static const HotSpotID kNorad07DoorOutSpotID = 5018;
static const HotSpotID kNorad10DoorSpotID = 5019;
static const HotSpotID kNorad10EastOutSpotID = 5020;
static const HotSpotID kAlphaUpperPressureDoorUpSpotID = 5021;
static const HotSpotID kAlphaUpperPressureDoorDownSpotID = 5022;
static const HotSpotID kNorad11ElevatorSpotID = 5023;
static const HotSpotID kNorad11ElevatorOutSpotID = 5024;
static const HotSpotID kNorad11ElevatorDownSpotID = 5025;
static const HotSpotID kNorad12ElevatorSpotID = 5026;
static const HotSpotID kNorad12ElevatorOutSpotID = 5027;
static const HotSpotID kNorad12ElevatorUpSpotID = 5028;
static const HotSpotID kNorad19MonitorSpotID = 5029;
static const HotSpotID kNorad19MonitorOutSpotID = 5030;
static const HotSpotID kNorad19ActivateMonitorSpotID = 5031;
static const HotSpotID kNorad21WestSpotID = 5032;
static const HotSpotID kNorad21WestOutSpotID = 5033;
static const HotSpotID kAlphaLowerPressureDoorUpSpotID = 5034;
static const HotSpotID kAlphaLowerPressureDoorDownSpotID = 5035;
static const HotSpotID kNorad22MonitorSpotID = 5036;
static const HotSpotID kNorad22MonitorOutSpotID = 5037;
static const HotSpotID kNorad22LaunchPrepSpotID = 5038;
static const HotSpotID kNorad22ClawControlSpotID = 5039;
static const HotSpotID kNorad22ClawPinchSpotID = 5040;
static const HotSpotID kNorad22ClawDownSpotID = 5041;
static const HotSpotID kNorad22ClawRightSpotID = 5042;
static const HotSpotID kNorad22ClawLeftSpotID = 5043;
static const HotSpotID kNorad22ClawUpSpotID = 5044;
static const HotSpotID kNorad22ClawCCWSpotID = 5045;
static const HotSpotID kNorad22ClawCWSpotID = 5046;
// Extra sequence IDs.
static const ExtraID kNoradArriveFromTSA = 0;
static const ExtraID kNorad01RobotTaunt = 1;
static const ExtraID kNorad01ZoomInWithGasCanister = 2;
static const ExtraID kN01WGasCanister = 3;
static const ExtraID kNorad01ZoomOutWithGasCanister = 4;
static const ExtraID kN01WZEmptyLit = 5;
static const ExtraID kN01WZGasCanisterDim = 6;
static const ExtraID kN01WZGasCanisterLit = 7;
static const ExtraID kN01WZArgonCanisterDim = 8;
static const ExtraID kN01WZArgonCanisterLit = 9;
static const ExtraID kN01WZAirMaskDim = 10;
static const ExtraID kN01WZAirMaskLit = 11;
static const ExtraID kN01WZNitrogenCanisterDim = 12;
static const ExtraID kN01WZNitrogenCanisterLit = 13;
static const ExtraID kNorad04EastDeath = 14;
static const ExtraID kNorad19PrepSub = 15;
static const ExtraID kNorad19ExitToSub = 16;
static const ExtraID kNorad22SouthIntro = 17;
static const ExtraID kNorad22SouthReply = 18;
static const ExtraID kNorad22SouthFinish = 19;
static const ExtraID kN22ClawFromAToB = 20;
static const ExtraID kN22ClawALoop = 21;
static const ExtraID kN22ClawAPinch = 22;
static const ExtraID kN22ClawACounterclockwise = 23;
static const ExtraID kN22ClawAClockwise = 24;
static const ExtraID kN22ClawFromBToA = 25;
static const ExtraID kN22ClawFromBToC = 26;
static const ExtraID kN22ClawFromBToD = 27;
static const ExtraID kN22ClawBLoop = 28;
static const ExtraID kN22ClawBPinch = 29;
static const ExtraID kN22ClawBCounterclockwise = 30;
static const ExtraID kN22ClawBClockwise = 31;
static const ExtraID kN22ClawFromCToB = 32;
static const ExtraID kN22ClawCLoop = 33;
static const ExtraID kN22ClawCPinch = 34;
static const ExtraID kN22ClawCCounterclockwise = 35;
static const ExtraID kN22ClawCClockwise = 36;
static const ExtraID kN22ClawFromDToB = 37;
static const ExtraID kN22ClawDLoop = 38;
static const ExtraID kN22ClawDPinch = 39;
static const ExtraID kN22ClawDCounterclockwise = 40;
static const ExtraID kN22ClawDClockwise = 41;
// Norad Delta Extra sequence IDs.
static const ExtraID kArriveFromSubChase = 0;
static const ExtraID kN59ZoomWithRobot = 1;
static const ExtraID kN59RobotApproaches = 2;
static const ExtraID kN59RobotPunchLoop = 3;
static const ExtraID kN59PlayerWins1 = 4;
static const ExtraID kN59PlayerWins2 = 5;
static const ExtraID kN59RobotWins = 6;
static const ExtraID kN59RobotHeadOpens = 7;
static const ExtraID kN59Biochips111 = 8;
static const ExtraID kN59Biochips011 = 9;
static const ExtraID kN59Biochips101 = 10;
static const ExtraID kN59Biochips001 = 11;
static const ExtraID kN59Biochips110 = 12;
static const ExtraID kN59Biochips010 = 13;
static const ExtraID kN59Biochips100 = 14;
static const ExtraID kN59Biochips000 = 15;
static const ExtraID kN59RobotDisappears = 16;
static const ExtraID kN60ClawFromAToB = 17;
static const ExtraID kN60ClawALoop = 18;
static const ExtraID kN60ClawAPinch = 19;
static const ExtraID kN60ClawACounterclockwise = 20;
static const ExtraID kN60ClawAClockwise = 21;
static const ExtraID kN60ClawFromBToA = 22;
static const ExtraID kN60ClawFromBToC = 23;
static const ExtraID kN60ClawFromBToD = 24;
static const ExtraID kN60ClawBLoop = 25;
static const ExtraID kN60ClawBPinch = 26;
static const ExtraID kN60ClawBCounterclockwise = 27;
static const ExtraID kN60ClawBClockwise = 28;
static const ExtraID kN60ClawFromCToB = 29;
static const ExtraID kN60ClawCLoop = 30;
static const ExtraID kN60ClawCPinch = 31;
static const ExtraID kN60ClawCCounterclockwise = 32;
static const ExtraID kN60ClawCClockwise = 33;
static const ExtraID kN60ClawFromDToB = 34;
static const ExtraID kN60ClawDLoop = 35;
static const ExtraID kN60ClawDPinch = 36;
static const ExtraID kN60ClawDCounterclockwise = 37;
static const ExtraID kN60ClawDClockwise = 38;
static const ExtraID kN60RobotApproaches = 39;
static const ExtraID kN60FirstMistake = 40;
static const ExtraID kN60ArmActivated = 41;
static const ExtraID kN60SecondMistake = 42;
static const ExtraID kN60ArmToPositionB = 43;
static const ExtraID kN60ThirdMistake = 44;
static const ExtraID kN60ArmGrabsRobot = 45;
static const ExtraID kN60FourthMistake = 46;
static const ExtraID kN60ArmCarriesRobotToPositionA = 47;
static const ExtraID kN60PlayerFollowsRobotToDoor = 48;
static const ExtraID kN60RobotHeadOpens = 49;
static const ExtraID kN60Biochips111 = 50;
static const ExtraID kN60Biochips011 = 51;
static const ExtraID kN60Biochips101 = 52;
static const ExtraID kN60Biochips001 = 53;
static const ExtraID kN60Biochips110 = 54;
static const ExtraID kN60Biochips010 = 55;
static const ExtraID kN60Biochips100 = 56;
static const ExtraID kN60Biochips000 = 57;
static const ExtraID kN60RobotDisappears = 58;
static const ExtraID kNoradDeltaRetinalScanBad = 59;
static const ExtraID kNoradDeltaRetinalScanGood = 60;
static const ExtraID kN79BrightView = 61;
// Norad Delta Tables
static const TimeScale kNoradDeltaMovieScale = 600;
static const TimeScale kNoradDeltaFramesPerSecond = 15;
static const TimeScale kNoradDeltaFrameDuration = 40;
// Alternate IDs.
static const AlternateID kAltNoradDeltaNormal = 0;
// Room IDs.
static const RoomID kNorad41 = 0;
static const RoomID kNorad42 = 1;
static const RoomID kNorad43 = 2;
static const RoomID kNorad44 = 3;
static const RoomID kNorad45 = 4;
static const RoomID kNorad46 = 5;
static const RoomID kNorad47 = 6;
static const RoomID kNorad48 = 7;
static const RoomID kNorad48South = 8;
static const RoomID kNorad49 = 9;
static const RoomID kNorad49South = 10;
static const RoomID kNorad50 = 11;
static const RoomID kNorad50East = 12;
static const RoomID kNorad51 = 13;
static const RoomID kNorad52 = 14;
static const RoomID kNorad53 = 15;
static const RoomID kNorad54 = 16;
static const RoomID kNorad54North = 17;
static const RoomID kNorad55 = 18;
static const RoomID kNorad56 = 19;
static const RoomID kNorad57 = 20;
static const RoomID kNorad58 = 21;
static const RoomID kNorad59 = 22;
static const RoomID kNorad59West = 23;
static const RoomID kNorad60 = 24;
static const RoomID kNorad60West = 25;
static const RoomID kNorad61 = 26;
static const RoomID kNorad62 = 27;
static const RoomID kNorad63 = 28;
static const RoomID kNorad64 = 29;
static const RoomID kNorad65 = 30;
static const RoomID kNorad66 = 31;
static const RoomID kNorad67 = 32;
static const RoomID kNorad68 = 33;
static const RoomID kNorad68West = 34;
static const RoomID kNorad69 = 35;
static const RoomID kNorad78 = 36;
static const RoomID kNorad79 = 37;
static const RoomID kNorad79West = 38;
// Hot Spot Activation IDs.
// Hot Spot IDs.
static const HotSpotID kNorad48ElevatorSpotID = 5000;
static const HotSpotID kNorad48ElevatorOutSpotID = 5001;
static const HotSpotID kNorad48ElevatorUpSpotID = 5002;
static const HotSpotID kNorad49ElevatorSpotID = 5003;
static const HotSpotID kNorad49ElevatorOutSpotID = 5004;
static const HotSpotID kNorad49ElevatorDownSpotID = 5005;
static const HotSpotID kNorad50DoorSpotID = 5006;
static const HotSpotID kNorad50DoorOutSpotID = 5007;
static const HotSpotID kDeltaUpperPressureDoorUpSpotID = 5008;
static const HotSpotID kDeltaUpperPressureDoorDownSpotID = 5009;
static const HotSpotID kNorad54DoorSpotID = 5010;
static const HotSpotID kNorad54DoorOutSpotID = 5011;
static const HotSpotID kNorad59WestSpotID = 5012;
static const HotSpotID kNorad59WestOutSpotID = 5013;
static const HotSpotID kDeltaLowerPressureDoorUpSpotID = 5014;
static const HotSpotID kDeltaLowerPressureDoorDownSpotID = 5015;
static const HotSpotID kDelta59RobotHeadSpotID = 5016;
static const HotSpotID kDelta59RobotShieldBiochipSpotID = 5017;
static const HotSpotID kDelta59RobotOpMemBiochipSpotID = 5018;
static const HotSpotID kDelta59RobotRetinalBiochipSpotID = 5019;
static const HotSpotID kNorad60MonitorSpotID = 5020;
static const HotSpotID kNorad60MonitorOutSpotID = 5021;
static const HotSpotID kNorad60LaunchPrepSpotID = 5022;
static const HotSpotID kNorad60ClawControlSpotID = 5023;
static const HotSpotID kNorad60ClawPinchSpotID = 5024;
static const HotSpotID kNorad60ClawDownSpotID = 5025;
static const HotSpotID kNorad60ClawRightSpotID = 5026;
static const HotSpotID kNorad60ClawLeftSpotID = 5027;
static const HotSpotID kNorad60ClawUpSpotID = 5028;
static const HotSpotID kNorad60ClawCCWSpotID = 5029;
static const HotSpotID kNorad60ClawCWSpotID = 5030;
static const HotSpotID kDelta60RobotHeadSpotID = 5031;
static const HotSpotID kDelta60RobotShieldBiochipSpotID = 5032;
static const HotSpotID kDelta60RobotOpMemBiochipSpotID = 5033;
static const HotSpotID kDelta60RobotRetinalBiochipSpotID = 5034;
static const HotSpotID kNorad68WestSpotID = 5035;
static const HotSpotID kNorad68WestOutSpotID = 5036;
static const HotSpotID kNorad79WestSpotID = 5037;
static const HotSpotID kNorad79WestOutSpotID = 5038;
static const HotSpotID kNorad79SpinLeftSpotID = 5039;
static const HotSpotID kNorad79SpinRightSpotID = 5040;
static const HotSpotID kNorad79SpinUpSpotID = 5041;
static const HotSpotID kNorad79SpinDownSpotID = 5042;
static const HotSpotID kNorad79SiloAreaSpotID = 5043;
} // End of namespace Pegasus
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
/* 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/>.
*
*/
#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 *);
~GlobeTracker() override {}
void setTrackParameters(const Hotspot *, GlobeTrackDirection);
void continueTracking(const Input &) override;
void startTracking(const Input &) override;
void stopTracking(const Input &) override;
void activateHotspots() override;
bool stopTrackingInput(const Input &) override;
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);
~GlobeCountdown() override {}
void setCountdownTime(const int);
void startCountdown();
void stopCountdown();
void setDisplayOrder(const DisplayOrder);
void show() override;
void hide() override;
void moveElementTo(const CoordType, const CoordType) override;
void draw(const Common::Rect &) override;
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 *);
~GlobeGame() override {}
void setSoundFXLevel(const uint16) override;
void handleInput(const Input &, const Hotspot *) override;
void clickInHotspot(const Input &, const Hotspot *) override;
void activateHotspots() override;
bool canSolve() override;
void doSolve() override;
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() override;
void initInteraction() override;
void closeInteraction() override;
void receiveNotification(Notification *, const NotificationFlags) override;
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 _robotMovie;
Movie _monitorMovie;
Movie _globeMovie;
Movie _upperNamesMovie;
Movie _lowerNamesMovie;
Notification _globeNotification;
NotificationCallBack _robotCallBack;
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

View File

@@ -0,0 +1,984 @@
/* 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/energymonitor.h"
#include "pegasus/gamestate.h"
#include "pegasus/interface.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/items/biochips/arthurchip.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 = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID);
hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
hotspot = _vm->getAllHotspots().findHotspotByID(kNorad79WestSpotID);
hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag);
hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotShieldBiochipSpotID);
hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID);
hotspotEntry->hotspotItem = kShieldBiochip;
hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotOpMemBiochipSpotID);
hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID);
hotspotEntry->hotspotItem = kOpticalBiochip;
hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotRetinalBiochipSpotID);
hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID);
hotspotEntry->hotspotItem = kRetinalScanBiochip;
hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotShieldBiochipSpotID);
hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID);
hotspotEntry->hotspotItem = kShieldBiochip;
hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotOpMemBiochipSpotID);
hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag);
hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID);
hotspotEntry->hotspotItem = kOpticalBiochip;
hotspot = _vm->getAllHotspots().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);
// fall through
// FIXME: fall through intentional?
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 >= kNorad78 && room <= kNorad79West) {
if (GameState.getNoradPlayedGlobeGame()) {
// blitter moved clone2727's globe room loop fix up here
// since the original didn't play the red alert sound during the
// globe game.
// clone2727 added this fix so we can play the correct sound
// with the DVD version. This originally loaded it into slot 2,
// which in addition to having the corrupted sound on the disk,
// caused it to never play.
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/GlobAmb2.32K.AIFF");
else
loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF");
} else
loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF");
} else if (GameState.getNoradGassed()) {
if (room >= kNorad41 && room <= kNorad49South) {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", kNoradWarningVolume * 3);
else
loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
} else if (room >= kNorad59 && room <= kNorad60West) {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.32K.AIFF", kNoradWarningVolume * 3);
else
loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
} else {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/WARNING LOOP.32K.AIFF", kNoradWarningVolume);
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) {
if (_vm->isDVD())
loadLoopSound2("Sounds/Norad/N54NAS.32K.AIFF", 0x100 / 2);
else
loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2);
} else
loadLoopSound2("");
}
} else {
// Start them off at zero...
if (GameState.getNoradGassed()) {
if (_vm->isDVD())
loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", 0, 0, 0);
else
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;
default:
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) {
if (!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);
} else {
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA08", kArthurNoradExitedSub);
}
}
break;
case kNorad54:
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA71", kArthurNoradApproachedDamagedDoor);
break;
case kNorad54North:
GameState.setScoringSawRobotAt54North(true);
break;
case kNorad68:
if (GameState.getNoradRetScanGood())
openDoor();
else if (!_vm->playerHasItemID(kRetinalScanBiochip) && g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurNoradAtRetScanNoBiochip);
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 != nullptr && retScan->getObjectID() == kRetinalScanBiochip) {
((RetScanChip *)retScan)->searchForLaser();
succeedRetinalScan();
} else {
failRetinalScan();
}
}
void NoradDelta::arriveAtNorad79West() {
if (!GameState.getNoradPlayedGlobeGame())
newInteraction(kNoradGlobeGameInteractionID);
}
void NoradDelta::turnTo(const DirectionConstant direction) {
Norad::turnTo(direction);
if (g_arthurChip) {
switch (GameState.getCurrentRoomAndView()) {
case MakeRoomView(kNorad54, kNorth):
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA71", kArthurNoradApproachedDamagedDoor);
break;
case MakeRoomView(kNorad68, kWest):
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurNoradApproachedDamagedDoor);
break;
}
}
}
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();
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA63", kArthurNoradThreatenedByRobot);
if (_vm->isChattyAI())
g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption);
updateViewFrame();
}
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);
if (_vm->isChattyAI())
g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
}
void NoradDelta::playerBeatRobotWithClaw() {
GameState.setNoradBeatRobotWithClaw(true);
updateViewFrame();
GameState.setScoringStoppedNoradRobot(true);
GameState.setScoringNoradGandhi(true);
if (_vm->isChattyAI())
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, _vm->getAllHotspots().findHotspotByID(kNorad59WestSpotID));
} else {
Norad::openDoor();
}
}
void NoradDelta::cantMoveThatWay(CanOpenDoorReason reason) {
// WORKAROUND: The door outside the launch console room isn't treated as a door,
// so play the correct sound.
if (reason == kCantMoveBlocked && GameState.getCurrentRoomAndView() == MakeRoomView(kNorad67, kNorth)) {
cantOpenDoor(kCantOpenLocked);
return;
}
Neighborhood::cantMoveThatWay(reason);
}
void NoradDelta::activateHotspots() {
Norad::activateHotspots();
if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) {
_vm->getAllHotspots().deactivateOneHotspot(kNorad59WestOutSpotID);
if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta59RobotShieldBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID);
if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID);
if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID);
} else
_vm->getAllHotspots().activateOneHotspot(kDelta59RobotHeadSpotID);
} else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest &&
GameState.getNoradBeatRobotWithClaw()) {
_vm->getAllHotspots().deactivateOneHotspot(kNorad60MonitorOutSpotID);
if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) {
if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta60RobotShieldBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID);
if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID);
if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag))
_vm->getAllHotspots().activateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
else
_vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID);
} else {
_vm->getAllHotspots().activateOneHotspot(kDelta60RobotHeadSpotID);
}
} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) {
if (GameState.isCurrentDoorOpen())
_vm->getAllHotspots().deactivateOneHotspot(kNorad50DoorSpotID);
} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) {
if (GameState.isCurrentDoorOpen())
_vm->getAllHotspots().deactivateOneHotspot(kNorad59WestSpotID);
} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) {
// WORKAROUND: Make sure the retinal hotspot is disabled after the door opens.
// Fixes a bug in the original.
if (GameState.isCurrentDoorOpen())
_vm->getAllHotspots().deactivateOneHotspot(kNorad68WestSpotID);
}
}
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);
if (g_arthurChip) {
switch (_vm->getRandomNumber(2)) {
case 0:
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA36", kArthurNoradRobotHeadOpen);
break;
case 1:
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA37", kArthurNoradRobotHeadOpen);
break;
case 2:
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA40", kArthurNoradRobotHeadOpen);
break;
}
}
break;
case kNoradDeltaRetinalScanBad:
retScan = (RetScanChip *)_vm->getCurrentBiochip();
retScan->setItemState(kNormalItem);
playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut);
downButton(dummy);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA13", kArthurNoradAtRetScanNoBiochip);
break;
case kNoradDeltaRetinalScanGood:
retScan = (RetScanChip *)_vm->getCurrentBiochip();
retScan->setItemState(kNormalItem);
downButton(dummy);
break;
case kN59RobotDisappears:
case kN60RobotDisappears:
recallToTSASuccess();
break;
default:
break;
}
_interruptionFilter = kFilterAllInput;
} else if ((flags & kSpotSoundCompletedFlag) != 0) {
if (_spotSounds.getStart() == kToDeactivateIn && g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB41", kArthurNoradStartGlobeGame);
}
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;
default:
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;
default:
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;
default:
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;
default:
break;
}
if (id != kNoHotSpotID)
return _vm->getAllHotspots().findHotspotByID(id);
return Norad::getItemScreenSpot(item, element);
}
Common::Path 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 == nullptr || retScan->getObjectID() != kRetinalScanBiochip)
numHints = 2;
} else if (!GameState.isCurrentDoorOpen()) {
numHints = 2;
}
break;
default:
break;
}
}
return numHints;
}
Common::Path NoradDelta::getHintMovie(uint hintNum) {
Common::Path 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";
default:
break;
}
}
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 != nullptr && 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 *)_vm->getAllItems().findItemByID(kRetinalScanBiochip));
BiochipItem *biochip = _vm->getCurrentBiochip();
if (biochip != nullptr && biochip->getObjectID() != kRetinalScanBiochip && g_interface)
g_interface->setCurrentBiochipID(kRetinalScanBiochip);
Hotspot *spot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID);
Input scratch;
InputHandler::_inputHandler->clickInHotspot(scratch, spot);
}
}
void NoradDelta::setSoundFXLevel(const uint16 level) {
Neighborhood::setSoundFXLevel(level);
if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad54North, kNorth))
_loop2Fader.setMasterVolume(level);
}
Common::Path NoradDelta::getSoundSpotsName() {
return "Sounds/Norad/Norad Delta Spots";
}
Common::Path NoradDelta::getNavMovieName() {
return "Images/Norad Delta/Norad Delta.movie";
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,120 @@
/* 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/>.
*
*/
#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 *);
~NoradDelta() override {}
void init() override;
void start() override;
void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &) override;
void finishedGlobeGame();
GameInteraction *makeInteraction(const InteractionID) override;
void playClawMonitorIntro() override;
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 *&) override;
void playerBeatRobotWithClaw();
void playerBeatRobotWithDoor();
void loadAmbientLoops() override;
void setUpAIRules() override;
Common::Path getEnvScanMovie() override;
uint getNumHints() override;
Common::Path getHintMovie(uint) override;
void closeDoorOffScreen(const RoomID, const DirectionConstant) override;
void checkContinuePoint(const RoomID, const DirectionConstant) override;
bool canSolve() override;
void doSolve() override;
void setSoundFXLevel(const uint16) override;
void doorOpened() override;
protected:
enum {
kNoradPrivateArrivedFromSubFlag,
kNoradPrivateFinishedGlobeGameFlag,
kNoradPrivateRobotHeadOpenFlag,
kNoradPrivateGotShieldChipFlag,
kNoradPrivateGotOpticalChipFlag,
kNoradPrivateGotRetScanChipFlag,
kNumNoradPrivateFlags
};
static const uint32 _noradDeltaClawExtras[22];
void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &) override;
void getZoomEntry(const HotSpotID, ZoomTable::Entry &) override;
void arriveAt(const RoomID, const DirectionConstant) override;
void arriveAtNorad68West();
void arriveAtNorad79West();
void turnTo(const DirectionConstant) override;
TimeValue getViewTime(const RoomID, const DirectionConstant) override;
void openDoor() override;
void cantMoveThatWay(CanMoveForwardReason) override;
void activateHotspots() override;
void clickInHotspot(const Input &, const Hotspot *) override;
void receiveNotification(Notification *, const NotificationFlags) override;
void pickedUpItem(Item *item) override;
void takeItemFromRoom(Item *item) override;
void dropItemIntoRoom(Item *item, Hotspot *) override;
Hotspot *getItemScreenSpot(Item *, DisplayElement *) override;
bool playingAgainstRobot() override;
void failRetinalScan();
void succeedRetinalScan();
void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &) override;
void bumpIntoWall() override;
FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags;
Common::Path getSoundSpotsName() override;
Common::Path getNavMovieName() override;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,299 @@
/* 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/energymonitor.h"
#include "pegasus/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/items/biochips/arthurchip.h"
#include "pegasus/items/inventory/airmask.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/noradelevator.h"
#include "pegasus/neighborhood/norad/pressuredoor.h"
#include "pegasus/neighborhood/norad/subcontrolroom.h"
#include "pegasus/neighborhood/norad/subplatform.h"
namespace Pegasus {
const NotificationFlags kDoneWithPressureDoorNotification = 1;
const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification;
// This class handles everything that Norad Alpha and Delta have in common, such as
// oxygen mask usage, the elevator and the pressure doors.
Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) :
Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) {
_elevatorUpSpotID = kNoHotSpotID;
_elevatorDownSpotID = kNoHotSpotID;
_elevatorUpRoomID = kNoHotSpotID;
_elevatorDownRoomID = kNoHotSpotID;
_subRoomEntryRoom1 = kNoRoomID;
_subRoomEntryDir1 = kNoDirection;
_subRoomEntryRoom2 = kNoRoomID;
_subRoomEntryDir2 = kNoDirection;
_upperPressureDoorRoom = kNoRoomID;
_lowerPressureDoorRoom = kNoRoomID;
_upperPressureDoorUpSpotID = kNoHotSpotID;
_upperPressureDoorDownSpotID = kNoHotSpotID;
_upperPressureDoorAbortSpotID = kNoHotSpotID;
_lowerPressureDoorUpSpotID = kNoHotSpotID;
_lowerPressureDoorDownSpotID = kNoHotSpotID;
_lowerPressureDoorAbortSpotID = kNoHotSpotID;
_pressureSoundIn = 0xffffffff;
_pressureSoundOut = 0xffffffff;
_equalizeSoundIn = 0xffffffff;
_equalizeSoundOut = 0xffffffff;
_accessDeniedIn = 0xffffffff;
_accessDeniedOut = 0xffffffff;
_platformRoom = kNoRoomID;
_subControlRoom = kNoRoomID;
_doneWithPressureDoor = false;
_noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags);
}
GameInteraction *Norad::makeInteraction(const InteractionID interactionID) {
PressureDoor *pressureDoor;
SubControlRoom *subControl;
switch (interactionID) {
case kNoradElevatorInteractionID:
return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID);
case kNoradPressureDoorInteractionID:
if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID,
_upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
else
pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID,
_lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot())
pressureDoor->playAgainstRobot();
return pressureDoor;
case kNoradSubControlRoomInteractionID:
subControl = new SubControlRoom(this);
if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot())
subControl->playAgainstRobot();
return subControl;
case kNoradSubPlatformInteractionID:
return new SubPlatform(this);
default:
return nullptr;
}
}
void Norad::flushGameState() {
g_energyMonitor->saveCurrentEnergyValue();
}
void Norad::start() {
setUpAirMask();
Neighborhood::start();
}
void Norad::activateHotspots() {
Neighborhood::activateHotspots();
RoomID room = GameState.getCurrentRoom();
if (room == _elevatorUpRoomID)
_neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID);
else if (room == _elevatorDownRoomID)
_neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID);
}
void Norad::arriveAt(const RoomID room, const DirectionConstant direction) {
Neighborhood::arriveAt(room, direction);
if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID)
arriveAtNoradElevator();
else if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
arriveAtUpperPressureDoorRoom();
else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom)
arriveAtLowerPressureDoorRoom();
else if (GameState.getCurrentRoom() == _platformRoom)
arriveAtSubPlatformRoom();
else if (GameState.getCurrentRoom() == _subControlRoom)
arriveAtSubControlRoom();
if (_doneWithPressureDoor) {
_doneWithPressureDoor = false;
openDoor();
}
}
void Norad::arriveAtNoradElevator() {
if (_currentInteraction)
_currentInteraction->startOverInteraction();
else
newInteraction(kNoradElevatorInteractionID);
}
void Norad::arriveAtUpperPressureDoorRoom() {
newInteraction(kNoradPressureDoorInteractionID);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
}
void Norad::arriveAtLowerPressureDoorRoom() {
newInteraction(kNoradPressureDoorInteractionID);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
}
void Norad::arriveAtSubPlatformRoom() {
newInteraction(kNoradSubPlatformInteractionID);
}
void Norad::arriveAtSubControlRoom() {
newInteraction(kNoradSubControlRoomInteractionID);
}
int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
int16 result = Neighborhood::getStaticCompassAngle(room, dir);
if (room == _elevatorUpRoomID || room == _elevatorDownRoomID)
result += kElevatorCompassAngle;
else if (room == _platformRoom)
result += kSubPlatformCompassAngle;
else if (room == _subControlRoom)
result += kSubControlCompassAngle;
return result;
}
CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) ||
(GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) &&
GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure)
return kCantOpenBadPressure;
return Neighborhood::canOpenDoor(entry);
}
void Norad::cantOpenDoor(CanOpenDoorReason reason) {
bool firstLockedDoor;
if (reason == kCantOpenBadPressure)
playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
else
playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
if (g_arthurChip) {
firstLockedDoor = g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA65", kArthurNoradAttemptedLockedDoor);
if (!firstLockedDoor)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA68", kArthurNoradAttemptedLockedDoorAgain);
}
}
void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
if (GameState.getCurrentRoom() == _elevatorUpRoomID) {
if (exitEntry.exitRoom != _elevatorDownRoomID)
newInteraction(kNoInteractionID);
} else if (GameState.getCurrentRoom() == _elevatorDownRoomID) {
if (exitEntry.exitRoom != _elevatorUpRoomID)
newInteraction(kNoInteractionID);
} else {
newInteraction(kNoInteractionID);
}
Neighborhood::startExitMovie(exitEntry);
}
void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
newInteraction(kNoInteractionID);
Neighborhood::startZoomMovie(zoomEntry);
}
void Norad::upButton(const Input &input) {
if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID)
Neighborhood::upButton(input);
}
void Norad::setUpAirMask() {
_airMaskCallBack.setNotification(&_neighborhoodNotification);
_airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes);
_airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag);
_neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag);
_airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_airMaskTimer.setScale(1);
_airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit);
checkAirMask();
}
void Norad::checkAirMask() {
// WORKAROUND: The original game forgot to handle the case where the canister would
// be removed, leading to the timer remaining active.
if (!GameState.getNoradGassed() || (g_airMask && g_airMask->isAirFilterOn())) {
_airMaskTimer.stop();
} else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) {
_airMaskTimer.setTime(0);
_airMaskTimer.start();
}
loadAmbientLoops();
}
void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) {
if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0)
g_vm->die(kDeathGassedInNorad);
Neighborhood::receiveNotification(notification, flags);
if (notification == &_noradNotification) {
// Must be kDoneWithPressureDoorNotification...
Input scratch;
_doneWithPressureDoor = true;
downButton(scratch);
}
}
uint16 Norad::getDateResID() const {
return kDate2112ID;
}
Common::Path Norad::getBriefingMovie() {
return "Images/AI/Norad/XNO";
}
void Norad::pickedUpItem(Item *item) {
Neighborhood::pickedUpItem(item);
g_AIArea->checkMiddleArea();
}
void Norad::doneWithPressureDoor() {
_noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification);
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,120 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H
#include "pegasus/neighborhood/neighborhood.h"
namespace Pegasus {
// This is the code common to both Norad Alpha and Norad Delta
class Norad : public Neighborhood {
public:
Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const NeighborhoodID);
~Norad() override {}
void flushGameState() override;
void start() override;
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 *&) = 0;
void checkAirMask() override;
uint16 getDateResID() const override;
GameInteraction *makeInteraction(const InteractionID) override;
Common::Path getBriefingMovie() override;
void pickedUpItem(Item *) override;
virtual void playClawMonitorIntro() {}
void doneWithPressureDoor();
protected:
CanOpenDoorReason canOpenDoor(DoorTable::Entry &) override;
void cantOpenDoor(CanOpenDoorReason) override;
int16 getStaticCompassAngle(const RoomID, const DirectionConstant) override;
void startExitMovie(const ExitTable::Entry &) override;
void startZoomMovie(const ZoomTable::Entry &) override;
void upButton(const Input &) override;
void activateHotspots() override;
void arriveAt(const RoomID, const DirectionConstant) override;
virtual void arriveAtNoradElevator();
virtual void arriveAtUpperPressureDoorRoom();
virtual void arriveAtLowerPressureDoorRoom();
virtual void arriveAtSubPlatformRoom();
virtual void arriveAtSubControlRoom();
void setUpAirMask();
void receiveNotification(Notification *, const NotificationFlags) override;
virtual bool playingAgainstRobot() { return false; }
Notification _noradNotification;
bool _doneWithPressureDoor;
RoomID _elevatorUpRoomID;
RoomID _elevatorDownRoomID;
HotSpotID _elevatorUpSpotID;
HotSpotID _elevatorDownSpotID;
TimeBase _airMaskTimer;
NotificationCallBack _airMaskCallBack;
RoomID _subRoomEntryRoom1;
DirectionConstant _subRoomEntryDir1;
RoomID _subRoomEntryRoom2;
DirectionConstant _subRoomEntryDir2;
RoomID _upperPressureDoorRoom;
RoomID _lowerPressureDoorRoom;
HotSpotID _upperPressureDoorUpSpotID;
HotSpotID _upperPressureDoorDownSpotID;
HotSpotID _upperPressureDoorAbortSpotID;
HotSpotID _lowerPressureDoorUpSpotID;
HotSpotID _lowerPressureDoorDownSpotID;
HotSpotID _lowerPressureDoorAbortSpotID;
TimeValue _pressureSoundIn;
TimeValue _pressureSoundOut;
TimeValue _equalizeSoundIn;
TimeValue _equalizeSoundOut;
TimeValue _accessDeniedIn;
TimeValue _accessDeniedOut;
RoomID _platformRoom;
RoomID _subControlRoom;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,129 @@
/* 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/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/noradelevator.h"
namespace Pegasus {
// Norad elevator PICTs:
static const ResIDType kElevatorLabelID = 200;
static const ResIDType kElevatorButtonsID = 201;
static const ResIDType kElevatorDownOnID = 202;
static const ResIDType kElevatorUpOnID = 203;
NoradElevator::NoradElevator(Neighborhood *handler, const RoomID upRoom, const RoomID downRoom,
const HotSpotID upHotspot, const HotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler),
_elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, g_vm) {
_timerExpired = false;
_upRoom = upRoom;
_downRoom = downRoom;
_upHotspot = upHotspot;
_downHotspot = downHotspot;
}
void NoradElevator::openInteraction() {
SpriteFrame *frame = new SpriteFrame();
frame->initFromPICTResource(g_vm->_resFork, kElevatorLabelID, true);
_elevatorControls.addFrame(frame, 0, 0);
frame = new SpriteFrame();
frame->initFromPICTResource(g_vm->_resFork, kElevatorButtonsID, true);
_elevatorControls.addFrame(frame, 0, 0);
frame = new SpriteFrame();
frame->initFromPICTResource(g_vm->_resFork, kElevatorDownOnID, true);
_elevatorControls.addFrame(frame, 0, 0);
frame = new SpriteFrame();
frame->initFromPICTResource(g_vm->_resFork, kElevatorUpOnID, true);
_elevatorControls.addFrame(frame, 0, 0);
_elevatorControls.setCurrentFrameIndex(0);
_elevatorControls.setDisplayOrder(kElevatorControlsOrder);
Common::Rect r;
frame->getSurfaceBounds(r);
r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop);
_elevatorControls.setBounds(r);
_elevatorControls.startDisplaying();
_elevatorControls.show();
}
void NoradElevator::initInteraction() {
_elevatorTimer.setScale(2);
_elevatorTimer.setSegment(0, 1);
_elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes);
_elevatorCallBack.setCallBackFlag(1);
_elevatorCallBack.setNotification(&_elevatorNotification);
_elevatorNotification.notifyMe(this, 1, 1);
_elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_elevatorTimer.start();
}
void NoradElevator::closeInteraction() {
_elevatorControls.stopDisplaying();
_elevatorControls.discardFrames();
_elevatorCallBack.releaseCallBack();
}
void NoradElevator::resetInteraction() {
_elevatorControls.setCurrentFrameIndex(1);
}
void NoradElevator::activateHotspots() {
GameInteraction::activateHotspots();
if (_timerExpired) {
if (GameState.getCurrentRoom() == _upRoom)
g_allHotspots.activateOneHotspot(_downHotspot);
else if (GameState.getCurrentRoom() == _downRoom)
g_allHotspots.activateOneHotspot(_upHotspot);
}
}
void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) {
HotSpotID id = spot->getObjectID();
if (id == _upHotspot || id == _downHotspot) {
g_neighborhood->moveForward();
if (id == _downHotspot)
_elevatorControls.setCurrentFrameIndex(2);
else
_elevatorControls.setCurrentFrameIndex(3);
} else {
GameInteraction::clickInHotspot(input, spot);
}
}
void NoradElevator::receiveNotification(Notification *, const NotificationFlags) {
_elevatorControls.setCurrentFrameIndex(1);
_timerExpired = true;
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H
#include "pegasus/interaction.h"
#include "pegasus/notification.h"
#include "pegasus/surface.h"
#include "pegasus/timers.h"
namespace Pegasus {
class Neighborhood;
class NoradElevator : public GameInteraction, private NotificationReceiver {
public:
NoradElevator(Neighborhood *, const RoomID, const RoomID, const HotSpotID, const HotSpotID);
~NoradElevator() override {}
protected:
void openInteraction() override;
void initInteraction() override;
void closeInteraction() override;
void resetInteraction() override;
void activateHotspots() override;
void clickInHotspot(const Input &, const Hotspot *) override;
void receiveNotification(Notification *, const NotificationFlags) override;
RoomID _upRoom;
RoomID _downRoom;
HotSpotID _upHotspot;
HotSpotID _downHotspot;
Sprite _elevatorControls;
TimeBase _elevatorTimer;
NotificationCallBack _elevatorCallBack;
Notification _elevatorNotification;
bool _timerExpired;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,562 @@
/* 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/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/pressuredoor.h"
#include "pegasus/neighborhood/norad/delta/noraddelta.h"
namespace Pegasus {
static const TimeValue kLevelsSplashStart = 0;
static const TimeValue kLevelsSplashStop = 1;
static const TimeValue kPressureBase = 1;
static const TimeValue kDoorSealedTime = 0;
static const TimeValue kEqualizeTime = 1;
static const TimeValue kMaxPressureLoopStart = 2;
static const TimeValue kMaxPressureLoopStop = 3;
static const TimeValue kOpeningDoorLoopStart = 3;
static const TimeValue kOpeningDoorLoopStop = 4;
static const TimeValue kIncreasingPressureTime = 4;
static const TimeValue kDecreasingPressureTime = 5;
static const TimeValue kCautionLoopStart = 6;
static const TimeValue kCautionLoopStop = 7;
static const NotificationFlags kSplashFinished = 1;
static const NotificationFlags kPressureDroppingFlag = kSplashFinished << 1;
static const NotificationFlags kPressureNotificationFlags = kSplashFinished |
kPressureDroppingFlag;
static const NotificationFlags kDoorJumpsUpFlag = 1;
static const NotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1;
static const NotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1;
static const NotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag |
kDoorJumpsBackFlag |
kDoorCrushedFlag;
enum {
kPlayingRobotApproaching,
kRobotPunching,
kRobotComingThrough,
kRobotDying,
kRobotDead
};
const short kMaxPunches = 5;
enum {
kPlayingSplash,
kPlayingPressureMessage,
kPlayingEqualizeMessage,
kWaitingForPlayer,
kPlayingDoneMessage,
kGameOver
};
// Pressure values range from 0 to 11.
static const short kMinPressure = 0;
static const short kMaxPressure = 11;
static const TimeScale kNavTimeScale = 600;
static const TimeValue kNavFrameRate = 15;
static const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate;
static const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame;
static const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame;
static const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame;
// Pressure door PICTs:
static const ResIDType kUpperPressureUpOffPICTID = 400;
static const ResIDType kUpperPressureUpOnPICTID = 401;
static const ResIDType kUpperPressureDownOffPICTID = 402;
static const ResIDType kUpperPressureDownOnPICTID = 403;
static const ResIDType kLowerPressureUpOffPICTID = 404;
static const ResIDType kLowerPressureUpOnPICTID = 405;
static const ResIDType kLowerPressureDownOffPICTID = 406;
static const ResIDType kLowerPressureDownOnPICTID = 407;
PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const HotSpotID upSpotID, const HotSpotID downSpotID,
const HotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn,
TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler),
_levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID),
_downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, g_vm),
_doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, g_vm) {
_neighborhoodNotification = handler->getNeighborhoodNotification();
_upHotspotID = upSpotID;
_downHotspotID = downSpotID;
_outHotspotID = outSpotID;
_pressureSoundIn = pressureSoundIn;
_pressureSoundOut = pressureSoundOut;
_equalizeSoundIn = equalizeSoundIn;
_equalizeSoundOut = equalizeSoundOut;
_playingAgainstRobot = false;
_isUpperDoor = isUpperDoor;
}
void PressureDoor::openInteraction() {
if (_isUpperDoor) {
_levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie");
_levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop);
} else {
_levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie");
_levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop);
}
_levelsScale = _levelsMovie.getScale();
_levelsMovie.setDisplayOrder(kPressureLevelsOrder);
_levelsMovie.startDisplaying();
_levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale);
_levelsMovie.setTime(kLevelsSplashStart * _levelsScale);
_levelsMovie.redrawMovieWorld();
_levelsMovie.show();
_pressureCallBack.setNotification(&_pressureNotification);
_pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes);
_pressureCallBack.setCallBackFlag(kSplashFinished);
_pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags);
if (_isUpperDoor) {
_typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie");
_typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop);
} else {
_typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie");
_typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop);
}
_typeScale = _typeMovie.getScale();
_typeMovie.setDisplayOrder(kPressureTypeOrder);
_typeMovie.startDisplaying();
_typeMovie.setTime(kDoorSealedTime * _typeScale);
_typeMovie.redrawMovieWorld();
SpriteFrame *frame = new SpriteFrame();
if (_isUpperDoor)
frame->initFromPICTResource(g_vm->_resFork, kLowerPressureUpOffPICTID);
else
frame->initFromPICTResource(g_vm->_resFork, kUpperPressureUpOffPICTID);
_upButton.addFrame(frame, 0, 0);
frame = new SpriteFrame();
if (_isUpperDoor)
frame->initFromPICTResource(g_vm->_resFork, kLowerPressureUpOnPICTID);
else
frame->initFromPICTResource(g_vm->_resFork, kUpperPressureUpOnPICTID);
_upButton.addFrame(frame, 0, 0);
_upButton.setCurrentFrameIndex(0);
_upButton.setDisplayOrder(kPressureUpOrder);
Common::Rect r;
frame->getSurfaceBounds(r);
if (_isUpperDoor)
r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop);
else
r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop);
_upButton.setBounds(r);
_upButton.startDisplaying();
_upButton.show();
frame = new SpriteFrame();
if (_isUpperDoor)
frame->initFromPICTResource(g_vm->_resFork, kLowerPressureDownOffPICTID);
else
frame->initFromPICTResource(g_vm->_resFork, kUpperPressureDownOffPICTID);
_downButton.addFrame(frame, 0, 0);
frame = new SpriteFrame();
if (_isUpperDoor)
frame->initFromPICTResource(g_vm->_resFork, kLowerPressureDownOnPICTID);
else
frame->initFromPICTResource(g_vm->_resFork, kUpperPressureDownOnPICTID);
_downButton.addFrame(frame, 0, 0);
_downButton.setCurrentFrameIndex(0);
_downButton.setDisplayOrder(kPressureDownOrder);
frame->getSurfaceBounds(r);
if (_isUpperDoor)
r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop);
else
r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop);
_downButton.setBounds(r);
_downButton.startDisplaying();
_downButton.show();
_utilityCallBack.setNotification(&_utilityNotification);
_utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime);
_utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags);
_utilityTimer.setMasterTimeBase(getOwner()->getNavMovie());
if (_playingAgainstRobot)
_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag |
kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag);
else
_neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag,
kDelayCompletedFlag | kSpotSoundCompletedFlag);
_gameState = kPlayingSplash;
}
void PressureDoor::initInteraction() {
_levelsMovie.start();
if (_playingAgainstRobot) {
ExtraTable::Entry entry;
_owner->getExtraEntry(kN59RobotApproaches, entry);
_utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
_punchInTime = kApproachPunchInTime + entry.movieStart;
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
_utilityTimer.setTime(entry.movieStart);
_owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput);
_utilityTimer.start();
_robotState = kPlayingRobotApproaching;
}
_levelsMovie.redrawMovieWorld();
}
void PressureDoor::closeInteraction() {
_pressureNotification.cancelNotification(this);
_pressureCallBack.releaseCallBack();
_utilityNotification.cancelNotification(this);
_utilityCallBack.releaseCallBack();
_neighborhoodNotification->cancelNotification(this);
}
void PressureDoor::playAgainstRobot() {
_playingAgainstRobot = true;
}
void PressureDoor::receiveNotification(Notification *notification, const NotificationFlags flags) {
Neighborhood *owner = getOwner();
if (notification == _neighborhoodNotification) {
if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) {
ExtraTable::Entry entry;
switch (_robotState) {
case kPlayingRobotApproaching:
_utilityTimer.stop();
if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
owner->getExtraEntry(kN59PlayerWins1, entry);
_utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
_utilityTimer.setTime(entry.movieStart);
_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
_punchInTime = kLoopPunchInTime + entry.movieStart;
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
_utilityTimer.start();
_robotState = kRobotDying;
} else {
owner->getExtraEntry(kN59RobotPunchLoop, entry);
_utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
_utilityTimer.setTime(entry.movieStart);
_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
_punchInTime = kLoopPunchInTime + entry.movieStart;
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag);
_utilityTimer.start();
_robotState = kRobotPunching;
_punchCount = 1;
}
break;
case kRobotPunching:
if (GameState.getNoradSubRoomPressure() == kMaxPressure) {
owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput);
_robotState = kRobotDying;
} else if (++_punchCount >= kMaxPunches) {
_robotState = kRobotComingThrough;
owner->getExtraEntry(kN59RobotWins, entry);
_utilityTimer.stop();
_utilityTimer.setSegment(entry.movieStart, entry.movieEnd);
_utilityTimer.setTime(entry.movieStart);
_utilityCallBack.cancelCallBack();
_utilityCallBack.setCallBackFlag(kDoorCrushedFlag);
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale);
owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput);
_utilityTimer.start();
} else {
_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag);
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale);
owner->scheduleNavCallBack(kExtraCompletedFlag);
}
break;
case kRobotComingThrough:
g_system->delayMillis(2 * 1000);
g_vm->die(kDeathRobotThroughNoradDoor);
break;
case kRobotDying:
_robotState = kRobotDead;
_levelsMovie.stop();
_levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale,
(GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale + 1);
_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
_pressureCallBack.setCallBackFlag(kPressureDroppingFlag);
_pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0);
_typeMovie.stop();
_typeMovie.setSegment(0, _typeMovie.getDuration());
_typeMovie.setTime(kDecreasingPressureTime * _typeScale);
_typeMovie.redrawMovieWorld();
_typeMovie.show();
_downButton.show();
_downButton.setCurrentFrameIndex(1);
_gameState = kGameOver;
allowInput(false);
_levelsMovie.setRate(Common::Rational(-4, 3)); // Should match door tracker.
break;
case kRobotDead:
allowInput(true);
((NoradDelta *)owner)->playerBeatRobotWithDoor();
owner->requestDeleteCurrentInteraction();
break;
default:
break;
}
}
if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) {
switch (_gameState) {
case kPlayingPressureMessage:
_typeMovie.setTime(kEqualizeTime * _typeScale);
_typeMovie.redrawMovieWorld();
owner->requestDelay(1, 5, kFilterNoInput, 0);
owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0);
owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
_gameState = kPlayingEqualizeMessage;
break;
case kPlayingEqualizeMessage:
_gameState = kWaitingForPlayer;
stopChangingPressure();
break;
case kPlayingDoneMessage:
_gameState = kWaitingForPlayer;
_typeMovie.stop();
_typeMovie.setFlags(0);
_typeMovie.hide();
if (!_playingAgainstRobot)
((Norad *)_owner)->doneWithPressureDoor();
break;
default:
break;
}
}
} else if (notification == &_pressureNotification) {
switch (flags) {
case kSplashFinished:
_levelsMovie.stop();
_levelsMovie.setSegment(0, _levelsMovie.getDuration());
_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
_levelsMovie.redrawMovieWorld();
if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) {
_typeMovie.show();
owner->requestDelay(1, 5, kFilterNoInput, 0);
owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0);
owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag);
_gameState = kPlayingPressureMessage;
} else {
_gameState = kWaitingForPlayer;
}
break;
case kPressureDroppingFlag:
_levelsMovie.stop();
_levelsMovie.hide();
_typeMovie.stop();
_typeMovie.hide();
_upButton.hide();
_downButton.hide();
owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput);
break;
default:
break;
}
} else if (notification == &_utilityNotification) {
switch (flags) {
case kDoorJumpsUpFlag:
_utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag);
_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale);
_levelsMovie.hide();
_typePunched = _typeMovie.isVisible();
if (_typePunched == true)
_typeMovie.hide();
_upButton.hide();
_downButton.hide();
break;
case kDoorJumpsBackFlag:
_levelsMovie.show();
_upButton.show();
_downButton.show();
if (_typePunched)
_typeMovie.show();
break;
case kDoorCrushedFlag:
_levelsMovie.hide();
_typeMovie.hide();
_upButton.hide();
_downButton.hide();
break;
default:
break;
}
}
}
void PressureDoor::activateHotspots() {
GameInteraction::activateHotspots();
switch (_gameState) {
case kWaitingForPlayer:
g_allHotspots.activateOneHotspot(_upHotspotID);
g_allHotspots.activateOneHotspot(_downHotspotID);
if (!_playingAgainstRobot)
g_allHotspots.activateOneHotspot(_outHotspotID);
break;
default:
break;
}
}
void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) {
HotSpotID id = spot->getObjectID();
if (id == _upHotspotID || id == _downHotspotID) {
if (id == _upHotspotID)
_doorTracker.setTrackParameters(spot, &_upButton);
else
_doorTracker.setTrackParameters(spot, &_downButton);
_doorTracker.startTracking(input);
} else {
GameInteraction::clickInHotspot(input, spot);
}
}
void PressureDoor::incrementPressure(const HotSpotID id) {
_typeMovie.stop();
_typeMovie.setSegment(0, _typeMovie.getDuration());
_typeMovie.setFlags(0);
if (id == _upHotspotID) {
if (GameState.getNoradSubRoomPressure() < kMaxPressure) {
GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1);
_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
_levelsMovie.redrawMovieWorld();
_typeMovie.setTime(kIncreasingPressureTime * _typeScale);
_typeMovie.redrawMovieWorld();
_typeMovie.show();
g_AIArea->checkMiddleArea();
} else {
_typeMovie.hide();
}
} else if (id == _downHotspotID) {
if (GameState.getNoradSubRoomPressure() > kMinPressure) {
GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1);
_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale);
_levelsMovie.redrawMovieWorld();
_typeMovie.setTime(kDecreasingPressureTime * _typeScale);
_typeMovie.redrawMovieWorld();
_typeMovie.show();
g_AIArea->checkMiddleArea();
} else {
_typeMovie.hide();
}
}
}
void PressureDoor::stopChangingPressure() {
Neighborhood *owner;
switch (GameState.getNoradSubRoomPressure()) {
case 11:
_typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
_typeMovie.setFlags(kLoopTimeBase);
_typeMovie.show();
_typeMovie.start();
break;
case 10:
_typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale);
_typeMovie.setFlags(kLoopTimeBase);
_typeMovie.show();
_typeMovie.start();
break;
case kNormalSubRoomPressure:
owner = getOwner();
_typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
_typeMovie.setFlags(kLoopTimeBase);
_typeMovie.show();
_gameState = kPlayingDoneMessage;
owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
_typeMovie.start();
break;
default:
_typeMovie.hide();
break;
}
}
bool PressureDoor::canSolve() {
if (_playingAgainstRobot)
return GameState.getNoradSubRoomPressure() < 11;
return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure;
}
void PressureDoor::doSolve() {
if (_playingAgainstRobot) {
GameState.setNoradSubRoomPressure(11);
_levelsMovie.setTime((11 + kPressureBase) * _levelsScale);
_levelsMovie.redrawMovieWorld();
_typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale);
_typeMovie.setFlags(kLoopTimeBase);
_typeMovie.show();
_typeMovie.start();
g_AIArea->checkMiddleArea();
} else {
GameState.setNoradSubRoomPressure(kNormalSubRoomPressure);
_levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale);
_levelsMovie.redrawMovieWorld();
_typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale);
_typeMovie.setFlags(kLoopTimeBase);
_typeMovie.show();
Neighborhood *owner = getOwner();
owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
_gameState = kPlayingDoneMessage;
_typeMovie.start();
g_AIArea->checkMiddleArea();
}
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,92 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H
#include "pegasus/interaction.h"
#include "pegasus/movie.h"
#include "pegasus/notification.h"
#include "pegasus/neighborhood/norad/pressuretracker.h"
namespace Pegasus {
static const short kNormalSubRoomPressure = 2;
class PressureDoor : public GameInteraction, public NotificationReceiver {
public:
PressureDoor(Neighborhood *, bool isUpperDoor, const HotSpotID, const HotSpotID,
const HotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut,
TimeValue equalizeSoundIn, TimeValue equalizeSoundOut);
~PressureDoor() override {}
void incrementPressure(const HotSpotID);
void stopChangingPressure();
void playAgainstRobot();
bool canSolve() override;
void doSolve() override;
protected:
void openInteraction() override;
void initInteraction() override;
void closeInteraction() override;
void activateHotspots() override;
void clickInHotspot(const Input &, const Hotspot *) override;
void receiveNotification(Notification *, const NotificationFlags) override;
Movie _levelsMovie;
TimeScale _levelsScale;
Movie _typeMovie;
TimeScale _typeScale;
Sprite _upButton;
Sprite _downButton;
Notification _pressureNotification;
NotificationCallBack _pressureCallBack;
Notification *_neighborhoodNotification;
int _gameState;
HotSpotID _upHotspotID;
HotSpotID _downHotspotID;
HotSpotID _outHotspotID;
PressureTracker _doorTracker;
TimeValue _pressureSoundIn;
TimeValue _pressureSoundOut;
TimeValue _equalizeSoundIn;
TimeValue _equalizeSoundOut;
bool _isUpperDoor;
bool _playingAgainstRobot, _typePunched;
int _robotState, _punchCount;
TimeBase _utilityTimer;
Notification _utilityNotification;
NotificationCallBack _utilityCallBack;
TimeValue _punchInTime;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,87 @@
/* 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/hotspot.h"
#include "pegasus/pegasus.h"
#include "pegasus/neighborhood/norad/pressuredoor.h"
#include "pegasus/neighborhood/norad/pressuretracker.h"
namespace Pegasus {
PressureTracker::PressureTracker(PressureDoor *pressureDoor) {
_pressureDoor = pressureDoor;
_trackSpot = nullptr;
_trackTime = 0;
_trackButton = nullptr;
}
void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) {
_trackSpot = trackSpot;
_trackButton = trackButton;
_trackTime = 0;
}
void PressureTracker::activateHotspots() {
Tracker::activateHotspots();
if (_trackSpot)
g_allHotspots.activateOneHotspot(_trackSpot->getObjectID());
}
// For click-hold dragging.
bool PressureTracker::stopTrackingInput(const Input &input) {
return !JMPPPInput::isPressingInput(input);
}
void PressureTracker::continueTracking(const Input &input) {
Common::Point where;
input.getInputLocation(where);
if (g_allHotspots.findHotspot(where) == _trackSpot) {
trackPressure();
_trackButton->setCurrentFrameIndex(1);
} else {
_trackButton->setCurrentFrameIndex(0);
}
}
void PressureTracker::startTracking(const Input &input) {
Tracker::startTracking(input);
trackPressure();
}
void PressureTracker::stopTracking(const Input &input) {
_trackButton->setCurrentFrameIndex(0);
_pressureDoor->stopChangingPressure();
Tracker::stopTracking(input);
}
void PressureTracker::trackPressure() {
if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) {
_pressureDoor->incrementPressure(_trackSpot->getObjectID());
_trackTime = g_system->getMillis();
}
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,68 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H
#include "pegasus/input.h"
namespace Pegasus {
// This class assumes that the globe movie is built at 15 frames per second with a
// time scale of 600, yielding 40 time unit per frame.
enum PressureTrackDirection {
kTrackPressureUp,
kTrackPressureDown
};
static const int kPressureDoorTrackInterval = 45;
class PressureDoor;
class Sprite;
class PressureTracker : public Tracker {
public:
PressureTracker(PressureDoor *);
~PressureTracker() override {}
void setTrackParameters(const Hotspot *, Sprite *);
void continueTracking(const Input &) override;
void startTracking(const Input &) override;
void stopTracking(const Input &) override;
void activateHotspots() override;
bool stopTrackingInput(const Input &) override;
protected:
void trackPressure();
PressureDoor *_pressureDoor;
const Hotspot *_trackSpot;
Sprite *_trackButton;
long _trackTime;
};
} // End of namespace Pegasus
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,132 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H
#include "pegasus/interaction.h"
#include "pegasus/notification.h"
namespace Pegasus {
static const uint32 kClawAtA = 0;
static const uint32 kClawAtB = 1;
static const uint32 kClawAtC = 2;
static const uint32 kClawAtD = 3;
static const int kNumClawButtons = 7;
class Norad;
class SubControlRoom : public GameInteraction, public NotificationReceiver {
public:
SubControlRoom(Neighborhood *);
~SubControlRoom() override {}
void playAgainstRobot();
void setSoundFXLevel(const uint16) override;
bool canSolve() override;
void doSolve() override;
protected:
void openInteraction() override;
void initInteraction() override;
void closeInteraction() override;
void activateHotspots() override;
void clickInHotspot(const Input &, const Hotspot *) override;
void receiveNotification(Notification *, const NotificationFlags) override;
void robotKillsPlayer(const uint32, Neighborhood *);
InputBits getInputFilter() override;
int findActionIndex(HotSpotID);
void dispatchClawAction(const int);
void performActionImmediately(const int, const uint32, Neighborhood *);
void hideEverything();
void showButtons();
void hideButtons();
void updateGreenBall();
void moveGreenBallToA();
void moveGreenBallToB();
void moveGreenBallToC();
void moveGreenBallToD();
void setControlMonitorToTime(const TimeValue, const int, const bool);
void playControlMonitorSection(const TimeValue, const TimeValue, const NotificationFlags,
const int, const bool);
void updateClawMonitor();
void setClawMonitorToTime(const TimeValue);
void playClawMonitorSection(const TimeValue, const TimeValue, const NotificationFlags,
const int, const bool);
Movie _subControlMovie;
TimeScale _subControlScale;
Notification _subControlNotification;
NotificationCallBack _subControlCallBack;
Movie _clawMonitorMovie;
NotificationCallBack _clawMonitorCallBack;
int _gameState;
uint32 _clawStartPosition;
uint32 _clawPosition;
uint32 _clawNextPosition;
const uint32 *_clawExtraIDs;
int _currentAction;
int _nextAction;
Sprite *_buttons[kNumClawButtons];
Sprite _pinchButton;
Sprite _downButton;
Sprite _rightButton;
Sprite _leftButton;
Sprite _upButton;
Sprite _ccwButton;
Sprite _cwButton;
Sprite _greenBall;
TimeBase _greenBallTimer;
Notification _greenBallNotification;
NotificationCallBack _greenBallCallBack;
HotSpotID _outSpotID;
HotSpotID _prepSpotID;
HotSpotID _clawControlSpotID;
HotSpotID _clawButtonSpotIDs[kNumClawButtons];
Notification *_neighborhoodNotification;
bool _playingAgainstRobot;
int _robotState;
};
} // End of namespace Pegasus
#endif

View File

@@ -0,0 +1,210 @@
/* 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/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/items/biochips/arthurchip.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/subplatform.h"
#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
namespace Pegasus {
// As usual, times here are in seconds.
static const TimeValue kNormalSplashStart = 0;
static const TimeValue kNormalSplashStop = 5;
static const TimeValue kPrepSubStart = 5;
static const TimeValue kPrepSubStop = 15;
static const TimeValue kPrepIncompleteStart = 15;
static const TimeValue kPrepIncompleteStop = 19;
static const TimeValue kDamagedStart = 19;
static const TimeValue kDamagedStop = 28;
static const NotificationFlags kNormalSplashFinished = 1;
static const NotificationFlags kPrepSubFinished = kNormalSplashFinished << 1;
static const NotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1;
static const NotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1;
static const NotificationFlags kPlatformNotificationFlags = kNormalSplashFinished |
kPrepSubFinished |
kPrepIncompleteFinished |
kDamagedFinished;
static const uint16 kSubPreppedBit = (1 << 0);
static const uint16 kWaitingForPlayerBit = (1 << 1);
SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler),
_platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, g_vm) {
_neighborhoodNotification = handler->getNeighborhoodNotification();
}
void SubPlatform::openInteraction() {
_stateBits = 0;
// TODO: These next two lines seem unused?
if (GameState.getNoradSubPrepState() == kSubPrepped)
_stateBits |= kSubPreppedBit;
_stateBits |= kWaitingForPlayerBit;
_platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie");
_platformMovie.setVolume(g_vm->getSoundFXLevel());
_platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop);
_platformScale = _platformMovie.getScale();
_platformMovie.setDisplayOrder(kPlatformOrder);
_platformMovie.startDisplaying();
_platformCallBack.setNotification(&_platformNotification);
_platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes);
_platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags);
}
void SubPlatform::initInteraction() {
_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag);
}
void SubPlatform::closeInteraction() {
_platformNotification.cancelNotification(this);
_platformCallBack.releaseCallBack();
_neighborhoodNotification->cancelNotification(this);
}
void SubPlatform::setSoundFXLevel(const uint16 fxLevel) {
_platformMovie.setVolume(fxLevel);
}
void SubPlatform::receiveNotification(Notification *notification, const NotificationFlags flags) {
FaderMoveSpec loop1Spec, loop2Spec;
ExtraTable::Entry entry;
Norad *owner = (Norad *)getOwner();
if (notification == &_platformNotification) {
switch (flags) {
case kNormalSplashFinished:
_platformMovie.stop();
switch (GameState.getNoradSubPrepState()) {
case kSubNotPrepped:
_platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale);
_platformMovie.setTime(kPrepIncompleteStart * _platformScale);
_platformCallBack.setCallBackFlag(kPrepIncompleteFinished);
_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_platformMovie.start();
break;
case kSubPrepped:
_platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale);
_platformMovie.setTime(kPrepSubStart * _platformScale);
_platformCallBack.setCallBackFlag(kPrepSubFinished);
_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput);
_platformMovie.start();
break;
case kSubDamaged:
// Shouldn't happen.
default:
break;
}
break;
case kPrepSubFinished:
_platformMovie.stop();
_platformMovie.stopDisplaying();
owner->getExtraEntry(kNorad19ExitToSub, entry);
loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume,
entry.movieEnd - entry.movieStart, 0);
loop1Spec.insertFaderKnot(4560, kNoradWarningVolume);
loop1Spec.insertFaderKnot(5080, 0);
loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume,
entry.movieEnd - entry.movieStart, 0);
loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume);
loop1Spec.insertFaderKnot(5080, 0);
owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput);
if (g_arthurChip)
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA07", kArthurNoradEnteredSub);
owner->startLoop1Fader(loop1Spec);
owner->startLoop2Fader(loop2Spec);
break;
case kPrepIncompleteFinished:
((NoradAlpha *)owner)->setSubPrepFailed(true);
g_AIArea->checkMiddleArea();
// Fall through...
case kDamagedFinished:
_platformMovie.stop();
_platformMovie.hide();
_stateBits |= kWaitingForPlayerBit;
allowInput(true);
break;
default:
break;
}
} else if (notification == _neighborhoodNotification && !g_vm->isDVD()) {
allowInput(true);
g_vm->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection);
GameState.setScoringEnteredSub(true);
}
}
void SubPlatform::activateHotspots() {
if (_stateBits & kWaitingForPlayerBit)
g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID);
GameInteraction::activateHotspots();
}
void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) {
if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) {
if (GameState.getNoradSubPrepState() == kSubDamaged) {
_platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale);
_platformMovie.setTime(kDamagedStart * _platformScale);
_platformCallBack.setCallBackFlag(kDamagedFinished);
} else {
_platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale);
_platformMovie.setTime(kNormalSplashStart * _platformScale);
_platformCallBack.setCallBackFlag(kNormalSplashFinished);
}
_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
_platformMovie.show();
_platformMovie.start();
_platformMovie.redrawMovieWorld();
_stateBits &= ~kWaitingForPlayerBit;
allowInput(false);
} else {
GameInteraction::clickInHotspot(input, spot);
}
}
} // End of namespace Pegasus

View File

@@ -0,0 +1,62 @@
/* 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/>.
*
*/
#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H
#include "pegasus/interaction.h"
#include "pegasus/movie.h"
#include "pegasus/notification.h"
#include "pegasus/timers.h"
namespace Pegasus {
class SubPlatform : public GameInteraction, public NotificationReceiver {
public:
SubPlatform(Neighborhood *);
~SubPlatform() override {}
void setSoundFXLevel(const uint16) override;
protected:
void openInteraction() override;
void initInteraction() override;
void closeInteraction() override;
void activateHotspots() override;
void clickInHotspot(const Input &, const Hotspot *) override;
void receiveNotification(Notification *, const NotificationFlags) override;
Movie _platformMovie;
TimeScale _platformScale;
Notification _platformNotification;
NotificationCallBack _platformCallBack;
Notification *_neighborhoodNotification;
uint16 _stateBits;
};
} // End of namespace Pegasus
#endif