300 lines
10 KiB
C++
300 lines
10 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1995-1997 Presto Studios, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "pegasus/energymonitor.h"
|
|
#include "pegasus/gamestate.h"
|
|
#include "pegasus/pegasus.h"
|
|
#include "pegasus/ai/ai_area.h"
|
|
#include "pegasus/items/biochips/arthurchip.h"
|
|
#include "pegasus/items/inventory/airmask.h"
|
|
#include "pegasus/neighborhood/norad/constants.h"
|
|
#include "pegasus/neighborhood/norad/norad.h"
|
|
#include "pegasus/neighborhood/norad/noradelevator.h"
|
|
#include "pegasus/neighborhood/norad/pressuredoor.h"
|
|
#include "pegasus/neighborhood/norad/subcontrolroom.h"
|
|
#include "pegasus/neighborhood/norad/subplatform.h"
|
|
|
|
namespace Pegasus {
|
|
|
|
const NotificationFlags kDoneWithPressureDoorNotification = 1;
|
|
|
|
const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification;
|
|
|
|
// This class handles everything that Norad Alpha and Delta have in common, such as
|
|
// oxygen mask usage, the elevator and the pressure doors.
|
|
|
|
Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) :
|
|
Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) {
|
|
_elevatorUpSpotID = kNoHotSpotID;
|
|
_elevatorDownSpotID = kNoHotSpotID;
|
|
_elevatorUpRoomID = kNoHotSpotID;
|
|
_elevatorDownRoomID = kNoHotSpotID;
|
|
|
|
_subRoomEntryRoom1 = kNoRoomID;
|
|
_subRoomEntryDir1 = kNoDirection;
|
|
_subRoomEntryRoom2 = kNoRoomID;
|
|
_subRoomEntryDir2 = kNoDirection;
|
|
_upperPressureDoorRoom = kNoRoomID;
|
|
_lowerPressureDoorRoom = kNoRoomID;
|
|
|
|
_upperPressureDoorUpSpotID = kNoHotSpotID;
|
|
_upperPressureDoorDownSpotID = kNoHotSpotID;
|
|
_upperPressureDoorAbortSpotID = kNoHotSpotID;
|
|
|
|
_lowerPressureDoorUpSpotID = kNoHotSpotID;
|
|
_lowerPressureDoorDownSpotID = kNoHotSpotID;
|
|
_lowerPressureDoorAbortSpotID = kNoHotSpotID;
|
|
|
|
_pressureSoundIn = 0xffffffff;
|
|
_pressureSoundOut = 0xffffffff;
|
|
_equalizeSoundIn = 0xffffffff;
|
|
_equalizeSoundOut = 0xffffffff;
|
|
_accessDeniedIn = 0xffffffff;
|
|
_accessDeniedOut = 0xffffffff;
|
|
|
|
_platformRoom = kNoRoomID;
|
|
_subControlRoom = kNoRoomID;
|
|
|
|
_doneWithPressureDoor = false;
|
|
|
|
_noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags);
|
|
}
|
|
|
|
GameInteraction *Norad::makeInteraction(const InteractionID interactionID) {
|
|
PressureDoor *pressureDoor;
|
|
SubControlRoom *subControl;
|
|
|
|
switch (interactionID) {
|
|
case kNoradElevatorInteractionID:
|
|
return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID);
|
|
case kNoradPressureDoorInteractionID:
|
|
if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
|
|
pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID,
|
|
_upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
|
|
else
|
|
pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID,
|
|
_lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
|
|
|
|
if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot())
|
|
pressureDoor->playAgainstRobot();
|
|
|
|
return pressureDoor;
|
|
case kNoradSubControlRoomInteractionID:
|
|
subControl = new SubControlRoom(this);
|
|
|
|
if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot())
|
|
subControl->playAgainstRobot();
|
|
|
|
return subControl;
|
|
case kNoradSubPlatformInteractionID:
|
|
return new SubPlatform(this);
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void Norad::flushGameState() {
|
|
g_energyMonitor->saveCurrentEnergyValue();
|
|
}
|
|
|
|
void Norad::start() {
|
|
setUpAirMask();
|
|
Neighborhood::start();
|
|
}
|
|
|
|
void Norad::activateHotspots() {
|
|
Neighborhood::activateHotspots();
|
|
|
|
RoomID room = GameState.getCurrentRoom();
|
|
if (room == _elevatorUpRoomID)
|
|
_neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID);
|
|
else if (room == _elevatorDownRoomID)
|
|
_neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID);
|
|
}
|
|
|
|
void Norad::arriveAt(const RoomID room, const DirectionConstant direction) {
|
|
Neighborhood::arriveAt(room, direction);
|
|
|
|
if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID)
|
|
arriveAtNoradElevator();
|
|
else if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
|
|
arriveAtUpperPressureDoorRoom();
|
|
else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom)
|
|
arriveAtLowerPressureDoorRoom();
|
|
else if (GameState.getCurrentRoom() == _platformRoom)
|
|
arriveAtSubPlatformRoom();
|
|
else if (GameState.getCurrentRoom() == _subControlRoom)
|
|
arriveAtSubControlRoom();
|
|
|
|
if (_doneWithPressureDoor) {
|
|
_doneWithPressureDoor = false;
|
|
openDoor();
|
|
}
|
|
}
|
|
|
|
void Norad::arriveAtNoradElevator() {
|
|
if (_currentInteraction)
|
|
_currentInteraction->startOverInteraction();
|
|
else
|
|
newInteraction(kNoradElevatorInteractionID);
|
|
}
|
|
|
|
void Norad::arriveAtUpperPressureDoorRoom() {
|
|
newInteraction(kNoradPressureDoorInteractionID);
|
|
if (g_arthurChip)
|
|
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
|
|
}
|
|
|
|
void Norad::arriveAtLowerPressureDoorRoom() {
|
|
newInteraction(kNoradPressureDoorInteractionID);
|
|
if (g_arthurChip)
|
|
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
|
|
}
|
|
|
|
void Norad::arriveAtSubPlatformRoom() {
|
|
newInteraction(kNoradSubPlatformInteractionID);
|
|
}
|
|
|
|
void Norad::arriveAtSubControlRoom() {
|
|
newInteraction(kNoradSubControlRoomInteractionID);
|
|
}
|
|
|
|
int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
|
|
int16 result = Neighborhood::getStaticCompassAngle(room, dir);
|
|
|
|
if (room == _elevatorUpRoomID || room == _elevatorDownRoomID)
|
|
result += kElevatorCompassAngle;
|
|
else if (room == _platformRoom)
|
|
result += kSubPlatformCompassAngle;
|
|
else if (room == _subControlRoom)
|
|
result += kSubControlCompassAngle;
|
|
|
|
return result;
|
|
}
|
|
|
|
CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
|
|
if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) ||
|
|
(GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) &&
|
|
GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure)
|
|
return kCantOpenBadPressure;
|
|
|
|
return Neighborhood::canOpenDoor(entry);
|
|
}
|
|
|
|
void Norad::cantOpenDoor(CanOpenDoorReason reason) {
|
|
bool firstLockedDoor;
|
|
|
|
if (reason == kCantOpenBadPressure)
|
|
playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
|
|
else
|
|
playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
|
|
if (g_arthurChip) {
|
|
firstLockedDoor = g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA65", kArthurNoradAttemptedLockedDoor);
|
|
|
|
if (!firstLockedDoor)
|
|
g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA68", kArthurNoradAttemptedLockedDoorAgain);
|
|
}
|
|
}
|
|
|
|
void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
|
|
if (GameState.getCurrentRoom() == _elevatorUpRoomID) {
|
|
if (exitEntry.exitRoom != _elevatorDownRoomID)
|
|
newInteraction(kNoInteractionID);
|
|
} else if (GameState.getCurrentRoom() == _elevatorDownRoomID) {
|
|
if (exitEntry.exitRoom != _elevatorUpRoomID)
|
|
newInteraction(kNoInteractionID);
|
|
} else {
|
|
newInteraction(kNoInteractionID);
|
|
}
|
|
|
|
Neighborhood::startExitMovie(exitEntry);
|
|
}
|
|
|
|
void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
|
|
newInteraction(kNoInteractionID);
|
|
Neighborhood::startZoomMovie(zoomEntry);
|
|
}
|
|
|
|
void Norad::upButton(const Input &input) {
|
|
if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID)
|
|
Neighborhood::upButton(input);
|
|
}
|
|
|
|
void Norad::setUpAirMask() {
|
|
_airMaskCallBack.setNotification(&_neighborhoodNotification);
|
|
_airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes);
|
|
_airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag);
|
|
_neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag);
|
|
_airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
_airMaskTimer.setScale(1);
|
|
_airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit);
|
|
checkAirMask();
|
|
}
|
|
|
|
void Norad::checkAirMask() {
|
|
// WORKAROUND: The original game forgot to handle the case where the canister would
|
|
// be removed, leading to the timer remaining active.
|
|
if (!GameState.getNoradGassed() || (g_airMask && g_airMask->isAirFilterOn())) {
|
|
_airMaskTimer.stop();
|
|
} else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) {
|
|
_airMaskTimer.setTime(0);
|
|
_airMaskTimer.start();
|
|
}
|
|
|
|
loadAmbientLoops();
|
|
}
|
|
|
|
void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) {
|
|
if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0)
|
|
g_vm->die(kDeathGassedInNorad);
|
|
|
|
Neighborhood::receiveNotification(notification, flags);
|
|
|
|
if (notification == &_noradNotification) {
|
|
// Must be kDoneWithPressureDoorNotification...
|
|
Input scratch;
|
|
_doneWithPressureDoor = true;
|
|
downButton(scratch);
|
|
}
|
|
}
|
|
|
|
uint16 Norad::getDateResID() const {
|
|
return kDate2112ID;
|
|
}
|
|
|
|
Common::Path Norad::getBriefingMovie() {
|
|
return "Images/AI/Norad/XNO";
|
|
}
|
|
|
|
void Norad::pickedUpItem(Item *item) {
|
|
Neighborhood::pickedUpItem(item);
|
|
g_AIArea->checkMiddleArea();
|
|
}
|
|
|
|
void Norad::doneWithPressureDoor() {
|
|
_noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification);
|
|
}
|
|
|
|
} // End of namespace Pegasus
|