mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 02:12:14 +00:00
1788 lines
53 KiB
C++
1788 lines
53 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 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 "common/debug.h"
|
|
#include "common/stream.h"
|
|
|
|
#include "pegasus/compass.h"
|
|
#include "pegasus/cursor.h"
|
|
#include "pegasus/energymonitor.h"
|
|
#include "pegasus/gamestate.h"
|
|
#include "pegasus/graphics.h"
|
|
#include "pegasus/input.h"
|
|
#include "pegasus/interaction.h"
|
|
#include "pegasus/interface.h"
|
|
#include "pegasus/pegasus.h"
|
|
#include "pegasus/ai/ai_area.h"
|
|
#include "pegasus/items/biochips/mapchip.h"
|
|
#include "pegasus/neighborhood/neighborhood.h"
|
|
#include "pegasus/neighborhood/tsa/fulltsa.h"
|
|
#include "pegasus/neighborhood/tsa/tinytsa.h"
|
|
|
|
namespace Pegasus {
|
|
|
|
StriderCallBack::StriderCallBack(Neighborhood *neighborhood) {
|
|
_neighborhood = neighborhood;
|
|
}
|
|
|
|
void StriderCallBack::callBack() {
|
|
_neighborhood->checkStriding();
|
|
}
|
|
|
|
static const TimeValue kStridingSlop = 39;
|
|
|
|
Neighborhood *g_neighborhood = 0;
|
|
|
|
Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id)
|
|
: InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName), _navMovie(kNavMovieID), _stridingCallBack(this),
|
|
_neighborhoodNotification(kNeighborhoodNotificationID, (NotificationManager *)vm), _pushIn(kNoDisplayElement),
|
|
_turnPush(kTurnPushID), _croppedMovie(kCroppedMovieID) {
|
|
GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
|
|
_currentAlternate = 0;
|
|
_currentActivation = kActivateHotSpotAlways;
|
|
_interruptionFilter = kFilterAllInput;
|
|
allowInput(true);
|
|
resetLastExtra();
|
|
g_neighborhood = this;
|
|
_currentInteraction = 0;
|
|
_doneWithInteraction = false;
|
|
_croppedMovie.setDisplayOrder(kCroppedMovieLayer);
|
|
}
|
|
|
|
Neighborhood::~Neighborhood() {
|
|
for (HotspotIterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
|
|
_vm->getAllHotspots().remove(*it);
|
|
|
|
_neighborhoodHotspots.deleteHotspots();
|
|
g_neighborhood = 0;
|
|
|
|
loadLoopSound1("");
|
|
loadLoopSound2("");
|
|
newInteraction(kNoInteractionID);
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->removeAllRules();
|
|
}
|
|
|
|
void Neighborhood::init() {
|
|
_neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags);
|
|
_navMovieCallBack.setNotification(&_neighborhoodNotification);
|
|
_turnPushCallBack.setNotification(&_neighborhoodNotification);
|
|
_delayCallBack.setNotification(&_neighborhoodNotification);
|
|
_spotSoundCallBack.setNotification(&_neighborhoodNotification);
|
|
|
|
debug(0, "Loading '%s' neighborhood resources", _resName.c_str());
|
|
|
|
Common::SeekableReadStream *stream = _vm->_resFork->getResource(_doorTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load doors");
|
|
_doorTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_exitTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load exits");
|
|
_exitTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_extraTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load extras");
|
|
_extraTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_hotspotInfoTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load hotspot info");
|
|
_hotspotInfoTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_spotTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load spots");
|
|
_spotTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_turnTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load turns");
|
|
_turnTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_viewTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load views");
|
|
_viewTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
stream = _vm->_resFork->getResource(_zoomTable.getResTag(), _resName);
|
|
if (!stream)
|
|
error("Failed to load zooms");
|
|
_zoomTable.loadFromStream(stream);
|
|
delete stream;
|
|
|
|
createNeighborhoodSpots();
|
|
|
|
_navMovie.initFromMovieFile(getNavMovieName());
|
|
_navMovie.setVolume(_vm->getSoundFXLevel());
|
|
|
|
Common::String soundSpotsName = getSoundSpotsName();
|
|
if (soundSpotsName.empty()) {
|
|
_spotSounds.disposeSound();
|
|
} else {
|
|
_spotSounds.initFromQuickTime(getSoundSpotsName());
|
|
_spotSounds.setVolume(_vm->getSoundFXLevel());
|
|
}
|
|
|
|
_navMovie.setDisplayOrder(kNavMovieOrder);
|
|
_navMovie.startDisplaying();
|
|
|
|
Common::Rect bounds;
|
|
_navMovie.getBounds(bounds);
|
|
_pushIn.allocateSurface(bounds);
|
|
|
|
_turnPush.setInAndOutElements(&_pushIn, &_navMovie);
|
|
_turnPush.setDisplayOrder(kTurnPushOrder);
|
|
_turnPush.startDisplaying();
|
|
_navMovieCallBack.initCallBack(&_navMovie, kCallBackAtExtremes);
|
|
_stridingCallBack.initCallBack(&_navMovie, kCallBackAtTime);
|
|
_turnPushCallBack.initCallBack(&_turnPush, kCallBackAtExtremes);
|
|
_delayCallBack.initCallBack(&_delayTimer, kCallBackAtExtremes);
|
|
_spotSoundCallBack.initCallBack(&_spotSounds, kCallBackAtExtremes);
|
|
|
|
setUpAIRules();
|
|
|
|
if (g_compass)
|
|
g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
|
|
_soundLoop1.attachFader(&_loop1Fader);
|
|
_soundLoop2.attachFader(&_loop2Fader);
|
|
startIdling();
|
|
}
|
|
|
|
void Neighborhood::start() {
|
|
GameState.setCurrentRoom(GameState.getLastRoom());
|
|
GameState.setCurrentDirection(GameState.getLastDirection());
|
|
arriveAt(GameState.getNextRoom(), GameState.getNextDirection());
|
|
}
|
|
|
|
void Neighborhood::receiveNotification(Notification *, const NotificationFlags flags) {
|
|
if ((flags & (kNeighborhoodMovieCompletedFlag | kTurnCompletedFlag)) != 0 && g_AIArea)
|
|
g_AIArea->unlockAI();
|
|
if (flags & kMoveForwardCompletedFlag)
|
|
arriveAt(GameState.getNextRoom(), GameState.getNextDirection());
|
|
if (flags & kTurnCompletedFlag)
|
|
turnTo(GameState.getNextDirection());
|
|
if (flags & kSpotCompletedFlag)
|
|
spotCompleted();
|
|
if (flags & kDoorOpenCompletedFlag)
|
|
doorOpened();
|
|
if (flags & kActionRequestCompletedFlag)
|
|
popActionQueue();
|
|
if (flags & kDeathExtraCompletedFlag)
|
|
die(_extraDeathReason);
|
|
}
|
|
|
|
void Neighborhood::arriveAt(const RoomID room, const DirectionConstant direction) {
|
|
if (g_map)
|
|
g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), room, direction);
|
|
|
|
GameState.setCurrentNeighborhood(getObjectID());
|
|
|
|
_currentActivation = kActivateHotSpotAlways;
|
|
_interruptionFilter = kFilterAllInput;
|
|
|
|
if (room != GameState.getCurrentRoom() || direction != GameState.getCurrentDirection()) {
|
|
GameState.setCurrentRoom(room);
|
|
GameState.setCurrentDirection(direction);
|
|
loadAmbientLoops();
|
|
activateCurrentView(room, direction, kSpotOnArrivalMask);
|
|
} else {
|
|
loadAmbientLoops();
|
|
showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
}
|
|
|
|
if (GameState.getOpenDoorRoom() != kNoRoomID) {
|
|
// Arriving always closes a door.
|
|
loadAmbientLoops();
|
|
closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection());
|
|
GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
|
|
}
|
|
|
|
if (g_compass)
|
|
g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->checkMiddleArea();
|
|
|
|
checkContinuePoint(room, direction);
|
|
}
|
|
|
|
// These functions can be overridden to tweak the exact frames used.
|
|
|
|
void Neighborhood::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) {
|
|
entry = _exitTable.findEntry(room, direction, _currentAlternate);
|
|
|
|
if (entry.isEmpty())
|
|
entry = _exitTable.findEntry(room, direction, kNoAlternateID);
|
|
}
|
|
|
|
TimeValue Neighborhood::getViewTime(const RoomID room, const DirectionConstant direction) {
|
|
if (GameState.getOpenDoorRoom() == room && GameState.getOpenDoorDirection() == direction) {
|
|
// If we get here, the door entry for this location must exist.
|
|
DoorTable::Entry doorEntry = _doorTable.findEntry(room, direction, _currentAlternate);
|
|
|
|
if (doorEntry.isEmpty())
|
|
doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID);
|
|
|
|
return doorEntry.movieEnd - 1;
|
|
}
|
|
|
|
ViewTable::Entry viewEntry = _viewTable.findEntry(room, direction, _currentAlternate);
|
|
|
|
if (viewEntry.isEmpty())
|
|
viewEntry = _viewTable.findEntry(room, direction, kNoAlternateID);
|
|
|
|
return viewEntry.time;
|
|
}
|
|
|
|
void Neighborhood::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry) {
|
|
doorEntry = _doorTable.findEntry(room, direction, _currentAlternate);
|
|
|
|
if (doorEntry.isEmpty())
|
|
doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID);
|
|
}
|
|
|
|
DirectionConstant Neighborhood::getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turnDirection) {
|
|
TurnTable::Entry turnEntry = _turnTable.findEntry(room, direction, turnDirection, _currentAlternate);
|
|
|
|
if (turnEntry.isEmpty())
|
|
turnEntry = _turnTable.findEntry(room, direction, turnDirection, kNoAlternateID);
|
|
|
|
return turnEntry.endDirection;
|
|
}
|
|
|
|
void Neighborhood::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) {
|
|
spotEntry = _spotTable.findEntry(room, direction, flags, _currentAlternate);
|
|
|
|
if (spotEntry.isEmpty())
|
|
spotEntry = _spotTable.findEntry(room, direction, flags, kNoAlternateID);
|
|
}
|
|
|
|
void Neighborhood::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) {
|
|
zoomEntry = _zoomTable.findEntry(id);
|
|
}
|
|
|
|
void Neighborhood::getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry) {
|
|
hotspotEntry = _hotspotInfoTable.findEntry(id);
|
|
}
|
|
|
|
void Neighborhood::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
|
|
extraEntry = _extraTable.findEntry(id);
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
//
|
|
// "Can" functions: Called to see whether or not a user is allowed to do something
|
|
|
|
CanMoveForwardReason Neighborhood::canMoveForward(ExitTable::Entry &entry) {
|
|
DoorTable::Entry door;
|
|
|
|
getExitEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry);
|
|
getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), door);
|
|
|
|
// Fixed this so that doors that don't lead anywhere can be opened, but not walked
|
|
// through.
|
|
if (door.flags & kDoorPresentMask) {
|
|
if (GameState.isCurrentDoorOpen()) {
|
|
if (entry.exitRoom == kNoRoomID)
|
|
return kCantMoveBlocked;
|
|
else
|
|
return kCanMoveForward;
|
|
} else if (door.flags & kDoorLockedMask) {
|
|
return kCantMoveDoorLocked;
|
|
} else {
|
|
return kCantMoveDoorClosed;
|
|
}
|
|
} else if (entry.exitRoom == kNoRoomID) {
|
|
return kCantMoveBlocked;
|
|
}
|
|
|
|
return kCanMoveForward;
|
|
}
|
|
|
|
CanTurnReason Neighborhood::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) {
|
|
nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection);
|
|
|
|
if (nextDir == kNoDirection)
|
|
return kCantTurnNoTurn;
|
|
|
|
return kCanTurn;
|
|
}
|
|
|
|
CanOpenDoorReason Neighborhood::canOpenDoor(DoorTable::Entry &entry) {
|
|
getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry);
|
|
|
|
if (entry.flags & kDoorPresentMask) {
|
|
if (GameState.isCurrentDoorOpen())
|
|
return kCantOpenAlreadyOpen;
|
|
|
|
if (entry.flags & kDoorLockedMask)
|
|
return kCantOpenLocked;
|
|
|
|
return kCanOpenDoor;
|
|
}
|
|
|
|
return kCantOpenNoDoor;
|
|
}
|
|
|
|
void Neighborhood::createNeighborhoodSpots() {
|
|
Common::SeekableReadStream *hotspotList = _vm->_resFork->getResource(MKTAG('H', 'S', 'L', 's'), _resName);
|
|
if (!hotspotList)
|
|
error("Could not load neighborhood hotspots");
|
|
|
|
uint32 hotspotCount = hotspotList->readUint32BE();
|
|
|
|
while (hotspotCount--) {
|
|
uint16 id = hotspotList->readUint16BE();
|
|
uint32 flags = hotspotList->readUint32BE();
|
|
uint32 rgnSize = hotspotList->readUint32BE();
|
|
|
|
int32 startPos = hotspotList->pos();
|
|
|
|
debug(0, "Hotspot %d:", id);
|
|
Region region(hotspotList);
|
|
|
|
hotspotList->seek(startPos + rgnSize);
|
|
|
|
Hotspot *hotspot = new Hotspot(id);
|
|
hotspot->setHotspotFlags(flags);
|
|
hotspot->setArea(region);
|
|
|
|
_vm->getAllHotspots().push_back(hotspot);
|
|
_neighborhoodHotspots.push_back(hotspot);
|
|
}
|
|
|
|
delete hotspotList;
|
|
}
|
|
|
|
void Neighborhood::popActionQueue() {
|
|
if (!_actionQueue.empty()) {
|
|
QueueRequest topRequest = _actionQueue.pop();
|
|
|
|
switch (topRequest.requestType) {
|
|
case kNavExtraRequest:
|
|
_navMovie.stop();
|
|
break;
|
|
case kSpotSoundRequest:
|
|
_spotSounds.stopSound();
|
|
break;
|
|
case kDelayRequest:
|
|
_delayTimer.stop();
|
|
break;
|
|
}
|
|
|
|
serviceActionQueue();
|
|
}
|
|
}
|
|
|
|
void Neighborhood::serviceActionQueue() {
|
|
if (!_actionQueue.empty()) {
|
|
QueueRequest &topRequest = _actionQueue.front();
|
|
|
|
if (!topRequest.playing) {
|
|
topRequest.playing = true;
|
|
switch (topRequest.requestType) {
|
|
case kNavExtraRequest:
|
|
startExtraSequence(topRequest.extra, topRequest.flags, topRequest.interruptionFilter);
|
|
break;
|
|
case kSpotSoundRequest:
|
|
_spotSounds.stopSound();
|
|
_spotSounds.playSoundSegment(topRequest.start, topRequest.stop);
|
|
_interruptionFilter = topRequest.interruptionFilter;
|
|
_spotSoundCallBack.setCallBackFlag(topRequest.flags);
|
|
_spotSoundCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
break;
|
|
case kDelayRequest:
|
|
_delayTimer.stop();
|
|
_delayCallBack.setCallBackFlag(topRequest.flags);
|
|
_delayTimer.setSegment(0, topRequest.start, topRequest.stop);
|
|
_delayTimer.setTime(0);
|
|
_delayCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
_interruptionFilter = topRequest.interruptionFilter;
|
|
_delayTimer.start();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
_interruptionFilter = kFilterAllInput;
|
|
}
|
|
}
|
|
|
|
void Neighborhood::requestAction(const QueueRequestType requestType, const ExtraID extra, const TimeValue in, const TimeValue out,
|
|
const InputBits interruptionFilter, const NotificationFlags flags) {
|
|
|
|
QueueRequest request;
|
|
|
|
request.requestType = requestType;
|
|
request.extra = extra;
|
|
request.start = in;
|
|
request.stop = out;
|
|
request.interruptionFilter = interruptionFilter;
|
|
request.playing = false;
|
|
request.flags = flags | kActionRequestCompletedFlag;
|
|
request.notification = &_neighborhoodNotification;
|
|
_actionQueue.push(request);
|
|
if (_actionQueue.size() == 1)
|
|
serviceActionQueue();
|
|
}
|
|
|
|
void Neighborhood::requestExtraSequence(const ExtraID whichExtra, const NotificationFlags flags, const InputBits interruptionFilter) {
|
|
requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags);
|
|
}
|
|
|
|
void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const InputBits interruptionFilter, const NotificationFlags flags) {
|
|
requestAction(kSpotSoundRequest, 0xFFFFFFFF, in, out, interruptionFilter, flags);
|
|
}
|
|
|
|
void Neighborhood::playSpotSoundSync(const TimeValue in, const TimeValue out) {
|
|
// Let the action queue play out first...
|
|
while (!actionQueueEmpty()) {
|
|
InputDevice.pumpEvents();
|
|
_vm->checkCallBacks();
|
|
_vm->refreshDisplay();
|
|
_vm->checkNotifications();
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
_spotSounds.stopSound();
|
|
_spotSounds.playSoundSegment(in, out);
|
|
|
|
while (_spotSounds.isPlaying()) {
|
|
InputDevice.pumpEvents();
|
|
_vm->checkCallBacks();
|
|
_vm->refreshDisplay();
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
}
|
|
|
|
void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const InputBits interruptionFilter, const NotificationFlags flags) {
|
|
requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags);
|
|
}
|
|
|
|
bool operator==(const QueueRequest &arg1, const QueueRequest &arg2) {
|
|
return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra &&
|
|
arg1.start == arg2.start && arg1.stop == arg2.stop;
|
|
}
|
|
|
|
bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2) {
|
|
return !operator==(arg1, arg2);
|
|
}
|
|
|
|
Common::String Neighborhood::getBriefingMovie() {
|
|
if (_currentInteraction)
|
|
return _currentInteraction->getBriefingMovie();
|
|
|
|
return Common::String();
|
|
}
|
|
|
|
Common::String Neighborhood::getEnvScanMovie() {
|
|
if (_currentInteraction)
|
|
return _currentInteraction->getEnvScanMovie();
|
|
|
|
return Common::String();
|
|
}
|
|
|
|
uint Neighborhood::getNumHints() {
|
|
if (_currentInteraction)
|
|
return _currentInteraction->getNumHints();
|
|
|
|
return 0;
|
|
}
|
|
|
|
Common::String Neighborhood::getHintMovie(uint hintNum) {
|
|
if (_currentInteraction)
|
|
return _currentInteraction->getHintMovie(hintNum);
|
|
|
|
return Common::String();
|
|
}
|
|
|
|
bool Neighborhood::canSolve() {
|
|
if (_currentInteraction)
|
|
return _currentInteraction->canSolve();
|
|
|
|
return false;
|
|
}
|
|
|
|
void Neighborhood::doSolve() {
|
|
if (_currentInteraction)
|
|
_currentInteraction->doSolve();
|
|
}
|
|
|
|
bool Neighborhood::okayToJump() {
|
|
return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun);
|
|
}
|
|
|
|
AirQuality Neighborhood::getAirQuality(const RoomID) {
|
|
return kAirQualityGood;
|
|
}
|
|
|
|
void Neighborhood::checkStriding() {
|
|
if (stillMoveForward()) {
|
|
ExitTable::Entry nextExit;
|
|
getExitEntry(GameState.getNextRoom(), GameState.getNextDirection(), nextExit);
|
|
keepStriding(nextExit);
|
|
} else {
|
|
stopStriding();
|
|
}
|
|
}
|
|
|
|
bool Neighborhood::stillMoveForward() {
|
|
Input input;
|
|
|
|
InputHandler::readInputDevice(input);
|
|
return input.upButtonAnyDown();
|
|
}
|
|
|
|
void Neighborhood::keepStriding(ExitTable::Entry &nextExitEntry) {
|
|
FaderMoveSpec compassMove;
|
|
|
|
if (g_map)
|
|
g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection());
|
|
|
|
if (g_compass)
|
|
getExitCompassMove(nextExitEntry, compassMove);
|
|
|
|
GameState.setCurrentRoom(GameState.getNextRoom());
|
|
GameState.setCurrentDirection(GameState.getNextDirection());
|
|
GameState.setNextRoom(nextExitEntry.exitRoom);
|
|
GameState.setNextDirection(nextExitEntry.exitDirection);
|
|
|
|
if (nextExitEntry.movieEnd == nextExitEntry.exitEnd)
|
|
scheduleNavCallBack(kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag);
|
|
else
|
|
scheduleStridingCallBack(nextExitEntry.movieEnd - kStridingSlop, kStrideCompletedFlag);
|
|
|
|
if (g_compass)
|
|
g_compass->startFader(compassMove);
|
|
}
|
|
|
|
void Neighborhood::stopStriding() {
|
|
_navMovie.stop();
|
|
_neighborhoodNotification.setNotificationFlags(kNeighborhoodMovieCompletedFlag |
|
|
kMoveForwardCompletedFlag, kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag);
|
|
}
|
|
|
|
// Compass support
|
|
int16 Neighborhood::getStaticCompassAngle(const RoomID, const DirectionConstant dir) {
|
|
// North, south, east, west
|
|
static const int16 compassAngles[] = { 0, 180, 90, 270 };
|
|
return compassAngles[dir];
|
|
}
|
|
|
|
void Neighborhood::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {
|
|
int32 startAngle = getStaticCompassAngle(exitEntry.room, exitEntry.direction);
|
|
int32 stopAngle = getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection);
|
|
|
|
if (startAngle > stopAngle) {
|
|
if (stopAngle + 180 < startAngle)
|
|
stopAngle += 360;
|
|
} else {
|
|
if (startAngle + 180 < stopAngle)
|
|
startAngle += 360;
|
|
}
|
|
|
|
compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, startAngle, exitEntry.movieEnd, stopAngle);
|
|
}
|
|
|
|
void Neighborhood::scheduleNavCallBack(NotificationFlags flags) {
|
|
_navMovieCallBack.cancelCallBack();
|
|
|
|
if (flags != 0) {
|
|
_navMovieCallBack.setCallBackFlag(flags);
|
|
_navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
}
|
|
}
|
|
|
|
void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, NotificationFlags flags) {
|
|
_stridingCallBack.cancelCallBack();
|
|
|
|
if (flags != 0)
|
|
_stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale());
|
|
}
|
|
|
|
void Neighborhood::moveNavTo(const CoordType h, const CoordType v) {
|
|
CoordType oldH, oldV;
|
|
_navMovie.getLocation(oldH, oldV);
|
|
|
|
CoordType offH = h - oldH;
|
|
CoordType offV = v - oldV;
|
|
|
|
_navMovie.moveElementTo(h, v);
|
|
_turnPush.moveElementTo(h, v);
|
|
|
|
if (offH != 0 || offV != 0)
|
|
for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
|
|
if ((*it)->getHotspotFlags() & kNeighborhoodSpotFlag)
|
|
(*it)->moveSpot(offH, offV);
|
|
}
|
|
|
|
void Neighborhood::activateHotspots() {
|
|
InputHandler::activateHotspots();
|
|
|
|
for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) {
|
|
HotspotInfoTable::Entry entry = *it;
|
|
|
|
if (entry.hotspotRoom == GameState.getCurrentRoom() && entry.hotspotDirection == GameState.getCurrentDirection()
|
|
&& (entry.hotspotActivation == _currentActivation || entry.hotspotActivation == kActivateHotSpotAlways)) {
|
|
Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(entry.hotspot);
|
|
if (hotspot)
|
|
activateOneHotspot(entry, hotspot);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
|
|
HotSpotFlags flags = clickedSpot->getHotspotFlags();
|
|
|
|
if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) {
|
|
ItemID itemID = kNoItemID;
|
|
|
|
for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) {
|
|
if (it->hotspot == clickedSpot->getObjectID()) {
|
|
itemID = it->hotspotItem;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (itemID != kNoItemID) {
|
|
Item *draggingItem = _vm->getAllItems().findItemByID(itemID);
|
|
|
|
if (draggingItem) {
|
|
takeItemFromRoom(draggingItem);
|
|
|
|
if ((flags & kPickUpItemSpotFlag) != 0)
|
|
_vm->dragItem(input, draggingItem, kDragInventoryPickup);
|
|
else
|
|
_vm->dragItem(input, draggingItem, kDragBiochipPickup);
|
|
}
|
|
}
|
|
} else {
|
|
// Check other flags here?
|
|
if ((flags & kZoomSpotFlags) != 0) {
|
|
zoomTo(clickedSpot);
|
|
} else if ((flags & kPlayExtraSpotFlag) != 0) {
|
|
HotspotInfoTable::Entry hotspotEntry;
|
|
getHotspotEntry(clickedSpot->getObjectID(), hotspotEntry);
|
|
startExtraSequence(hotspotEntry.hotspotExtra, kExtraCompletedFlag, kFilterNoInput);
|
|
} else if ((flags & kOpenDoorSpotFlag) != 0) {
|
|
openDoor();
|
|
} else {
|
|
InputHandler::clickInHotspot(input, clickedSpot);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) {
|
|
switch (reason) {
|
|
case kCantMoveDoorClosed:
|
|
case kCantMoveDoorLocked:
|
|
openDoor();
|
|
break;
|
|
case kCantMoveBlocked:
|
|
zoomUpOrBump();
|
|
break;
|
|
default:
|
|
bumpIntoWall();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Neighborhood::cantOpenDoor(CanOpenDoorReason) {
|
|
bumpIntoWall();
|
|
}
|
|
|
|
void Neighborhood::turnTo(const DirectionConstant direction) {
|
|
if (g_map)
|
|
g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction);
|
|
|
|
// clone2727 says: Is this necessary?
|
|
_vm->_gfx->setCurSurface(_navMovie.getSurface());
|
|
_pushIn.copyToCurrentPort();
|
|
_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
|
|
|
|
// Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to
|
|
// always when turning to a new view?
|
|
_currentActivation = kActivateHotSpotAlways;
|
|
|
|
_interruptionFilter = kFilterAllInput;
|
|
|
|
if (direction != GameState.getCurrentDirection()) {
|
|
GameState.setCurrentDirection(direction);
|
|
activateCurrentView(GameState.getCurrentRoom(), direction, kSpotOnTurnMask);
|
|
} else {
|
|
showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
}
|
|
|
|
if (GameState.getOpenDoorRoom() != kNoRoomID) {
|
|
// Turning always closes a door.
|
|
loadAmbientLoops();
|
|
closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection());
|
|
GameState.setOpenDoorLocation(kNoRoomID, kNoDirection);
|
|
}
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->checkMiddleArea();
|
|
|
|
checkContinuePoint(GameState.getCurrentRoom(), direction);
|
|
|
|
_vm->_cursor->hideUntilMoved();
|
|
}
|
|
|
|
void Neighborhood::spotCompleted() {
|
|
_interruptionFilter = kFilterAllInput;
|
|
showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
}
|
|
|
|
void Neighborhood::doorOpened() {
|
|
_interruptionFilter = kFilterAllInput;
|
|
|
|
// 2/23/97
|
|
// Fixes funny bug with doors that are opened by dropping things on them...
|
|
setCurrentActivation(kActivateHotSpotAlways);
|
|
|
|
GameState.setOpenDoorLocation(GameState.getCurrentRoom(), GameState.getCurrentDirection());
|
|
|
|
SpotTable::Entry entry;
|
|
findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask, entry);
|
|
|
|
if (entry.dstFlags & kSpotOnDoorOpenMask) {
|
|
startSpotOnceOnly(entry.movieStart, entry.movieEnd);
|
|
} else {
|
|
findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask | kSpotLoopsMask, entry);
|
|
|
|
if (entry.dstFlags & kSpotOnDoorOpenMask)
|
|
startSpotLoop(entry.movieStart, entry.movieEnd);
|
|
}
|
|
|
|
loadAmbientLoops();
|
|
|
|
if (g_map)
|
|
g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection());
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->checkMiddleArea();
|
|
}
|
|
|
|
void Neighborhood::moveForward() {
|
|
ExitTable::Entry exitEntry;
|
|
CanMoveForwardReason moveReason = canMoveForward(exitEntry);
|
|
|
|
if (moveReason == kCanMoveForward)
|
|
startExitMovie(exitEntry);
|
|
else
|
|
cantMoveThatWay(moveReason);
|
|
}
|
|
|
|
void Neighborhood::turn(const TurnDirection turnDirection) {
|
|
DirectionConstant nextDir;
|
|
CanTurnReason turnReason = canTurn(turnDirection, nextDir);
|
|
|
|
if (turnReason == kCanTurn)
|
|
startTurnPush(turnDirection, getViewTime(GameState.getCurrentRoom(), nextDir), nextDir);
|
|
else
|
|
cantTurnThatWay(turnReason);
|
|
}
|
|
|
|
void Neighborhood::turnLeft() {
|
|
turn(kTurnLeft);
|
|
}
|
|
|
|
void Neighborhood::turnRight() {
|
|
turn(kTurnRight);
|
|
}
|
|
|
|
void Neighborhood::turnUp() {
|
|
turn(kTurnUp);
|
|
}
|
|
|
|
void Neighborhood::turnDown() {
|
|
turn(kTurnDown);
|
|
}
|
|
|
|
void Neighborhood::openDoor() {
|
|
DoorTable::Entry door;
|
|
CanOpenDoorReason doorReason = canOpenDoor(door);
|
|
|
|
if (doorReason == kCanOpenDoor)
|
|
startDoorOpenMovie(door.movieStart, door.movieEnd);
|
|
else
|
|
cantOpenDoor(doorReason);
|
|
}
|
|
|
|
void Neighborhood::zoomTo(const Hotspot *hotspot) {
|
|
ZoomTable::Entry zoomEntry;
|
|
getZoomEntry(hotspot->getObjectID(), zoomEntry);
|
|
if (!zoomEntry.isEmpty())
|
|
startZoomMovie(zoomEntry);
|
|
}
|
|
|
|
void Neighborhood::updateViewFrame() {
|
|
showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection()));
|
|
}
|
|
|
|
void Neighborhood::startSpotLoop(TimeValue startTime, TimeValue stopTime, NotificationFlags flags) {
|
|
_turnPush.hide();
|
|
startMovieSequence(startTime, stopTime, flags, true, kFilterAllInput);
|
|
}
|
|
|
|
void Neighborhood::showViewFrame(TimeValue viewTime) {
|
|
if ((int32)viewTime >= 0) {
|
|
_turnPush.hide();
|
|
_navMovie.stop();
|
|
_navMovie.setFlags(0);
|
|
_navMovie.setSegment(0, _navMovie.getDuration());
|
|
_navMovie.setTime(viewTime);
|
|
|
|
Common::Rect pushBounds;
|
|
_turnPush.getBounds(pushBounds);
|
|
|
|
_navMovie.moveElementTo(pushBounds.left, pushBounds.top);
|
|
_navMovie.show();
|
|
_navMovie.redrawMovieWorld();
|
|
}
|
|
}
|
|
|
|
void Neighborhood::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
|
|
ExtraTable::Entry entry;
|
|
getExtraEntry(extraID, entry);
|
|
|
|
if (entry.movieStart != 0xffffffff)
|
|
playExtraMovie(entry, flags, interruptionFilter);
|
|
}
|
|
|
|
bool Neighborhood::startExtraSequenceSync(const ExtraID extraID, const InputBits interruptionFilter) {
|
|
InputDevice.waitInput(interruptionFilter);
|
|
return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter);
|
|
}
|
|
|
|
void Neighborhood::loopExtraSequence(const uint32 extraID, NotificationFlags flags) {
|
|
ExtraTable::Entry entry;
|
|
getExtraEntry(extraID, entry);
|
|
|
|
if (entry.movieStart != 0xffffffff) {
|
|
_lastExtra = extraID;
|
|
startSpotLoop(entry.movieStart, entry.movieEnd, flags);
|
|
}
|
|
}
|
|
|
|
bool Neighborhood::navMoviePlaying() {
|
|
return _navMovie.isRunning();
|
|
}
|
|
|
|
void Neighborhood::playDeathExtra(ExtraID extra, DeathReason deathReason) {
|
|
_extraDeathReason = deathReason;
|
|
startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput);
|
|
}
|
|
|
|
void Neighborhood::die(const DeathReason deathReason) {
|
|
loadLoopSound1("");
|
|
loadLoopSound2("");
|
|
_vm->die(deathReason);
|
|
}
|
|
|
|
void Neighborhood::setSoundFXLevel(const uint16 fxLevel) {
|
|
if (_navMovie.isSurfaceValid())
|
|
_navMovie.setVolume(fxLevel);
|
|
if (_spotSounds.isSoundLoaded())
|
|
_spotSounds.setVolume(fxLevel);
|
|
if (_currentInteraction)
|
|
_currentInteraction->setSoundFXLevel(fxLevel);
|
|
}
|
|
|
|
void Neighborhood::setAmbienceLevel(const uint16 ambientLevel) {
|
|
if (_soundLoop1.isSoundLoaded())
|
|
_loop1Fader.setMasterVolume(_vm->getAmbienceLevel());
|
|
if (_soundLoop2.isSoundLoaded())
|
|
_loop2Fader.setMasterVolume(_vm->getAmbienceLevel());
|
|
if (_currentInteraction)
|
|
_currentInteraction->setAmbienceLevel(ambientLevel);
|
|
}
|
|
|
|
// Force the exit taken from (room, direction, alternate) to come to a stop.
|
|
void Neighborhood::forceStridingStop(const RoomID room, const DirectionConstant direction, const AlternateID alternate) {
|
|
ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate);
|
|
|
|
if (entry.movieStart != 0xffffffff) {
|
|
TimeValue strideStop = entry.exitEnd;
|
|
TimeValue exitStop = entry.movieEnd;
|
|
|
|
if (strideStop != exitStop) {
|
|
for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) {
|
|
entry = *it;
|
|
|
|
if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) {
|
|
entry.exitEnd = exitStop;
|
|
*it = entry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore the exit taken from (room, direction, alternate) to stride.
|
|
void Neighborhood::restoreStriding(const RoomID room, const DirectionConstant direction, const AlternateID alternate) {
|
|
ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate);
|
|
|
|
if (entry.movieStart != 0xffffffff) {
|
|
TimeValue strideStop = entry.exitEnd;
|
|
TimeValue exitStop = entry.movieEnd;
|
|
|
|
if (strideStop != entry.originalEnd) {
|
|
for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) {
|
|
entry = *it;
|
|
|
|
if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) {
|
|
entry.exitEnd = entry.originalEnd;
|
|
*it = entry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HotspotInfoTable::Entry *Neighborhood::findHotspotEntry(const HotSpotID id) {
|
|
for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++)
|
|
if (it->hotspot == id)
|
|
return &(*it);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Neighborhood::hideNav() {
|
|
_isRunning = _navMovie.isRunning();
|
|
_navMovie.stop();
|
|
_navMovie.hide();
|
|
_turnPush.stopFader();
|
|
_turnPush.hide();
|
|
}
|
|
|
|
void Neighborhood::showNav() {
|
|
_navMovie.show();
|
|
_turnPush.hide();
|
|
if (_isRunning)
|
|
_navMovie.start();
|
|
}
|
|
|
|
void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) {
|
|
FaderMoveSpec compassMove;
|
|
|
|
if (g_compass)
|
|
getExitCompassMove(exitEntry, compassMove);
|
|
|
|
GameState.setNextRoom(exitEntry.exitRoom);
|
|
GameState.setNextDirection(exitEntry.exitDirection);
|
|
|
|
if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk.
|
|
startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
|
|
else // We're stridin'!
|
|
startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd);
|
|
|
|
if (g_compass)
|
|
g_compass->startFader(compassMove);
|
|
}
|
|
|
|
void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
|
|
FaderMoveSpec compassMove;
|
|
|
|
if (g_compass)
|
|
getZoomCompassMove(zoomEntry, compassMove);
|
|
|
|
GameState.setNextRoom(zoomEntry.room);
|
|
GameState.setNextDirection(zoomEntry.direction);
|
|
|
|
startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
|
|
|
|
if (g_compass)
|
|
g_compass->startFader(compassMove);
|
|
}
|
|
|
|
void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
|
|
startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false);
|
|
}
|
|
|
|
void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) {
|
|
if (g_AIArea)
|
|
g_AIArea->lockAIOut();
|
|
|
|
_vm->_cursor->hide();
|
|
|
|
GameState.setNextDirection(nextDir);
|
|
|
|
_interruptionFilter = kFilterNoInput;
|
|
_turnPush.stopFader();
|
|
|
|
// Set up callback.
|
|
_turnPushCallBack.setCallBackFlag(kTurnCompletedFlag);
|
|
_turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
|
|
|
|
// Stop nav movie.
|
|
_navMovie.stop();
|
|
_navMovie.setFlags(0);
|
|
|
|
// Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame
|
|
// will work.
|
|
_navMovie.setSegment(0, _navMovie.getDuration());
|
|
|
|
_pushIn.initFromMovieFrame(_navMovie.getMovie(), newView);
|
|
|
|
_navMovie.hide();
|
|
|
|
switch (turnDirection) {
|
|
case kTurnLeft:
|
|
_turnPush.setSlideDirection(kSlideRightMask);
|
|
break;
|
|
case kTurnRight:
|
|
_turnPush.setSlideDirection(kSlideLeftMask);
|
|
break;
|
|
case kTurnUp:
|
|
_turnPush.setSlideDirection(kSlideDownMask);
|
|
break;
|
|
case kTurnDown:
|
|
_turnPush.setSlideDirection(kSlideUpMask);
|
|
break;
|
|
}
|
|
|
|
_turnPush.show();
|
|
|
|
FaderMoveSpec moveSpec;
|
|
moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000);
|
|
_turnPush.startFader(moveSpec);
|
|
|
|
if (g_compass) {
|
|
_turnPush.pauseFader();
|
|
|
|
int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
|
|
int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir);
|
|
|
|
if (turnDirection == kTurnLeft) {
|
|
if (startAngle < stopAngle)
|
|
startAngle += 360;
|
|
} else {
|
|
if (stopAngle < startAngle)
|
|
stopAngle += 360;
|
|
}
|
|
|
|
FaderMoveSpec turnSpec;
|
|
_turnPush.getCurrentFaderMove(turnSpec);
|
|
|
|
FaderMoveSpec compassMove;
|
|
compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle);
|
|
g_compass->startFader(compassMove);
|
|
}
|
|
|
|
_turnPushCallBack.cancelCallBack();
|
|
_turnPush.continueFader();
|
|
|
|
do {
|
|
InputDevice.pumpEvents();
|
|
_vm->checkCallBacks();
|
|
_vm->refreshDisplay();
|
|
_vm->_system->delayMillis(10);
|
|
} while (_turnPush.isFading());
|
|
|
|
_turnPush.stopFader();
|
|
_neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag);
|
|
}
|
|
|
|
void Neighborhood::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) {
|
|
FaderMoveSpec compassMove;
|
|
|
|
if (g_compass)
|
|
getExtraCompassMove(extraEntry, compassMove);
|
|
|
|
_lastExtra = extraEntry.extra;
|
|
_turnPush.hide();
|
|
startMovieSequence(extraEntry.movieStart, extraEntry.movieEnd, flags, false, interruptionInput);
|
|
|
|
if (g_compass)
|
|
g_compass->startFader(compassMove);
|
|
}
|
|
|
|
void Neighborhood::activateCurrentView(const RoomID room, const DirectionConstant direction, SpotFlags flag) {
|
|
SpotTable::Entry entry;
|
|
findSpotEntry(room, direction, flag, entry);
|
|
|
|
if (entry.dstFlags & flag) {
|
|
startSpotOnceOnly(entry.movieStart, entry.movieEnd);
|
|
} else {
|
|
findSpotEntry(room, direction, flag | kSpotLoopsMask, entry);
|
|
|
|
if (entry.dstFlags & flag)
|
|
startSpotLoop(entry.movieStart, entry.movieEnd);
|
|
else
|
|
showViewFrame(getViewTime(room, direction));
|
|
}
|
|
}
|
|
|
|
void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) {
|
|
switch (_vm->getDragType()) {
|
|
case kDragInventoryUse:
|
|
if ((hotspot->getHotspotFlags() & kDropItemSpotFlag) != 0 &&
|
|
_vm->getDraggingItem()->getObjectID() == entry.hotspotItem)
|
|
hotspot->setActive();
|
|
break;
|
|
case kDragInventoryPickup:
|
|
case kDragBiochipPickup:
|
|
// Do nothing -- neighborhoods activate no hot spots in this case...
|
|
break;
|
|
default:
|
|
if ((hotspot->getHotspotFlags() & kPickUpBiochipSpotFlag) != 0) {
|
|
Item *item = _vm->getAllItems().findItemByID(entry.hotspotItem);
|
|
if (item && item->getItemNeighborhood() == getObjectID())
|
|
hotspot->setActive();
|
|
} else {
|
|
HotSpotFlags flags = hotspot->getHotspotFlags();
|
|
|
|
if ((flags & kNeighborhoodSpotFlag) != 0) {
|
|
if (flags & kOpenDoorSpotFlag) {
|
|
if (!GameState.isCurrentDoorOpen())
|
|
hotspot->setActive();
|
|
} else if ((flags & (kZoomSpotFlags | kClickSpotFlag | kPlayExtraSpotFlag)) != 0) {
|
|
hotspot->setActive();
|
|
} else if ((flags & kPickUpItemSpotFlag) != 0) {
|
|
// Changed this 2/19/96
|
|
// Should only light up this hot spot if the item's taken flag is not
|
|
// set. It's not based on neighborhood ID since that can be reset by the
|
|
// destroying process.
|
|
|
|
if (!GameState.isTakenItemID(entry.hotspotItem))
|
|
hotspot->setActive();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) {
|
|
_turnPush.hide();
|
|
startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false);
|
|
}
|
|
|
|
void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence,
|
|
const InputBits interruptionInput, const TimeValue strideStop) {
|
|
if (!loopSequence && g_AIArea)
|
|
g_AIArea->lockAIOut();
|
|
|
|
_interruptionFilter = interruptionInput;
|
|
|
|
// Stop the movie before doing anything else
|
|
_navMovie.stop();
|
|
|
|
Common::Rect pushBounds;
|
|
_turnPush.getBounds(pushBounds);
|
|
|
|
_navMovie.moveElementTo(pushBounds.left, pushBounds.top);
|
|
_navMovie.show();
|
|
_navMovie.setFlags(0);
|
|
_navMovie.setSegment(startTime, stopTime);
|
|
_navMovie.setTime(startTime);
|
|
|
|
if (loopSequence)
|
|
_navMovie.setFlags(kLoopTimeBase);
|
|
else
|
|
flags |= kNeighborhoodMovieCompletedFlag;
|
|
|
|
if (strideStop != 0xffffffff)
|
|
// Subtract a little slop from the striding stop time to keep from "pumping" at the
|
|
// end of a walk.
|
|
// 40 is one frame (scale == 600, 15 fps).
|
|
scheduleStridingCallBack(strideStop - kStridingSlop, flags);
|
|
else
|
|
scheduleNavCallBack(flags);
|
|
|
|
_navMovie.start();
|
|
}
|
|
|
|
void Neighborhood::throwAwayInterface() {
|
|
_doorTable.clear();
|
|
_exitTable.clear();
|
|
_extraTable.clear();
|
|
_hotspotInfoTable.clear();
|
|
_spotTable.clear();
|
|
_turnTable.clear();
|
|
_viewTable.clear();
|
|
_zoomTable.clear();
|
|
|
|
_navMovie.stopDisplaying();
|
|
_navMovie.releaseMovie();
|
|
_pushIn.deallocateSurface();
|
|
_turnPush.stopDisplaying();
|
|
_turnPush.setInAndOutElements(0, 0);
|
|
_turnPush.disposeAllCallBacks();
|
|
|
|
for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++)
|
|
_vm->getAllHotspots().remove(*it);
|
|
|
|
_neighborhoodHotspots.deleteHotspots();
|
|
_spotSounds.disposeSound();
|
|
_delayTimer.disposeAllCallBacks();
|
|
|
|
if (g_AIArea) {
|
|
g_AIArea->saveAIState();
|
|
g_AIArea->removeAllRules();
|
|
}
|
|
|
|
if (_currentInteraction)
|
|
newInteraction(kNoInteractionID);
|
|
|
|
_croppedMovie.releaseMovie();
|
|
|
|
loadLoopSound1("");
|
|
loadLoopSound2("");
|
|
|
|
if (g_energyMonitor) {
|
|
g_energyMonitor->stopEnergyDraining();
|
|
g_energyMonitor->saveCurrentEnergyValue();
|
|
}
|
|
|
|
delete g_interface;
|
|
}
|
|
|
|
bool Neighborhood::prepareExtraSync(const ExtraID extraID) {
|
|
ExtraTable::Entry extraEntry;
|
|
FaderMoveSpec compassMove;
|
|
|
|
if (g_compass) {
|
|
getExtraEntry(extraID, extraEntry);
|
|
getExtraCompassMove(extraEntry, compassMove);
|
|
}
|
|
|
|
ExtraTable::Entry entry;
|
|
getExtraEntry(extraID, entry);
|
|
bool result;
|
|
|
|
if (entry.movieStart != 0xffffffff) {
|
|
_turnPush.hide();
|
|
|
|
// Stop the movie before doing anything else
|
|
_navMovie.stop();
|
|
|
|
Common::Rect pushBounds;
|
|
_turnPush.getBounds(pushBounds);
|
|
_navMovie.moveElementTo(pushBounds.left, pushBounds.top);
|
|
|
|
_navMovie.show();
|
|
_navMovie.setFlags(0);
|
|
_navMovie.setSegment(entry.movieStart, entry.movieEnd);
|
|
_navMovie.setTime(entry.movieStart);
|
|
_navMovie.start();
|
|
result = true;
|
|
} else {
|
|
result = false;
|
|
}
|
|
|
|
if (result && g_compass)
|
|
g_compass->startFader(compassMove);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Neighborhood::waitMovieFinish(Movie *movie, const InputBits interruptionFilter) {
|
|
Input input;
|
|
bool result = true;
|
|
bool saveAllowed = _vm->swapSaveAllowed(false);
|
|
bool openAllowed = _vm->swapLoadAllowed(false);
|
|
|
|
while (movie->isRunning()) {
|
|
InputDevice.getInput(input, interruptionFilter);
|
|
|
|
if (input.anyInput() || _vm->shouldQuit()) {
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
_vm->checkCallBacks();
|
|
_vm->refreshDisplay();
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
movie->stop();
|
|
_vm->swapSaveAllowed(saveAllowed);
|
|
_vm->swapLoadAllowed(openAllowed);
|
|
|
|
return result;
|
|
}
|
|
|
|
InputBits Neighborhood::getInputFilter() {
|
|
return _interruptionFilter & InputHandler::getInputFilter();
|
|
}
|
|
|
|
void Neighborhood::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) {
|
|
int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
|
|
int32 stopAngle = getStaticCompassAngle(zoomEntry.room, zoomEntry.direction);
|
|
|
|
if (startAngle > stopAngle) {
|
|
if (stopAngle + 180 < startAngle)
|
|
stopAngle += 360;
|
|
} else {
|
|
if (startAngle + 180 < stopAngle)
|
|
startAngle += 360;
|
|
}
|
|
|
|
compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), zoomEntry.movieStart, startAngle, zoomEntry.movieEnd, stopAngle);
|
|
}
|
|
|
|
void Neighborhood::getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &compassMove) {
|
|
compassMove.makeOneKnotFaderSpec(g_compass->getFaderValue());
|
|
}
|
|
|
|
void Neighborhood::setUpAIRules() {
|
|
// Set up default rules here:
|
|
// -- Energy warning rules.
|
|
|
|
if (g_AIArea) {
|
|
g_AIArea->forceAIUnlocked();
|
|
|
|
if (!_vm->isOldDemo() && (getObjectID() == kPrehistoricID || getObjectID() == kNoradAlphaID ||
|
|
getObjectID() == kNoradDeltaID || getObjectID() == kMarsID || getObjectID() == kWSCID)) {
|
|
|
|
AIEnergyMonitorCondition *condition50 = new AIEnergyMonitorCondition(kWorriedEnergy);
|
|
AIPlayMessageAction *message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4A", false);
|
|
AIRule *rule50 = new AIRule(condition50, message);
|
|
|
|
AIEnergyMonitorCondition *condition25 = new AIEnergyMonitorCondition(kNervousEnergy);
|
|
AICompoundAction *compound = new AICompoundAction();
|
|
message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4B", false);
|
|
compound->addAction(message);
|
|
AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule50);
|
|
compound->addAction(deactivate);
|
|
AIRule *rule25 = new AIRule(condition25, compound);
|
|
|
|
AIEnergyMonitorCondition *condition5 = new AIEnergyMonitorCondition(kPanicStrickenEnergy);
|
|
compound = new AICompoundAction();
|
|
message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4C", false);
|
|
compound->addAction(message);
|
|
deactivate = new AIDeactivateRuleAction(rule50);
|
|
compound->addAction(deactivate);
|
|
deactivate = new AIDeactivateRuleAction(rule25);
|
|
compound->addAction(deactivate);
|
|
AIRule *rule5 = new AIRule(condition5, compound);
|
|
|
|
g_AIArea->addAIRule(rule5);
|
|
g_AIArea->addAIRule(rule25);
|
|
g_AIArea->addAIRule(rule50);
|
|
}
|
|
}
|
|
}
|
|
|
|
GameInteraction *Neighborhood::makeInteraction(const InteractionID interactionID) {
|
|
if (interactionID == kNoInteractionID)
|
|
return 0;
|
|
|
|
return new GameInteraction(interactionID, this);
|
|
}
|
|
|
|
void Neighborhood::newInteraction(const InteractionID interactionID) {
|
|
GameInteraction *interaction = makeInteraction(interactionID);
|
|
_doneWithInteraction = false;
|
|
|
|
if (_currentInteraction) {
|
|
_currentInteraction->stopInteraction();
|
|
delete _currentInteraction;
|
|
}
|
|
|
|
_currentInteraction = interaction;
|
|
|
|
if (_currentInteraction)
|
|
_currentInteraction->startInteraction();
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->checkMiddleArea();
|
|
}
|
|
|
|
void Neighborhood::bumpIntoWall() {
|
|
_vm->_gfx->shakeTheWorld(15, 30);
|
|
}
|
|
|
|
void Neighborhood::zoomUpOrBump() {
|
|
Hotspot *zoomSpot = 0;
|
|
|
|
for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) {
|
|
Hotspot *hotspot = *it;
|
|
|
|
if ((hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomInSpotFlag)) == (kNeighborhoodSpotFlag | kZoomInSpotFlag)) {
|
|
HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID());
|
|
|
|
if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) {
|
|
if (zoomSpot) {
|
|
zoomSpot = 0;
|
|
break;
|
|
} else {
|
|
zoomSpot = hotspot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (zoomSpot)
|
|
zoomTo(zoomSpot);
|
|
else
|
|
bumpIntoWall();
|
|
}
|
|
|
|
void Neighborhood::loadLoopSound1(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) {
|
|
FaderMoveSpec faderMove;
|
|
|
|
if (!loop1Loaded(soundName)) {
|
|
_loop1SoundString = soundName;
|
|
|
|
if (_soundLoop1.isSoundLoaded()) {
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeOut, 0);
|
|
_loop1Fader.startFaderSync(faderMove);
|
|
}
|
|
|
|
if (!_loop1SoundString.empty()) {
|
|
_soundLoop1.initFromAIFFFile(_loop1SoundString);
|
|
_soundLoop1.loopSound();
|
|
_loop1Fader.setMasterVolume(_vm->getAmbienceLevel());
|
|
_loop1Fader.setFaderValue(0);
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume);
|
|
_loop1Fader.startFaderSync(faderMove);
|
|
} else {
|
|
_soundLoop1.disposeSound();
|
|
}
|
|
} else if (_loop1Fader.getFaderValue() != volume) {
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeIn, volume);
|
|
_loop1Fader.startFaderSync(faderMove);
|
|
}
|
|
}
|
|
|
|
void Neighborhood::loadLoopSound2(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) {
|
|
FaderMoveSpec faderMove;
|
|
|
|
if (!loop2Loaded(soundName)) {
|
|
_loop2SoundString = soundName;
|
|
|
|
if (_soundLoop2.isSoundLoaded()) {
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeOut, 0);
|
|
_loop2Fader.startFaderSync(faderMove);
|
|
}
|
|
|
|
if (!_loop2SoundString.empty()) {
|
|
_soundLoop2.initFromAIFFFile(_loop2SoundString);
|
|
_soundLoop2.loopSound();
|
|
// HACK: Some ambient loops are actually sound effects, like Ares waiting at
|
|
// the reactor and Poseidon at the launch console. Detect these and use the
|
|
// SFX volume instead of ambience.
|
|
if (soundName == "Sounds/Mars/Robot Loop.aiff" ||
|
|
soundName == "Sounds/Norad/Breathing Typing.22K.AIFF" ||
|
|
soundName == "Sounds/Norad/N54NAS.32K.AIFF")
|
|
_loop2Fader.setMasterVolume(_vm->getSoundFXLevel());
|
|
else
|
|
_loop2Fader.setMasterVolume(_vm->getAmbienceLevel());
|
|
_loop2Fader.setFaderValue(0);
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume);
|
|
_loop2Fader.startFaderSync(faderMove);
|
|
} else {
|
|
_soundLoop2.disposeSound();
|
|
}
|
|
} else if (_loop2Fader.getFaderValue() != volume) {
|
|
faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeIn, volume);
|
|
_loop2Fader.startFaderSync(faderMove);
|
|
}
|
|
}
|
|
|
|
void Neighborhood::takeItemFromRoom(Item *item) {
|
|
item->setItemRoom(kNoNeighborhoodID, kNoRoomID, kNoDirection);
|
|
// Also set the taken item flag. Do this before updating the view frame.
|
|
GameState.setTakenItem(item, true);
|
|
updateViewFrame();
|
|
}
|
|
|
|
void Neighborhood::dropItemIntoRoom(Item *item, Hotspot *) {
|
|
item->setItemRoom(getObjectID(), GameState.getCurrentRoom(), GameState.getCurrentDirection());
|
|
// Also set the taken item flag. Do this before updating the view frame.
|
|
GameState.setTakenItem(item, false);
|
|
updateViewFrame();
|
|
}
|
|
|
|
void Neighborhood::makeContinuePoint() {
|
|
_vm->makeContinuePoint();
|
|
}
|
|
|
|
void Neighborhood::startLoop1Fader(const FaderMoveSpec &faderMove) {
|
|
_loop1Fader.startFader(faderMove);
|
|
}
|
|
|
|
void Neighborhood::startLoop2Fader(const FaderMoveSpec &faderMove) {
|
|
_loop2Fader.startFader(faderMove);
|
|
}
|
|
|
|
// *** Revised 6/13/96 to use the last frame of the extra sequence.
|
|
// Necessary for Cinepak buildup.
|
|
void Neighborhood::showExtraView(uint32 extraID) {
|
|
ExtraTable::Entry entry;
|
|
getExtraEntry(extraID, entry);
|
|
|
|
if (entry.movieEnd != 0xffffffff)
|
|
showViewFrame(entry.movieEnd - 1);
|
|
}
|
|
|
|
void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32 lastExtra, NotificationFlags flags,
|
|
const InputBits interruptionFilter) {
|
|
ExtraTable::Entry firstEntry, lastEntry;
|
|
getExtraEntry(firstExtra, firstEntry);
|
|
|
|
if (firstEntry.movieStart != 0xffffffff) {
|
|
getExtraEntry(lastExtra, lastEntry);
|
|
_lastExtra = firstExtra;
|
|
_turnPush.hide();
|
|
startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter);
|
|
}
|
|
}
|
|
|
|
void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) {
|
|
if (_croppedMovie.isMovieValid())
|
|
closeCroppedMovie();
|
|
|
|
_croppedMovie.initFromMovieFile(movieName);
|
|
_croppedMovie.moveElementTo(left, top);
|
|
_croppedMovie.startDisplaying();
|
|
_croppedMovie.show();
|
|
}
|
|
|
|
void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) {
|
|
openCroppedMovie(movieName, left, top);
|
|
_croppedMovie.redrawMovieWorld();
|
|
_croppedMovie.setFlags(kLoopTimeBase);
|
|
_croppedMovie.start();
|
|
}
|
|
|
|
void Neighborhood::closeCroppedMovie() {
|
|
_croppedMovie.releaseMovie();
|
|
}
|
|
|
|
void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, CoordType left, CoordType top, const InputBits interruptionFilter) {
|
|
openCroppedMovie(movieName, left, top);
|
|
_croppedMovie.setVolume(_vm->getSoundFXLevel());
|
|
_croppedMovie.redrawMovieWorld();
|
|
_croppedMovie.start();
|
|
|
|
InputBits oldInterruptionFilter = _interruptionFilter;
|
|
if (oldInterruptionFilter != kFilterNoInput)
|
|
_interruptionFilter = kFilterNoInput;
|
|
|
|
bool saveAllowed = _vm->swapSaveAllowed(false);
|
|
bool openAllowed = _vm->swapLoadAllowed(false);
|
|
|
|
Input input;
|
|
while (_croppedMovie.isRunning() && !_vm->shouldQuit()) {
|
|
_vm->processShell();
|
|
InputDevice.getInput(input, interruptionFilter);
|
|
if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested() || _vm->shouldQuit())
|
|
break;
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
if (oldInterruptionFilter != kFilterNoInput)
|
|
_interruptionFilter = oldInterruptionFilter;
|
|
|
|
closeCroppedMovie();
|
|
_vm->swapSaveAllowed(saveAllowed);
|
|
_vm->swapLoadAllowed(openAllowed);
|
|
}
|
|
|
|
void Neighborhood::playMovieSegment(Movie *movie, TimeValue startTime, TimeValue stopTime) {
|
|
TimeValue oldStart, oldStop;
|
|
movie->getSegment(oldStart, oldStop);
|
|
|
|
if (stopTime == 0xffffffff)
|
|
stopTime = movie->getDuration();
|
|
|
|
movie->setSegment(startTime, stopTime);
|
|
movie->setTime(startTime);
|
|
movie->start();
|
|
|
|
while (movie->isRunning()) {
|
|
InputDevice.pumpEvents();
|
|
_vm->checkCallBacks();
|
|
_vm->refreshDisplay();
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
|
|
movie->stop();
|
|
movie->setSegment(oldStart, oldStop);
|
|
}
|
|
|
|
void Neighborhood::recallToTSASuccess() {
|
|
if (GameState.allTimeZonesFinished())
|
|
_vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth);
|
|
else
|
|
_vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth);
|
|
}
|
|
|
|
void Neighborhood::recallToTSAFailure() {
|
|
_vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth);
|
|
}
|
|
|
|
void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) {
|
|
if (_vm->getGameMode() == kModeNavigation) {
|
|
if (input.upButtonAnyDown())
|
|
upButton(input);
|
|
else if (input.downButtonAnyDown())
|
|
downButton(input);
|
|
else if (input.leftButtonAnyDown())
|
|
leftButton(input);
|
|
else if (input.rightButtonAnyDown())
|
|
rightButton(input);
|
|
}
|
|
|
|
InputHandler::handleInput(input, cursorSpot);
|
|
}
|
|
|
|
void Neighborhood::setHotspotFlags(const HotSpotID id, const HotSpotFlags flags) {
|
|
Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(id);
|
|
hotspot->setMaskedHotspotFlags(flags, flags);
|
|
}
|
|
|
|
void Neighborhood::setIsItemTaken(const ItemID id) {
|
|
GameState.setTakenItemID(id, _vm->playerHasItemID(id));
|
|
}
|
|
|
|
void Neighborhood::upButton(const Input &) {
|
|
moveForward();
|
|
}
|
|
|
|
void Neighborhood::leftButton(const Input &) {
|
|
turnLeft();
|
|
}
|
|
|
|
void Neighborhood::rightButton(const Input &) {
|
|
turnRight();
|
|
}
|
|
|
|
void Neighborhood::downButton(const Input &) {
|
|
if (_inputHandler->wantsCursor()) {
|
|
_vm->getAllHotspots().deactivateAllHotspots();
|
|
_inputHandler->activateHotspots();
|
|
|
|
for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) {
|
|
Hotspot *hotspot = *it;
|
|
|
|
if (hotspot->isSpotActive() && (hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) == (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) {
|
|
HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID());
|
|
|
|
if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) {
|
|
Input scratch;
|
|
_inputHandler->clickInHotspot(scratch, hotspot);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Neighborhood::initOnePicture(Picture *picture, const Common::String &pictureName, DisplayOrder order, CoordType left, CoordType top, bool show) {
|
|
picture->initFromPICTFile(pictureName);
|
|
picture->setDisplayOrder(order);
|
|
picture->moveElementTo(left, top);
|
|
picture->startDisplaying();
|
|
if (show)
|
|
picture->show();
|
|
}
|
|
|
|
void Neighborhood::initOneMovie(Movie *movie, const Common::String &movieName, DisplayOrder order, CoordType left, CoordType top, bool show) {
|
|
movie->initFromMovieFile(movieName);
|
|
movie->setDisplayOrder(order);
|
|
movie->moveElementTo(left, top);
|
|
movie->startDisplaying();
|
|
|
|
if (show)
|
|
movie->show();
|
|
|
|
movie->redrawMovieWorld();
|
|
}
|
|
|
|
void Neighborhood::reinstateMonocleInterface() {
|
|
_vm->_gfx->disableErase();
|
|
|
|
_vm->createInterface();
|
|
|
|
if (g_AIArea)
|
|
setNextHandler(g_AIArea);
|
|
|
|
init();
|
|
|
|
moveNavTo(kNavAreaLeft, kNavAreaTop);
|
|
|
|
if (g_interface)
|
|
g_interface->setDate(getDateResID());
|
|
|
|
if (g_AIArea)
|
|
g_AIArea->restoreAIState();
|
|
}
|
|
|
|
void Neighborhood::useIdleTime() {
|
|
if (_doneWithInteraction) {
|
|
newInteraction(kNoInteractionID);
|
|
loadAmbientLoops();
|
|
}
|
|
}
|
|
|
|
void Neighborhood::timerFunction() {
|
|
timerExpired(getTimerEvent());
|
|
}
|
|
|
|
void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) {
|
|
_eventTimer.stopFuse();
|
|
_eventTimer.primeFuse(time, scale);
|
|
_timerEvent = eventType;
|
|
_eventTimer.setFunctor(new Common::Functor0Mem<void, Neighborhood>(this, &Neighborhood::timerFunction));
|
|
_eventTimer.lightFuse();
|
|
}
|
|
|
|
void Neighborhood::cancelEvent() {
|
|
_eventTimer.stopFuse();
|
|
}
|
|
|
|
void Neighborhood::pauseTimer() {
|
|
_eventTimer.pauseFuse();
|
|
}
|
|
|
|
void Neighborhood::resumeTimer() {
|
|
// NOTE: The original calls pauseFuse() here, which causes a bug with the robot
|
|
// in WSC on the catwalk, causing him never to come after you if you don't act
|
|
// against him.
|
|
_eventTimer.resumeFuse();
|
|
}
|
|
|
|
bool Neighborhood::timerPaused() {
|
|
return _eventTimer.isFusePaused();
|
|
}
|
|
|
|
} // End of namespace Pegasus
|