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