Files
2026-02-02 04:50:13 +01:00

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