From f9ecab1953644c79913fb483b1d64c6d9e779481 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 22 Oct 2011 22:29:09 -0400 Subject: PEGASUS: Add Norad Alpha Not yet completable, but you can at least fill the oxygen mask to go through the Mars Maze now. --- engines/pegasus/constants.h | 1 - engines/pegasus/module.mk | 2 + engines/pegasus/movie.cpp | 7 +- .../neighborhood/norad/alpha/fillingstation.cpp | 445 ++++++++++++ .../neighborhood/norad/alpha/fillingstation.h | 91 +++ .../neighborhood/norad/alpha/noradalpha.cpp | 763 +++++++++++++++++++++ .../pegasus/neighborhood/norad/alpha/noradalpha.h | 142 ++++ engines/pegasus/neighborhood/norad/norad.h | 106 ++- .../pegasus/neighborhood/norad/pressuredoor.cpp | 2 +- .../pegasus/neighborhood/norad/subcontrolroom.cpp | 3 +- .../pegasus/neighborhood/norad/subcontrolroom.h | 2 +- engines/pegasus/neighborhood/norad/subplatform.cpp | 7 +- engines/pegasus/neighborhood/tsa/fulltsa.cpp | 1 + engines/pegasus/neighborhood/tsa/tinytsa.cpp | 1 + engines/pegasus/neighborhood/zoom.cpp | 4 + engines/pegasus/neighborhood/zoom.h | 1 + engines/pegasus/pegasus.cpp | 4 + 17 files changed, 1565 insertions(+), 17 deletions(-) create mode 100755 engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp create mode 100755 engines/pegasus/neighborhood/norad/alpha/fillingstation.h create mode 100755 engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp create mode 100755 engines/pegasus/neighborhood/norad/alpha/noradalpha.h (limited to 'engines/pegasus') diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h index 8d2c09332f..cc096ddfb0 100755 --- a/engines/pegasus/constants.h +++ b/engines/pegasus/constants.h @@ -737,7 +737,6 @@ static const tGameMode kModeBiochipPick = kModeInventoryPick + 1; static const tGameMode kModeInfoScreen = kModeBiochipPick + 1; // TODO: Remove me -static const tRoomID kNorad01 = 0; static const tRoomID kNorad41 = 0; } // End of namespace Pegasus diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk index 58c9dc99d7..c8a67bf53c 100644 --- a/engines/pegasus/module.mk +++ b/engines/pegasus/module.mk @@ -65,6 +65,8 @@ MODULE_OBJS = \ neighborhood/norad/pressuretracker.o \ neighborhood/norad/subcontrolroom.o \ neighborhood/norad/subplatform.o \ + neighborhood/norad/alpha/fillingstation.o \ + neighborhood/norad/alpha/noradalpha.o \ neighborhood/prehistoric/prehistoric.o \ neighborhood/tsa/fulltsa.o \ neighborhood/tsa/tinytsa.o \ diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp index 9551248ccc..fe152614d2 100755 --- a/engines/pegasus/movie.cpp +++ b/engines/pegasus/movie.cpp @@ -140,8 +140,11 @@ void Movie::setTime(const TimeValue time, const TimeScale scale) { } void Movie::setRate(const Common::Rational rate) { - if (rate != 1 && rate != 0) - error("Cannot set movie rate"); + if (rate != 1 && rate != 0) { + warning("Cannot set movie rate"); + start(); + return; + } TimeBase::setRate(rate); } diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp new file mode 100755 index 0000000000..a858e75928 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp @@ -0,0 +1,445 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +const tNotificationFlags kFSPowerUpFinishedFlag = 1; +const tNotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1; +const tNotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1; +const tNotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1; +const tNotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1; +const tNotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1; +const tNotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1; +const tNotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1; +const tNotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1; +const tNotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1; + +const tNotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag | + kFSSplashFinishedFlag | + kFSIntakeWarningFinishedFlag | + kFSIntakeHiliteFinishedFlag | + kFSDispenseHiliteFinishedFlag | + kFSArHiliteFinishedFlag | + kFSCO2HiliteFinishedFlag | + kFSHeHiliteFinishedFlag | + kFSOHiliteFinishedFlag | + kFSNHiliteFinishedFlag; + +const int16 kNoState = 0; +const int16 kMainMenu = 1; +const int16 kWaitingForAttach = 2; +const int16 kDispenseMenu = 3; +const int16 kWaitingForDispense = 4; + +// Dummy itemIDs + +const tItemID kCO2Item = 10000; +const tItemID kHeItem = 10001; + +// Interactive points. + +const TimeValue kFSPowerUpStartStart = 0; +const TimeValue kFSPowerUpStartStop = 600; +const TimeValue kFSSplashStart = 600; +const TimeValue kFSSplashStop = 7800; +const TimeValue kFSSplashIntakeStart = 7800; +const TimeValue kFSSplashIntakeStop = 18600; + +const TimeValue kFSMainMenu = 18600; +const TimeValue kFSIntakeHiliteStart = 19200; +const TimeValue kFSIntakeHiliteStop = 19800; +const TimeValue kFSDispenseHiliteStart = 19800; +const TimeValue kFSDispenseHiliteStop = 20400; + +const TimeValue kFSDispenseMenu = 20400; + +const TimeValue kFSArHiliteStart = 21000; +const TimeValue kFSArHiliteStop = 21600; +const TimeValue kFSArAttach = 21600; +const TimeValue kFSArFilledStart = 22200; +const TimeValue kFSArFilledStop = 25200; +const TimeValue kFSArIncompatibleStart = 25200; +const TimeValue kFSArIncompatibleStop = 30000; + +const TimeValue kFSCO2HiliteStart = 30000; +const TimeValue kFSCO2HiliteStop = 30600; +const TimeValue kFSCO2Attach = 30600; +const TimeValue kFSCO2FilledStart = 31200; +const TimeValue kFSCO2FilledStop = 34200; +const TimeValue kFSCO2IncompatibleStart = 34200; +const TimeValue kFSCO2IncompatibleStop = 39000; + +const TimeValue kFSHeHiliteStart = 39000; +const TimeValue kFSHeHiliteStop = 39600; +const TimeValue kFSHeAttach = 39600; +const TimeValue kFSHeFilledStart = 40200; +const TimeValue kFSHeFilledStop = 43200; +const TimeValue kFSHeIncompatibleStart = 43200; +const TimeValue kFSHeIncompatibleStop = 48000; + +const TimeValue kFSOHiliteStart = 48000; +const TimeValue kFSOHiliteStop = 48600; +const TimeValue kFSOAttach = 48600; +const TimeValue kFSOFilledStart = 49200; +const TimeValue kFSOFilledStop = 52200; +const TimeValue kFSOIncompatibleStart = 52200; +const TimeValue kFSOIncompatibleStop = 57000; + +const TimeValue kFSNHiliteStart = 57000; +const TimeValue kFSNHiliteStop = 57600; +const TimeValue kFSNAttach = 57600; +const TimeValue kFSNFilledStart = 58200; +const TimeValue kFSNFilledStop = 61200; +const TimeValue kFSNIncompatibleStart = 61200; +const TimeValue kFSNIncompatibleStop = 66000; + +const TimeValue kFSIntakeMenu = 66000; +const TimeValue kFSIntakeInProgressStart = 66600; +const TimeValue kFSIntakeInProgressStop = 69600; + +NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner), + _rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) { + _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); + _state = state; + allowInput(true); +} + +void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, tNotificationFlags 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); +} + +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())->loadAmbientLoops(); + 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; + } + 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; + } + } +} + +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 tNotificationFlags 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; + } +} + +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; + } +} + +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; + } +} + +void NoradAlphaFillingStation::newFillingItem(Item *item) { + switch (_state) { + case kWaitingForAttach: + if (item) + showIntakeInProgress(0); + break; + case kWaitingForDispense: + dispenseGas(); + break; + default: + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.h b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h new file mode 100755 index 0000000000..e3fb1f6fd8 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_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 *); + virtual ~NoradAlphaFillingStation() {} + + virtual void handleInput(const Input &, const Hotspot *); + + virtual void clickInHotspot(const Input &, const Hotspot *); + virtual void activateHotspots(); + + void newFillingItem(Item *); + +protected: + void receiveNotification(Notification *, const tNotificationFlags); + + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + 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, tNotificationFlags, int16); + + Movie _rightSideMovie; + Notification _rightSideNotification; + NotificationCallBack _rightSideCallBack; + int16 _state; + tItemID _dispenseItemID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp new file mode 100755 index 0000000000..c12623f6cc --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp @@ -0,0 +1,763 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +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) { + _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 = kPressureDoorIntro1In; + _pressureSoundOut = kPressureDoorIntro1Out; + _equalizeSoundIn = kPressureDoorIntro2In; + _equalizeSoundOut = kPressureDoorIntro2Out; + _accessDeniedIn = kAlphaAccessDeniedIn; + _accessDeniedOut = kAlphaAccessDeniedOut; + + _platformRoom = kNorad19West; + _subControlRoom = kNorad22West; + + _subPrepFailed = false; + + setIsItemTaken(kGasCanister); +} + +void NoradAlpha::init() { + Norad::init(); + + Hotspot *hotspot = g_allHotspots.findHotspotByID(kN01GasCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID); + hotspotEntry->hotspotItem = kGasCanister; + + hotspot = g_allHotspots.findHotspotByID(kN01ArgonCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID); + hotspotEntry->hotspotItem = kArgonCanister; + + hotspot = g_allHotspots.findHotspotByID(kN01NitrogenCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID); + hotspotEntry->hotspotItem = kNitrogenCanister; + + hotspot = g_allHotspots.findHotspotByID(kN01AirMaskSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01AirMaskSpotID); + hotspotEntry->hotspotItem = kAirMask; + + hotspot = g_allHotspots.findHotspotByID(kN01GasOutletSpotID); + hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); +} + +void NoradAlpha::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + tNeighborhoodID itemNeighborhood; + tRoomID itemRoom; + tDirectionConstant itemDirection; + + Item *item = (Item *)g_allItems.findItemByID(kGasCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)g_allItems.findItemByID(kAirMask); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)g_allItems.findItemByID(kNitrogenCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)g_allItems.findItemByID(kArgonCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + if (itemNeighborhood == getObjectID()) + _fillingStationItem = item; + else + _fillingStationItem = 0; + } + } + } + + 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(kLoadClawIntroIn, kLoadClawIntroOut); +} + +GameInteraction *NoradAlpha::makeInteraction(const tInteractionID interactionID) { + switch (interactionID) { + case kNoradECRMonitorInteractionID: + // TODO + warning("Unhandled ECR monitor interaction"); + break; + case kNoradFillingStationInteractionID: + return new NoradAlphaFillingStation(this); + } + + 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()) + return; + + tRoomID room = GameState.getCurrentRoom(); + if (GameState.getNoradGassed()) { + if (room >= kNorad11 && room <= kNorad19West) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); + else if (room >= kNorad21 && room <= kNorad22West) + loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); + else + loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); + } else { + loadLoopSound1(""); + } + + if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { + if (room >= 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 tRoomID room, const tDirectionConstant 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; + } +} + +void NoradAlpha::arriveAt(const tRoomID room, const tDirectionConstant 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 kNorad07North: + GameState.setScoringSawUnconsciousOperator(true); + break; + case kNorad11: + GameState.setScoringWentThroughPressureDoor(true); + break; + case kNorad22: + arriveAtNorad22(); + break; + } +} + +void NoradAlpha::arriveAtNorad01() { + if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) { + GameState.setNoradN22MessagePlayed(false); + requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput); + // You are no match for me, human. + requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput); + } +} + +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::bumpIntoWall() { + requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void NoradAlpha::receiveNotification(Notification *notification, const tNotificationFlags flags) { + if ((flags & kExtraCompletedFlag) != 0) { + switch (_lastExtra) { + case kNoradArriveFromTSA: + GameState.setNoradSeenTimeStream(true); + loadAmbientLoops(); + break; + case kNorad01RobotTaunt: + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption); + _interruptionFilter = kFilterAllInput; + makeContinuePoint(); + 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: + _interruptionFilter = kFilterAllInput; + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNorad21); + arriveAt(kNorad22, kSouth); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void NoradAlpha::getZoomEntry(const tHotSpotID 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 tRoomID room, const tDirectionConstant 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::turnOnFillingStation() { + if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) { + GameState.setNoradFillingStationOn(true); + updateViewFrame(); + } +} + +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) { + tItemID itemID = _vm->getDraggingItem()->getObjectID(); + if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask || + itemID == kNitrogenCanister) + g_allHotspots.activateOneHotspot(kN01GasOutletSpotID); + } + } else { + tHotSpotID spotID; + + if (_fillingStationItem) { + switch (_fillingStationItem->getObjectID()) { + case kArgonCanister: + spotID = kN01ArgonCanisterSpotID; + g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kGasCanister: + spotID = kN01GasCanisterSpotID; + break; + case kAirMask: + spotID = kN01AirMaskSpotID; + g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kNitrogenCanister: + spotID = kN01NitrogenCanisterSpotID; + g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); + break; + default: + // Should never happen. + spotID = kNoHotSpotID; + break; + } + g_allHotspots.activateOneHotspot(spotID); + } + } + break; + case MakeRoomView(kNorad10, kEast): + if (GameState.isCurrentDoorOpen()) + g_allHotspots.deactivateOneHotspot(kNorad10DoorSpotID); + break; + case MakeRoomView(kNorad21, kWest): + if (GameState.isCurrentDoorOpen()) + g_allHotspots.deactivateOneHotspot(kNorad21WestSpotID); + 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(); + } + } + } +} + +void NoradAlpha::takeItemFromRoom(Item *item) { + if (GameState.getCurrentRoom() == kNorad01West) { + if (_fillingStationItem == item) { + _fillingStationItem = 0; + GameState.setNoradGassed(false); + loadAmbientLoops(); + ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(0); + 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(tHotSpotID &outSpotID, tHotSpotID &prepSpotID, tHotSpotID &clawControlSpotID, tHotSpotID &pinchClawSpotID, + tHotSpotID &moveClawDownSpotID, tHotSpotID &moveClawRightSpotID, tHotSpotID &moveClawLeftSpotID, tHotSpotID &moveClawUpSpotID, + tHotSpotID &clawCCWSpotID, tHotSpotID &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 g_allHotspots.findHotspotByID(kN01GasCanisterSpotID); + case kAirMask: + return g_allHotspots.findHotspotByID(kN01AirMaskSpotID); + case kArgonCanister: + return g_allHotspots.findHotspotByID(kN01ArgonCanisterSpotID); + case kNitrogenCanister: + return g_allHotspots.findHotspotByID(kN01NitrogenCanisterSpotID); + } + + return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradAlpha::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + tRoomID 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; + } + } + + return numHints; +} + +Common::String NoradAlpha::getHintMovie(uint hintNum) { + Common::String 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"; + } + break; + case MakeRoomView(kNorad19West, kWest): + return "Images/AI/Norad/XN19NH"; + case MakeRoomView(kNorad22, kWest): + return "Images/AI/Globals/XGLOB1C"; + } + } + + return movieName; +} + +void NoradAlpha::closeDoorOffScreen(const tRoomID room, const tDirectionConstant) { + switch (room) { + case kNorad12: + case kNorad13: + case kNorad18: + case kNorad19: + playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut); + break; + default: + playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut); + break; + } +} + +void NoradAlpha::findSpotEntry(const tRoomID room, const tDirectionConstant direction, tSpotFlags 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::String NoradAlpha::getNavMovieName() { + return "Images/Norad Alpha/Norad Alpha.movie"; +} + +Common::String NoradAlpha::getSoundSpotsName() { + return "Sounds/Norad/Norad Alpha Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h new file mode 100755 index 0000000000..72a7ac6339 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h @@ -0,0 +1,142 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +const TimeValue kAlphaBumpIntoWallIn = 0; +const TimeValue kAlphaBumpIntoWallOut = 303; + +const TimeValue kAlphaAccessDeniedIn = 303; +const TimeValue kAlphaAccessDeniedOut = 3045; + +const TimeValue kAlphaRegDoorCloseIn = 3045; +const TimeValue kAlphaRegDoorCloseOut = 4476; + +const TimeValue kAlphaElevatorDoorCloseIn = 4476; +const TimeValue kAlphaElevatorDoorCloseOut = 5071; + +const TimeValue kAlphaCantTransportIn = 5071; +const TimeValue kAlphaCantTransportOut = 9348; + +const TimeValue kPressureDoorIntro1In = 9348; +const TimeValue kPressureDoorIntro1Out = 11061; + +const TimeValue kPressureDoorIntro2In = 11061; +const TimeValue kPressureDoorIntro2Out = 14098; + +const TimeValue kN22ReplyIn = 14098; +const TimeValue kN22ReplyOut = 18442; + +const TimeValue kLoadClawIntroIn = 18442; +const TimeValue kLoadClawIntroOut = 20698; + +class Item; + +class NoradAlpha : public Norad { +public: + NoradAlpha(InputHandler *, PegasusEngine *); + virtual ~NoradAlpha() {} + + virtual void init(); + void start(); + + virtual bool okayToJump(); + + void playClawMonitorIntro(); + + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + + void turnOnFillingStation(); + void turnOffFillingStation(); + Item *getFillingItem() { return _fillingStationItem; } + bool gasCanisterIntake(); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + + virtual GameInteraction *makeInteraction(const tInteractionID); + + virtual void getClawInfo(tHotSpotID &outSpotID, tHotSpotID &prepSpotID, tHotSpotID &clawControlSpotID, + tHotSpotID &pinchClawSpotID, tHotSpotID &moveClawDownSpotID, tHotSpotID &moveClawRightSpotID, + tHotSpotID &moveClawLeftSpotID, tHotSpotID &moveClawUpSpotID, tHotSpotID &clawCCWSpotID, + tHotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + + void loadAmbientLoops(); + + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void setUpAIRules(); + + void setSubPrepFailed(bool value) { _subPrepFailed = value; } + bool getSubPrepFailed() { return _subPrepFailed; } + + void closeDoorOffScreen(const tRoomID, const tDirectionConstant); + void findSpotEntry(const tRoomID, const tDirectionConstant, tSpotFlags, SpotTable::Entry &); + void clickInHotspot(const Input &, const Hotspot *); + + void checkContinuePoint(const tRoomID, const tDirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + static const uint32 _noradAlphaClawExtras[22]; + + virtual void arriveAtNorad01(); + virtual void arriveAtNorad01East(); + virtual void arriveAtNorad01West(); + virtual void arriveAtNorad04(); + virtual void arriveAtNorad22(); + + virtual void arriveAt(const tRoomID, const tDirectionConstant); + + virtual void getZoomEntry(const tHotSpotID, ZoomTable::Entry &); + virtual TimeValue getViewTime(const tRoomID, const tDirectionConstant); + + virtual void receiveNotification(Notification *, const tNotificationFlags); + + virtual void activateHotspots(); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + + void bumpIntoWall(); + + Item *_fillingStationItem; + + bool _subPrepFailed; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h index 4b4473cd44..5e818901dc 100755 --- a/engines/pegasus/neighborhood/norad/norad.h +++ b/engines/pegasus/neighborhood/norad/norad.h @@ -276,7 +276,103 @@ const tDisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1; const tDisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1; -// Norad Alpha Extra sequence IDs. +// Norad Alpha constants + +const TimeScale kNoradAlphaMovieScale = 600; +const TimeScale kNoradAlphaFramesPerSecond = 15; +const TimeScale kNoradAlphaFrameDuration = 40; + +// Alternate IDs. + +const tAlternateID kAltNoradAlphaNormal = 0; + +// Room IDs. + +const tRoomID kNorad01 = 0; +const tRoomID kNorad01East = 1; +const tRoomID kNorad01West = 2; +const tRoomID kNorad02 = 3; +const tRoomID kNorad03 = 4; +const tRoomID kNorad04 = 5; +const tRoomID kNorad05 = 6; +const tRoomID kNorad06 = 7; +const tRoomID kNorad07 = 8; +const tRoomID kNorad07North = 9; +const tRoomID kNorad08 = 10; +const tRoomID kNorad09 = 11; +const tRoomID kNorad10 = 12; +const tRoomID kNorad10East = 13; +const tRoomID kNorad11 = 14; +const tRoomID kNorad11South = 15; +const tRoomID kNorad12 = 16; +const tRoomID kNorad12South = 17; +const tRoomID kNorad13 = 18; +const tRoomID kNorad14 = 19; +const tRoomID kNorad15 = 20; +const tRoomID kNorad16 = 21; +const tRoomID kNorad17 = 22; +const tRoomID kNorad18 = 23; +const tRoomID kNorad19 = 24; +const tRoomID kNorad19West = 25; +const tRoomID kNorad21 = 26; +const tRoomID kNorad21West = 27; +const tRoomID kNorad22 = 28; +const tRoomID kNorad22West = 29; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +const tHotSpotID kNorad01ECRSpotID = 5000; +const tHotSpotID kNorad01GasSpotID = 5001; +const tHotSpotID kNorad01ECROutSpotID = 5002; +const tHotSpotID kNorad01GasOutSpotID = 5003; +const tHotSpotID kNorad01MonitorSpotID = 5004; +const tHotSpotID kNorad01IntakeSpotID = 5005; +const tHotSpotID kNorad01DispenseSpotID = 5006; +const tHotSpotID kNorad01ArSpotID = 5007; +const tHotSpotID kNorad01CO2SpotID = 5008; +const tHotSpotID kNorad01HeSpotID = 5009; +const tHotSpotID kNorad01OSpotID = 5010; +const tHotSpotID kNorad01NSpotID = 5011; +const tHotSpotID kN01GasCanisterSpotID = 5012; +const tHotSpotID kN01ArgonCanisterSpotID = 5013; +const tHotSpotID kN01AirMaskSpotID = 5014; +const tHotSpotID kN01NitrogenCanisterSpotID = 5015; +const tHotSpotID kN01GasOutletSpotID = 5016; +const tHotSpotID kNorad07DoorSpotID = 5017; +const tHotSpotID kNorad07DoorOutSpotID = 5018; +const tHotSpotID kNorad10DoorSpotID = 5019; +const tHotSpotID kNorad10EastOutSpotID = 5020; +const tHotSpotID kAlphaUpperPressureDoorUpSpotID = 5021; +const tHotSpotID kAlphaUpperPressureDoorDownSpotID = 5022; +const tHotSpotID kNorad11ElevatorSpotID = 5023; +const tHotSpotID kNorad11ElevatorOutSpotID = 5024; +const tHotSpotID kNorad11ElevatorDownSpotID = 5025; +const tHotSpotID kNorad12ElevatorSpotID = 5026; +const tHotSpotID kNorad12ElevatorOutSpotID = 5027; +const tHotSpotID kNorad12ElevatorUpSpotID = 5028; +const tHotSpotID kNorad19MonitorSpotID = 5029; +const tHotSpotID kNorad19MonitorOutSpotID = 5030; +const tHotSpotID kNorad19ActivateMonitorSpotID = 5031; +const tHotSpotID kNorad21WestSpotID = 5032; +const tHotSpotID kNorad21WestOutSpotID = 5033; +const tHotSpotID kAlphaLowerPressureDoorUpSpotID = 5034; +const tHotSpotID kAlphaLowerPressureDoorDownSpotID = 5035; +const tHotSpotID kNorad22MonitorSpotID = 5036; +const tHotSpotID kNorad22MonitorOutSpotID = 5037; +const tHotSpotID kNorad22LaunchPrepSpotID = 5038; +const tHotSpotID kNorad22ClawControlSpotID = 5039; +const tHotSpotID kNorad22ClawPinchSpotID = 5040; +const tHotSpotID kNorad22ClawDownSpotID = 5041; +const tHotSpotID kNorad22ClawRightSpotID = 5042; +const tHotSpotID kNorad22ClawLeftSpotID = 5043; +const tHotSpotID kNorad22ClawUpSpotID = 5044; +const tHotSpotID kNorad22ClawCCWSpotID = 5045; +const tHotSpotID kNorad22ClawCWSpotID = 5046; + +// Extra sequence IDs. const tExtraID kNoradArriveFromTSA = 0; const tExtraID kNorad01RobotTaunt = 1; @@ -321,6 +417,7 @@ const tExtraID kN22ClawDPinch = 39; const tExtraID kN22ClawDCounterclockwise = 40; const tExtraID kN22ClawDClockwise = 41; + // Norad Delta Extra sequence IDs. const tExtraID kArriveFromSubChase = 0; @@ -386,13 +483,8 @@ const tExtraID kNoradDeltaRetinalScanBad = 59; const tExtraID kNoradDeltaRetinalScanGood = 60; const tExtraID kN79BrightView = 61; -const TimeScale kNoradAlphaMovieScale = 600; -const TimeScale kNoradAlphaFramesPerSecond = 15; -const TimeScale kNoradAlphaFrameDuration = 40; - const tRoomID kNorad59West = 23; const tRoomID kNorad60West = 25; -const tHotSpotID kNorad19ActivateMonitorSpotID = 5031; // This is the code common to both Norad Alpha and Norad Delta @@ -409,7 +501,7 @@ public: tHotSpotID &clawControlSpotID, tHotSpotID &pinchClawSpotID, tHotSpotID &moveClawDownSpotID, tHotSpotID &moveClawRightSpotID, tHotSpotID &moveClawLeftSpotID,tHotSpotID &moveClawUpSpotID, - tHotSpotID &clawCCWSpotID, tHotSpotID &clawCWSpotID, uint32 &, uint32 *&) = 0; + tHotSpotID &clawCCWSpotID, tHotSpotID &clawCWSpotID, uint32 &, const uint32 *&) = 0; void checkAirMask(); virtual uint16 getDateResID() const; diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp index 066914c6db..0fd5f45ded 100755 --- a/engines/pegasus/neighborhood/norad/pressuredoor.cpp +++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp @@ -243,7 +243,7 @@ void PressureDoor::initInteraction() { _robotState = kPlayingRobotApproaching; } - // TODO: MoviesTask call -- needed? + _levelsMovie.redrawMovieWorld(); } void PressureDoor::closeInteraction() { diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp index 7a01d32211..69244bcb7f 100755 --- a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp @@ -538,7 +538,8 @@ void SubControlRoom::initInteraction() { playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false); } - // TODO: MoviesTask call. Needed? + _subControlMovie.redrawMovieWorld(); + _clawMonitorMovie.redrawMovieWorld(); GameState.setScoringPlayedWithClaw(true); } diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h index 4a6889f82c..deea5042d0 100755 --- a/engines/pegasus/neighborhood/norad/subcontrolroom.h +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h @@ -98,7 +98,7 @@ protected: uint32 _clawStartPosition; uint32 _clawPosition; uint32 _clawNextPosition; - uint32 *_clawExtraIDs; + const uint32 *_clawExtraIDs; int _currentAction; int _nextAction; diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp index 0d4827b910..2665ab4ec5 100755 --- a/engines/pegasus/neighborhood/norad/subplatform.cpp +++ b/engines/pegasus/neighborhood/norad/subplatform.cpp @@ -28,6 +28,7 @@ #include "pegasus/ai/ai_area.h" #include "pegasus/neighborhood/norad/norad.h" #include "pegasus/neighborhood/norad/subplatform.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" namespace Pegasus { @@ -150,8 +151,7 @@ void SubPlatform::receiveNotification(Notification *notification, const tNotific owner->startLoop2Fader(loop2Spec); break; case kPrepIncompleteFinished: - // TODO - //((NoradAlpha *)owner)->setSubPrepFailed(true); + ((NoradAlpha *)owner)->setSubPrepFailed(true); g_AIArea->checkMiddleArea(); // Fall through... case kDamagedFinished: @@ -191,8 +191,7 @@ void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) { _platformMovie.show(); _platformMovie.start(); - - // TODO: MoviesTask call? I don't think it's needed + _platformMovie.redrawMovieWorld(); _stateBits &= ~kWaitingForPlayerBit; diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp index fcfae06676..0ce533c05d 100755 --- a/engines/pegasus/neighborhood/tsa/fulltsa.cpp +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -31,6 +31,7 @@ #include "pegasus/items/biochips/aichip.h" #include "pegasus/items/biochips/opticalchip.h" #include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" #include "pegasus/neighborhood/prehistoric/prehistoric.h" #include "pegasus/neighborhood/mars/mars.h" #include "pegasus/neighborhood/tsa/fulltsa.h" diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp index 3075895d81..831ab08850 100755 --- a/engines/pegasus/neighborhood/tsa/tinytsa.cpp +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -30,6 +30,7 @@ #include "pegasus/items/biochips/aichip.h" #include "pegasus/items/biochips/opticalchip.h" #include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" #include "pegasus/neighborhood/tsa/tinytsa.h" #include "pegasus/neighborhood/wsc/wsc.h" diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp index 942289038f..ee2d8be24a 100755 --- a/engines/pegasus/neighborhood/zoom.cpp +++ b/engines/pegasus/neighborhood/zoom.cpp @@ -52,6 +52,10 @@ void ZoomTable::clear() { } ZoomTable::Entry::Entry() { + clear(); +} + +void ZoomTable::Entry::clear() { hotspot = kNoHotSpotID; movieStart = 0xffffffff; movieEnd = 0xffffffff; diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h index 4046a76827..27290a88d5 100755 --- a/engines/pegasus/neighborhood/zoom.h +++ b/engines/pegasus/neighborhood/zoom.h @@ -49,6 +49,7 @@ public: struct Entry { Entry(); + void clear(); bool isEmpty() { return movieStart == 0xffffffff; } tHotSpotID hotspot; diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp index 2871ed1f73..b823ff2369 100644 --- a/engines/pegasus/pegasus.cpp +++ b/engines/pegasus/pegasus.cpp @@ -63,6 +63,7 @@ #include "pegasus/neighborhood/neighborhood.h" #include "pegasus/neighborhood/caldoria/caldoria.h" #include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" #include "pegasus/neighborhood/prehistoric/prehistoric.h" #include "pegasus/neighborhood/tsa/fulltsa.h" #include "pegasus/neighborhood/tsa/tinytsa.h" @@ -1363,6 +1364,9 @@ void PegasusEngine::makeNeighborhood(tNeighborhoodID neighborhoodID, Neighborhoo case kWSCID: neighborhood = new WSC(g_AIArea, this); break; + case kNoradAlphaID: + neighborhood = new NoradAlpha(g_AIArea, this); + break; default: error("Unhandled neighborhood %d", neighborhoodID); } -- cgit v1.2.3