diff options
123 files changed, 31984 insertions, 0 deletions
diff --git a/base/plugins.cpp b/base/plugins.cpp index 3e18e0ec4e..acb6080944 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -145,6 +145,9 @@ public: #if PLUGIN_ENABLED_STATIC(PARALLACTION) LINK_PLUGIN(PARALLACTION) #endif + #if PLUGIN_ENABLED_STATIC(PEGASUS) + LINK_PLUGIN(PEGASUS) + #endif #if PLUGIN_ENABLED_STATIC(QUEEN) LINK_PLUGIN(QUEEN) #endif diff --git a/common/rational.h b/common/rational.h index 45aa6a7a20..8270d2194e 100644 --- a/common/rational.h +++ b/common/rational.h @@ -80,6 +80,9 @@ public: double toDouble() const; frac_t toFrac() const; + int getNumerator() const { return _num; } + int getDenominator() const { return _denom; } + void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const; private: diff --git a/common/rect.h b/common/rect.h index 768d1ebbb9..1106ec1714 100644 --- a/common/rect.h +++ b/common/rect.h @@ -170,6 +170,20 @@ struct Rect { } /** + * Find the intersecting rectangle between this rectangle and the given rectangle + * + * @param r the intersecting rectangle + * + * @return the intersection of the rectangles or an empty rectangle if not intersecting + */ + Rect findIntersectingRect(const Rect &r) const { + if (!intersects(r)) + return Rect(); + + return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom)); + } + + /** * Extend this rectangle so that it contains r * * @param r the rectangle to extend by @@ -105,6 +105,7 @@ add_engine cstime "Where in Time is Carmen Sandiego?" no add_engine riven "Riven: The Sequel to Myst" no add_engine myst "Myst" no add_engine parallaction "Parallaction" yes +add_engine pegasus "The Journeyman Project: Pegasus Prime" no add_engine queen "Flight of the Amazon Queen" yes add_engine saga "SAGA" yes "ihnm saga2" add_engine ihnm "IHNM" yes diff --git a/engines/engines.mk b/engines/engines.mk index eddd22039d..bf0c24c57d 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -131,6 +131,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION) MODULES += engines/parallaction endif +ifdef ENABLE_PEGASUS +DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS) +MODULES += engines/pegasus +endif + ifdef ENABLE_QUEEN DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN) MODULES += engines/queen diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp new file mode 100755 index 0000000000..36367eb1d6 --- /dev/null +++ b/engines/pegasus/ai/ai_action.cpp @@ -0,0 +1,78 @@ +/* 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/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" + +namespace Pegasus { + +AICompoundAction::~AICompoundAction() { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + delete *it; +} + +void AICompoundAction::performAIAction(AIRule *rule) { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + (*it)->performAIAction(rule); +} + +AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const tInputBits interruptionFilter) { + _movieName = movieName; + _keepLastFrame = keepLastFrame; + _interruptionFilter = interruptionFilter; +} + +void AIPlayMessageAction::performAIAction(AIRule *) { + if (g_AIArea) { + g_AIArea->checkMiddleArea(); + g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter); + } +} + +AIStartTimerAction::AIStartTimerAction(AITimerCondition *timerCondition) { + _timerCondition = timerCondition; +} + +void AIStartTimerAction::performAIAction(AIRule *) { + _timerCondition->startTimer(); +} + +AIActivateRuleAction::AIActivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIActivateRuleAction::performAIAction(AIRule *) { + _rule->activateRule(); +} + +AIDeactivateRuleAction::AIDeactivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIDeactivateRuleAction::performAIAction(AIRule *) { + _rule->deactivateRule(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_action.h b/engines/pegasus/ai/ai_action.h new file mode 100755 index 0000000000..09a1d13699 --- /dev/null +++ b/engines/pegasus/ai/ai_action.h @@ -0,0 +1,136 @@ +/* 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_AI_AIACTION_H +#define PEGASUS_AI_AIACTION_H + +#include "common/list.h" + +#include "pegasus/input.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class AIRule; +class AITimerCondition; + +///////////////////////////////////////////// +// +// AIAction + +class AIAction { +friend class AIRule; +public: + AIAction() { _actionCount = 1; } + virtual ~AIAction() {} + + virtual void performAIAction(AIRule *) = 0; + + void setActionCount(const uint32 count) { _actionCount = count; } + +protected: + uint32 _actionCount; +}; + +typedef Common::List<AIAction *> AIActionList; + +///////////////////////////////////////////// +// +// AICompoundAction + +class AICompoundAction : public AIAction { +public: + AICompoundAction() {} + virtual ~AICompoundAction(); + + void addAction(AIAction *action) { _compoundActions.push_back(action); } + + virtual void performAIAction(AIRule *); + +protected: + AIActionList _compoundActions; +}; + +///////////////////////////////////////////// +// +// AIPlayMessageAction + +class AIPlayMessageAction : public AIAction { +public: + AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const tInputBits = kWarningInterruption); + + virtual void performAIAction(AIRule *); + +protected: + Common::String _movieName; + tInputBits _interruptionFilter; + bool _keepLastFrame; +}; + +///////////////////////////////////////////// +// +// AIStartTimerAction + +class AIStartTimerAction : public AIAction { +public: + AIStartTimerAction(AITimerCondition *); + + virtual void performAIAction(AIRule *); + +protected: + AITimerCondition *_timerCondition; +}; + +///////////////////////////////////////////// +// +// AIActivateRuleAction + +class AIActivateRuleAction : public AIAction { +public: + AIActivateRuleAction(AIRule *); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +///////////////////////////////////////////// +// +// AIDeactivateRuleAction + +class AIDeactivateRuleAction : public AIAction { +public: + AIDeactivateRuleAction(AIRule *rule); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp new file mode 100755 index 0000000000..9f297e3df6 --- /dev/null +++ b/engines/pegasus/ai/ai_area.cpp @@ -0,0 +1,599 @@ +/* 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/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIArea *g_AIArea = 0; + +AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID), + _middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) { + g_AIArea = this; + _leftAreaOwner = kNoClientSignature; + _middleAreaOwner = kNoClientSignature; + _rightAreaOwner = kNoClientSignature; + _leftInventoryTime = 0xffffffff; + _middleInventoryTime = 0xffffffff; + _middleBiochipTime = 0xffffffff; + _rightBiochipTime = 0xffffffff; + _lockCount = 0; + startIdling(); +} + +AIArea::~AIArea() { + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + + stopIdling(); + + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + g_AIArea = 0; +} + +// Save last state of AI rules... +void AIArea::saveAIState() { + // TODO +} + +void AIArea::restoreAIState() { + // TODO +} + +void AIArea::writeAIRules(Common::WriteStream *stream) { + _AIRules.writeAIRules(stream); +} + +void AIArea::readAIRules(Common::ReadStream *stream) { + _AIRules.readAIRules(stream); +} + +void AIArea::initAIArea() { + allocateSurface(Common::Rect(0, 0, 384, 96)); + + _leftAreaMovie.shareSurface(this); + _leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie"); + _leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.setDisplayOrder(kAILeftAreaOrder); + _leftAreaMovie.startDisplaying(); + _leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _middleAreaMovie.shareSurface(this); + _middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie"); + _middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop); + _middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0); + _middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder); + _middleAreaMovie.startDisplaying(); + _middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _rightAreaMovie.shareSurface(this); + _rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie"); + _rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.setDisplayOrder(kAIRightAreaOrder); + _rightAreaMovie.startDisplaying(); + _rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _AIMovie.setDisplayOrder(kAIMovieOrder); +} + +void AIArea::setAIVolume(const uint16 volume) { + _leftAreaMovie.setVolume(volume); + _middleAreaMovie.setVolume(volume); + _rightAreaMovie.setVolume(volume); +} + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kInventorySignature kLeftAreaSignature +// kInventorySignature kMiddleAreaSignature +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kAISignature kLeftAreaSignature +// Further, the kAISignature never sets a static frame time in the left area, +// but only plays a sequence. + +// If this function is called while a sequence is playing, it will just "remember" +// the time value, so that when the sequence finishes, the new time is asserted. + +void AIArea::setAIAreaToTime(const tLowerClientSignature client, const tLowerAreaSignature area, const TimeValue time) { + switch (area) { + case kLeftAreaSignature: + // Only support kInventorySignature client, since AI never calls SetAIAreaToTime. + _leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _leftAreaMovie.hide(); + _leftAreaOwner = kNoClientSignature; + } else { + setLeftMovieTime(time); + } + break; + case kMiddleAreaSignature: + // Only support kInventorySignature and kBiochipSignature clients. + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + + if (time == 0xffffffff) { + if (client == kInventorySignature) { + if (_middleBiochipTime != 0xffffffff) { + setMiddleMovieTime(kBiochipSignature, _middleBiochipTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } else { // client == kBiochipSignature + if (_middleInventoryTime != 0xffffffff) { + setMiddleMovieTime(kInventorySignature, _middleInventoryTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } + } else { + setMiddleMovieTime(client, time); + } + break; + case kRightAreaSignature: + // Only support kBiochipSignature client. + _rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _rightAreaMovie.hide(); + _rightAreaOwner = kNoClientSignature; + } else { + setRightMovieTime(time); + } + break; + } +} + +// Plays a sequence on an area. When the sequence ends, the previous image +// is restored. +// Also, is input disabled or not? +// Easy answer: yes. + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kInventorySignature kMiddleAreaSignature + +void AIArea::playAIAreaSequence(const tLowerClientSignature, const tLowerAreaSignature area, const TimeValue start, const TimeValue stop) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + switch (area) { + case kLeftAreaSignature: + break; + case kMiddleAreaSignature: + if (_middleAreaOwner == kInventorySignature) + _middleInventoryTime = _middleAreaMovie.getTime(); + else if (_middleAreaOwner == kBiochipSignature) + _middleBiochipTime = _middleAreaMovie.getTime(); + + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + vm->_cursor->hide(); + + while (_middleAreaMovie.isRunning()) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _middleAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + + if (_middleAreaOwner == kInventorySignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime); + else if (_middleAreaOwner == kBiochipSignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime); + else + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff); + break; + case kRightAreaSignature: + _rightBiochipTime = _rightAreaMovie.getTime(); + _rightAreaMovie.setSegment(start, stop); + _rightAreaMovie.setTime(start); + _rightAreaMovie.show(); + _rightAreaMovie.start(); + vm->_cursor->hide(); + + while (_rightAreaMovie.isRunning()) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _rightAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); + break; + } + + unlockAI(); +} + +bool AIArea::playAIMovie(const tLowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const tInputBits interruptFilter) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + InputHandler::getCurrentInputDevice()->waitInput(interruptFilter); + if (_AIMovie.isMovieValid()) + _AIMovie.releaseMovie(); + + _AIMovie.shareSurface(this); + _AIMovie.initFromMovieFile(movieName); + + if (area == kLeftAreaSignature) { + _AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.hide(); + } else { + _AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.hide(); + } + + _AIMovie.setTime(0); + _AIMovie.startDisplaying(); + _AIMovie.show(); + _AIMovie.redrawMovieWorld(); + _AIMovie.setVolume(vm->getSoundFXLevel()); + _AIMovie.start(); + vm->_cursor->hide(); + + bool result = true; + bool saveAllowed = vm->swapSaveAllowed(false); + bool openAllowed = vm->swapLoadAllowed(false); + + while (_AIMovie.isRunning()) { + Input input; + InputHandler::getCurrentInputDevice()->getInput(input, interruptFilter); + + if (input.anyInput() || vm->shouldQuit()) { + result = false; + break; + } + + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _AIMovie.stop(); + + vm->swapSaveAllowed(saveAllowed); + vm->swapLoadAllowed(openAllowed); + + // This used to keep the last frame up even if the movie was interrupted. + // However, this only occurs in the recalibration, where interruption means skip the + // whole thing, so skipping causes the AI to go away even when keepLastFrame is true. + + if (!(result && keepLastFrame)) { + _AIMovie.stopDisplaying(); + _AIMovie.releaseMovie(); + + if (area == kLeftAreaSignature) { + _leftAreaMovie.setTime(_leftInventoryTime); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } else { + _rightAreaMovie.setTime(_rightBiochipTime); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + } + + vm->_cursor->hideUntilMoved(); + unlockAI(); + return result; +} + +// Only implemented for kMiddleAreaSignature, kInventorySignature +void AIArea::loopAIAreaSequence(const tLowerClientSignature owner, const tLowerAreaSignature area, const TimeValue start, const TimeValue stop) { + if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) { + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setFlags(kLoopTimeBase); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + } +} + +// Only called by kInventorySignature. +void AIArea::setLeftMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + _leftAreaMovie.setTime(time); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } + + _leftAreaOwner = kInventorySignature; + _leftInventoryTime = time; +} + +void AIArea::setMiddleMovieTime(const tLowerClientSignature client, const TimeValue time) { + if (client == kInventorySignature) { + _middleInventoryTime = time; + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } + } else { + _middleBiochipTime = time; + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + } + + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setTime(time); + _middleAreaMovie.show(); + _middleAreaMovie.redrawMovieWorld(); + _middleAreaOwner = client; +} + +// Only called by kBiochipSignature. +void AIArea::setRightMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + // Can't do it when the AI movie is up... + _rightAreaMovie.setTime(time); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + + _rightAreaOwner = kBiochipSignature; + _rightBiochipTime = time; +} + +void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (JMPPPInput::isToggleAIMiddleInput(input)) + toggleMiddleAreaOwner(); + else + InputHandler::handleInput(input, cursorSpot); +} + +void AIArea::toggleMiddleAreaOwner() { + if (_middleAreaOwner == kInventorySignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) { + setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime()); + currentBiochip->takeSharedArea(); + } + } else if (_middleAreaOwner == kBiochipSignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem) { + setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime()); + currentItem->takeSharedArea(); + } + } +} + +void AIArea::activateHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->activateAIHotspots(); + break; + case kPegasusBiochip: + if (!vm->isDemo()) + ((PegasusChip *)currentBiochip)->activatePegasusHotspots(); + break; + case kOpticalBiochip: + ((OpticalChip *)currentBiochip)->activateOpticalHotspots(); + break; + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->getObjectID() == kAirMask) + ((AirMask *)currentItem)->activateAirMaskHotspots(); + } + + InputHandler::activateHotspots(); +} + +void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + bool handled = false; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) { + ((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID()); + handled = true; + } + break; + case kPegasusBiochip: + if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) { + ((PegasusChip *)currentBiochip)->clickInPegasusHotspot(); + handled = true; + } + break; + case kOpticalBiochip: + if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) { + ((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID()); + handled = true; + } + break; + } + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (currentItem) { + switch (currentItem->getObjectID()) { + case kAirMask: + if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) { + ((AirMask *)currentItem)->clickInAirMaskHotspot(); + handled = true; + } + break; + } + } + } + + if (!handled) + InputHandler::clickInHotspot(input, hotspot); +} + +void AIArea::lockAIOut() { + if (_lockCount == 0) + stopIdling(); + + _lockCount++; +} + +void AIArea::unlockAI() { + if (_lockCount > 0) { + _lockCount--; + if (_lockCount == 0) + startIdling(); + } +} + +void AIArea::forceAIUnlocked() { + if (_lockCount > 0) { + _lockCount = 1; + unlockAI(); + } +} + +void AIArea::checkRules() { + if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive()) + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + if ((*it)->fireRule()) + break; +} + +void AIArea::useIdleTime() { + checkRules(); +} + +void AIArea::addAIRule(AIRule *rule) { + _AIRules.push_back(rule); +} + +void AIArea::removeAllRules() { + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + _AIRules.clear(); +} + +void AIArea::checkMiddleArea() { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + if (_middleAreaOwner == kBiochipSignature) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChip(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChip(); + break; + } + } else { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChipRude(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChipRude(); + break; + } + } + } +} + +TimeValue AIArea::getBigInfoTime() { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + return currentItem->getInfoLeftTime(); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + return currentBiochip->getInfoLeftTime(); + } + + return 0xffffffff; +} + +void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + currentItem->getInfoRightTimes(start, stop); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + currentBiochip->getInfoRightTimes(start, stop); + } else { + start = 0xffffffff; + stop = 0xffffffff; + } +} + +tLowerClientSignature AIArea::getMiddleAreaOwner() { + return _middleAreaOwner; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_area.h b/engines/pegasus/ai/ai_area.h new file mode 100755 index 0000000000..7387b4081b --- /dev/null +++ b/engines/pegasus/ai/ai_area.h @@ -0,0 +1,172 @@ +/* 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_AI_AIAREA_H +#define PEGASUS_AI_AIAREA_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_rule.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +/* + + The AI area is the area at the bottom of the screen. There are three areas within + the AI area: + 1) the inventory/AI help area + 2) the middle area + 3) the biochip display area + + Area 1 is used for displaying the current inventory item. When the player changes the + current item, either by selecting a new one in the inventory list or by picking + up a new item, area 1 updates to show the new item. + + If the AI decides to give a message, the AI's head temporarily takes over area 1 + for the duration of the message, then goes away, returning the area to the current + inventory item. + + Area 2 is used to display the current inventory item's state, the current biochip's + state, and any extra information from the AI. The contention for this area is + resolved as follows: + -- If the AI needs to use the area while giving a message in area 1, it takes over + area 2 for the duration of its message. +*** This is not true. + -- If the player selects a new inventory item, the inventory item's state gets + displayed immediately. + -- If the player selects a new biochip, the biochip's state info gets displayed + immediately. + -- If any auto drawing is to occur, it seizes the area as soon as the drawing is + to occur. For example, the mapping biochip does auto drawing every time the + player takes a step. The only exception to this rule is if the AI is presenting + a warning. When the AI seizes areas 1 and 2, it preempts all other uses. + Some inventory items and biochips can cause arbitrary drawing to occur in this area + at arbitrary times. The map biochip is one example which causes drawing when the + player takes a step. Another example is the poison gas canister, which flashes in + this area to indicate a dangerous compound. + + Area 3 is used to display the current biochip. When the player changes the current + biochip, either by selecting a new one from the biochip list or by picking up a + new one, area 3 updates to show the new item. In addition, some biochips can play + animation in this area. + +*/ + +namespace Pegasus { + +class AIArea : public Surface, public Idler, public InputHandler { +public: + AIArea(InputHandler *); + virtual ~AIArea(); + + void writeAIRules(Common::WriteStream *stream); + void readAIRules(Common::ReadStream *stream); + + void initAIArea(); + + void saveAIState(); + void restoreAIState(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + + void setAIVolume(const uint16); + + // There are only so many legal combinations of client/area. + // Here is the list of supported pairs: + // kInventorySignature kLeftAreaSignature + // kInventorySignature kMiddleAreaSignature + // kBiochipSignature kMiddleAreaSignature + // kBiochipSignature kRightAreaSignature + // kAISignature kLeftAreaSignature + // Further, the kAISignature never sets a static frame time in the left area, + // but only plays a sequence from the AI movie. + void setAIAreaToTime(const tLowerClientSignature, const tLowerAreaSignature, const TimeValue); + + // The "Play" functions play the requested sequence synchronously. + void playAIAreaSequence(const tLowerClientSignature, const tLowerAreaSignature, const TimeValue, const TimeValue); + + // For PlayAIMovie, it is assumed that the client is the AI itself. + // This is used to play AI messages as well as Optical Memory video. + // Returns true if the movie played all the way through, false if it was interrupted. + bool playAIMovie(const tLowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const tInputBits); + + // Loop the requested sequence indefinitely. + void loopAIAreaSequence(const tLowerClientSignature, const tLowerAreaSignature, const TimeValue, const TimeValue); + + void addAIRule(AIRule *); + + // Remove and delete all rules. + void removeAllRules(); + + void lockAIOut(); + void unlockAI(); + void forceAIUnlocked(); + + void checkMiddleArea(); + void checkRules(); + + tLowerClientSignature getMiddleAreaOwner(); + void toggleMiddleAreaOwner(); + + TimeValue getBigInfoTime(); + void getSmallInfoSegment(TimeValue &, TimeValue &); + +protected: + void useIdleTime(); + + void setLeftMovieTime(const TimeValue); + void setMiddleMovieTime(const tLowerClientSignature, const TimeValue); + void setRightMovieTime(const TimeValue); + + Movie _leftAreaMovie; + Movie _middleAreaMovie; + Movie _rightAreaMovie; + Movie _AIMovie; + + tLowerClientSignature _leftAreaOwner; + tLowerClientSignature _middleAreaOwner; + tLowerClientSignature _rightAreaOwner; + + TimeValue _leftInventoryTime; + TimeValue _middleInventoryTime; + TimeValue _middleBiochipTime; + TimeValue _rightBiochipTime; + + AIRuleList _AIRules; + + uint _lockCount; +}; + +extern AIArea *g_AIArea; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_condition.cpp b/engines/pegasus/ai/ai_condition.cpp new file mode 100755 index 0000000000..8b77d723c6 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.cpp @@ -0,0 +1,290 @@ +/* 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/stream.h" + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIOneChildCondition::AIOneChildCondition(AICondition *child) { + _child = child; +} + +AIOneChildCondition::~AIOneChildCondition() { + delete _child; +} + +void AIOneChildCondition::writeAICondition(Common::WriteStream *stream) { + if (_child) + _child->writeAICondition(stream); +} + +void AIOneChildCondition::readAICondition(Common::ReadStream *stream) { + if (_child) + _child->readAICondition(stream); +} + +AITwoChildrenCondition::AITwoChildrenCondition(AICondition *leftChild, AICondition *rightChild) { + _leftChild = leftChild; + _rightChild = rightChild; +} + +AITwoChildrenCondition::~AITwoChildrenCondition() { + delete _leftChild; + delete _rightChild; +} + +void AITwoChildrenCondition::writeAICondition(Common::WriteStream *stream) { + if (_leftChild) + _leftChild->writeAICondition(stream); + + if (_rightChild) + _rightChild->writeAICondition(stream); +} + +void AITwoChildrenCondition::readAICondition(Common::ReadStream *stream) { + if (_leftChild) + _leftChild->readAICondition(stream); + + if (_rightChild) + _rightChild->readAICondition(stream); +} + +AINotCondition::AINotCondition(AICondition* child) : AIOneChildCondition(child) { +} + +bool AINotCondition::fireCondition() { + return _child && !_child->fireCondition(); +} + +AIAndCondition::AIAndCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIAndCondition::fireCondition() { + return _leftChild && _leftChild->fireCondition() && _rightChild && _rightChild->fireCondition(); +} + +AIOrCondition::AIOrCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIOrCondition::fireCondition() { + return (_leftChild && _leftChild->fireCondition()) || (_rightChild && _rightChild->fireCondition()); +} + +AITimerCondition::AITimerCondition(const TimeValue time, const TimeScale scale, const bool shouldStartTimer) { + _timerFuse.primeFuse(time, scale); + _timerFuse.setFunctionPtr((tFunctionPtr)&AITimerFunction, (void *)this); + _fired = false; + + if (shouldStartTimer) + startTimer(); +} + +void AITimerCondition::startTimer() { + _fired = false; + _timerFuse.lightFuse(); +} + +void AITimerCondition::stopTimer() { + _timerFuse.stopFuse(); +} + +void AITimerCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeByte(_timerFuse.isFuseLit()); + stream->writeByte(_fired); + stream->writeUint32BE(_timerFuse.getTimeRemaining()); + stream->writeUint32BE(_timerFuse.getFuseScale()); +} + +void AITimerCondition::readAICondition(Common::ReadStream *stream) { + bool running = stream->readByte(); + _fired = stream->readByte(); + TimeValue time = stream->readUint32BE(); + TimeScale scale = stream->readUint32BE(); + + _timerFuse.stopFuse(); + _timerFuse.primeFuse(time, scale); + + if (running) + _timerFuse.lightFuse(); +} + +void AITimerCondition::AITimerFunction(FunctionPtr *, AITimerCondition *condition) { + condition->_fired = true; +} + +bool AITimerCondition::fireCondition() { + return _fired; +} + +AILocationCondition::AILocationCondition(uint32 maxLocations) { + _numLocations = 0; + _maxLocations = maxLocations; + _locations = new tRoomViewID[maxLocations]; +} + +AILocationCondition::~AILocationCondition() { + delete[] _locations; +} + +void AILocationCondition::addLocation(const tRoomViewID location) { + if (_numLocations < _maxLocations) + _locations[_numLocations++] = location; +} + +bool AILocationCondition::fireCondition() { + tRoomViewID test = GameState.getCurrentRoomAndView(), *p; + uint32 i; + + for (i = 0, p = _locations; i < _numLocations; i++, p++) { + if (test == *p) { + *p = MakeRoomView(kNoRoomID, kNoDirection); + return true; + } + } + + return false; +} + +void AILocationCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeUint32BE(_maxLocations); + stream->writeUint32BE(_numLocations); + + uint32 i; + tRoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + stream->writeUint32BE(*p); +} + +void AILocationCondition::readAICondition(Common::ReadStream *stream) { + uint32 maxLocations = stream->readUint32BE(); + + if (_maxLocations != maxLocations) { + delete[] _locations; + _locations = new tRoomViewID[maxLocations]; + _maxLocations = maxLocations; + } + + _numLocations = stream->readUint32BE(); + + uint32 i; + tRoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + *p = stream->readUint32BE(); +} + +AIDoorOpenedCondition::AIDoorOpenedCondition(tRoomViewID doorLocation) { + _doorLocation = doorLocation; +} + +bool AIDoorOpenedCondition::fireCondition() { + return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen(); +} + +AIHasItemCondition::AIHasItemCondition(const tItemID item) { + _item = item; +} + +bool AIHasItemCondition::fireCondition() { + return _item == kNoItemID || GameState.isTakenItemID(_item); +} + +AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const tItemID item) { + _item = item; +} + +bool AIDoesntHaveItemCondition::fireCondition() { + return _item == kNoItemID || !GameState.isTakenItemID(_item); +} + +AICurrentItemCondition::AICurrentItemCondition(const tItemID item) { + _item = item; +} + +bool AICurrentItemCondition::fireCondition() { + InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (_item == kNoItemID) + return item == 0; + + return item != 0 && item->getObjectID() == _item; +} + +AICurrentBiochipCondition::AICurrentBiochipCondition(const tItemID biochip) { + _biochip = biochip; +} + +bool AICurrentBiochipCondition::fireCondition() { + BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (_biochip == kNoItemID) + return biochip == 0; + + return biochip != 0 && biochip->getObjectID() == _biochip; +} + +AIItemStateCondition::AIItemStateCondition(const tItemID item, const tItemState state) { + _item = item; + _state = state; +} + +bool AIItemStateCondition::fireCondition() { + Item *item = g_allItems.findItemByID(_item); + return item != 0 && item->getItemState() == _state; +} + +AIEnergyMonitorCondition::AIEnergyMonitorCondition(const int32 energyThreshold) { + _energyThreshold = energyThreshold; +} + +bool AIEnergyMonitorCondition::fireCondition() { + return g_energyMonitor != 0 && g_energyMonitor->getCurrentEnergy() < _energyThreshold; +} + +AILastExtraCondition::AILastExtraCondition(const tExtraID lastExtra) { + _lastExtra = lastExtra; +} + +bool AILastExtraCondition::fireCondition() { + return g_neighborhood && (tExtraID)g_neighborhood->getLastExtra() == _lastExtra; +} + +AICondition *makeLocationAndDoesntHaveItemCondition(const tRoomID room, const tDirectionConstant direction, const tItemID item) { + AILocationCondition *location = new AILocationCondition(1); + location->addLocation(MakeRoomView(room, direction)); + + AIDoesntHaveItemCondition *doesntHaveItem = new AIDoesntHaveItemCondition(item); + + return new AIAndCondition(location, doesntHaveItem); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_condition.h b/engines/pegasus/ai/ai_condition.h new file mode 100755 index 0000000000..559ec55c80 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.h @@ -0,0 +1,287 @@ +/* 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_AI_AICONDITION_H +#define PEGASUS_AI_AICONDITION_H + +#include "pegasus/timers.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +///////////////////////////////////////////// +// +// AICondition + +class AICondition { +public: + AICondition() {} + virtual ~AICondition() {} + + virtual bool fireCondition() = 0; + + // Only need these for conditions that are dynamic, like timer conditions... + // other conditions, like item related conditions, which don't change during + // the run of an environment, are completely initted when the environment + // is created. + virtual void writeAICondition(Common::WriteStream *) {} + virtual void readAICondition(Common::ReadStream *) {} +}; + +///////////////////////////////////////////// +// +// AIOneChildCondition + +class AIOneChildCondition : public AICondition { +public: + AIOneChildCondition(AICondition *); + virtual ~AIOneChildCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_child; +}; + +///////////////////////////////////////////// +// +// AITwoChildrenCondition + +class AITwoChildrenCondition : public AICondition { +public: + AITwoChildrenCondition(AICondition *, AICondition *); + virtual ~AITwoChildrenCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_leftChild, *_rightChild; +}; + +///////////////////////////////////////////// +// +// AINotCondition + +class AINotCondition : public AIOneChildCondition { +public: + AINotCondition(AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIAndCondition + +class AIAndCondition : public AITwoChildrenCondition { +public: + AIAndCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIOrCondition + +class AIOrCondition : public AITwoChildrenCondition { +public: + AIOrCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AITimerCondition + +class AITimerCondition : public AICondition { +public: + AITimerCondition(const TimeValue, const TimeScale, const bool); + + void startTimer(); + void stopTimer(); + + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + static void AITimerFunction(FunctionPtr *, AITimerCondition *); + + FuseFunction _timerFuse; + bool _fired; +}; + +///////////////////////////////////////////// +// +// AILocationCondition + +class AILocationCondition : public AICondition { +public: + AILocationCondition(uint32); + virtual ~AILocationCondition(); + + void addLocation(tRoomViewID); + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + uint32 _numLocations, _maxLocations; + tRoomViewID *_locations; +}; + +///////////////////////////////////////////// +// +// AIDoorOpenedCondition + +class AIDoorOpenedCondition : public AICondition { +public: + AIDoorOpenedCondition(tRoomViewID); + virtual ~AIDoorOpenedCondition() {} + + virtual bool fireCondition(); + +protected: + tRoomViewID _doorLocation; +}; + +///////////////////////////////////////////// +// +// AIHasItemCondition + +class AIHasItemCondition : public AICondition { +public: + AIHasItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AIDoesntHaveItemCondition + +class AIDoesntHaveItemCondition : public AICondition { +public: + AIDoesntHaveItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentItemCondition + +class AICurrentItemCondition : public AICondition { +public: + AICurrentItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentBiochipCondition + +class AICurrentBiochipCondition : public AICondition { +public: + AICurrentBiochipCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _biochip; +}; + +///////////////////////////////////////////// +// +// AIItemStateCondition + +class AIItemStateCondition : public AICondition { +public: + AIItemStateCondition(const tItemID, const tItemState); + + virtual bool fireCondition(); + +protected: + tItemID _item; + tItemState _state; +}; + +///////////////////////////////////////////// +// +// AIEnergyMonitorCondition + +class AIEnergyMonitorCondition : public AICondition { +public: + AIEnergyMonitorCondition(const int32); + + virtual bool fireCondition(); + +protected: + int32 _energyThreshold; +}; + +///////////////////////////////////////////// +// +// AILastExtraCondition + +class AILastExtraCondition : public AICondition { +public: + AILastExtraCondition(const tExtraID); + + virtual bool fireCondition(); + +protected: + tExtraID _lastExtra; +}; + +///////////////////////////////////////////// +// +// Helper functions + +AICondition *makeLocationAndDoesntHaveItemCondition(const tRoomID room, const tDirectionConstant direction, const tItemID item); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_rule.cpp b/engines/pegasus/ai/ai_rule.cpp new file mode 100755 index 0000000000..3aaa530a4a --- /dev/null +++ b/engines/pegasus/ai/ai_rule.cpp @@ -0,0 +1,78 @@ +/* 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/stream.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" + +namespace Pegasus { + +bool AIRule::fireRule() { + if (_ruleActive && _ruleCondition && _ruleAction && _ruleCondition->fireCondition()) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + _ruleAction->performAIAction(this); + + if (--_ruleAction->_actionCount <= 0) + deactivateRule(); + + if (g_AIArea) + g_AIArea->unlockAI(); + + return true; + } + + return false; +} + +void AIRule::writeAIRule(Common::WriteStream *stream) { + stream->writeByte(_ruleActive); + + if (_ruleCondition) + _ruleCondition->writeAICondition(stream); +} + +void AIRule::readAIRule(Common::ReadStream *stream) { + _ruleActive = stream->readByte(); + + if (_ruleCondition) + _ruleCondition->readAICondition(stream); +} + +void AIRuleList::writeAIRules(Common::WriteStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->writeAIRule(stream); +} + +void AIRuleList::readAIRules(Common::ReadStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->readAIRule(stream); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_rule.h b/engines/pegasus/ai/ai_rule.h new file mode 100755 index 0000000000..bccd4ecb06 --- /dev/null +++ b/engines/pegasus/ai/ai_rule.h @@ -0,0 +1,86 @@ +/* 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_AI_AIRULE_H +#define PEGASUS_AI_AIRULE_H + +#include "common/list.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_condition.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class AICondition; +class AIAction; + +class AIRule { +public: + AIRule(AICondition *condition, AIAction *rule) { + _ruleCondition = condition; + _ruleAction = rule; + _ruleActive = true; + } + + ~AIRule() { + if (_ruleCondition) + delete _ruleCondition; + + if (_ruleAction) + delete _ruleAction; + } + + bool fireRule(); + + void activateRule() { _ruleActive = true; } + void deactivateRule() { _ruleActive = false; } + bool isRuleActive() { return _ruleActive; } + + void writeAIRule(Common::WriteStream *); + void readAIRule(Common::ReadStream *); + +protected: + AICondition *_ruleCondition; + AIAction *_ruleAction; + bool _ruleActive; +}; + +class AIRuleList : public Common::List<AIRule *> { +public: + AIRuleList() {} + ~AIRuleList() {} + + void writeAIRules(Common::WriteStream *); + void readAIRules(Common::ReadStream *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/compass.cpp b/engines/pegasus/compass.cpp new file mode 100755 index 0000000000..2c80c5c41d --- /dev/null +++ b/engines/pegasus/compass.cpp @@ -0,0 +1,82 @@ +/* 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/compass.h" + +namespace Pegasus { + +Compass *g_compass = 0; + +Compass::Compass() : FaderAnimation(kCompassID) { + // Initialize it to east... + setFaderValue(90); + g_compass = this; +} + +Compass::~Compass() { + g_compass = 0; +} + +void Compass::initCompass() { + if (!isCompassValid()) { + Common::Rect r; + _compassImage.initFromPICTFile("Images/Compass/Compass"); + _compassImage.getSurfaceBounds(r); + r.right = kCompassWidth; + setBounds(r); + } +} + +void Compass::deallocateCompass() { + _compassImage.deallocateSurface(); +} + +void Compass::setFaderValue(const int32 angle) { + int16 a = angle % 360; + + if (a < 0) + a += 360; + + FaderAnimation::setFaderValue(a); +} + +void Compass::draw(const Common::Rect &r1) { + if (_compassImage.isSurfaceValid()) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r2; + _compassImage.getSurfaceBounds(r2); + + tCoordType width = r2.width(); + tCoordType offsetH = width / 10 - bounds.width() / 2 + (getFaderValue() * width) / 450 - bounds.left; + tCoordType offsetV = -bounds.top; + r2 = r1; + r2.translate(offsetH, offsetV); + _compassImage.drawImage(r2, r1); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/compass.h b/engines/pegasus/compass.h new file mode 100755 index 0000000000..d66b432a38 --- /dev/null +++ b/engines/pegasus/compass.h @@ -0,0 +1,58 @@ +/* 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_COMPASS_H +#define PEGASUS_COMPASS_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +// Compass is defined with 0 as north, 90 east, 180 south, 270 west. +// Clockwise rotation increases the angle, counterclockwise rotation decreases the angle. + +class Compass : public FaderAnimation { +public: + Compass(); + virtual ~Compass(); + + void initCompass(); + void deallocateCompass(); + bool isCompassValid() const { return _compassImage.isSurfaceValid(); } + + void setFaderValue(const int32); + +protected: + void draw(const Common::Rect &); + + Frame _compassImage; +}; + +extern Compass *g_compass; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/console.cpp b/engines/pegasus/console.cpp new file mode 100644 index 0000000000..e6738bc83f --- /dev/null +++ b/engines/pegasus/console.cpp @@ -0,0 +1,104 @@ +/* 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. + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "pegasus/console.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +PegasusConsole::PegasusConsole(PegasusEngine *vm) : GUI::Debugger(), _vm(vm) { + DCmd_Register("die", WRAP_METHOD(PegasusConsole, Cmd_Die)); + + // These functions are non-demo specific + if (!_vm->isDemo()) + DCmd_Register("jump", WRAP_METHOD(PegasusConsole, Cmd_Jump)); +} + +PegasusConsole::~PegasusConsole() { +} + +bool PegasusConsole::Cmd_Die(int argc, const char **argv) { + if (argc == 1) { + DebugPrintf("Usage: die <death reason>\n"); + return true; + } + + int reason = atoi(argv[1]); + + bool invalidReason = (reason == 0 || reason > kPlayerWonGame); + + if (!invalidReason && _vm->isDemo()) + invalidReason = (reason != kDeathFallOffCliff) && (reason != kDeathEatenByDinosaur) && + (reason != kDeathStranded) && (reason != kPlayerWonGame); + + + if (invalidReason) { + DebugPrintf("Invalid death reason %d\n", reason); + return true; + } + + _vm->die(atoi(argv[1])); + return false; +} + +bool PegasusConsole::Cmd_Jump(int argc, const char **argv) { + if (!g_interface) { + // TODO + DebugPrintf("Cannot jump without interface set up\n"); + return true; + } + + // TODO: Default room/direction for each neighborhood + + if (argc < 4) { + DebugPrintf("Usage: jump <neighborhood> <room> <direction>\n"); + return true; + } + + tNeighborhoodID neighborhood = (tNeighborhoodID)atoi(argv[1]); + tRoomID room = (tRoomID)atoi(argv[2]); + tDirectionConstant direction = (tDirectionConstant)atoi(argv[3]); + + if (neighborhood < kCaldoriaID || neighborhood > kNoradDeltaID || neighborhood == kFinalTSAID) { + DebugPrintf("Invalid neighborhood %d", neighborhood); + return true; + } + + // No real way to check room validity at this point + + if (direction > kWest) { + DebugPrintf("Invalid direction %d", direction); + return true; + } + + // Here we go! + // TODO: Can't clear menu since the engine is paused + _vm->jumpToNewEnvironment(neighborhood, room, direction); + return false; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/console.h b/engines/pegasus/console.h new file mode 100644 index 0000000000..b73b70679d --- /dev/null +++ b/engines/pegasus/console.h @@ -0,0 +1,49 @@ +/* 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. + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef PEGASUS_CONSOLE_H +#define PEGASUS_CONSOLE_H + +#include "gui/debugger.h" + +namespace Pegasus { + +class PegasusEngine; + +class PegasusConsole : public GUI::Debugger { +public: + PegasusConsole(PegasusEngine *vm); + virtual ~PegasusConsole(); + +private: + bool Cmd_Die(int argc, const char **argv); + bool Cmd_Jump(int argc, const char **argv); + + PegasusEngine *_vm; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h new file mode 100755 index 0000000000..20a7f69bd4 --- /dev/null +++ b/engines/pegasus/constants.h @@ -0,0 +1,746 @@ +/* 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_CONSTANTS_H +#define PEGASUS_CONSTANTS_H + +#include "common/endian.h" +#include "common/rect.h" + +#include "pegasus/types.h" + +namespace Pegasus { + +// TODO: Organize these + +const tGameID kGameIDNothing = -1; + +const tActorID kNoActorID = kGameIDNothing; +const tActorID kPlayerID = 0; +const tItemID kNoItemID = kGameIDNothing; +const tRoomID kNoRoomID = kGameIDNothing; +const tExtraID kNoExtraID = 0xFFFFFFFF; +const tNeighborhoodID kNoNeighborhoodID = kGameIDNothing; +const tAlternateID kNoAlternateID = 0; +const tGameMenuCommand kMenuCmdNoCommand = 0; + +const tHotSpotActivationID kActivateHotSpotAlways = 0; +const tHotSpotActivationID kActivateHotSpotNever = -1; + +const tItemState kNoItemState = -1; + +const tDirectionConstant kNoDirection = 0xFF; + +const tTurnDirection kNoTurn = 0xFF; +const tTurnDirection kTurnLeft = 0; +const tTurnDirection kTurnRight = 1; +const tTurnDirection kTurnUp = 2; +const tTurnDirection kTurnDown = 3; +const tTurnDirection kMaxTurns = 4; + +const tGameMode kNoMode = -1; +const tGameMode kModeNavigation = 0; +const tGameMode kLastGameShellMode = kModeNavigation; + +const tCanMoveForwardReason kCanMoveForward = 0; +const tCanMoveForwardReason kCantMoveBlocked = kCanMoveForward + 1; +const tCanMoveForwardReason kCantMoveDoorClosed = kCantMoveBlocked + 1; +const tCanMoveForwardReason kCantMoveDoorLocked = kCantMoveDoorClosed + 1; +const tCanMoveForwardReason kCantMoveLastReason = kCantMoveDoorLocked; + +const tCanTurnReason kCanTurn = 0; +const tCanTurnReason kCantTurnNoTurn = kCanTurn + 1; +const tCanTurnReason kCantTurnLastReason = kCantTurnNoTurn; + +const tCanOpenDoorReason kCanOpenDoor = 0; +const tCanOpenDoorReason kCantOpenNoDoor = kCanOpenDoor + 1; +const tCanOpenDoorReason kCantOpenLocked = kCantOpenNoDoor + 1; +const tCanOpenDoorReason kCantOpenAlreadyOpen = kCantOpenLocked + 1; +const tCanOpenDoorReason kCantOpenLastReason = kCantOpenAlreadyOpen; + +const tDisplayElementID kNoDisplayElement = -1; +const tDisplayElementID kHighestReservedElementID = -2; + +const tDisplayElementID kCursorID = kHighestReservedElementID; +const tDisplayElementID kLoadScreenID = kCursorID - 1; + +const tDisplayOrder kMinAvailableOrder = 0; +const tDisplayOrder kMaxAvailableOrder = 999998; +const tDisplayOrder kLoadScreenOrder = 900000; +const tDisplayOrder kCursorOrder = 1000000; + +const tHotSpotID kNoHotSpotID = -1; +const tHotSpotFlags kNoHotSpotFlags = 0; +const tHotSpotFlags kAllHotSpotFlags = ~kNoHotSpotFlags; + +const tNotificationFlags kNoNotificationFlags = 0; + +const tDisplayElementID kCurrentDragSpriteID = 1000; + +// TODO +//const Fixed kFixed1 = 1 << 16; +//const Fixed kFixedMinus1 = -1 << 16; + +const TimeScale kDefaultTimeScale = 600; + +// TODO +//const RGBColor kWhiteRGB = {0xFFFF, 0xFFFF, 0xFFFF}; + +// Ticks per second. + +const TimeScale kOneTickPerSecond = 1; +const TimeScale kTwoTicksPerSecond = 2; +const TimeScale kFifteenTicksPerSecond = 15; +const TimeScale kThirtyTicksPerSecond = 30; +const TimeScale kSixtyTicksPerSecond = 60; +const TimeScale kMovieTicksPerSecond = 600; + +// These times are in seconds. + +const TimeValue kOneSecond = 1; +const TimeValue kTwoSeconds = 2; +const TimeValue kThreeSeconds = 3; +const TimeValue kFourSeconds = 4; +const TimeValue kFiveSeconds = 5; +const TimeValue kSixSeconds = 6; +const TimeValue kSevenSeconds = 7; +const TimeValue kEightSeconds = 8; +const TimeValue kNineSeconds = 9; +const TimeValue kTenSeconds = 10; +const TimeValue kElevenSeconds = 11; +const TimeValue kTwelveSeconds = 12; +const TimeValue kThirteenSeconds = 13; +const TimeValue kFourteenSeconds = 14; +const TimeValue kFifteenSeconds = 15; +const TimeValue kSixteenSeconds = 16; +const TimeValue kSeventeenSeconds = 17; +const TimeValue kEighteenSeconds = 18; +const TimeValue kNineteenSeconds = 19; +const TimeValue kTwentySeconds = 20; +const TimeValue kThirtySeconds = 30; +const TimeValue kFortySeconds = 40; +const TimeValue kFiftySeconds = 50; +const TimeValue kSixtySeconds = 60; +const TimeValue kOneMinute = 60; +const TimeValue kTwoMinutes = kOneMinute * 2; +const TimeValue kThreeMinutes = kOneMinute * 3; +const TimeValue kFourMinutes = kOneMinute * 4; +const TimeValue kFiveMinutes = kOneMinute * 5; +const TimeValue kSixMinutes = kOneMinute * 6; +const TimeValue kSevenMinutes = kOneMinute * 7; +const TimeValue kEightMinutes = kOneMinute * 8; +const TimeValue kNineMinutes = kOneMinute * 9; +const TimeValue kTenMinutes = kOneMinute * 10; +const TimeValue kElevenMinutes = kOneMinute * 11; +const TimeValue kTwelveMinutes = kOneMinute * 12; +const TimeValue kThirteenMinutes = kOneMinute * 13; +const TimeValue kFourteenMinutes = kOneMinute * 14; +const TimeValue kFifteenMinutes = kOneMinute * 15; +const TimeValue kSixteenMinutes = kOneMinute * 16; +const TimeValue kSeventeenMinutes = kOneMinute * 17; +const TimeValue kEighteenMinutes = kOneMinute * 18; +const TimeValue kNineteenMinutes = kOneMinute * 19; +const TimeValue kTwentyMinutes = kOneMinute * 20; +const TimeValue kThirtyMinutes = kOneMinute * 30; +const TimeValue kFortyMinutes = kOneMinute * 40; +const TimeValue kFiftyMinutes = kOneMinute * 50; +const TimeValue kOneHour = kOneMinute * 60; +const TimeValue kTwoHours = kOneHour * 2; + +// Common times. + +const TimeValue kHalfSecondPerTwoTicks = kTwoTicksPerSecond / 2; +const TimeValue kHalfSecondPerThirtyTicks = kThirtyTicksPerSecond / 2; +const TimeValue kHalfSecondPerSixtyTicks = kSixtyTicksPerSecond / 2; + +const TimeValue kOneSecondPerTwoTicks = kTwoTicksPerSecond; +const TimeValue kOneSecondPerThirtyTicks = kThirtyTicksPerSecond; +const TimeValue kOneSecondPerSixtyTicks = kSixtyTicksPerSecond; + +const TimeValue kOneMinutePerFifteenTicks = kOneMinute * kFifteenTicksPerSecond; +const TimeValue kFiveMinutesPerFifteenTicks = kFiveMinutes * kFifteenTicksPerSecond; +const TimeValue kTenMinutesPerFifteenTicks = kTenMinutes * kFifteenTicksPerSecond; + +const TimeValue kOneMinutePerThirtyTicks = kOneMinute * kThirtyTicksPerSecond; +const TimeValue kFiveMinutesPerThirtyTicks = kFiveMinutes * kThirtyTicksPerSecond; +const TimeValue kTenMinutesPerThirtyTicks = kTenMinutes * kThirtyTicksPerSecond; + +const TimeValue kOneMinutePerSixtyTicks = kOneMinute * kSixtyTicksPerSecond; +const TimeValue kFiveMinutesPerSixtyTicks = kFiveMinutes * kSixtyTicksPerSecond; +const TimeValue kTenMinutesPerSixtyTicks = kTenMinutes * kSixtyTicksPerSecond; + +// Time in seconds you can hang around Caldoria without going to work... +const TimeValue kLateWarning2TimeLimit = kFiveMinutes; +const TimeValue kLateWarning3TimeLimit = kTenMinutes; + +const TimeValue kSinclairShootsTimeLimit = kThreeMinutes; +const TimeValue kCardBombCountDownTime = kTwelveSeconds; + +const TimeValue kOxyMaskFullTime = kThirtyMinutes; + +const TimeValue kTSAUncreatedTimeLimit = kFiveMinutes; +const TimeValue kRipTimeLimit = kTenMinutesPerFifteenTicks; +const TimeScale kRipTimeScale = kFifteenTicksPerSecond; + +const TimeValue kIntroTimeOut = kThirtySeconds; + +const TimeValue kMarsRobotPatienceLimit = kFifteenSeconds; +const TimeValue kLockFreezeTimeLmit = kFifteenSeconds; +const TimeValue kSpaceChaseTimeLimit = kTenMinutes; +const TimeValue kVacuumSurvivalTimeLimit = kThirtySeconds; +const TimeValue kColorMatchingTimeLimit = kFourMinutes; +const TimeScale kJunkTimeScale = kFifteenTicksPerSecond; +const TimeValue kJunkDropBaseTime = kFiveSeconds; +const TimeValue kJunkDropSlopTime = kThreeSeconds; +const TimeValue kJunkTravelTime = kTenSeconds * kJunkTimeScale; +const TimeValue kCollisionReboundTime = kOneSecond * kJunkTimeScale; +const TimeValue kWeaponReboundTime = kTwoSeconds * kJunkTimeScale; + +const TimeValue kGawkAtRobotTime = kTenSeconds; +const TimeValue kGawkAtRobotTime2 = kThirteenSeconds; +const TimeValue kPlasmaImpactTime = kTwoSeconds; + +const TimeValue kNoradAirMaskTimeLimit = kOneMinute + kFifteenSeconds; + +const tNotificationID kNeighborhoodNotificationID = 1; +const tNotificationID kLastNeighborhoodNotificationID = kNeighborhoodNotificationID; + +const tNotificationFlags kNeighborhoodMovieCompletedFlag = 1; +const tNotificationFlags kMoveForwardCompletedFlag = kNeighborhoodMovieCompletedFlag << 1; +const tNotificationFlags kStrideCompletedFlag = kMoveForwardCompletedFlag << 1; +const tNotificationFlags kTurnCompletedFlag = kStrideCompletedFlag << 1; +const tNotificationFlags kSpotCompletedFlag = kTurnCompletedFlag << 1; +const tNotificationFlags kDoorOpenCompletedFlag = kSpotCompletedFlag << 1; +const tNotificationFlags kExtraCompletedFlag = kDoorOpenCompletedFlag << 1; +const tNotificationFlags kSpotSoundCompletedFlag = kExtraCompletedFlag << 1; +const tNotificationFlags kDelayCompletedFlag = kSpotSoundCompletedFlag << 1; +const tNotificationFlags kActionRequestCompletedFlag = kDelayCompletedFlag << 1; +const tNotificationFlags kDeathExtraCompletedFlag = kActionRequestCompletedFlag << 1; +const tNotificationFlags kLastNeighborhoodNotificationFlag = kDeathExtraCompletedFlag; + +const tNotificationFlags kNeighborhoodFlags = kNeighborhoodMovieCompletedFlag | + kMoveForwardCompletedFlag | + kStrideCompletedFlag | + kTurnCompletedFlag | + kSpotCompletedFlag | + kDoorOpenCompletedFlag | + kExtraCompletedFlag | + kSpotSoundCompletedFlag | + kDelayCompletedFlag | + kActionRequestCompletedFlag | + kDeathExtraCompletedFlag; + +const uint32 kPegasusPrimeCreator = MKTAG('J', 'P', 'P', 'P'); +const uint32 kPegasusPrimeContinueType = MKTAG('P', 'P', 'C', 'T'); + +const uint32 kPegasusPrimeDisk1GameType = MKTAG('P', 'P', 'G', '1'); +const uint32 kPegasusPrimeDisk2GameType = MKTAG('P', 'P', 'G', '2'); +const uint32 kPegasusPrimeDisk3GameType = MKTAG('P', 'P', 'G', '3'); +const uint32 kPegasusPrimeDisk4GameType = MKTAG('P', 'P', 'G', '4'); + +// We only support one of the save versions; the rest are from betas +// and we are not supporting them. +const uint32 kPegasusPrimeVersion = 0x00009019; + +const char kNormalSave = 0; +const char kContinueSave = 1; + +// Display IDs. + +const tDisplayElementID kNavMovieID = 1; +const tDisplayElementID kTurnPushID = 2; + +const tDisplayElementID kMaxGameShellDisplayID = kTurnPushID; + +// Display ordering. + +const tDisplayOrder kNavLayer = 10000; +const tDisplayOrder kNavMovieOrder = kNavLayer; +const tDisplayOrder kTurnPushOrder = kNavMovieOrder + 1; + +///////////////////////////////////////////// +// +// Display IDs. + +const tDisplayElementID kScreenDimmerID = kMaxGameShellDisplayID + 1; +const tDisplayElementID kInterface1ID = kScreenDimmerID + 1; +const tDisplayElementID kInterface2ID = kInterface1ID + 1; +const tDisplayElementID kInterface3ID = kInterface2ID + 1; +const tDisplayElementID kInterface4ID = kInterface3ID + 1; +const tDisplayElementID kDateID = kInterface4ID + 1; +const tDisplayElementID kCompassID = kDateID + 1; +const tDisplayElementID kInventoryPushID = kCompassID + 1; +const tDisplayElementID kInventoryLidID = kInventoryPushID + 1; +const tDisplayElementID kBiochipPushID = kInventoryLidID + 1; +const tDisplayElementID kBiochipLidID = kBiochipPushID + 1; +const tDisplayElementID kEnergyBarID = kBiochipLidID + 1; +const tDisplayElementID kWarningLightID = kEnergyBarID + 1; +const tDisplayElementID kAILeftAreaID = kWarningLightID + 1; +const tDisplayElementID kAIMiddleAreaID = kAILeftAreaID + 1; +const tDisplayElementID kAIRightAreaID = kAIMiddleAreaID + 1; +const tDisplayElementID kAIMovieID = kAIRightAreaID + 1; +const tDisplayElementID kInventoryDropHighlightID = kAIMovieID + 1; +const tDisplayElementID kBiochipDropHighlightID = kInventoryDropHighlightID + 1; + +const tDisplayElementID kDraggingSpriteID = 1000; + +const tDisplayElementID kCroppedMovieID = 2000; + +const tDisplayElementID kNeighborhoodDisplayID = 3000; + +const tDisplayElementID kItemPictureBaseID = 5000; + +const tCoordType kNavAreaLeft = 64; +const tCoordType kNavAreaTop = 64; + +const tCoordType kBackground1Left = 0; +const tCoordType kBackground1Top = 64; + +const tCoordType kBackground2Left = 0; +const tCoordType kBackground2Top = 0; + +const tCoordType kBackground3Left = 576; +const tCoordType kBackground3Top = 64; + +const tCoordType kBackground4Left = 0; +const tCoordType kBackground4Top = 320; + +const tCoordType kOverviewControllerLeft = 540; +const tCoordType kOverviewControllerTop = 348; + +const tCoordType kSwapLeft = 194; +const tCoordType kSwapTop = 116; + +const tCoordType kSwapHiliteLeft = 200; +const tCoordType kSwapHiliteTop = 206; + +const tCoordType kDateLeft = 136; +const tCoordType kDateTop = 44; + +const tCoordType kCompassLeft = 222; +const tCoordType kCompassTop = 42; +const tCoordType kCompassWidth = 92; + +const tCoordType kInventoryPushLeft = 74; +const tCoordType kInventoryPushTop = 92; + +const tCoordType kInventoryLidLeft = 74; +const tCoordType kInventoryLidTop = 316; + +const tCoordType kBiochipPushLeft = 362; +const tCoordType kBiochipPushTop = 192; + +const tCoordType kBiochipLidLeft = 362; +const tCoordType kBiochipLidTop = 316; + +// TODO: Remove global variable needs +//const Common::Rect kInventoryHiliteBounds(334, 76, 430, 172); +//const Common::Rect kBiochipHiliteBounds (334, 364, 430, 460); + +const tCoordType kInventoryDropLeft = 0; +const tCoordType kInventoryDropTop = 320; +const tCoordType kInventoryDropRight = 232; +const tCoordType kInventoryDropBottom = 480; + +const tCoordType kBiochipDropLeft = 302; +const tCoordType kBiochipDropTop = 320; +const tCoordType kBiochipDropRight = 640; +const tCoordType kBiochipDropBottom = 480; + +const tCoordType kFinalMessageLeft = kInventoryPushLeft + 1; +const tCoordType kFinalMessageTop = kInventoryPushTop + 24; + +///////////////////////////////////////////// +// +// Notifications. + +const tNotificationID kJMPDCShellNotificationID = kLastNeighborhoodNotificationID + 1; +const tNotificationID kInterfaceNotificationID = kJMPDCShellNotificationID + 1; +const tNotificationID kAINotificationID = kInterfaceNotificationID + 1; +const tNotificationID kNoradNotificationID = kAINotificationID + 1; +const tNotificationID kNoradECRNotificationID = kNoradNotificationID + 1; +const tNotificationID kNoradFillingStationNotificationID = kNoradECRNotificationID + 1; +const tNotificationID kNoradPressureNotificationID = kNoradFillingStationNotificationID + 1; +const tNotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1; +const tNotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1; +const tNotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1; +const tNotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1; +const tNotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1; +const tNotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1; +const tNotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1; +const tNotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1; +const tNotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1; + +// Sent to the shell by fShellNotification. +const tNotificationFlags kGameStartingFlag = 1; +const tNotificationFlags kNeedNewJumpFlag = kGameStartingFlag << 1; +const tNotificationFlags kPlayerDiedFlag = kNeedNewJumpFlag << 1; + +const tNotificationFlags kJMPShellNotificationFlags = kGameStartingFlag | + kNeedNewJumpFlag | + kPlayerDiedFlag; + +// Sent to the interface. +const tNotificationFlags kInventoryLidOpenFlag = 1; +const tNotificationFlags kInventoryLidClosedFlag = kInventoryLidOpenFlag << 1; +const tNotificationFlags kInventoryDrawerUpFlag = kInventoryLidClosedFlag << 1; +const tNotificationFlags kInventoryDrawerDownFlag = kInventoryDrawerUpFlag << 1; +const tNotificationFlags kBiochipLidOpenFlag = kInventoryDrawerDownFlag << 1; +const tNotificationFlags kBiochipLidClosedFlag = kBiochipLidOpenFlag << 1; +const tNotificationFlags kBiochipDrawerUpFlag = kBiochipLidClosedFlag << 1; +const tNotificationFlags kBiochipDrawerDownFlag = kBiochipDrawerUpFlag << 1; + +const tNotificationFlags kInterfaceNotificationFlags = kInventoryLidOpenFlag | + kInventoryLidClosedFlag | + kInventoryDrawerUpFlag | + kInventoryDrawerDownFlag | + kBiochipLidOpenFlag | + kBiochipLidClosedFlag | + kBiochipDrawerUpFlag | + kBiochipDrawerDownFlag; + +// Hot spots. + +// Neighborhood hot spots. + +const tHotSpotID kFirstNeighborhoodSpotID = 5000; + +// kShellSpotFlag is a flag which marks all hot spots which belong to the shell, like +// the current item and current biochip spots. +const tHotSpotFlags kShellSpotFlag = 1; +// kNeighborhoodSpotFlag is a flag which marks all hot spots which belong to a +// neighborhood, like buttons on walls and so on. +const tHotSpotFlags kNeighborhoodSpotFlag = kShellSpotFlag << 1; +// kZoomInSpotFlag is a flag which marks all hot spots which indicate a zoom. +const tHotSpotFlags kZoomInSpotFlag = kNeighborhoodSpotFlag << 1; +// kZoomOutSpotFlag is a flag which marks all hot spots which indicate a zoom. +const tHotSpotFlags kZoomOutSpotFlag = kZoomInSpotFlag << 1; + +const tHotSpotFlags kClickSpotFlag = kZoomOutSpotFlag << 1; +const tHotSpotFlags kPlayExtraSpotFlag = kClickSpotFlag << 1; +const tHotSpotFlags kPickUpItemSpotFlag = kPlayExtraSpotFlag << 1; +const tHotSpotFlags kDropItemSpotFlag = kPickUpItemSpotFlag << 1; +const tHotSpotFlags kOpenDoorSpotFlag = kDropItemSpotFlag << 1; + +const tHotSpotFlags kZoomSpotFlags = kZoomInSpotFlag | kZoomOutSpotFlag; + +const tHotSpotFlags kHighestGameShellSpotFlag = kOpenDoorSpotFlag; + +///////////////////////////////////////////// +// +// Hot spots. + +// Shell hot spots. +// The shell reserves all hot spot IDs from 0 to 999 + +const tHotSpotID kCurrentItemSpotID = 0; +const tHotSpotID kCurrentBiochipSpotID = kCurrentItemSpotID + 1; + +const tHotSpotID kInventoryDropSpotID = kCurrentBiochipSpotID + 1; +const tHotSpotID kBiochipDropSpotID = kInventoryDropSpotID + 1; + +const tHotSpotID kInfoReturnSpotID = kBiochipDropSpotID + 1; + +const tHotSpotID kAIHint1SpotID = kInfoReturnSpotID + 1; +const tHotSpotID kAIHint2SpotID = kAIHint1SpotID + 1; +const tHotSpotID kAIHint3SpotID = kAIHint2SpotID + 1; +const tHotSpotID kAISolveSpotID = kAIHint3SpotID + 1; +const tHotSpotID kAIBriefingSpotID = kAISolveSpotID + 1; +const tHotSpotID kAIScanSpotID = kAIBriefingSpotID + 1; + +const tHotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1; + +const tHotSpotID kAriesSpotID = kPegasusRecallSpotID + 1; +const tHotSpotID kMercurySpotID = kAriesSpotID + 1; +const tHotSpotID kPoseidonSpotID = kMercurySpotID + 1; + +const tHotSpotID kAirMaskToggleSpotID = kPoseidonSpotID + 1; + +const tHotSpotID kShuttleEnergySpotID = kAirMaskToggleSpotID + 1; +const tHotSpotID kShuttleGravitonSpotID = kShuttleEnergySpotID + 1; +const tHotSpotID kShuttleTractorSpotID = kShuttleGravitonSpotID + 1; +const tHotSpotID kShuttleViewSpotID = kShuttleTractorSpotID + 1; +const tHotSpotID kShuttleTransportSpotID = kShuttleViewSpotID + 1; + +// Most of these are obsolete: + +// kInventoryDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for inventory items. +// const tHotSpotFlags kInventoryDropSpotFlag = kHighestGameShellSpotFlag << 1; + +// kBiochipDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for biochips. +// const tHotSpotFlags kBiochipDropSpotFlag = kInventoryDropSpotFlag << 1; + +// kInventorySpotFlag is a flag which marks hot spots which indicate inventory items +// in the environment. +// const tHotSpotFlags kInventorySpotFlag = kBiochipDropSpotFlag << 1; + +// kBiochipSpotFlag is a flag which marks hot spots which indicate biochips +// in the environment. +const tHotSpotFlags kPickUpBiochipSpotFlag = kHighestGameShellSpotFlag << 1; +const tHotSpotFlags kDropBiochipSpotFlag = kPickUpBiochipSpotFlag << 1; + +const tHotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1; + +// Biochip and inventory hot spot flags... + +const tHotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1; +const tHotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1; +const tHotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1; +const tHotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1; + +const tHotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag | + kPlayExtraSpotFlag | + kOpenDoorSpotFlag | + kInfoReturnSpotFlag | + kAIBiochipSpotFlag | + kPegasusBiochipSpotFlag | + kOpticalBiochipSpotFlag | + kAirMaskSpotFlag; + +const tMM32BitID kMainMenuID = 1; +const tMM32BitID kPauseMenuID = 2; +const tMM32BitID kCreditsMenuID = 3; +const tMM32BitID kDeathMenuID = 4; + +///////////////////////////////////////////// +// +// Menu commands. + +const tGameMenuCommand kMenuCmdOverview = kMenuCmdNoCommand + 1; +const tGameMenuCommand kMenuCmdStartAdventure = kMenuCmdOverview + 1; +const tGameMenuCommand kMenuCmdStartWalkthrough = kMenuCmdStartAdventure + 1; +const tGameMenuCommand kMenuCmdRestore = kMenuCmdStartWalkthrough + 1; +const tGameMenuCommand kMenuCmdCredits = kMenuCmdRestore + 1; +const tGameMenuCommand kMenuCmdQuit = kMenuCmdCredits + 1; + +const tGameMenuCommand kMenuCmdDeathContinue = kMenuCmdQuit + 1; + +const tGameMenuCommand kMenuCmdDeathQuitDemo = kMenuCmdDeathContinue + 1; +const tGameMenuCommand kMenuCmdDeathMainMenuDemo = kMenuCmdDeathQuitDemo + 1; + +const tGameMenuCommand kMenuCmdDeathRestore = kMenuCmdDeathMainMenuDemo + 1; +const tGameMenuCommand kMenuCmdDeathMainMenu = kMenuCmdDeathRestore + 1; + +const tGameMenuCommand kMenuCmdPauseSave = kMenuCmdDeathMainMenu + 1; +const tGameMenuCommand kMenuCmdPauseContinue = kMenuCmdPauseSave + 1; +const tGameMenuCommand kMenuCmdPauseRestore = kMenuCmdPauseContinue + 1; +const tGameMenuCommand kMenuCmdPauseQuit = kMenuCmdPauseRestore + 1; + +const tGameMenuCommand kMenuCmdCreditsMainMenu = kMenuCmdPauseQuit + 1; + +const tGameMenuCommand kMenuCmdCancelRestart = kMenuCmdCreditsMainMenu + 1; +const tGameMenuCommand kMenuCmdEjectRestart = kMenuCmdCancelRestart + 1; + +const TimeValue kMenuButtonHiliteTime = 20; +const TimeScale kMenuButtonHiliteScale = kSixtyTicksPerSecond; + +// PICT resources: + +// Warning light PICTs: + +const tResIDType kLightOffID = 128; +const tResIDType kLightYellowID = 129; +const tResIDType kLightOrangeID = 130; +const tResIDType kLightRedID = 131; + +// Date PICTs: + +const tResIDType kDatePrehistoricID = 138; +const tResIDType kDate2112ID = 139; +const tResIDType kDate2185ID = 140; +const tResIDType kDate2310ID = 141; +const tResIDType kDate2318ID = 142; + +///////////////////////////////////////////// +// +// Display Order + +const tDisplayOrder kCroppedMovieLayer = 11000; + +const tDisplayOrder kMonitorLayer = 12000; + +const tDisplayOrder kDragSpriteLayer = 15000; +const tDisplayOrder kDragSpriteOrder = kDragSpriteLayer; + +const tDisplayOrder kInterfaceLayer = 20000; +const tDisplayOrder kBackground1Order = kInterfaceLayer; +const tDisplayOrder kBackground2Order = kBackground1Order + 1; +const tDisplayOrder kBackground3Order = kBackground2Order + 1; +const tDisplayOrder kBackground4Order = kBackground3Order + 1; +const tDisplayOrder kDateOrder = kBackground4Order + 1; +const tDisplayOrder kCompassOrder = kDateOrder + 1; +const tDisplayOrder kEnergyBarOrder = kCompassOrder + 1; +const tDisplayOrder kEnergyLightOrder = kEnergyBarOrder + 1; + +const tDisplayOrder kAILayer = 22000; +const tDisplayOrder kAILeftAreaOrder = kAILayer; +const tDisplayOrder kAIMiddleAreaOrder = kAILeftAreaOrder + 1; +const tDisplayOrder kAIRightAreaOrder = kAIMiddleAreaOrder + 1; +const tDisplayOrder kAIMovieOrder = kAIRightAreaOrder + 1; + +const tDisplayOrder kHilitesLayer = 23000; +const tDisplayOrder kInventoryHiliteOrder = kHilitesLayer; +const tDisplayOrder kBiochipHiliteOrder = kInventoryHiliteOrder + 1; + +const tDisplayOrder kPanelsLayer = 25000; +const tDisplayOrder kInventoryPushOrder = kPanelsLayer; +const tDisplayOrder kInventoryLidOrder = kInventoryPushOrder + 1; +const tDisplayOrder kBiochipPushOrder = kInventoryLidOrder + 1; +const tDisplayOrder kBiochipLidOrder = kBiochipPushOrder + 1; +const tDisplayOrder kFinalMessageOrder = kBiochipLidOrder + 1; + +const tDisplayOrder kInfoLayer = 26000; +const tDisplayOrder kInfoBackgroundOrder = kInfoLayer; +const tDisplayOrder kInfoSpinOrder = kInfoBackgroundOrder + 1; + +const tDisplayOrder kScreenDimmerOrder = 30000; + +const tDisplayOrder kPauseScreenLayer = 31000; +const tDisplayOrder kPauseMenuOrder = kPauseScreenLayer; +const tDisplayOrder kSaveGameOrder = kPauseMenuOrder + 1; +const tDisplayOrder kContinueOrder = kSaveGameOrder + 1; +const tDisplayOrder kRestoreOrder = kContinueOrder + 1; +const tDisplayOrder kSoundFXOrder = kRestoreOrder + 1; +const tDisplayOrder kAmbienceOrder = kSoundFXOrder + 1; +const tDisplayOrder kWalkthruOrder = kAmbienceOrder + 1; +const tDisplayOrder kQuitToMainMenuOrder = kWalkthruOrder + 1; +const tDisplayOrder kPauseLargeHiliteOrder = kQuitToMainMenuOrder + 1; +const tDisplayOrder kPauseSmallHiliteOrder = kPauseLargeHiliteOrder + 1; + +///////////////////////////////////////////// +// +// Death reasons. +enum { + // Caldoria + kDeathUncreatedInCaldoria = 1, + kDeathCardBomb, + kDeathShotBySinclair, + kDeathSinclairShotDelegate, + kDeathNuclearExplosion, + + // TSA + kDeathUncreatedInTSA, + kDeathShotByTSARobots, + + // Prehistoric + kDeathFallOffCliff, + kDeathEatenByDinosaur, + kDeathStranded, + + // Norad + kDeathGassedInNorad, + kDeathArrestedInNorad, + kDeathWokeUpNorad, + kDeathSubDestroyed, // Unused + kDeathRobotThroughNoradDoor, + kDeathRobotSubControlRoom, + + // Mars + kDeathWrongShuttleLock, + kDeathArrestedInMars, + kDeathRunOverByPod, + kDeathDidntGetOutOfWay, + kDeathReactorBurn, + kDeathDidntFindMarsBomb, + kDeathDidntDisarmMarsBomb, + kDeathNoMaskInMaze, + kDeathNoAirInMaze, + kDeathGroundByMazebot, + kDeathMissedOreBucket, + kDeathDidntLeaveBucket, + kDeathRanIntoCanyonWall, // Unused + kDeathRanIntoSpaceJunk, + + // WSC + kDeathDidntStopPoison, + kDeathArrestedInWSC, + kDeathHitByPlasma, + kDeathShotOnCatwalk, + + // Winning + kPlayerWonGame +}; + +static const tCoordType kAILeftAreaLeft = 76; +static const tCoordType kAILeftAreaTop = 334; + +static const tCoordType kAILeftAreaWidth = 96; +static const tCoordType kAILeftAreaHeight = 96; + +static const tCoordType kAIMiddleAreaLeft = 172; +static const tCoordType kAIMiddleAreaTop = 334; + +static const tCoordType kAIMiddleAreaWidth = 192; +static const tCoordType kAIMiddleAreaHeight = 96; + +static const tCoordType kAIRightAreaLeft = 364; +static const tCoordType kAIRightAreaTop = 334; + +static const tCoordType kAIRightAreaWidth = 96; +static const tCoordType kAIRightAreaHeight = 96; + +enum { + kTSAPlayerNotArrived, // initial state, must be zero + kTSAPlayerForcedReview, // Player must watch TBP before rip occurs. + kTSAPlayerDetectedRip, // Player finished TBP, rip alarm just went off. + kTSAPlayerNeedsHistoricalLog, // Player is instructed to get historical log + kTSAPlayerGotHistoricalLog, + kTSAPlayerInstalledHistoricalLog, + kTSABossSawHistoricalLog, + kRobotsAtCommandCenter, + kRobotsAtFrontDoor, + kRobotsAtReadyRoom, + kPlayerLockedInPegasus, + kPlayerOnWayToPrehistoric, + kPlayerWentToPrehistoric, + kPlayerOnWayToNorad, + kPlayerOnWayToMars, + kPlayerOnWayToWSC, + kPlayerFinishedWithTSA +}; + +static const tDirectionConstant kNorth = 0; +static const tDirectionConstant kSouth = 1; +static const tDirectionConstant kEast = 2; +static const tDirectionConstant kWest = 3; + +///////////////////////////////////////////// +// +// Mode constants. + +static const tGameMode kModeInventoryPick = kLastGameShellMode + 1; +static const tGameMode kModeBiochipPick = kModeInventoryPick + 1; +static const tGameMode kModeInfoScreen = kModeBiochipPick + 1; + +// TODO: Remove me +static const tRoomID kNorad01 = 0; +static const tRoomID kMars0A = 0; +static const tRoomID kWSC01 = 0; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp new file mode 100755 index 0000000000..ee3ab87268 --- /dev/null +++ b/engines/pegasus/cursor.cpp @@ -0,0 +1,186 @@ +/* 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/events.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/cursorman.h" +#include "graphics/pict.h" +#include "graphics/surface.h" + +#include "pegasus/cursor.h" +#include "pegasus/graphics.h" +#include "pegasus/pegasus.h" + +namespace Pegasus { + +Cursor::Cursor() { + _cursorObscured = false; + _index = -1; + startIdling(); +} + +Cursor::~Cursor() { + for (uint32 i = 0; i < _info.size(); i++) { + if (_info[i].surface) { + _info[i].surface->free(); + delete _info[i].surface; + } + delete[] _info[i].palette; + } + + stopIdling(); +} + +void Cursor::addCursorFrames(uint16 id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id); + if (!cursStream) + error("Could not load cursor frames set %d", id); + + uint16 frameCount = cursStream->readUint16BE(); + for (uint16 i = 0; i < frameCount; i++) { + CursorInfo info; + info.tag = cursStream->readUint16BE(); + info.hotspot.x = cursStream->readUint16BE(); + info.hotspot.y = cursStream->readUint16BE(); + info.surface = 0; + info.palette = 0; + info.colorCount = 0; + _info.push_back(info); + } + + delete cursStream; + + setCurrentFrameIndex(0); +} + +void Cursor::setCurrentFrameIndex(int32 index) { + if (_index != index) { + _index = index; + if (index != -1) { + loadCursorImage(_info[index]); + CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount); + CursorMan.replaceCursor((byte *)_info[index].surface->pixels, _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } + } +} + +int32 Cursor::getCurrentFrameIndex() const { + return _index; +} + +void Cursor::show() { + if (!isVisible()) + CursorMan.showMouse(true); + + _cursorObscured = false; + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hide() { + CursorMan.showMouse(false); + setCurrentFrameIndex(0); +} + +void Cursor::hideUntilMoved() { + if (!_cursorObscured) { + hide(); + _cursorObscured = true; + } +} + +void Cursor::useIdleTime() { + if (g_system->getEventManager()->getMousePos() != _cursorLocation) { + _cursorLocation = g_system->getEventManager()->getMousePos(); + if (_index != -1 && _cursorObscured) + show(); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } +} + +void Cursor::getCursorLocation(Common::Point &pt) const { + pt = _cursorLocation; +} + +bool Cursor::isVisible() { + return CursorMan.isVisible(); +} + +void Cursor::loadCursorImage(CursorInfo &cursorInfo) { + if (cursorInfo.surface) + return; + + cursorInfo.surface = new Graphics::Surface(); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag); + + if (!cicnStream) + error("Failed to find color icon %d", cursorInfo.tag); + + // PixMap section + Graphics::PictDecoder::PixMap pixMap = Graphics::PictDecoder::readPixMap(cicnStream); + + // Mask section + cicnStream->readUint32BE(); // mask baseAddr + uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes + cicnStream->skip(3 * 2); // mask rect + /* uint16 maskHeight = */ cicnStream->readUint16BE(); + + // Bitmap section + cicnStream->readUint32BE(); // baseAddr + uint16 rowBytes = cicnStream->readUint16BE(); + cicnStream->readUint16BE(); // top + cicnStream->readUint16BE(); // left + uint16 height = cicnStream->readUint16BE(); // bottom + cicnStream->readUint16BE(); // right + + // Data section + cicnStream->readUint32BE(); // icon handle + cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should + cicnStream->skip(rowBytes * height); + + // Palette section + cicnStream->readUint32BE(); // always 0 + cicnStream->readUint16BE(); // always 0 + cursorInfo.colorCount = cicnStream->readUint16BE() + 1; + + cursorInfo.palette = new byte[256 * cursorInfo.colorCount]; + for (uint16 i = 0; i < cursorInfo.colorCount; i++) { + cicnStream->readUint16BE(); + cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8; + } + + // PixMap data + cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + cicnStream->read(cursorInfo.surface->pixels, pixMap.rowBytes * pixMap.bounds.height()); + delete cicnStream; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/cursor.h b/engines/pegasus/cursor.h new file mode 100755 index 0000000000..ada82e3967 --- /dev/null +++ b/engines/pegasus/cursor.h @@ -0,0 +1,84 @@ +/* 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_CURSOR_H +#define PEGASUS_CURSOR_H + +#include "common/array.h" +#include "common/rect.h" + +#include "pegasus/timers.h" + +namespace Graphics { + struct Surface; +} + +namespace Pegasus { + +// The original cursor code was in the graphics code directly, +// unlike ScummVM where we have the cursor code separate. We're +// going to go with CursorManager here and therefore not inherit +// from the Sprite class. + +class Cursor : private Idler { +public: + Cursor(); + virtual ~Cursor(); + + void addCursorFrames(uint16 id); + + void setCurrentFrameIndex(int32 index); + int32 getCurrentFrameIndex() const; + + void show(); + void hide(); + void hideUntilMoved(); + bool isVisible(); + + void getCursorLocation(Common::Point &) const; + +protected: + virtual void useIdleTime(); + +private: + struct CursorInfo { + uint16 tag; + Common::Point hotspot; + Graphics::Surface *surface; + byte *palette; + uint16 colorCount; + }; + + Common::Point _cursorLocation; + Common::Array<CursorInfo> _info; + bool _cursorObscured; + int _index; + + void loadCursorImage(CursorInfo &cursorInfo); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp new file mode 100644 index 0000000000..fe797b4eee --- /dev/null +++ b/engines/pegasus/detection.cpp @@ -0,0 +1,156 @@ +/* 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. + * + * 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 "base/plugins.h" + +#include "engines/advancedDetector.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/savefile.h" + +#include "pegasus/pegasus.h" + +namespace Pegasus { + +struct PegasusGameDescription { + ADGameDescription desc; +}; + +bool PegasusEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) + || (f == kSupportsLoadingDuringRuntime) + || (f == kSupportsSavingDuringRuntime); +} + +bool PegasusEngine::isDemo() const { + return (_gameDescription->desc.flags & ADGF_DEMO) != 0; +} + +} // End of namespace Pegasus + +static const PlainGameDescriptor pegasusGames[] = { + {"pegasus", "The Journeyman Project: Pegasus Prime"}, + {0, 0} +}; + + +namespace Pegasus { + +static const PegasusGameDescription gameDescriptions[] = { + { + { + "pegasus", + "", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK, + Common::GUIO_NONE + }, + }, + + { + { + "pegasus", + "", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK|ADGF_DEMO, + Common::GUIO_NOLAUNCHLOAD + }, + }, + + { AD_TABLE_END_MARKER } +}; + +} // End of namespace Pegasus + + +class PegasusMetaEngine : public AdvancedMetaEngine { +public: + PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) { + _singleid = "pegasus"; + } + + virtual const char *getName() const { + return "The Journeyman Project: Pegasus Prime"; + } + + virtual const char *getOriginalCopyright() const { + return "The Journeyman Project: Pegasus Prime (C) Presto Studios"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const { return 999; } + virtual void removeSaveState(const char *target, int slot) const; +}; + +bool PegasusMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) + || (f == kSupportsDeleteSave); +} + +SaveStateList PegasusMetaEngine::listSaves(const char *target) const { + // The original had no pattern, so the user must rename theirs + // Note that we ignore the target because saves are compatible between + // all versions + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + + SaveStateList saveList; + for (uint32 i = 0; i < filenames.size(); i++) { + // Isolate the description from the file name + Common::String desc = filenames[i].c_str() + 8; + for (int j = 0; j < 4; j++) + desc.deleteLastChar(); + + saveList.push_back(SaveStateDescriptor(i, desc)); + } + + return saveList; +} + +void PegasusMetaEngine::removeSaveState(const char *target, int slot) const { + // See listSaves() for info on the pattern + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); +} + +bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc; + + if (gd) + *engine = new Pegasus::PegasusEngine(syst, gd); + + return (gd != 0); +} + +#if PLUGIN_ENABLED_DYNAMIC(PEGASUS) + REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#else + REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#endif + diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp new file mode 100644 index 0000000000..79faa1ae85 --- /dev/null +++ b/engines/pegasus/elements.cpp @@ -0,0 +1,568 @@ +/* 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/macresman.h" +#include "common/stream.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +DisplayElement::DisplayElement(const tDisplayElementID id) : IDObject(id) { + _elementIsDisplaying = false; + _elementIsVisible = false; + _elementOrder = 0; + _triggeredElement = this; + _nextElement = 0; +} + +DisplayElement::~DisplayElement() { + if (isDisplaying()) + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +} + +void DisplayElement::setDisplayOrder(const tDisplayOrder order) { + if (_elementOrder != order) { + _elementOrder = order; + if (isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } + } +} + +void DisplayElement::startDisplaying() { + if (!isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } +} + +void DisplayElement::stopDisplaying() { + if (isDisplaying()) { + triggerRedraw(); + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + } +} + +void DisplayElement::setBounds(const tCoordType left, const tCoordType top, const tCoordType right, const tCoordType bottom) { + setBounds(Common::Rect(left, top, right, bottom)); +} + +void DisplayElement::getBounds(Common::Rect &r) const { + r = _bounds; +} + +void DisplayElement::sizeElement(const tCoordType h, const tCoordType v) { + Common::Rect newBounds = _bounds; + newBounds.right = _bounds.left + h; + newBounds.bottom = _bounds.top + v; + setBounds(newBounds); +} + +void DisplayElement::moveElementTo(const tCoordType h, const tCoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h, v); + setBounds(newBounds); +} + +void DisplayElement::moveElement(const tCoordType dh, const tCoordType dv) { + Common::Rect newBounds = _bounds; + newBounds.translate(dh, dv); + setBounds(newBounds); +} + +void DisplayElement::getLocation(tCoordType &h, tCoordType &v) const { + h = _bounds.left; + v = _bounds.top; +} + +void DisplayElement::centerElementAt(const tCoordType h, const tCoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2)); + setBounds(newBounds); +} + +void DisplayElement::getCenter(tCoordType &h, tCoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void DisplayElement::setBounds(const Common::Rect &r) { + if (r != _bounds) { + triggerRedraw(); + _bounds = r; + triggerRedraw(); + } +} + +void DisplayElement::hide() { + if (_elementIsVisible) { + triggerRedraw(); + _elementIsVisible = false; + } +} + +void DisplayElement::show() { + if (!_elementIsVisible) { + _elementIsVisible = true; + triggerRedraw(); + } +} + +// Only invalidates this element's bounding rectangle if all these conditions are true: +// -- The triggered element is this element. +// -- The element is displaying on the display list. +// -- The element is visible. +// -- The element is part of the active layer OR is one of the reserved items. +void DisplayElement::triggerRedraw() { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + + if (_triggeredElement == this) { + if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer())) + gfx->invalRect(_bounds); + } else { + _triggeredElement->triggerRedraw(); + } +} + +void DisplayElement::setTriggeredElement(DisplayElement *element) { + if (element) + _triggeredElement = element; + else + _triggeredElement = this; +} + +bool DisplayElement::validToDraw(tDisplayOrder backLayer, tDisplayOrder frontLayer) { + return isDisplaying() && _elementIsVisible && + (getObjectID() <= kHighestReservedElementID || + (getDisplayOrder() >= backLayer && + getDisplayOrder() <= frontLayer)); +} + +DropHighlight::DropHighlight(const tDisplayElementID id) : DisplayElement(id) { + _highlightColor = g_system->getScreenFormat().RGBToColor(0x48, 0x80, 0xD8); + _thickness = 2; + _cornerDiameter = 0; +} + +void DropHighlight::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + // Since this is only used in two different ways, I'm only + // going to implement it in those two ways. Deal with it. + + Common::Rect rect = _bounds; + rect.grow(-_thickness); + screen->frameRect(rect, _highlightColor); + rect.grow(1); + screen->frameRect(rect, _highlightColor); + + if (_cornerDiameter == 8 && _thickness == 4) { + rect.grow(1); + screen->frameRect(rect, _highlightColor); + screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor); + screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor); + screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor); + screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor); + } +} + +IdlerAnimation::IdlerAnimation(const tDisplayElementID id) : Animation(id) { + _lastTime = 0xffffffff; +} + +void IdlerAnimation::startDisplaying() { + if (!isDisplaying()) { + Animation::startDisplaying(); + startIdling(); + } +} + +void IdlerAnimation::stopDisplaying() { + if (isDisplaying()) { + Animation::stopDisplaying(); + stopIdling(); + } +} + +void IdlerAnimation::useIdleTime() { + uint32 currentTime = getTime(); + + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +void IdlerAnimation::timeChanged(const TimeValue) { + triggerRedraw(); +} + +FrameSequence::FrameSequence(const tDisplayElementID id) : IdlerAnimation(id) { + _duration = 0; + _currentFrameNum = 0; + _resFork = new Common::MacResManager(); + _numFrames = 0; +} + +FrameSequence::~FrameSequence() { + delete _resFork; +} + +void FrameSequence::useFileName(const Common::String &fileName) { + _resFork->open(fileName); +} + +void FrameSequence::openFrameSequence() { + if (!_resFork->hasResFork()) + return; + + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80); + + if (!res) + return; + + uint32 scale = res->readUint32BE(); + _bounds.top = res->readUint16BE(); + _bounds.left = res->readUint16BE(); + _bounds.bottom = res->readUint16BE(); + _bounds.right = res->readUint16BE(); + _numFrames = res->readUint16BE(); + _duration = 0; + + _frameTimes.clear(); + for (uint32 i = 0; i < _numFrames; i++) { + TimeValue time = res->readUint32BE(); + _duration += time; + _frameTimes.push_back(_duration); + } + + setScale(scale); + setSegment(0, _duration); + setTime(0); + _currentFrameNum = 0; + newFrame(_currentFrameNum); + triggerRedraw(); + + delete res; +} + +void FrameSequence::closeFrameSequence() { + stop(); + _resFork->close(); + _duration = 0; + _numFrames = 0; + _frameTimes.clear(); +} + +void FrameSequence::timeChanged(const TimeValue time) { + int16 frameNum = 0; + for (int16 i = _numFrames - 1; i >= 0; i--) { + if (_frameTimes[i] < time) { + frameNum = i; + break; + } + } + + if (frameNum != _currentFrameNum) { + _currentFrameNum = frameNum; + newFrame(_currentFrameNum); + triggerRedraw(); + } +} + +void FrameSequence::setFrameNum(const int16 frameNum) { + int16 f = CLIP<int>(frameNum, 0, _numFrames); + + if (_currentFrameNum != f) { + _currentFrameNum = f; + setTime(_frameTimes[f]); + newFrame(f); + triggerRedraw(); + } +} + +bool FrameSequence::isSequenceOpen() const { + return _numFrames != 0; +} + +Sprite::Sprite(const tDisplayElementID id) : DisplayElement(id) { + _numFrames = 0; + _currentFrameNum = 0xffffffff; + _currentFrame = 0; +} + +Sprite::~Sprite() { + discardFrames(); +} + +void Sprite::discardFrames() { + if (!_frameArray.empty()) { + for (uint32 i = 0; i < _numFrames; i++) { + SpriteFrame *frame = _frameArray[i].frame; + frame->_referenceCount--; + if (frame->_referenceCount == 0) + delete frame; + } + + _frameArray.clear(); + _numFrames = 0; + _currentFrame = 0; + _currentFrameNum = 0xffffffff; + setBounds(0, 0, 0, 0); + } +} + +void Sprite::addPICTResourceFrame(const tResIDType pictID, bool transparent, const tCoordType left, const tCoordType top) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent); + addFrame(frame, left, top); +} + +uint32 Sprite::addFrame(SpriteFrame *frame, const tCoordType left, const tCoordType top) { + SpriteFrameRec frameRecord; + frameRecord.frame = frame; + frameRecord.frameLeft = left; + frameRecord.frameTop = top; + _frameArray.push_back(frameRecord); + _numFrames++; + frame->_referenceCount++; + + Common::Rect frameBounds; + frame->getSurfaceBounds(frameBounds); + + // 9/3/96 + // BB Should this be + left or - left? + frameBounds.moveTo(_bounds.left + left, _bounds.top + top); + + frameBounds.extend(_bounds); + + if (_bounds != frameBounds) + setBounds(frameBounds); + + return _numFrames - 1; +} + +void Sprite::removeFrame(const uint32 frameNum) { + _frameArray[frameNum].frame->_referenceCount--; + if (_frameArray[frameNum].frame->_referenceCount == 0) + delete _frameArray[frameNum].frame; + + // Calculate the new bounds + Common::Rect frameBounds; + for (uint32 i = 0; i < _numFrames; i++) { + if (i == frameNum) + continue; + + Common::Rect r; + _frameArray[i].frame->getSurfaceBounds(r); + r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop); + frameBounds.extend(r); + } + + _frameArray.remove_at(frameNum); + + frameBounds.moveTo(_bounds.left, _bounds.top); + setBounds(frameBounds); + + if (_currentFrameNum == frameNum) + triggerRedraw(); + else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum) + --_currentFrameNum; +} + +void Sprite::setCurrentFrameIndex(const int32 frameNum) { + if (frameNum < 0) { + if (_currentFrameNum != 0xffffffff) { + _currentFrameNum = 0xffffffff; + _currentFrame = 0; + triggerRedraw(); + } + } else if (_numFrames > 0) { + uint32 f = frameNum % _numFrames; + if (f != _currentFrameNum) { + _currentFrameNum = f; + _currentFrame = &_frameArray[f]; + triggerRedraw(); + } + } +} + +SpriteFrame *Sprite::getFrame(const int32 index) { + if (index < 0 || (uint32)index >= _numFrames) + return 0; + + return _frameArray[index].frame; +} + +void Sprite::draw(const Common::Rect &r) { + if (_currentFrame) { + Common::Rect frameBounds; + _currentFrame->frame->getSurfaceBounds(frameBounds); + + frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop); + Common::Rect r1 = frameBounds.findIntersectingRect(r); + + Common::Rect r2 = frameBounds; + r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop); + + _currentFrame->frame->drawImage(r2, r1); + } +} + +SpriteSequence::SpriteSequence(const tDisplayElementID id, const tDisplayElementID spriteID) : + FrameSequence(id), _sprite(spriteID), _transparent(false) { +} + +void SpriteSequence::openFrameSequence() { + if (!isSequenceOpen()) { + FrameSequence::openFrameSequence(); + + if (isSequenceOpen()) { + uint32 numFrames = getNumFrames(); + + for (uint32 i = 0; i < numFrames; ++i) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(_resFork, i + 0x80, _transparent); + _sprite.addFrame(frame, 0, 0); + } + + _sprite.setBounds(_bounds); + } + } +} + +void SpriteSequence::closeFrameSequence() { + if (isSequenceOpen()) { + FrameSequence::closeFrameSequence(); + _sprite.discardFrames(); + } +} + +void SpriteSequence::setBounds(const Common::Rect &bounds) { + FrameSequence::setBounds(bounds); + _sprite.setBounds(_bounds); +} + +void SpriteSequence::draw(const Common::Rect &r) { + _sprite.draw(r); +} + +void SpriteSequence::newFrame(const uint16 frame) { + _sprite.setCurrentFrameIndex(frame); +} + +#define DRAW_PIXEL() \ + if (bytesPerPixel == 2) \ + *((uint16 *)dst) = black; \ + else \ + *((uint32 *)dst) = black; \ + dst += bytesPerPixel + +#define SKIP_PIXEL() \ + dst += bytesPerPixel + +void ScreenDimmer::draw(const Common::Rect &r) { + // We're going to emulate QuickDraw's srcOr+gray mode here + // In this mode, every other y column is all black (odd-columns). + // Basically, every row does three black and then one transparent + // repeatedly. + + // The output is identical to the original + + uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0); + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel; + + // We're currently doing it to the whole screen to simplify the code + + for (int y = 0; y < 480; y++) { + byte *dst = (byte *)screen->getBasePtr(0, y); + + for (int x = 0; x < 640; x += 4) { + if (y & 1) { + DRAW_PIXEL(); + DRAW_PIXEL(); + SKIP_PIXEL(); + DRAW_PIXEL(); + } else { + SKIP_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + } + } + } +} + +#undef DRAW_PIXEL +#undef SKIP_PIXEL + +SoundLevel::SoundLevel(const tDisplayElementID id) : DisplayElement(id) { + _soundLevel = 0; +} + +void SoundLevel::incrementLevel() { + if (_soundLevel < 12) { + _soundLevel++; + triggerRedraw(); + } +} + +void SoundLevel::decrementLevel() { + if (_soundLevel > 0) { + _soundLevel--; + triggerRedraw(); + } +} + +uint16 SoundLevel::getSoundLevel() { + return CLIP<int>(_soundLevel * 22, 0, 256); +} + +void SoundLevel::setSoundLevel(uint16 level) { + uint16 newLevel = (level + 21) / 22; + + if (newLevel != _soundLevel) { + _soundLevel = newLevel; + triggerRedraw(); + } +} + +void SoundLevel::draw(const Common::Rect &r) { + Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom); + levelRect = r.findIntersectingRect(levelRect); + + if (!levelRect.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0)); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h new file mode 100644 index 0000000000..6e20e72201 --- /dev/null +++ b/engines/pegasus/elements.h @@ -0,0 +1,254 @@ +/* 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_ELEMENTS_H +#define PEGASUS_ELEMENTS_H + +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/pict.h" +#include "graphics/surface.h" + +#include "pegasus/timers.h" +#include "pegasus/util.h" + +namespace Common { + class MacResManager; +} + +namespace Pegasus { + +class DisplayElement : public IDObject { +friend class GraphicsManager; +public: + DisplayElement(const tDisplayElementID); + virtual ~DisplayElement(); + + void setDisplayOrder(const tDisplayOrder); + tDisplayOrder getDisplayOrder() const { return _elementOrder; } + + bool validToDraw(tDisplayOrder, tDisplayOrder); + + virtual void draw(const Common::Rect&) {} + bool isDisplaying() { return _elementIsDisplaying; } + virtual void startDisplaying(); + virtual void stopDisplaying(); + + virtual void show(); + virtual void hide(); + bool isVisible() { return _elementIsVisible; } + + // triggerRedraw only triggers a draw if the element is displaying and visible. + void triggerRedraw(); + void setTriggeredElement(DisplayElement *); + + virtual void setBounds(const tCoordType, const tCoordType, const tCoordType, const tCoordType); + virtual void setBounds(const Common::Rect&); + virtual void getBounds(Common::Rect&) const; + virtual void sizeElement(const tCoordType, const tCoordType); + virtual void moveElementTo(const tCoordType, const tCoordType); + virtual void moveElement(const tCoordType, const tCoordType); + virtual void getLocation(tCoordType&, tCoordType&) const; + virtual void getCenter(tCoordType&, tCoordType&) const; + virtual void centerElementAt(const tCoordType, const tCoordType); + +protected: + Common::Rect _bounds; + bool _elementIsVisible; + DisplayElement *_triggeredElement; + + // Used only by PegasusEngine + bool _elementIsDisplaying; + tDisplayOrder _elementOrder; + DisplayElement *_nextElement; +}; + +// I'm using the proper "highlight" instead of the evil +// QuickDraw "hilite" :P (deal with it!) +class DropHighlight : public DisplayElement { +public: + DropHighlight(const tDisplayElementID); + virtual ~DropHighlight() {} + + void setHighlightColor(const uint32 &highlight) { _highlightColor = highlight; } + void getHighlightColor(uint32 &highlight) const { highlight = _highlightColor; } + + void setHighlightThickness(const uint16 thickness) { _thickness = thickness; } + uint16 getHighlightThickness() const { return _thickness; } + + void setHighlightCornerDiameter(const uint16 diameter) { _cornerDiameter = diameter; } + uint16 getHighlightCornerDiameter() const { return _cornerDiameter; } + + virtual void draw(const Common::Rect&); + +protected: + uint32 _highlightColor; + uint16 _thickness; + uint16 _cornerDiameter; +}; + +class Animation : public DisplayElement, public DynamicElement { +public: + Animation(const tDisplayElementID id) : DisplayElement(id) {} +}; + +class IdlerAnimation : public Animation, public Idler { +public: + IdlerAnimation(const tDisplayElementID); + + virtual void startDisplaying(); + virtual void stopDisplaying(); + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue); + + TimeValue _lastTime; +}; + +// This class reads PICT resources and plays them like a movie. +// Assumes there is a resource of type 'PFrm' describing the time values for each +// PICT frame, as well as the total time in the movie. +// Assumes that PICT frames begin at PICT 128 + +class FrameSequence : public IdlerAnimation { +public: + FrameSequence(const tDisplayElementID); + virtual ~FrameSequence(); + + void useFileName(const Common::String &fileName); + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + bool isSequenceOpen() const; + + uint16 getNumFrames() const { return _numFrames; } + virtual uint16 getFrameNum() const { return _currentFrameNum; } + virtual void setFrameNum(const int16); + +protected: + virtual void timeChanged(const TimeValue); + virtual void newFrame(const uint16) {} + + Common::MacResManager *_resFork; + TimeValue _duration; + + uint16 _numFrames; + Common::Array<TimeValue> _frameTimes; + + uint16 _currentFrameNum; +}; + +class SpriteFrame; + +class Sprite : public DisplayElement { +friend class SpriteFrame; +public: + Sprite(const tDisplayElementID); + virtual ~Sprite(); + + virtual void addPICTResourceFrame(const tResIDType, const bool, const tCoordType, const tCoordType); + virtual uint32 addFrame(SpriteFrame *, const tCoordType, const tCoordType); + virtual void removeFrame(const uint32); + virtual void discardFrames(); + + // Setting the current frame. + // If the index is negative, sets the current frame to NULL and hides the sprite. + // If the index is larger than the number of frames in the sprite, the number + // is treated modulo the number of frames. + virtual void setCurrentFrameIndex(const int32); + virtual uint32 getCurrentFrameIndex() const { return _currentFrameNum; } + + virtual SpriteFrame *getFrame(const int32); + + virtual void draw(const Common::Rect &); + + uint32 getNumFrames() const { return _numFrames; } + +protected: + struct SpriteFrameRec { + SpriteFrame *frame; + tCoordType frameLeft; + tCoordType frameTop; + }; + + uint32 _numFrames; + uint32 _currentFrameNum; + SpriteFrameRec *_currentFrame; + Common::Array<SpriteFrameRec> _frameArray; +}; + +class SpriteSequence : public FrameSequence { +public: + SpriteSequence(const tDisplayElementID id, const tDisplayElementID spriteID); + virtual ~SpriteSequence() {} + + void useTransparent(bool transparent) { _transparent = transparent; } + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + + virtual void draw(const Common::Rect &); + + virtual void setBounds(const Common::Rect &); + +protected: + virtual void newFrame(const uint16); + + bool _transparent; + Sprite _sprite; +}; + +class ScreenDimmer : public DisplayElement { +public: + ScreenDimmer() : DisplayElement(kScreenDimmerID) {} + virtual ~ScreenDimmer() {} + + virtual void draw(const Common::Rect &); +}; + +class SoundLevel : public DisplayElement { +public: + SoundLevel(const tDisplayElementID); + virtual ~SoundLevel() {} + + void incrementLevel(); + void decrementLevel(); + + uint16 getSoundLevel(); + void setSoundLevel(uint16); + + void draw(const Common::Rect &); + +protected: + uint16 _soundLevel; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp new file mode 100755 index 0000000000..2076ae1240 --- /dev/null +++ b/engines/pegasus/energymonitor.cpp @@ -0,0 +1,296 @@ +/* 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/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Blinker::Blinker() { + _sprite = 0; + _frame1 = -1; + _frame2 = -1; + _blinkDuration = 0; +} + +void Blinker::startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale) { + stopBlinking(); + _sprite = sprite; + _frame1 = frame1; + _frame2 = frame2; + _blinkDuration = blinkDuration; + setScale(blinkScale); + setSegment(0, blinkDuration * numBlinks * 2, blinkScale); + setTime(0); + start(); +} + +void Blinker::stopBlinking() { + if (_sprite) { + _sprite->setCurrentFrameIndex(_frame2); + _sprite = 0; + stop(); + } +} + +void Blinker::timeChanged(const TimeValue time) { + if (_sprite && _blinkDuration != 0) { + if (((time / _blinkDuration) & 1) != 0 || time == getDuration()) { + _sprite->setCurrentFrameIndex(_frame2); + if (!isRunning()) + stopBlinking(); + } else { + _sprite->setCurrentFrameIndex(_frame1); + } + } +} + +static const tNotificationFlags kEnergyExpiredFlag = 1; + +EnergyMonitor *g_energyMonitor = 0; + +EnergyMonitor::EnergyMonitor() : IdlerAnimation(kEnergyBarID), _energyLight(kWarningLightID) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _stage = kStageNoStage; + + _calibrating = false; + _dontFlash = false; + + setBounds(338, 48, 434, 54); + + setDisplayOrder(kEnergyBarOrder); + startDisplaying(); + + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOffID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightYellowID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOrangeID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightRedID); + _energyLight.addFrame(frame, 0, 0); + + _energyLight.setBounds(540, 35, 600, 59); + _energyLight.setDisplayOrder(kEnergyLightOrder); + _energyLight.startDisplaying(); + + setScale(1); + setSegment(0, kMaxJMPEnergy); + + setEnergyValue(kCasualEnergy); + + g_energyMonitor = this; +} + +EnergyMonitor::~EnergyMonitor() { + g_energyMonitor = 0; +} + +void EnergyMonitor::setEnergyValue(const uint32 value) { + if (isRunning()) { + stop(); + setTime(getStop() - value); + start(); + } else { + setTime(getStop() - value); + } +} + +void EnergyMonitor::startEnergyDraining() { + if (!isRunning()) { + _energyLight.show(); + start(); + show(); + } +} + +void EnergyMonitor::setEnergyDrainRate(Common::Rational rate) { + setRate(rate); +} + +Common::Rational EnergyMonitor::getEnergyDrainRate() { + return getRate(); +} + +void EnergyMonitor::stopEnergyDraining() { + if (isRunning()) { + stop(); + _energyLight.hide(); + hide(); + } +} + +void EnergyMonitor::drainEnergy(const int32 delta) { + setTime(getTime() + delta); +} + +int32 EnergyMonitor::getCurrentEnergy() { + return kMaxJMPEnergy - getTime(); +} + +void EnergyMonitor::timeChanged(const TimeValue currentTime) { + if (currentTime == getStop()) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + if (vm->getEnergyDeathReason() != -1) + vm->die(vm->getEnergyDeathReason()); + } else { + uint32 currentEnergy = kMaxJMPEnergy - currentTime; + + tEnergyStage newStage; + if (currentEnergy > kWorriedEnergy) + newStage = kStageCasual; + else if (currentEnergy > kNervousEnergy) + newStage = kStageWorried; + else if (currentEnergy > kPanicStrickenEnergy) + newStage = kStageNervous; + else + newStage = kStagePanicStricken; + + if (_stage != newStage) { + uint32 newFrame; + + switch (newStage) { + case kStageCasual: + _barColor = g_system->getScreenFormat().RGBToColor(0x48, 0xB0, 0xD8); + newFrame = kFrameLightOff; + break; + case kStageWorried: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0xC0, 0x30); + newFrame = kFrameLightYellow; + break; + case kStageNervous: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x78, 0x38); + newFrame = kFrameLightOrange; + break; + case kStagePanicStricken: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x40, 0x38); + newFrame = kFrameLightRed; + break; + default: + error("no stage in energy monitor?"); + break; + } + + _stage = newStage; + uint32 oldFrame = _energyLight.getCurrentFrameIndex(); + + if (!_calibrating) { + if (oldFrame > newFrame || oldFrame == 0xffffffff || _dontFlash) { + _energyLight.setCurrentFrameIndex(newFrame); + _dontFlash = false; + } else { + _lightBlinker.startBlinking(&_energyLight, oldFrame, newFrame, 4, 1, 3); + triggerRedraw(); + } + } + } + + Common::Rect r; + calcLevelRect(r); + if (r != _levelRect) { + _levelRect = r; + triggerRedraw(); + } + } +} + +void EnergyMonitor::calcLevelRect(Common::Rect &r) { + if (getStop() == 0) { + r = Common::Rect(); + } else { + getBounds(r); + r.left = r.right - r.width() * (kMaxJMPEnergy - getTime()) / getStop(); + } +} + +void EnergyMonitor::draw(const Common::Rect &r) { + Common::Rect r2 = r.findIntersectingRect(_levelRect); + + if (!r2.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(r2, _barColor); + } +} + +void EnergyMonitor::calibrateEnergyBar() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _calibrating = true; + + vm->setEnergyDeathReason(-1); + + uint32 numFrames = _energyLight.getNumFrames(); + for (uint32 i = 1; i < numFrames; i++) { + _energyLight.setCurrentFrameIndex(i); + _energyLight.show(); + vm->delayShell(1, 3); + _energyLight.hide(); + vm->delayShell(1, 3); + } + + _energyLight.setCurrentFrameIndex(0); + _energyLight.hide(); + + show(); + setEnergyValue(0); + setEnergyDrainRate(-(int32)kMaxJMPEnergy / 2); + + // Make sure warning light is hidden... + _energyLight.hide(); + while (getCurrentEnergy() != (int32)kMaxJMPEnergy) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + setEnergyDrainRate(0); + hide(); + + _calibrating = false; +} + +void EnergyMonitor::restoreLastEnergyValue() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _dontFlash = true; + setEnergyValue(vm->getSavedEnergyValue()); + vm->resetEnergyDeathReason(); +} + +void EnergyMonitor::saveCurrentEnergyValue() { + ((PegasusEngine *)g_engine)->setLastEnergyValue(getCurrentEnergy()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/energymonitor.h b/engines/pegasus/energymonitor.h new file mode 100755 index 0000000000..cb5d4498fc --- /dev/null +++ b/engines/pegasus/energymonitor.h @@ -0,0 +1,111 @@ +/* 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_ENERGYMONITOR_H +#define PEGASUS_ENERGYMONITOR_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class Sprite; + +class Blinker : private IdlerTimeBase { +public: + Blinker(); + virtual ~Blinker() {} + + void startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale); + void stopBlinking(); + +protected: + virtual void timeChanged(const TimeValue); + + Sprite *_sprite; + int32 _frame1; + int32 _frame2; + TimeValue _blinkDuration; +}; + +// Energy monitor constants. + +// These are in seconds. +// Max is two hours +static const uint32 kMaxJMPEnergy = 7200; + +static const uint32 kCasualEnergy = kMaxJMPEnergy * 100 / 100; // 100% +static const uint32 kWorriedEnergy = kMaxJMPEnergy * 50 / 100; // 50% +static const uint32 kNervousEnergy = kMaxJMPEnergy * 25 / 100; // 25% +static const uint32 kPanicStrickenEnergy = kMaxJMPEnergy * 5 / 100; // 5% + +static const uint32 kFullEnergy = kCasualEnergy; + +static const uint32 kFrameLightOff = 0; +static const uint32 kFrameLightYellow = 1; +static const uint32 kFrameLightOrange = 2; +static const uint32 kFrameLightRed = 3; + +static const int kEnergyDrainNormal = 1; +static const int kMarsReactorEnergyDrainNoShield = 6; +static const int kMarsReactorEnergyDrainWithShield = 3; +static const int kWSCPoisonEnergyDrainWithDart = 20; +static const int kWSCPoisonEnergyDrainNoDart = 10; + +class EnergyMonitor : private IdlerAnimation { +public: + EnergyMonitor(); + virtual ~EnergyMonitor(); + + void setEnergyValue(const uint32); + void startEnergyDraining(); + void setEnergyDrainRate(Common::Rational); + Common::Rational getEnergyDrainRate(); + void stopEnergyDraining(); + void drainEnergy(const int32); + int32 getCurrentEnergy(); + + void restoreLastEnergyValue(); + void saveCurrentEnergyValue(); + + void calibrateEnergyBar(); + +protected: + void timeChanged(const TimeValue); + void calcLevelRect(Common::Rect &); + void draw(const Common::Rect &); + + uint32 _barColor; + Common::Rect _levelRect; + tEnergyStage _stage; + Sprite _energyLight; + Blinker _lightBlinker; + bool _calibrating, _dontFlash; +}; + +extern EnergyMonitor *g_energyMonitor; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/fader.cpp b/engines/pegasus/fader.cpp new file mode 100755 index 0000000000..77ad2cbe4e --- /dev/null +++ b/engines/pegasus/fader.cpp @@ -0,0 +1,218 @@ +/* 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/fader.h" +#include "pegasus/pegasus.h" +#include "pegasus/sound.h" +#include "pegasus/util.h" + +namespace Pegasus { + +Fader::Fader() { + _currentValue = 0; + _currentFaderMove._numKnots = 0; +} + +void Fader::setFaderValue(const int32 newValue) { + _currentValue = newValue; +} + +bool Fader::initFaderMove(const FaderMoveSpec &spec) { + bool faderMoves = false; + int32 value = 0; + + if (spec._numKnots > 0) { + stopFader(); + value = spec._knots[0].knotValue; + TimeValue startTime = spec._knots[0].knotTime; + + if (startTime != 0xffffffff) { + if (spec._numKnots > 1) { + TimeValue stopTime = spec._knots[spec._numKnots - 1].knotTime; + + if (spec._faderScale > 0) { + if (stopTime > startTime) { + for (uint32 i = 1; i < spec._numKnots; ++i) { + if (spec._knots[i - 1].knotValue != spec._knots[i].knotValue) { + faderMoves = true; + break; + } + } + + if (faderMoves) + _currentFaderMove = spec; + } else if (spec._knots[spec._numKnots - 1].knotValue != value) { + value = spec._knots[spec._numKnots - 1].knotValue; + } + } + } + } + } + + setFaderValue(value); + return faderMoves; +} + +void Fader::startFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::startFaderSync(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + + while (isFading()) { + ((PegasusEngine *)g_engine)->checkCallBacks(); + useIdleTime(); + } + + // Once more, for good measure, to make sure that there are no boundary + // condition problems. + useIdleTime(); + stopFader(); + } +} + +void Fader::loopFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(kLoopTimeBase); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::stopFader() { + stop(); +} + +void Fader::pauseFader() { + stopFader(); +} + +void Fader::continueFader() { + if (getTime() < getStop()) + start(); +} + +void Fader::timeChanged(const TimeValue newTime) { + if (_currentFaderMove._numKnots != 0) { + uint32 i; + for (i = 0; i < _currentFaderMove._numKnots; i++) + if (_currentFaderMove._knots[i].knotTime > newTime) + break; + + int32 newValue; + if (i == 0) + newValue = _currentFaderMove._knots[0].knotValue; + else if (i == _currentFaderMove._numKnots) + newValue = _currentFaderMove._knots[i - 1].knotValue; + else + newValue = linearInterp(_currentFaderMove._knots[i - 1].knotTime, _currentFaderMove._knots[i].knotTime, newTime, _currentFaderMove._knots[i - 1].knotValue, _currentFaderMove._knots[i].knotValue); + + if (newValue != _currentValue) + setFaderValue(newValue); + } +} + +void FaderMoveSpec::makeOneKnotFaderSpec(const int32 knotValue) { + _numKnots = 1; + _knots[0].knotTime = 0; + _knots[0].knotValue = knotValue; +} + +void FaderMoveSpec::makeTwoKnotFaderSpec(const TimeScale faderScale, const TimeValue time1, const int32 value1, const TimeValue time2, const int32 value2) { + _numKnots = 2; + _faderScale = faderScale; + _knots[0].knotTime = time1; + _knots[0].knotValue = value1; + _knots[1].knotTime = time2; + _knots[1].knotValue = value2; +} + +void FaderMoveSpec::insertFaderKnot(const TimeValue knotTime, const int32 knotValue) { + if (_numKnots != kMaxFaderKnots) { + uint32 index; + for (index = 0; index < _numKnots; index++) { + if (knotTime == _knots[index].knotTime) { + _knots[index].knotValue = knotValue; + return; + } else if (knotTime < _knots[index].knotTime) { + break; + } + } + + for (uint32 i = _numKnots; i > index; i--) + _knots[i] = _knots[i - 1]; + + _knots[index].knotTime = knotTime; + _knots[index].knotValue = knotValue; + _numKnots++; + } +} + +void FaderAnimation::setFaderValue(const int32 newValue) { + if (getFaderValue() != newValue) { + Fader::setFaderValue(newValue); + triggerRedraw(); + } +} + +SoundFader::SoundFader() { + _sound = 0; + _masterVolume = 0xff; +} + +void SoundFader::attachSound(Sound *sound) { + if (!sound && isFading()) + stopFader(); + + _sound = sound; +} + +void SoundFader::setFaderValue(const int32 newVolume) { + if (_sound) + _sound->setVolume((newVolume * _masterVolume) >> 8); + + _currentValue = newVolume; +} + +void SoundFader::setMasterVolume(const uint16 masterVolume) { + _masterVolume = masterVolume; + setFaderValue(getFaderValue()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/fader.h b/engines/pegasus/fader.h new file mode 100755 index 0000000000..8c4fb0ba81 --- /dev/null +++ b/engines/pegasus/fader.h @@ -0,0 +1,130 @@ +/* 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_FADER_H +#define PEGASUS_FADER_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Fader; + +class FaderMoveSpec { +friend class Fader; +public: + FaderMoveSpec() { + _faderScale = kDefaultTimeScale; + _numKnots = 0; + } + + FaderMoveSpec(const TimeScale scale) { + _faderScale = scale; + _numKnots = 0; + } + + void setFaderScale(const TimeScale scale) { _faderScale = scale; } + TimeScale getFaderScale() const { return _faderScale; } + + void makeOneKnotFaderSpec(const int32); + void makeTwoKnotFaderSpec(const TimeScale, const TimeValue, const int32, const TimeValue, const int32); + + void insertFaderKnot(const TimeValue, const int32); + + uint32 getNumKnots() const { return _numKnots; } + TimeValue getNthKnotTime(const uint32 index) const { return _knots[index].knotTime; } + int32 getNthKnotValue(const uint32 index) const { return _knots[index].knotValue; } + +protected: + struct FaderKnot { + TimeValue knotTime; + int32 knotValue; + }; + + TimeScale _faderScale; + uint32 _numKnots; + + static const uint32 kMaxFaderKnots = 20; + FaderKnot _knots[kMaxFaderKnots]; +}; + +class Fader : public IdlerTimeBase { +public: + Fader(); + virtual ~Fader() {} + + virtual void setFaderValue(const int32); + int32 getFaderValue() const { return _currentValue; } + virtual void startFader(const FaderMoveSpec &); + virtual void startFaderSync(const FaderMoveSpec &); + virtual void loopFader(const FaderMoveSpec &); + virtual void stopFader(); + virtual bool isFading() { return isRunning(); } + + void pauseFader(); + void continueFader(); + + void getCurrentFaderMove(FaderMoveSpec &spec) { spec = _currentFaderMove; } + +protected: + bool initFaderMove(const FaderMoveSpec &); + virtual void timeChanged(const TimeValue); + + int32 _currentValue; + FaderMoveSpec _currentFaderMove; +}; + +class FaderAnimation : public DisplayElement, public Fader { +public: + FaderAnimation(const tDisplayElementID id) : DisplayElement(id) {} + virtual ~FaderAnimation() {} + + void setFaderValue(const int32); +}; + +class Sound; + +class SoundFader : public Fader { +friend class Sound; +public: + SoundFader(); + virtual ~SoundFader() {} + + void setFaderValue(const int32); + + void setMasterVolume(const uint16); + uint16 getMasterVolume() const { return _masterVolume; } + +protected: + void attachSound(Sound *); + + Sound *_sound; + uint16 _masterVolume; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/gamestate.cpp b/engines/pegasus/gamestate.cpp new file mode 100755 index 0000000000..5c889bdd20 --- /dev/null +++ b/engines/pegasus/gamestate.cpp @@ -0,0 +1,2359 @@ +/* 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/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/gamestate.h" +#include "pegasus/scoring.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::GameStateManager); +} + +namespace Pegasus { + +Common::Error GameStateManager::writeGameState(Common::WriteStream *stream) { + stream->writeUint16BE(_currentNeighborhood); + stream->writeUint16BE(_currentRoom); + stream->writeByte(_currentDirection); + stream->writeUint16BE(_nextNeighborhoodID); + stream->writeUint16BE(_nextRoomID); + stream->writeByte(_nextDirection); + stream->writeUint16BE(_lastNeighborhood); + stream->writeUint16BE(_lastRoom); + stream->writeByte(_lastDirection); + stream->writeUint16BE(_openDoorRoom); + stream->writeByte(_openDoorDirection); + + _globalFlags.writeToStream(stream); + _scoringFlags.writeToStream(stream); + _itemTakenFlags.writeToStream(stream); + + writeCaldoriaState(stream); + writeTSAState(stream); + writePrehistoricState(stream); + writeNoradState(stream); + writeMarsState(stream); + writeWSCState(stream); + + if (stream->err()) + return Common::kWritingFailed; + + return Common::kNoError; +} + +Common::Error GameStateManager::readGameState(Common::ReadStream *stream) { + _currentNeighborhood = stream->readUint16BE(); + _currentRoom = stream->readUint16BE(); + _currentDirection = stream->readByte(); + _nextNeighborhoodID = stream->readUint16BE(); + _nextRoomID = stream->readUint16BE(); + _nextDirection = stream->readByte(); + _lastNeighborhood = stream->readUint16BE(); + _lastRoom = stream->readUint16BE(); + _lastDirection = stream->readByte(); + _openDoorRoom = stream->readUint16BE(); + _openDoorDirection = stream->readByte(); + + _globalFlags.readFromStream(stream); + _scoringFlags.readFromStream(stream); + _itemTakenFlags.readFromStream(stream); + + readCaldoriaState(stream); + readTSAState(stream); + readPrehistoricState(stream); + readNoradState(stream); + readMarsState(stream); + readWSCState(stream); + + if (stream->err()) + return Common::kReadingFailed; + + return Common::kNoError; +} + +void GameStateManager::resetGameState() { + _currentNeighborhood = kNoNeighborhoodID; + _currentRoom = kNoRoomID; + _currentDirection = kNoDirection; + _nextNeighborhoodID = kNoNeighborhoodID; + _nextRoomID = kNoRoomID; + _nextDirection = kNoDirection; + _lastNeighborhood = kNoNeighborhoodID; + _lastRoom = kNoRoomID; + _lastDirection = kNoDirection; + _openDoorRoom = kNoRoomID; + _openDoorDirection = kNoDirection; + + _globalFlags.clearAllFlags(); + _scoringFlags.clearAllFlags(); + _itemTakenFlags.clearAllFlags(); + + resetCaldoriaState(); + resetTSAState(); + resetPrehistoricState(); + resetNoradState(); + resetMarsState(); + resetWSCState(); +} + +void GameStateManager::getCurrentLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setCurrentLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) { + _lastNeighborhood = _currentNeighborhood; + _lastRoom = _currentRoom; + _lastDirection = _currentDirection; + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +tNeighborhoodID GameStateManager::getCurrentNeighborhood() { + return _currentNeighborhood; +} + +void GameStateManager::setCurrentNeighborhood(const tNeighborhoodID neighborhood) { + _lastNeighborhood = _currentNeighborhood; + _currentNeighborhood = neighborhood; +} + +tRoomID GameStateManager::getCurrentRoom() { + return _currentRoom; +} + +void GameStateManager::setCurrentRoom(const tRoomID room) { + _lastRoom = _currentRoom; + _currentRoom = room; +} + +tDirectionConstant GameStateManager::getCurrentDirection() { + return _currentDirection; +} + +void GameStateManager::setCurrentDirection(const tDirectionConstant direction) { + _lastDirection = _currentDirection; + _currentDirection = direction; +} + +tRoomViewID GameStateManager::getCurrentRoomAndView() { + return MakeRoomView(_currentRoom, _currentDirection); +} + +void GameStateManager::getNextLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction) { + neighborhood = _nextNeighborhoodID; + room = _nextRoomID; + direction = _nextDirection; +} + +void GameStateManager::setNextLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) { + _nextNeighborhoodID = neighborhood; + _nextRoomID = room; + _nextDirection = direction; +} + +tNeighborhoodID GameStateManager::getNextNeighborhood() { + return _nextNeighborhoodID; +} + +void GameStateManager::setNextNeighborhood(const tNeighborhoodID neighborhood) { + _nextNeighborhoodID = neighborhood; +} + +tRoomID GameStateManager::getNextRoom() { + return _nextRoomID; +} + +void GameStateManager::setNextRoom(const tRoomID room) { + _nextRoomID = room; +} + +tDirectionConstant GameStateManager::getNextDirection() { + return _nextDirection; +} + +void GameStateManager::setNextDirection(const tDirectionConstant direction) { + _nextDirection = direction; +} + +void GameStateManager::getLastLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setLastLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) { + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +tNeighborhoodID GameStateManager::getLastNeighborhood() { + return _lastNeighborhood; +} + +void GameStateManager::setLastNeighborhood(const tNeighborhoodID neighborhood) { + _lastNeighborhood = neighborhood; +} + +tRoomID GameStateManager::getLastRoom() { + return _lastRoom; +} + +void GameStateManager::setLastRoom(const tRoomID room) { + _lastRoom = room; +} + +tDirectionConstant GameStateManager::getLastDirection() { + return _lastDirection; +} + +void GameStateManager::setLastDirection(const tDirectionConstant direction) { + _lastDirection = direction; +} + +tRoomViewID GameStateManager::getLastRoomAndView() { + return MakeRoomView(_lastRoom, _lastDirection); +} + +void GameStateManager::getOpenDoorLocation(tRoomID &room, tDirectionConstant &direction) { + room = _openDoorRoom; + direction = _openDoorDirection; +} + +void GameStateManager::setOpenDoorLocation(const tRoomID room, const tDirectionConstant direction) { + _openDoorRoom = room; + _openDoorDirection = direction; +} + +tRoomID GameStateManager::getOpenDoorRoom() { + return _openDoorRoom; +} + +void GameStateManager::setOpenDoorRoom(const tRoomID room) { + _openDoorRoom = room; +} + +tDirectionConstant GameStateManager::getOpenDoorDirection() { + return _openDoorDirection; +} + +void GameStateManager::setOpenDoorDirection(const tDirectionConstant direction) { + _openDoorDirection = direction; +} + +tRoomViewID GameStateManager::getDoorOpenRoomAndView() { + return MakeRoomView(_openDoorRoom, _openDoorDirection); +} + +bool GameStateManager::isCurrentDoorOpen() { + return _openDoorRoom == _currentRoom && _openDoorDirection == _currentDirection; +} + +tGameScoreType GameStateManager::getCaldoriaTSAScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawINNFlag)) + result += kSawINNScore; + if (_scoringFlags.getFlag(kScoringTookShowerFlag)) + result += kTookShowerScore; + if (_scoringFlags.getFlag(kScoringFixedHairFlag)) + result += kFixedHairScore; + if (_scoringFlags.getFlag(kScoringGotKeyCardFlag)) + result += kGotKeyCardScore; + if (_scoringFlags.getFlag(kScoringReadPaperFlag)) + result += kReadPaperScore; + if (_scoringFlags.getFlag(kScoringLookThroughTelescopeFlag)) + result += kLookThroughTelescopeScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag)) + result += kSawCaldoriaKioskScore; + if (_scoringFlags.getFlag(kScoringGoToTSAFlag)) + result += kGoToTSAScore; + if (_scoringFlags.getFlag(kScoringEnterTSAFlag)) + result += kEnterTSAScore; + if (_scoringFlags.getFlag(kScoringSawBust1Flag)) + result += kSawBust1Score; + if (_scoringFlags.getFlag(kScoringSawBust2Flag)) + result += kSawBust2Score; + if (_scoringFlags.getFlag(kScoringSawBust3Flag)) + result += kSawBust3Score; + if (_scoringFlags.getFlag(kScoringSawBust4Flag)) + result += kSawBust4Score; + if (_scoringFlags.getFlag(kScoringSawBust5Flag)) + result += kSawBust5Score; + if (_scoringFlags.getFlag(kScoringSawBust6Flag)) + result += kSawBust6Score; + if (_scoringFlags.getFlag(kScoringSawTheoryFlag)) + result += kSawTheoryScore; + if (_scoringFlags.getFlag(kScoringSawBackgroundFlag)) + result += kSawBackgroundScore; + if (_scoringFlags.getFlag(kScoringSawProcedureFlag)) + result += kSawProcedureScore; + if (_scoringFlags.getFlag(kScoringGotJourneymanKeyFlag)) + result += kGotJourneymanKeyScore; + if (_scoringFlags.getFlag(kScoringGotPegasusBiochipFlag)) + result += kGotPegasusBiochipScore; + if (_scoringFlags.getFlag(kScoringGotBiosuitFlag)) + result += kGotBiosuitScore; + if (_scoringFlags.getFlag(kScoringGoToPrehistoricFlag)) + result += kGoToPrehistoricScore; + if (_scoringFlags.getFlag(kScoringPutLogInReaderFlag)) + result += kPutLogInReaderScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag)) + result += kSawCaldoriaNormalScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag)) + result += kSawCaldoriaAlteredScore; + if (_scoringFlags.getFlag(kScoringSawNoradNormalFlag)) + result += kSawNoradNormalScore; + if (_scoringFlags.getFlag(kScoringSawNoradAlteredFlag)) + result += kSawNoradAlteredScore; + if (_scoringFlags.getFlag(kScoringSawMarsNormalFlag)) + result += kSawMarsNormalScore; + if (_scoringFlags.getFlag(kScoringSawMarsAlteredFlag)) + result += kSawMarsAlteredScore; + if (_scoringFlags.getFlag(kScoringSawWSCNormalFlag)) + result += kSawWSCNormalScore; + if (_scoringFlags.getFlag(kScoringSawWSCAlteredFlag)) + result += kSawWSCAlteredScore; + if (_scoringFlags.getFlag(kScoringWentToReadyRoom2Flag)) + result += kWentToReadyRoom2Score; + if (_scoringFlags.getFlag(kScoringWentAfterSinclairFlag)) + result += kWentAfterSinclairScore; + if (_scoringFlags.getFlag(kScoringUsedCardBombFlag)) + result += kUsedCardBombScore; + if (_scoringFlags.getFlag(kScoringShieldedCardBombFlag)) + result += kShieldedCardBombScore; + if (_scoringFlags.getFlag(kScoringStunnedSinclairFlag)) + result += kStunnedSinclairScore; + if (_scoringFlags.getFlag(kScoringDisarmedNukeFlag)) + result += kDisarmedNukeScore; + + return result; +} + +tGameScoreType GameStateManager::getPrehistoricScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrewBreakerFlag)) + result += kThrewBreakerScore; + if (_scoringFlags.getFlag(kScoringExtendedBridgeFlag)) + result += kExtendedBridgeScore; + if (_scoringFlags.getFlag(kScoringGotHistoricalLogFlag)) + result += kGotHistoricalLogScore; + if (_scoringFlags.getFlag(kScoringFinishedPrehistoricFlag)) + result += kFinishedPrehistoricScore; + + return result; +} + +tGameScoreType GameStateManager::getMarsScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrownByRobotFlag)) + result += kThrownByRobotScore; + if (_scoringFlags.getFlag(kScoringGotMarsCardFlag)) + result += kGotMarsCardScore; + if (_scoringFlags.getFlag(kScoringSawMarsKioskFlag)) + result += kSawMarsKioskScore; + if (_scoringFlags.getFlag(kScoringSawTransportMapFlag)) + result += kSawTransportMapScore; + if (_scoringFlags.getFlag(kScoringGotCrowBarFlag)) + result += kGotCrowBarScore; + if (_scoringFlags.getFlag(kScoringTurnedOnTransportFlag)) + result += kTurnedOnTransportScore; + if (_scoringFlags.getFlag(kScoringGotOxygenMaskFlag)) + result += kGotOxygenMaskScore; + if (_scoringFlags.getFlag(kScoringAvoidedRobotFlag)) + result += kAvoidedRobotScore; + if (_scoringFlags.getFlag(kScoringActivatedPlatformFlag)) + result += kActivatedPlatformScore; + if (_scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag)) + result += kUsedLiquidNitrogenScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarFlag)) + result += kUsedCrowBarScore; + if (_scoringFlags.getFlag(kScoringFoundCardBombFlag)) + result += kFoundCardBombScore; + if (_scoringFlags.getFlag(kScoringDisarmedCardBombFlag)) + result += kDisarmedCardBombScore; + if (_scoringFlags.getFlag(kScoringGotCardBombFlag)) + result += kGotCardBombScore; + if (_scoringFlags.getFlag(kScoringThreadedMazeFlag)) + result += kThreadedMazeScore; + if (_scoringFlags.getFlag(kScoringThreadedGearRoomFlag)) + result += kThreadedGearRoomScore; + if (_scoringFlags.getFlag(kScoringEnteredShuttleFlag)) + result += kEnteredShuttleScore; + if (_scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag)) + result += kEnteredLaunchTubeScore; + if (_scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag)) + result += kStoppedRobotsShuttleScore; + if (_scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag)) + result += kGotMarsOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedMarsFlag)) + result += kFinishedMarsScore; + + return result; +} + +tGameScoreType GameStateManager::getNoradScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawSecurityMonitorFlag)) + result += kSawSecurityMonitorScore; + if (_scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag)) + result += kFilledOxygenCanisterScore; + if (_scoringFlags.getFlag(kScoringFilledArgonCanisterFlag)) + result += kFilledArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag)) + result += kSawUnconsciousOperatorScore; + if (_scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag)) + result += kWentThroughPressureDoorScore; + if (_scoringFlags.getFlag(kScoringPreppedSubFlag)) + result += kPreppedSubScore; + if (_scoringFlags.getFlag(kScoringEnteredSubFlag)) + result += kEnteredSubScore; + if (_scoringFlags.getFlag(kScoringExitedSubFlag)) + result += kExitedSubScore; + if (_scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag)) + result += kSawRobotAt54NorthScore; + if (_scoringFlags.getFlag(kScoringPlayedWithClawFlag)) + result += kPlayedWithClawScore; + if (_scoringFlags.getFlag(kScoringUsedRetinalChipFlag)) + result += kUsedRetinalChipScore; + if (_scoringFlags.getFlag(kScoringFinishedGlobeGameFlag)) + result += kFinishedGlobeGameScore; + if (_scoringFlags.getFlag(kScoringStoppedNoradRobotFlag)) + result += kStoppedNoradRobotScore; + if (_scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag)) + result += kGotNoradOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedNoradFlag)) + result += kFinishedNoradScore; + + return result; +} + +tGameScoreType GameStateManager::getWSCScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringRemovedDartFlag)) + result += kRemovedDartScore; + if (_scoringFlags.getFlag(kScoringAnalyzedDartFlag)) + result += kAnalyzedDartScore; + if (_scoringFlags.getFlag(kScoringBuiltAntidoteFlag)) + result += kBuiltAntidoteScore; + if (_scoringFlags.getFlag(kScoringGotSinclairKeyFlag)) + result += kGotSinclairKeyScore; + if (_scoringFlags.getFlag(kScoringGotArgonCanisterFlag)) + result += kGotArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag)) + result += kGotNitrogenCanisterScore; + if (_scoringFlags.getFlag(kScoringPlayedWithMessagesFlag)) + result += kPlayedWithMessagesScore; + if (_scoringFlags.getFlag(kScoringSawMorphExperimentFlag)) + result += kSawMorphExperimentScore; + if (_scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag)) + result += kEnteredSinclairOfficeScore; + if (_scoringFlags.getFlag(kScoringSawBrochureFlag)) + result += kSawBrochureScore; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry1Flag)) + result += kSawSinclairEntry1Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry2Flag)) + result += kSawSinclairEntry2Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry3Flag)) + result += kSawSinclairEntry3Score; + if (_scoringFlags.getFlag(kScoringSawWSCDirectoryFlag)) + result += kSawWSCDirectoryScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag)) + result += kUsedCrowBarInWSCScore; + if (_scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag)) + result += kFinishedPlasmaDodgeScore; + if (_scoringFlags.getFlag(kScoringOpenedCatwalkFlag)) + result += kOpenedCatwalkScore; + if (_scoringFlags.getFlag(kScoringStoppedWSCRobotFlag)) + result += kStoppedWSCRobotScore; + if (_scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag)) + result += kGotWSCOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedWSCFlag)) + result += kFinishedWSCScore; + + return result; +} + +tGameScoreType GameStateManager::getGandhiScore() { + tGameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringMarsGandhiFlag)) + result += kMarsGandhiScore; + if (_scoringFlags.getFlag(kScoringNoradGandhiFlag)) + result += kNoradGandhiScore; + if (_scoringFlags.getFlag(kScoringWSCGandhiFlag)) + result += kWSCGandhiScore; + + return result; +} + +tGameScoreType GameStateManager::getTotalScore() { + return getCaldoriaTSAScore() + + getPrehistoricScore() + + getMarsScore() + + getNoradScore() + + getWSCScore() + + getGandhiScore(); +} + +///////////////////////////////////////////// +// +// Caldoria data + +void GameStateManager::writeCaldoriaState(Common::WriteStream *stream) { + _caldoriaFlags.writeToStream(stream); + stream->writeUint32BE(_caldoriaFuseTimeLimit); +} + +void GameStateManager::readCaldoriaState(Common::ReadStream *stream) { + _caldoriaFlags.readFromStream(stream); + _caldoriaFuseTimeLimit = stream->readUint32BE(); +} + +void GameStateManager::resetCaldoriaState() { + _caldoriaFlags.clearAllFlags(); + _caldoriaFuseTimeLimit = 0; +} + +///////////////////////////////////////////// +// +// TSA data + +void GameStateManager::writeTSAState(Common::WriteStream *stream) { + _TSAFlags.writeToStream(stream); + stream->writeUint32BE(_TSARipTimerTime); + stream->writeUint32BE(_TSAFuseTimeLimit); + stream->writeByte(_TSAState); + stream->writeByte(_T0BMonitorMode); + stream->writeUint32BE(_T0BMonitorStart); +} + +void GameStateManager::readTSAState(Common::ReadStream *stream) { + _TSAFlags.readFromStream(stream); + _TSARipTimerTime = stream->readUint32BE(); + _TSAFuseTimeLimit = stream->readUint32BE(); + _TSAState = stream->readByte(); + _T0BMonitorMode = stream->readByte(); + _T0BMonitorStart = stream->readUint32BE(); +} + +void GameStateManager::resetTSAState() { + _TSAFlags.clearAllFlags(); + _TSAState = 0; + _T0BMonitorMode = 0; + _T0BMonitorStart = 0; + _TSARipTimerTime = 0; + _TSAFuseTimeLimit = kTSAUncreatedTimeLimit; +} + +///////////////////////////////////////////// +// +// Prehistoric data + +void GameStateManager::writePrehistoricState(Common::WriteStream *stream) { + _prehistoricFlags.writeToStream(stream); +} + +void GameStateManager::readPrehistoricState(Common::ReadStream *stream) { + _prehistoricFlags.readFromStream(stream); +} + +void GameStateManager::resetPrehistoricState() { + _prehistoricFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// Norad data + +void GameStateManager::writeNoradState(Common::WriteStream *stream) { + _noradFlags.writeToStream(stream); + stream->writeUint16BE(_noradSubRoomPressure); + stream->writeByte(_noradSubPrepState); +} + +void GameStateManager::readNoradState(Common::ReadStream *stream) { + _noradFlags.readFromStream(stream); + _noradSubRoomPressure = stream->readUint16BE(); + _noradSubPrepState = (tNoradSubPrepState)stream->readByte(); +} + +void GameStateManager::resetNoradState() { + _noradFlags.clearAllFlags(); + _noradSubRoomPressure = 9; + _noradSubPrepState = kSubNotPrepped; +} + +///////////////////////////////////////////// +// +// Mars data + +void GameStateManager::writeMarsState(Common::WriteStream *stream) { + _marsFlags.writeToStream(stream); +} + +void GameStateManager::readMarsState(Common::ReadStream *stream) { + _marsFlags.readFromStream(stream); +} + +void GameStateManager::resetMarsState() { + _marsFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// WSC data + +void GameStateManager::writeWSCState(Common::WriteStream *stream) { + _WSCFlags.writeToStream(stream); +} + +void GameStateManager::readWSCState(Common::ReadStream *stream) { + _WSCFlags.readFromStream(stream); +} + +void GameStateManager::resetWSCState() { + _WSCFlags.clearAllFlags(); +} + +void GameStateManager::setScoringSawINN(const bool flag) { + _scoringFlags.setFlag(kScoringSawINNFlag, flag); +} + +void GameStateManager::setScoringTookShower(const bool flag) { + _scoringFlags.setFlag(kScoringTookShowerFlag, flag); +} + +void GameStateManager::setScoringFixedHair(const bool flag) { + _scoringFlags.setFlag(kScoringFixedHairFlag, flag); +} + +void GameStateManager::setScoringGotKeyCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotKeyCardFlag, flag); +} + +void GameStateManager::setScoringReadPaper(const bool flag) { + _scoringFlags.setFlag(kScoringReadPaperFlag, flag); +} + +void GameStateManager::setScoringLookThroughTelescope(const bool flag) { + _scoringFlags.setFlag(kScoringLookThroughTelescopeFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaKioskFlag, flag); +} + +void GameStateManager::setScoringGoToTSA(const bool flag) { + _scoringFlags.setFlag(kScoringGoToTSAFlag, flag); +} + +void GameStateManager::setScoringEnterTSA(const bool flag) { + _scoringFlags.setFlag(kScoringEnterTSAFlag, flag); +} + +void GameStateManager::setScoringSawBust1(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust1Flag, flag); +} + +void GameStateManager::setScoringSawBust2(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust2Flag, flag); +} + +void GameStateManager::setScoringSawBust3(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust3Flag, flag); +} + +void GameStateManager::setScoringSawBust4(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust4Flag, flag); +} + +void GameStateManager::setScoringSawBust5(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust5Flag, flag); +} + +void GameStateManager::setScoringSawBust6(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust6Flag, flag); +} + +void GameStateManager::setScoringSawTheory(const bool flag) { + _scoringFlags.setFlag(kScoringSawTheoryFlag, flag); +} + +void GameStateManager::setScoringSawBackground(const bool flag) { + _scoringFlags.setFlag(kScoringSawBackgroundFlag, flag); +} + +void GameStateManager::setScoringSawProcedure(const bool flag) { + _scoringFlags.setFlag(kScoringSawProcedureFlag, flag); +} + +void GameStateManager::setScoringGotJourneymanKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotJourneymanKeyFlag, flag); +} + +void GameStateManager::setScoringGotPegasusBiochip(const bool flag) { + _scoringFlags.setFlag(kScoringGotPegasusBiochipFlag, flag); +} + +void GameStateManager::setScoringGotBiosuit(const bool flag) { + _scoringFlags.setFlag(kScoringGotBiosuitFlag, flag); +} + +void GameStateManager::setScoringGoToPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringGoToPrehistoricFlag, flag); +} + +void GameStateManager::setScoringPutLogInReader(const bool flag) { + _scoringFlags.setFlag(kScoringPutLogInReaderFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaNormalFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaAlteredFlag, flag); +} + +void GameStateManager::setScoringSawNoradNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradNormalFlag, flag); +} + +void GameStateManager::setScoringSawNoradAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradAlteredFlag, flag); +} + +void GameStateManager::setScoringSawMarsNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsNormalFlag, flag); +} + +void GameStateManager::setScoringSawMarsAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsAlteredFlag, flag); +} + +void GameStateManager::setScoringSawWSCNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCNormalFlag, flag); +} + +void GameStateManager::setScoringSawWSCAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCAlteredFlag, flag); +} + +void GameStateManager::setScoringWentToReadyRoom2(const bool flag) { + _scoringFlags.setFlag(kScoringWentToReadyRoom2Flag, flag); +} + +void GameStateManager::setScoringWentAfterSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringWentAfterSinclairFlag, flag); +} + +void GameStateManager::setScoringUsedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCardBombFlag, flag); +} + +void GameStateManager::setScoringShieldedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringShieldedCardBombFlag, flag); +} + +void GameStateManager::setScoringStunnedSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringStunnedSinclairFlag, flag); +} + +void GameStateManager::setScoringDisarmedNuke(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedNukeFlag, flag); +} + +void GameStateManager::setScoringThrewBreaker(const bool flag) { + _scoringFlags.setFlag(kScoringThrewBreakerFlag, flag); +} + +void GameStateManager::setScoringExtendedBridge(const bool flag) { + _scoringFlags.setFlag(kScoringExtendedBridgeFlag, flag); +} + +void GameStateManager::setScoringGotHistoricalLog(const bool flag) { + _scoringFlags.setFlag(kScoringGotHistoricalLogFlag, flag); +} + +void GameStateManager::setScoringFinishedPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPrehistoricFlag, flag); +} + +void GameStateManager::setScoringThrownByRobot(const bool flag) { + _scoringFlags.setFlag(kScoringThrownByRobotFlag, flag); +} + +void GameStateManager::setScoringGotMarsCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsCardFlag, flag); +} + +void GameStateManager::setScoringSawMarsKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsKioskFlag, flag); +} + +void GameStateManager::setScoringSawTransportMap(const bool flag) { + _scoringFlags.setFlag(kScoringSawTransportMapFlag, flag); +} + +void GameStateManager::setScoringGotCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringGotCrowBarFlag, flag); +} + +void GameStateManager::setScoringTurnedOnTransport(const bool flag) { + _scoringFlags.setFlag(kScoringTurnedOnTransportFlag, flag); +} + +void GameStateManager::setScoringGotOxygenMask(const bool flag) { + _scoringFlags.setFlag(kScoringGotOxygenMaskFlag, flag); +} + +void GameStateManager::setScoringAvoidedRobot(const bool flag) { + _scoringFlags.setFlag(kScoringAvoidedRobotFlag, flag); +} + +void GameStateManager::setScoringActivatedPlatform(const bool flag) { + _scoringFlags.setFlag(kScoringActivatedPlatformFlag, flag); +} + +void GameStateManager::setScoringUsedLiquidNitrogen(const bool flag) { + _scoringFlags.setFlag(kScoringUsedLiquidNitrogenFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarFlag, flag); +} + +void GameStateManager::setScoringFoundCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringFoundCardBombFlag, flag); +} + +void GameStateManager::setScoringDisarmedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedCardBombFlag, flag); +} + +void GameStateManager::setScoringGotCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringGotCardBombFlag, flag); +} + +void GameStateManager::setScoringThreadedMaze(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedMazeFlag, flag); +} + +void GameStateManager::setScoringThreadedGearRoom(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedGearRoomFlag, flag); +} + +void GameStateManager::setScoringEnteredShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredShuttleFlag, flag); +} + +void GameStateManager::setScoringEnteredLaunchTube(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredLaunchTubeFlag, flag); +} + +void GameStateManager::setScoringStoppedRobotsShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedRobotsShuttleFlag, flag); +} + +void GameStateManager::setScoringGotMarsOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedMars(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedMarsFlag, flag); +} + +void GameStateManager::setScoringSawSecurityMonitor(const bool flag) { + _scoringFlags.setFlag(kScoringSawSecurityMonitorFlag, flag); +} + +void GameStateManager::setScoringFilledOxygenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledOxygenCanisterFlag, flag); +} + +void GameStateManager::setScoringFilledArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringSawUnconsciousOperator(const bool flag) { + _scoringFlags.setFlag(kScoringSawUnconsciousOperatorFlag, flag); +} + +void GameStateManager::setScoringWentThroughPressureDoor(const bool flag) { + _scoringFlags.setFlag(kScoringWentThroughPressureDoorFlag, flag); +} + +void GameStateManager::setScoringPreppedSub(const bool flag) { + _scoringFlags.setFlag(kScoringPreppedSubFlag, flag); +} + +void GameStateManager::setScoringEnteredSub(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSubFlag, flag); +} + +void GameStateManager::setScoringExitedSub(const bool flag) { + _scoringFlags.setFlag(kScoringExitedSubFlag, flag); +} + +void GameStateManager::setScoringSawRobotAt54North(const bool flag) { + _scoringFlags.setFlag(kScoringSawRobotAt54NorthFlag, flag); +} + +void GameStateManager::setScoringPlayedWithClaw(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithClawFlag, flag); +} + +void GameStateManager::setScoringUsedRetinalChip(const bool flag) { + _scoringFlags.setFlag(kScoringUsedRetinalChipFlag, flag); +} + +void GameStateManager::setScoringFinishedGlobeGame(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedGlobeGameFlag, flag); +} + +void GameStateManager::setScoringStoppedNoradRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedNoradRobotFlag, flag); +} + +void GameStateManager::setScoringGotNoradOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotNoradOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedNorad(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedNoradFlag, flag); +} + +void GameStateManager::setScoringRemovedDart(const bool flag) { + _scoringFlags.setFlag(kScoringRemovedDartFlag, flag); +} + +void GameStateManager::setScoringAnalyzedDart(const bool flag) { + _scoringFlags.setFlag(kScoringAnalyzedDartFlag, flag); +} + +void GameStateManager::setScoringBuiltAntidote(const bool flag) { + _scoringFlags.setFlag(kScoringBuiltAntidoteFlag, flag); +} + +void GameStateManager::setScoringGotSinclairKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotSinclairKeyFlag, flag); +} + +void GameStateManager::setScoringGotArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringGotNitrogenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotNitrogenCanisterFlag, flag); +} + +void GameStateManager::setScoringPlayedWithMessages(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithMessagesFlag, flag); +} + +void GameStateManager::setScoringSawMorphExperiment(const bool flag) { + _scoringFlags.setFlag(kScoringSawMorphExperimentFlag, flag); +} + +void GameStateManager::setScoringEnteredSinclairOffice(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSinclairOfficeFlag, flag); +} + +void GameStateManager::setScoringSawBrochure(const bool flag) { + _scoringFlags.setFlag(kScoringSawBrochureFlag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry1(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry1Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry2(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry2Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry3(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry3Flag, flag); +} + +void GameStateManager::setScoringSawWSCDirectory(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCDirectoryFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBarInWSC(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarInWSCFlag, flag); +} + +void GameStateManager::setScoringFinishedPlasmaDodge(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPlasmaDodgeFlag, flag); +} + +void GameStateManager::setScoringOpenedCatwalk(const bool flag) { + _scoringFlags.setFlag(kScoringOpenedCatwalkFlag, flag); +} + +void GameStateManager::setScoringStoppedWSCRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedWSCRobotFlag, flag); +} + +void GameStateManager::setScoringGotWSCOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotWSCOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedWSC(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedWSCFlag, flag); +} + +void GameStateManager::setScoringMarsGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringMarsGandhiFlag, flag); +} + +void GameStateManager::setScoringNoradGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringNoradGandhiFlag, flag); +} + +void GameStateManager::setScoringWSCGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringWSCGandhiFlag, flag); +} + +bool GameStateManager::getScoringSawINN() { + return _scoringFlags.getFlag(kScoringSawINNFlag); +} + +bool GameStateManager::getScoringTookShower() { + return _scoringFlags.getFlag(kScoringTookShowerFlag); +} + +bool GameStateManager::getScoringFixedHair() { + return _scoringFlags.getFlag(kScoringFixedHairFlag); +} + +bool GameStateManager::getScoringGotKeyCard() { + return _scoringFlags.getFlag(kScoringGotKeyCardFlag); +} + +bool GameStateManager::getScoringReadPaper() { + return _scoringFlags.getFlag(kScoringReadPaperFlag); +} + +bool GameStateManager::getScoringLookThroughTelescope() { + return _scoringFlags.getFlag(kScoringLookThroughTelescopeFlag); +} + +bool GameStateManager::getScoringSawCaldoriaKiosk() { + return _scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag); +} + +bool GameStateManager::getScoringGoToTSA() { + return _scoringFlags.getFlag(kScoringGoToTSAFlag); +} + +bool GameStateManager::getScoringEnterTSA() { + return _scoringFlags.getFlag(kScoringEnterTSAFlag); +} + +bool GameStateManager::getScoringSawBust1() { + return _scoringFlags.getFlag(kScoringSawBust1Flag); +} + +bool GameStateManager::getScoringSawBust2() { + return _scoringFlags.getFlag(kScoringSawBust2Flag); +} + +bool GameStateManager::getScoringSawBust3() { + return _scoringFlags.getFlag(kScoringSawBust3Flag); +} + +bool GameStateManager::getScoringSawBust4() { + return _scoringFlags.getFlag(kScoringSawBust4Flag); +} + +bool GameStateManager::getScoringSawBust5() { + return _scoringFlags.getFlag(kScoringSawBust5Flag); +} + +bool GameStateManager::getScoringSawBust6() { + return _scoringFlags.getFlag(kScoringSawBust6Flag); +} + +bool GameStateManager::getScoringSawTheory() { + return _scoringFlags.getFlag(kScoringSawTheoryFlag); +} + +bool GameStateManager::getScoringSawBackground() { + return _scoringFlags.getFlag(kScoringSawBackgroundFlag); +} + +bool GameStateManager::getScoringSawProcedure() { + return _scoringFlags.getFlag(kScoringSawProcedureFlag); +} + +bool GameStateManager::getScoringGotJourneymanKey() { + return _scoringFlags.getFlag(kScoringGotJourneymanKeyFlag); +} + +bool GameStateManager::getScoringGotPegasusBiochip() { + return _scoringFlags.getFlag(kScoringGotPegasusBiochipFlag); +} + +bool GameStateManager::getScoringGotBiosuit() { + return _scoringFlags.getFlag(kScoringGotBiosuitFlag); +} + +bool GameStateManager::getScoringGoToPrehistoric() { + return _scoringFlags.getFlag(kScoringGoToPrehistoricFlag); +} + +bool GameStateManager::getScoringPutLogInReader() { + return _scoringFlags.getFlag(kScoringPutLogInReaderFlag); +} + +bool GameStateManager::getScoringSawCaldoriaNormal() { + return _scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag); +} + +bool GameStateManager::getScoringSawCaldoriaAltered() { + return _scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag); +} + +bool GameStateManager::getScoringSawNoradNormal() { + return _scoringFlags.getFlag(kScoringSawNoradNormalFlag); +} + +bool GameStateManager::getScoringSawNoradAltered() { + return _scoringFlags.getFlag(kScoringSawNoradAlteredFlag); +} + +bool GameStateManager::getScoringSawMarsNormal() { + return _scoringFlags.getFlag(kScoringSawMarsNormalFlag); +} + +bool GameStateManager::getScoringSawMarsAltered() { + return _scoringFlags.getFlag(kScoringSawMarsAlteredFlag); +} + +bool GameStateManager::getScoringSawWSCNormal() { + return _scoringFlags.getFlag(kScoringSawWSCNormalFlag); +} + +bool GameStateManager::getScoringSawWSCAltered() { + return _scoringFlags.getFlag(kScoringSawWSCAlteredFlag); +} + +bool GameStateManager::getScoringWentToReadyRoom2() { + return _scoringFlags.getFlag(kScoringWentToReadyRoom2Flag); +} + +bool GameStateManager::getScoringWentAfterSinclair() { + return _scoringFlags.getFlag(kScoringWentAfterSinclairFlag); +} + +bool GameStateManager::getScoringUsedCardBomb() { + return _scoringFlags.getFlag(kScoringUsedCardBombFlag); +} + +bool GameStateManager::getScoringShieldedCardBomb() { + return _scoringFlags.getFlag(kScoringShieldedCardBombFlag); +} + +bool GameStateManager::getScoringStunnedSinclair() { + return _scoringFlags.getFlag(kScoringStunnedSinclairFlag); +} + +bool GameStateManager::getScoringDisarmedNuke() { + return _scoringFlags.getFlag(kScoringDisarmedNukeFlag); +} + +bool GameStateManager::getScoringThrewBreaker() { + return _scoringFlags.getFlag(kScoringThrewBreakerFlag); +} + +bool GameStateManager::getScoringExtendedBridge() { + return _scoringFlags.getFlag(kScoringExtendedBridgeFlag); +} + +bool GameStateManager::getScoringGotHistoricalLog() { + return _scoringFlags.getFlag(kScoringGotHistoricalLogFlag); +} + +bool GameStateManager::getScoringFinishedPrehistoric() { + return _scoringFlags.getFlag(kScoringFinishedPrehistoricFlag); +} + +bool GameStateManager::getScoringThrownByRobot() { + return _scoringFlags.getFlag(kScoringThrownByRobotFlag); +} + +bool GameStateManager::getScoringGotMarsCard() { + return _scoringFlags.getFlag(kScoringGotMarsCardFlag); +} + +bool GameStateManager::getScoringSawMarsKiosk() { + return _scoringFlags.getFlag(kScoringSawMarsKioskFlag); +} + +bool GameStateManager::getScoringSawTransportMap() { + return _scoringFlags.getFlag(kScoringSawTransportMapFlag); +} + +bool GameStateManager::getScoringGotCrowBar() { + return _scoringFlags.getFlag(kScoringGotCrowBarFlag); +} + +bool GameStateManager::getScoringTurnedOnTransport() { + return _scoringFlags.getFlag(kScoringTurnedOnTransportFlag); +} + +bool GameStateManager::getScoringGotOxygenMask() { + return _scoringFlags.getFlag(kScoringGotOxygenMaskFlag); +} + +bool GameStateManager::getScoringAvoidedRobot() { + return _scoringFlags.getFlag(kScoringAvoidedRobotFlag); +} + +bool GameStateManager::getScoringActivatedPlatform() { + return _scoringFlags.getFlag(kScoringActivatedPlatformFlag); +} + +bool GameStateManager::getScoringUsedLiquidNitrogen() { + return _scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag); +} + +bool GameStateManager::getScoringUsedCrowBar() { + return _scoringFlags.getFlag(kScoringUsedCrowBarFlag); +} + +bool GameStateManager::getScoringFoundCardBomb() { + return _scoringFlags.getFlag(kScoringFoundCardBombFlag); +} + +bool GameStateManager::getScoringDisarmedCardBomb() { + return _scoringFlags.getFlag(kScoringDisarmedCardBombFlag); +} + +bool GameStateManager::getScoringGotCardBomb() { + return _scoringFlags.getFlag(kScoringGotCardBombFlag); +} + +bool GameStateManager::getScoringThreadedMaze() { + return _scoringFlags.getFlag(kScoringThreadedMazeFlag); +} + +bool GameStateManager::getScoringThreadedGearRoom() { + return _scoringFlags.getFlag(kScoringThreadedGearRoomFlag); +} + +bool GameStateManager::getScoringEnteredShuttle() { + return _scoringFlags.getFlag(kScoringEnteredShuttleFlag); +} + +bool GameStateManager::getScoringEnteredLaunchTube() { + return _scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag); +} + +bool GameStateManager::getScoringStoppedRobotsShuttle() { + return _scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag); +} + +bool GameStateManager::getScoringGotMarsOpMemChip() { + return _scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedMars() { + return _scoringFlags.getFlag(kScoringFinishedMarsFlag); +} + +bool GameStateManager::getScoringSawSecurityMonitor() { + return _scoringFlags.getFlag(kScoringSawSecurityMonitorFlag); +} + +bool GameStateManager::getScoringFilledOxygenCanister() { + return _scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag); +} + +bool GameStateManager::getScoringFilledArgonCanister() { + return _scoringFlags.getFlag(kScoringFilledArgonCanisterFlag); +} + +bool GameStateManager::getScoringSawUnconsciousOperator() { + return _scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag); +} + +bool GameStateManager::getScoringWentThroughPressureDoor() { + return _scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag); +} + +bool GameStateManager::getScoringPreppedSub() { + return _scoringFlags.getFlag(kScoringPreppedSubFlag); +} + +bool GameStateManager::getScoringEnteredSub() { + return _scoringFlags.getFlag(kScoringEnteredSubFlag); +} + +bool GameStateManager::getScoringExitedSub() { + return _scoringFlags.getFlag(kScoringExitedSubFlag); +} + +bool GameStateManager::getScoringSawRobotAt54North() { + return _scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag); +} + +bool GameStateManager::getScoringPlayedWithClaw() { + return _scoringFlags.getFlag(kScoringPlayedWithClawFlag); +} + +bool GameStateManager::getScoringUsedRetinalChip() { + return _scoringFlags.getFlag(kScoringUsedRetinalChipFlag); +} + +bool GameStateManager::getScoringFinishedGlobeGame() { + return _scoringFlags.getFlag(kScoringFinishedGlobeGameFlag); +} + +bool GameStateManager::getScoringStoppedNoradRobot() { + return _scoringFlags.getFlag(kScoringStoppedNoradRobotFlag); +} + +bool GameStateManager::getScoringGotNoradOpMemChip() { + return _scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedNorad() { + return _scoringFlags.getFlag(kScoringFinishedNoradFlag); +} + +bool GameStateManager::getScoringRemovedDart() { + return _scoringFlags.getFlag(kScoringRemovedDartFlag); +} + +bool GameStateManager::getScoringAnalyzedDart() { + return _scoringFlags.getFlag(kScoringAnalyzedDartFlag); +} + +bool GameStateManager::getScoringBuiltAntidote() { + return _scoringFlags.getFlag(kScoringBuiltAntidoteFlag); +} + +bool GameStateManager::getScoringGotSinclairKey() { + return _scoringFlags.getFlag(kScoringGotSinclairKeyFlag); +} + +bool GameStateManager::getScoringGotArgonCanister() { + return _scoringFlags.getFlag(kScoringGotArgonCanisterFlag); +} + +bool GameStateManager::getScoringGotNitrogenCanister() { + return _scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag); +} + +bool GameStateManager::getScoringPlayedWithMessages() { + return _scoringFlags.getFlag(kScoringPlayedWithMessagesFlag); +} + +bool GameStateManager::getScoringSawMorphExperiment() { + return _scoringFlags.getFlag(kScoringSawMorphExperimentFlag); +} + +bool GameStateManager::getScoringEnteredSinclairOffice() { + return _scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag); +} + +bool GameStateManager::getScoringSawBrochure() { + return _scoringFlags.getFlag(kScoringSawBrochureFlag); +} + +bool GameStateManager::getScoringSawSinclairEntry1() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry1Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry2() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry2Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry3() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry3Flag); +} + +bool GameStateManager::getScoringSawWSCDirectory() { + return _scoringFlags.getFlag(kScoringSawWSCDirectoryFlag); +} + +bool GameStateManager::getScoringUsedCrowBarInWSC() { + return _scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag); +} + +bool GameStateManager::getScoringFinishedPlasmaDodge() { + return _scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag); +} + +bool GameStateManager::getScoringOpenedCatwalk() { + return _scoringFlags.getFlag(kScoringOpenedCatwalkFlag); +} + +bool GameStateManager::getScoringStoppedWSCRobot() { + return _scoringFlags.getFlag(kScoringStoppedWSCRobotFlag); +} + +bool GameStateManager::getScoringGotWSCOpMemChip() { + return _scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedWSC() { + return _scoringFlags.getFlag(kScoringFinishedWSCFlag); +} + +bool GameStateManager::getScoringMarsGandhi() { + return _scoringFlags.getFlag(kScoringMarsGandhiFlag); +} + +bool GameStateManager::getScoringNoradGandhi() { + return _scoringFlags.getFlag(kScoringNoradGandhiFlag); +} + +bool GameStateManager::getScoringWSCGandhi() { + return _scoringFlags.getFlag(kScoringWSCGandhiFlag); +} + +void GameStateManager::setWalkthroughMode(bool value) { + _globalFlags.setFlag(kGlobalWalkthroughFlag, value); +} + +bool GameStateManager::getWalkthroughMode() { + return _globalFlags.getFlag(kGlobalWalkthroughFlag); +} + +void GameStateManager::setShieldOn(bool value) { + _globalFlags.setFlag(kGlobalShieldOnFlag, value); +} + +bool GameStateManager::getShieldOn() { + return _globalFlags.getFlag(kGlobalShieldOnFlag); +} + +void GameStateManager::setEasterEgg(bool value) { + _globalFlags.setFlag(kGlobalEasterEggFlag, value); +} + +bool GameStateManager::getEasterEgg() { + return _globalFlags.getFlag(kGlobalEasterEggFlag); +} + +void GameStateManager::setBeenToWSC(bool value) { + _globalFlags.setFlag(kGlobalBeenToWSCFlag, value); +} + +bool GameStateManager::getBeenToWSC() { + return _globalFlags.getFlag(kGlobalBeenToWSCFlag); +} + +void GameStateManager::setBeenToMars(bool value) { + _globalFlags.setFlag(kGlobalBeenToMarsFlag, value); +} + +bool GameStateManager::getBeenToMars() { + return _globalFlags.getFlag(kGlobalBeenToMarsFlag); +} + +void GameStateManager::setBeenToNorad(bool value) { + _globalFlags.setFlag(kGlobalBeenToNoradFlag, value); +} + +bool GameStateManager::getBeenToNorad() { + return _globalFlags.getFlag(kGlobalBeenToNoradFlag); +} + +void GameStateManager::setWSCFinished(bool value) { + _globalFlags.setFlag(kGlobalWSCFinishedFlag, value); +} + +bool GameStateManager::getWSCFinished() { + return _globalFlags.getFlag(kGlobalWSCFinishedFlag); +} + +void GameStateManager::setMarsFinished(bool value) { + _globalFlags.setFlag(kGlobalMarsFinishedFlag, value); +} + +bool GameStateManager::getMarsFinished() { + return _globalFlags.getFlag(kGlobalMarsFinishedFlag); +} + +void GameStateManager::setNoradFinished(bool value) { + _globalFlags.setFlag(kGlobalNoradFinishedFlag, value); +} + +bool GameStateManager::getNoradFinished() { + return _globalFlags.getFlag(kGlobalNoradFinishedFlag); +} + +bool GameStateManager::allTimeZonesFinished() { + return getWSCFinished() && getMarsFinished() && getNoradFinished(); +} + +void GameStateManager::setTakenItemID(tItemID id, bool value) { + _itemTakenFlags.setFlag(id, value); +} + +bool GameStateManager::isTakenItemID(tItemID id) { + return _itemTakenFlags.getFlag(id); +} + +void GameStateManager::setTakenItem(Item *item, bool value) { + setTakenItemID(item->getObjectID(), value); +} + +bool GameStateManager::isTakenItem(Item *item) { + return isTakenItemID(item->getObjectID()); +} + +void GameStateManager::setCaldoriaFuseTimeLimit(const TimeValue timeLimit) { + _caldoriaFuseTimeLimit = timeLimit; +} + +TimeValue GameStateManager::getCaldoriaFuseTimeLimit() { + return _caldoriaFuseTimeLimit; +} + +void GameStateManager::setCaldoriaSeenPullback(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenPullbackFlag, value); +} + +bool GameStateManager::getCaldoriaSeenPullback() { + return _caldoriaFlags.getFlag(kCaldoriaSeenPullbackFlag); +} + +void GameStateManager::setCaldoriaMadeOJ(bool value) { + _caldoriaFlags.setFlag(kCaldoriaMadeOJFlag, value); +} + +bool GameStateManager::getCaldoriaMadeOJ() { + return _caldoriaFlags.getFlag(kCaldoriaMadeOJFlag); +} + +void GameStateManager::setCaldoriaWokenUp(bool value) { + _caldoriaFlags.setFlag(kCaldoriaWokenUpFlag, value); +} + +bool GameStateManager::getCaldoriaWokenUp() { + return _caldoriaFlags.getFlag(kCaldoriaWokenUpFlag); +} + +void GameStateManager::setCaldoriaDidRecalibration(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDidRecalibrationFlag, value); +} + +bool GameStateManager::getCaldoriaDidRecalibration() { + return _caldoriaFlags.getFlag(kCaldoriaDidRecalibrationFlag); +} + +void GameStateManager::setCaldoriaSeenSinclairInElevator(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenSinclairInElevatorFlag, value); +} + +bool GameStateManager::getCaldoriaSeenSinclairInElevator() { + return _caldoriaFlags.getFlag(kCaldoriaSeenSinclairInElevatorFlag); +} + +void GameStateManager::setCaldoriaINNAnnouncing(bool value) { + _caldoriaFlags.setFlag(kCaldoriaINNAnnouncingFlag, value); +} + +bool GameStateManager::getCaldoriaINNAnnouncing() { + return _caldoriaFlags.getFlag(kCaldoriaINNAnnouncingFlag); +} + +void GameStateManager::setCaldoriaSeenINN(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenINNFlag, value); +} + +bool GameStateManager::getCaldoriaSeenINN() { + return _caldoriaFlags.getFlag(kCaldoriaSeenINNFlag); +} + +void GameStateManager::setCaldoriaSeenMessages(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenMessagesFlag, value); +} + +bool GameStateManager::getCaldoriaSeenMessages() { + return _caldoriaFlags.getFlag(kCaldoriaSeenMessagesFlag); +} + +void GameStateManager::setCaldoriaSinclairShot(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSinclairShotFlag, value); +} + +bool GameStateManager::getCaldoriaSinclairShot() { + return _caldoriaFlags.getFlag(kCaldoriaSinclairShotFlag); +} + +void GameStateManager::setCaldoriaBombDisarmed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaBombDisarmedFlag, value); +} + +bool GameStateManager::getCaldoriaBombDisarmed() { + return _caldoriaFlags.getFlag(kCaldoriaBombDisarmedFlag); +} + +void GameStateManager::setCaldoriaRoofDoorOpen(bool value) { + _caldoriaFlags.setFlag(kCaldoriaRoofDoorOpenFlag, value); +} + +bool GameStateManager::getCaldoriaRoofDoorOpen() { + return _caldoriaFlags.getFlag(kCaldoriaRoofDoorOpenFlag); +} + +void GameStateManager::setCaldoriaDoneHygiene(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoneHygieneFlag, value); +} + +bool GameStateManager::getCaldoriaDoneHygiene() { + return _caldoriaFlags.getFlag(kCaldoriaDoneHygieneFlag); +} + +void GameStateManager::setCaldoriaSawVoiceAnalysis(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSawVoiceAnalysisFlag, value); +} + +bool GameStateManager::getCaldoriaSawVoiceAnalysis() { + return _caldoriaFlags.getFlag(kCaldoriaSawVoiceAnalysisFlag); +} + +void GameStateManager::setCaldoriaDoorBombed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoorBombedFlag, value); +} + +bool GameStateManager::getCaldoriaDoorBombed() { + return _caldoriaFlags.getFlag(kCaldoriaDoorBombedFlag); +} + +void GameStateManager::setCaldoriaGunAimed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaGunAimedFlag, value); +} + +bool GameStateManager::getCaldoriaGunAimed() { + return _caldoriaFlags.getFlag(kCaldoriaGunAimedFlag); +} + +void GameStateManager::setRipTimerTime(TimeValue limit) { + _TSARipTimerTime = limit; +} + +TimeValue GameStateManager::getRipTimerTime() { + return _TSARipTimerTime; +} + +void GameStateManager::setTSAFuseTimeLimit(TimeValue limit) { + _TSAFuseTimeLimit = limit; +} + +TimeValue GameStateManager::getTSAFuseTimeLimit() { + return _TSAFuseTimeLimit; +} + +void GameStateManager::setTSAState(byte state) { + _TSAState = state; +} + +byte GameStateManager::getTSAState() { + return _TSAState; +} + +void GameStateManager::setT0BMonitorMode(byte mode) { + _T0BMonitorMode = mode; +} + +byte GameStateManager::getT0BMonitorMode() { + return _T0BMonitorMode; +} + +void GameStateManager::setT0BMonitorStart(TimeValue start) { + _T0BMonitorStart = start; +} + +TimeValue GameStateManager::getT0BMonitorStart() { + return _T0BMonitorStart; +} + +void GameStateManager::setTSAIDedAtDoor(bool value) { + _TSAFlags.setFlag(kTSAIDedAtDoorFlag, value); +} + +bool GameStateManager::getTSAIDedAtDoor() { + return _TSAFlags.getFlag(kTSAIDedAtDoorFlag); +} + +void GameStateManager::setTSA0BZoomedIn(bool value) { + _TSAFlags.setFlag(kTSA0BZoomedInFlag, value); +} + +bool GameStateManager::getTSA0BZoomedIn() { + return _TSAFlags.getFlag(kTSA0BZoomedInFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedOutside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedOutsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedOutside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedOutsideFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedInside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedInsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedInside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedInsideFlag); +} + +void GameStateManager::setTSASeenRobotGreeting(bool value) { + _TSAFlags.setFlag(kTSASeenRobotGreetingFlag, value); +} + +bool GameStateManager::getTSASeenRobotGreeting() { + return _TSAFlags.getFlag(kTSASeenRobotGreetingFlag); +} + +void GameStateManager::setTSASeenTheory(bool value) { + _TSAFlags.setFlag(kTSASeenTheoryFlag, value); +} + +bool GameStateManager::getTSASeenTheory() { + return _TSAFlags.getFlag(kTSASeenTheoryFlag); +} + +void GameStateManager::setTSASeenBackground(bool value) { + _TSAFlags.setFlag(kTSASeenBackgroundFlag, value); +} + +bool GameStateManager::getTSASeenBackground() { + return _TSAFlags.getFlag(kTSASeenBackgroundFlag); +} + +void GameStateManager::setTSASeenProcedure(bool value) { + _TSAFlags.setFlag(kTSASeenProcedureFlag, value); +} + +bool GameStateManager::getTSASeenProcedure() { + return _TSAFlags.getFlag(kTSASeenProcedureFlag); +} + +void GameStateManager::setTSASeenAgent3AtDoor(bool value) { + _TSAFlags.setFlag(kTSASeenAgent3AtDoorFlag, value); +} + +bool GameStateManager::getTSASeenAgent3AtDoor() { + return _TSAFlags.getFlag(kTSASeenAgent3AtDoorFlag); +} + +void GameStateManager::setTSACommandCenterLocked(bool value) { + _TSAFlags.setFlag(kTSACommandCenterLockedFlag, value); +} + +bool GameStateManager::getTSACommandCenterLocked() { + return _TSAFlags.getFlag(kTSACommandCenterLockedFlag); +} + +void GameStateManager::setTSASeenCaldoriaNormal(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaNormalFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaNormal() { + return _TSAFlags.getFlag(kTSASeenCaldoriaNormalFlag); +} + +void GameStateManager::setTSASeenCaldoriaAltered(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaAlteredFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaAltered() { + return _TSAFlags.getFlag(kTSASeenCaldoriaAlteredFlag); +} + +void GameStateManager::setTSASeenNoradNormal(bool value) { + _TSAFlags.setFlag(kTSASeenNoradNormalFlag, value); +} + +bool GameStateManager::getTSASeenNoradNormal() { + return _TSAFlags.getFlag(kTSASeenNoradNormalFlag); +} + +void GameStateManager::setTSASeenNoradAltered(bool value) { + _TSAFlags.setFlag(kTSASeenNoradAlteredFlag, value); +} + +bool GameStateManager::getTSASeenNoradAltered() { + return _TSAFlags.getFlag(kTSASeenNoradAlteredFlag); +} + +void GameStateManager::setTSASeenMarsNormal(bool value) { + _TSAFlags.setFlag(kTSASeenMarsNormalFlag, value); +} + +bool GameStateManager::getTSASeenMarsNormal() { + return _TSAFlags.getFlag(kTSASeenMarsNormalFlag); +} + +void GameStateManager::setTSASeenMarsAltered(bool value) { + _TSAFlags.setFlag(kTSASeenMarsAlteredFlag, value); +} + +bool GameStateManager::getTSASeenMarsAltered() { + return _TSAFlags.getFlag(kTSASeenMarsAlteredFlag); +} + +void GameStateManager::setTSASeenWSCNormal(bool value) { + _TSAFlags.setFlag(kTSASeenWSCNormalFlag, value); +} + +bool GameStateManager::getTSASeenWSCNormal() { + return _TSAFlags.getFlag(kTSASeenWSCNormalFlag); +} + +void GameStateManager::setTSASeenWSCAltered(bool value) { + _TSAFlags.setFlag(kTSASeenWSCAlteredFlag, value); +} + +bool GameStateManager::getTSASeenWSCAltered() { + return _TSAFlags.getFlag(kTSASeenWSCAlteredFlag); +} + +void GameStateManager::setTSABiosuitOn(bool value) { + _TSAFlags.setFlag(kTSABiosuitOnFlag, value); +} + +bool GameStateManager::getTSABiosuitOn() { + return _TSAFlags.getFlag(kTSABiosuitOnFlag); +} + +void GameStateManager::setPrehistoricTriedToExtendBridge(bool value) { + _prehistoricFlags.setFlag(kPrehistoricTriedToExtendBridgeFlag, value); +} + +bool GameStateManager::getPrehistoricTriedToExtendBridge() { + return _prehistoricFlags.getFlag(kPrehistoricTriedToExtendBridgeFlag); +} + +void GameStateManager::setPrehistoricSeenTimeStream(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenTimeStreamFlag, value); +} + +bool GameStateManager::getPrehistoricSeenTimeStream() { + return _prehistoricFlags.getFlag(kPrehistoricSeenTimeStreamFlag); +} + +void GameStateManager::setPrehistoricSeenFlyer1(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer1Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer1() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer1Flag); +} + +void GameStateManager::setPrehistoricSeenFlyer2(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer2Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer2() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer2Flag); +} + +void GameStateManager::setPrehistoricSeenBridgeZoom(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenBridgeZoomFlag, value); +} + +bool GameStateManager::getPrehistoricSeenBridgeZoom() { + return _prehistoricFlags.getFlag(kPrehistoricSeenBridgeZoomFlag); +} + +void GameStateManager::setPrehistoricBreakerThrown(bool value) { + _prehistoricFlags.setFlag(kPrehistoricBreakerThrownFlag, value); +} + +bool GameStateManager::getPrehistoricBreakerThrown() { + return _prehistoricFlags.getFlag(kPrehistoricBreakerThrownFlag); +} + +void GameStateManager::setNoradSeenTimeStream(bool value) { + _noradFlags.setFlag(kNoradSeenTimeStreamFlag, value); +} + +bool GameStateManager::getNoradSeenTimeStream() { + return _noradFlags.getFlag(kNoradSeenTimeStreamFlag); +} + +void GameStateManager::setNoradGassed(bool value) { + _noradFlags.setFlag(kNoradGassedFlag, value); +} + +bool GameStateManager::getNoradGassed() { + return _noradFlags.getFlag(kNoradGassedFlag); +} + +void GameStateManager::setNoradFillingStationOn(bool value) { + _noradFlags.setFlag(kNoradFillingStationOnFlag, value); +} + +bool GameStateManager::getNoradFillingStationOn() { + return _noradFlags.getFlag(kNoradFillingStationOnFlag); +} + +void GameStateManager::setNoradN22MessagePlayed(bool value) { + _noradFlags.setFlag(kNoradN22MessagePlayedFlag, value); +} + +bool GameStateManager::getNoradN22MessagePlayed() { + return _noradFlags.getFlag(kNoradN22MessagePlayedFlag); +} + +void GameStateManager::setNoradPlayedGlobeGame(bool value) { + _noradFlags.setFlag(kNoradPlayedGlobeGameFlag, value); +} + +bool GameStateManager::getNoradPlayedGlobeGame() { + return _noradFlags.getFlag(kNoradPlayedGlobeGameFlag); +} + +void GameStateManager::setNoradBeatRobotWithClaw(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithClawFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithClaw() { + return _noradFlags.getFlag(kNoradBeatRobotWithClawFlag); +} + +void GameStateManager::setNoradBeatRobotWithDoor(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithDoorFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithDoor() { + return _noradFlags.getFlag(kNoradBeatRobotWithDoorFlag); +} + +void GameStateManager::setNoradRetScanGood(bool value) { + _noradFlags.setFlag(kNoradRetScanGoodFlag, value); +} + +bool GameStateManager::getNoradRetScanGood() { + return _noradFlags.getFlag(kNoradRetScanGoodFlag); +} + +void GameStateManager::setNoradWaitingForLaser(bool value) { + _noradFlags.setFlag(kNoradWaitingForLaserFlag, value); +} + +bool GameStateManager::getNoradWaitingForLaser() { + return _noradFlags.getFlag(kNoradWaitingForLaserFlag); +} + +void GameStateManager::setNoradSubRoomPressure(uint16 pressure) { + _noradSubRoomPressure = pressure; +} + +uint16 GameStateManager::getNoradSubRoomPressure() { + return _noradSubRoomPressure; +} + +void GameStateManager::setNoradSubPrepState(tNoradSubPrepState state) { + _noradSubPrepState = state; +} + +tNoradSubPrepState GameStateManager::getNoradSubPrepState() { + return _noradSubPrepState; +} + +void GameStateManager::setNoradArrivedFromSub(bool value) { + _noradFlags.setFlag(kNoradArrivedFromSubFlag, value); +} + +bool GameStateManager::getNoradArrivedFromSub() { + return _noradFlags.getFlag(kNoradArrivedFromSubFlag); +} + +void GameStateManager::setMarsSeenTimeStream(bool value) { + _marsFlags.setFlag(kMarsSeenTimeStreamFlag, value); +} + +bool GameStateManager::getMarsSeenTimeStream() { + return _marsFlags.getFlag(kMarsSeenTimeStreamFlag); +} + +void GameStateManager::setMarsHeardUpperPodMessage(bool value) { + _marsFlags.setFlag(kMarsHeardUpperPodMessageFlag, value); +} + +bool GameStateManager::getMarsHeardUpperPodMessage() { + return _marsFlags.getFlag(kMarsHeardUpperPodMessageFlag); +} + +void GameStateManager::setMarsRobotThrownPlayer(bool value) { + _marsFlags.setFlag(kMarsRobotThrownPlayerFlag, value); +} + +bool GameStateManager::getMarsRobotThrownPlayer() { + return _marsFlags.getFlag(kMarsRobotThrownPlayerFlag); +} + +void GameStateManager::setMarsHeardCheckInMessage(bool value) { + _marsFlags.setFlag(kMarsHeardCheckInMessageFlag, value); +} + +bool GameStateManager::getMarsHeardCheckInMessage() { + return _marsFlags.getFlag(kMarsHeardCheckInMessageFlag); +} + +void GameStateManager::setMarsPodAtUpperPlatform(bool value) { + _marsFlags.setFlag(kMarsPodAtUpperPlatformFlag, value); +} + +bool GameStateManager::getMarsPodAtUpperPlatform() { + return _marsFlags.getFlag(kMarsPodAtUpperPlatformFlag); +} + +void GameStateManager::setMarsSeenThermalScan(bool value) { + _marsFlags.setFlag(kMarsSeenThermalScanFlag, value); +} + +bool GameStateManager::getMarsSeenThermalScan() { + return _marsFlags.getFlag(kMarsSeenThermalScanFlag); +} + +void GameStateManager::setMarsArrivedBelow(bool value) { + _marsFlags.setFlag(kMarsArrivedBelowFlag, value); +} + +bool GameStateManager::getMarsArrivedBelow() { + return _marsFlags.getFlag(kMarsArrivedBelowFlag); +} + +void GameStateManager::setMarsSeenRobotAtReactor(bool value) { + _marsFlags.setFlag(kMarsSeenRobotAtReactorFlag, value); +} + +bool GameStateManager::getMarsSeenRobotAtReactor() { + return _marsFlags.getFlag(kMarsSeenRobotAtReactorFlag); +} + +void GameStateManager::setMarsAvoidedReactorRobot(bool value) { + _marsFlags.setFlag(kMarsAvoidedReactorRobotFlag, value); +} + +bool GameStateManager::getMarsAvoidedReactorRobot() { + return _marsFlags.getFlag(kMarsAvoidedReactorRobotFlag); +} + +void GameStateManager::setMarsSecurityDown(bool value) { + _marsFlags.setFlag(kMarsSecurityDownFlag, value); +} + +bool GameStateManager::getMarsSecurityDown() { + return _marsFlags.getFlag(kMarsSecurityDownFlag); +} + +void GameStateManager::setMarsInAirlock(bool value) { + _marsFlags.setFlag(kMarsInAirlockFlag, value); +} + +bool GameStateManager::getMarsInAirlock() { + return _marsFlags.getFlag(kMarsInAirlockFlag); +} + +void GameStateManager::setMarsAirlockOpen(bool value) { + _marsFlags.setFlag(kMarsAirlockOpenFlag, value); +} + +bool GameStateManager::getMarsAirlockOpen() { + return _marsFlags.getFlag(kMarsAirlockOpenFlag); +} + +void GameStateManager::setMarsMaskOnFiller(bool value) { + _marsFlags.setFlag(kMarsMaskOnFillerFlag, value); +} + +bool GameStateManager::getMarsMaskOnFiller() { + return _marsFlags.getFlag(kMarsMaskOnFillerFlag); +} + +void GameStateManager::setMarsLockFrozen(bool value) { + _marsFlags.setFlag(kMarsLockFrozenFlag, value); +} + +bool GameStateManager::getMarsLockFrozen() { + return _marsFlags.getFlag(kMarsLockFrozenFlag); +} + +void GameStateManager::setMarsLockBroken(bool value) { + _marsFlags.setFlag(kMarsLockBrokenFlag, value); +} + +bool GameStateManager::getMarsLockBroken() { + return _marsFlags.getFlag(kMarsLockBrokenFlag); +} + +void GameStateManager::setMarsMazeDoorPair1(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair1Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair1() { + return _marsFlags.getFlag(kMarsMazeDoorPair1Flag); +} + +void GameStateManager::setMarsMazeDoorPair2(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair2Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair2() { + return _marsFlags.getFlag(kMarsMazeDoorPair2Flag); +} + +void GameStateManager::setMarsMazeDoorPair3(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair3Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair3() { + return _marsFlags.getFlag(kMarsMazeDoorPair3Flag); +} + +void GameStateManager::setMarsSawRobotLeave(bool value) { + _marsFlags.setFlag(kMarsSawRobotLeaveFlag, value); +} + +bool GameStateManager::getMarsSawRobotLeave() { + return _marsFlags.getFlag(kMarsSawRobotLeaveFlag); +} + +void GameStateManager::setMarsHitRobotWithCannon(bool flag) { + _marsFlags.setFlag(kMarsHitRobotWithCannonFlag, flag); +} + +bool GameStateManager::getMarsHitRobotWithCannon() { + return _marsFlags.getFlag(kMarsHitRobotWithCannonFlag); +} + +void GameStateManager::setMarsReadyForShuttleTransport(bool value) { + _marsFlags.setFlag(kMarsReadyForShuttleTransportFlag, value); +} + +bool GameStateManager::getMarsReadyForShuttleTransport() { + return _marsFlags.getFlag(kMarsReadyForShuttleTransportFlag); +} + +void GameStateManager::setMarsFinishedCanyonChase(bool flag) { + _marsFlags.setFlag(kMarsFinishedCanyonChaseFlag, flag); +} + +bool GameStateManager::getMarsFinishedCanyonChase() { + return _marsFlags.getFlag(kMarsFinishedCanyonChaseFlag); +} + +void GameStateManager::setMarsThreadedMaze(bool flag) { + _marsFlags.setFlag(kMarsThreadedMazeFlag, flag); +} + +bool GameStateManager::getMarsThreadedMaze() { + return _marsFlags.getFlag(kMarsThreadedMazeFlag); +} + +void GameStateManager::setWSCSeenTimeStream(bool value) { + _WSCFlags.setFlag(kWSCSeenTimeStreamFlag, value); +} + +bool GameStateManager::getWSCSeenTimeStream() { + return _WSCFlags.getFlag(kWSCSeenTimeStreamFlag); +} + +void GameStateManager::setWSCPoisoned(bool value) { + _WSCFlags.setFlag(kWSCPoisonedFlag, value); +} + +bool GameStateManager::getWSCPoisoned() { + return _WSCFlags.getFlag(kWSCPoisonedFlag); +} + +void GameStateManager::setWSCAnsweredAboutDart(bool value) { + _WSCFlags.setFlag(kWSCAnsweredAboutDartFlag, value); +} + +bool GameStateManager::getWSCAnsweredAboutDart() { + return _WSCFlags.getFlag(kWSCAnsweredAboutDartFlag); +} + +void GameStateManager::setWSCRemovedDart(bool value) { + _WSCFlags.setFlag(kWSCRemovedDartFlag, value); +} + +bool GameStateManager::getWSCRemovedDart() { + return _WSCFlags.getFlag(kWSCRemovedDartFlag); +} + +void GameStateManager::setWSCAnalyzerOn(bool value) { + _WSCFlags.setFlag(kWSCAnalyzerOnFlag, value); +} + +bool GameStateManager::getWSCAnalyzerOn() { + return _WSCFlags.getFlag(kWSCAnalyzerOnFlag); +} + +void GameStateManager::setWSCDartInAnalyzer(bool value) { + _WSCFlags.setFlag(kWSCDartInAnalyzerFlag, value); +} + +bool GameStateManager::getWSCDartInAnalyzer() { + return _WSCFlags.getFlag(kWSCDartInAnalyzerFlag); +} + +void GameStateManager::setWSCAnalyzedDart(bool value) { + _WSCFlags.setFlag(kWSCAnalyzedDartFlag, value); +} + +bool GameStateManager::getWSCAnalyzedDart() { + return _WSCFlags.getFlag(kWSCAnalyzedDartFlag); +} + +void GameStateManager::setWSCSawMorph(bool value) { + _WSCFlags.setFlag(kWSCSawMorphFlag, value); +} + +bool GameStateManager::getWSCSawMorph() { + return _WSCFlags.getFlag(kWSCSawMorphFlag); +} + +void GameStateManager::setWSCDesignedAntidote(bool value) { + _WSCFlags.setFlag(kWSCDesignedAntidoteFlag, value); +} + +bool GameStateManager::getWSCDesignedAntidote() { + return _WSCFlags.getFlag(kWSCDesignedAntidoteFlag); +} + +void GameStateManager::setWSCPickedUpAntidote(bool value) { + _WSCFlags.setFlag(kWSCPickedUpAntidoteFlag, value); +} + +bool GameStateManager::getWSCPickedUpAntidote() { + return _WSCFlags.getFlag(kWSCPickedUpAntidoteFlag); +} + +void GameStateManager::setWSCOfficeMessagesOpen(bool value) { + _WSCFlags.setFlag(kWSCOfficeMessagesOpenFlag, value); +} + +bool GameStateManager::getWSCOfficeMessagesOpen() { + return _WSCFlags.getFlag(kWSCOfficeMessagesOpenFlag); +} + +void GameStateManager::setWSCSeenNerd(bool value) { + _WSCFlags.setFlag(kWSCSeenNerdFlag, value); +} + +bool GameStateManager::getWSCSeenNerd() { + return _WSCFlags.getFlag(kWSCSeenNerdFlag); +} + +void GameStateManager::setWSCHeardPage1(bool value) { + _WSCFlags.setFlag(kWSCHeardPage1Flag, value); +} + +bool GameStateManager::getWSCHeardPage1() { + return _WSCFlags.getFlag(kWSCHeardPage1Flag); +} + +void GameStateManager::setWSCHeardPage2(bool value) { + _WSCFlags.setFlag(kWSCHeardPage2Flag, value); +} + +bool GameStateManager::getWSCHeardPage2() { + return _WSCFlags.getFlag(kWSCHeardPage2Flag); +} + +void GameStateManager::setWSCHeardCheckIn(bool value) { + _WSCFlags.setFlag(kWSCHeardCheckInFlag, value); +} + +bool GameStateManager::getWSCHeardCheckIn() { + return _WSCFlags.getFlag(kWSCHeardCheckInFlag); +} + +void GameStateManager::setWSCDidPlasmaDodge(bool value) { + _WSCFlags.setFlag(kWSCDidPlasmaDodgeFlag, value); +} + +bool GameStateManager::getWSCDidPlasmaDodge() { + return _WSCFlags.getFlag(kWSCDidPlasmaDodgeFlag); +} + +void GameStateManager::setWSCSeenSinclairLecture(bool value) { + _WSCFlags.setFlag(kWSCSeenSinclairLectureFlag, value); +} + +bool GameStateManager::getWSCSeenSinclairLecture() { + return _WSCFlags.getFlag(kWSCSeenSinclairLectureFlag); +} + +void GameStateManager::setWSCBeenAtWSC93(bool value) { + _WSCFlags.setFlag(kWSCBeenAtWSC93Flag, value); +} + +bool GameStateManager::getWSCBeenAtWSC93() { + return _WSCFlags.getFlag(kWSCBeenAtWSC93Flag); +} + +void GameStateManager::setWSCCatwalkDark(bool value) { + _WSCFlags.setFlag(kWSCCatwalkDarkFlag, value); +} + +bool GameStateManager::getWSCCatwalkDark() { + return _WSCFlags.getFlag(kWSCCatwalkDarkFlag); +} + +void GameStateManager::setWSCRobotDead(bool value) { + _WSCFlags.setFlag(kWSCRobotDeadFlag, value); +} + +bool GameStateManager::getWSCRobotDead() { + return _WSCFlags.getFlag(kWSCRobotDeadFlag); +} + +void GameStateManager::setWSCRobotGone(bool value) { + _WSCFlags.setFlag(kWSCRobotGoneFlag, value); +} + +bool GameStateManager::getWSCRobotGone() { + return _WSCFlags.getFlag(kWSCRobotGoneFlag); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h new file mode 100755 index 0000000000..b95a5d7fdd --- /dev/null +++ b/engines/pegasus/gamestate.h @@ -0,0 +1,886 @@ +/* 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_GAMESTATE_H +#define PEGASUS_GAMESTATE_H + +#include "common/singleton.h" +#include "common/util.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" +#include "pegasus/items/item.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +// The only things saved in here are things which get written out to a saved game file... + +enum { + kGlobalWalkthroughFlag, + kGlobalShieldOnFlag, + kGlobalEasterEggFlag, + kGlobalBeenToWSCFlag, + kGlobalBeenToMarsFlag, + kGlobalBeenToNoradFlag, + kGlobalWSCFinishedFlag, + kGlobalMarsFinishedFlag, + kGlobalNoradFinishedFlag, + kNumGlobalFlags +}; + +enum { + kScoringSawINNFlag, + kScoringTookShowerFlag, + kScoringFixedHairFlag, + kScoringGotKeyCardFlag, + kScoringReadPaperFlag, + kScoringLookThroughTelescopeFlag, + kScoringSawCaldoriaKioskFlag, + kScoringGoToTSAFlag, + kScoringEnterTSAFlag, + kScoringSawBust1Flag, + kScoringSawBust2Flag, + kScoringSawBust3Flag, + kScoringSawBust4Flag, + kScoringSawBust5Flag, + kScoringSawBust6Flag, + kScoringSawTheoryFlag, + kScoringSawBackgroundFlag, + kScoringSawProcedureFlag, + kScoringGotJourneymanKeyFlag, + kScoringGotPegasusBiochipFlag, + kScoringGotBiosuitFlag, + kScoringGoToPrehistoricFlag, + kScoringPutLogInReaderFlag, + kScoringSawCaldoriaNormalFlag, + kScoringSawCaldoriaAlteredFlag, + kScoringSawNoradNormalFlag, + kScoringSawNoradAlteredFlag, + kScoringSawMarsNormalFlag, + kScoringSawMarsAlteredFlag, + kScoringSawWSCNormalFlag, + kScoringSawWSCAlteredFlag, + kScoringWentToReadyRoom2Flag, + kScoringWentAfterSinclairFlag, + kScoringUsedCardBombFlag, + kScoringShieldedCardBombFlag, + kScoringStunnedSinclairFlag, + kScoringDisarmedNukeFlag, + + kScoringThrewBreakerFlag, + kScoringExtendedBridgeFlag, + kScoringGotHistoricalLogFlag, + kScoringFinishedPrehistoricFlag, + + kScoringThrownByRobotFlag, + kScoringGotMarsCardFlag, + kScoringSawMarsKioskFlag, + kScoringSawTransportMapFlag, + kScoringGotCrowBarFlag, + kScoringTurnedOnTransportFlag, + kScoringGotOxygenMaskFlag, + kScoringAvoidedRobotFlag, + kScoringActivatedPlatformFlag, + kScoringUsedLiquidNitrogenFlag, + kScoringUsedCrowBarFlag, + kScoringFoundCardBombFlag, + kScoringDisarmedCardBombFlag, + kScoringGotCardBombFlag, + kScoringThreadedMazeFlag, + kScoringThreadedGearRoomFlag, + kScoringEnteredShuttleFlag, + kScoringEnteredLaunchTubeFlag, + kScoringStoppedRobotsShuttleFlag, + kScoringGotMarsOpMemChipFlag, + kScoringFinishedMarsFlag, + + kScoringSawSecurityMonitorFlag, + kScoringFilledOxygenCanisterFlag, + kScoringFilledArgonCanisterFlag, + kScoringSawUnconsciousOperatorFlag, + kScoringWentThroughPressureDoorFlag, + kScoringPreppedSubFlag, + kScoringEnteredSubFlag, + kScoringExitedSubFlag, + kScoringSawRobotAt54NorthFlag, + kScoringPlayedWithClawFlag, + kScoringUsedRetinalChipFlag, + kScoringFinishedGlobeGameFlag, + kScoringStoppedNoradRobotFlag, + kScoringGotNoradOpMemChipFlag, + kScoringFinishedNoradFlag, + + kScoringRemovedDartFlag, + kScoringAnalyzedDartFlag, + kScoringBuiltAntidoteFlag, + kScoringGotSinclairKeyFlag, + kScoringGotArgonCanisterFlag, + kScoringGotNitrogenCanisterFlag, + kScoringPlayedWithMessagesFlag, + kScoringSawMorphExperimentFlag, + kScoringEnteredSinclairOfficeFlag, + kScoringSawBrochureFlag, + kScoringSawSinclairEntry1Flag, + kScoringSawSinclairEntry2Flag, + kScoringSawSinclairEntry3Flag, + kScoringSawWSCDirectoryFlag, + kScoringUsedCrowBarInWSCFlag, + kScoringFinishedPlasmaDodgeFlag, + kScoringOpenedCatwalkFlag, + kScoringStoppedWSCRobotFlag, + kScoringGotWSCOpMemChipFlag, + kScoringFinishedWSCFlag, + + kScoringMarsGandhiFlag, + kScoringNoradGandhiFlag, + kScoringWSCGandhiFlag, + + kNumScoringFlags +}; + +enum { + kCaldoriaSeenPullbackFlag, + kCaldoriaMadeOJFlag, + kCaldoriaWokenUpFlag, + kCaldoriaDidRecalibrationFlag, + kCaldoriaSeenSinclairInElevatorFlag, + kCaldoriaINNAnnouncingFlag, + kCaldoriaSeenINNFlag, + kCaldoriaSeenMessagesFlag, + kCaldoriaSinclairShotFlag, + kCaldoriaBombDisarmedFlag, + kCaldoriaRoofDoorOpenFlag, + kCaldoriaDoneHygieneFlag, + kCaldoriaSawVoiceAnalysisFlag, + kCaldoriaDoorBombedFlag, + kCaldoriaGunAimedFlag, + kNumCaldoriaFlags +}; + +enum { + kCaldoriaNoFuseRunning, + kCaldoriaDoorBombFuseRunning, + kCaldoriaSinclairFuseRunning +}; + +enum { + kTSAIDedAtDoorFlag, + kTSA0BZoomedInFlag, + kTSAFrontDoorUnlockedOutsideFlag, + kTSAFrontDoorUnlockedInsideFlag, + kTSASeenRobotGreetingFlag, + kTSASeenTheoryFlag, + kTSASeenBackgroundFlag, + kTSASeenProcedureFlag, + kTSASeenAgent3AtDoorFlag, + kTSACommandCenterLockedFlag, + kTSASeenCaldoriaNormalFlag, + kTSASeenCaldoriaAlteredFlag, + kTSASeenNoradNormalFlag, + kTSASeenNoradAlteredFlag, + kTSASeenMarsNormalFlag, + kTSASeenMarsAlteredFlag, + kTSASeenWSCNormalFlag, + kTSASeenWSCAlteredFlag, + kTSABiosuitOnFlag, + kNumTSAFlags +}; + +enum { + kPrehistoricTriedToExtendBridgeFlag, + kPrehistoricSeenTimeStreamFlag, + kPrehistoricSeenFlyer1Flag, + kPrehistoricSeenFlyer2Flag, + kPrehistoricSeenBridgeZoomFlag, + kPrehistoricBreakerThrownFlag, + kNumPrehistoricFlags +}; + +enum { + kNoradSeenTimeStreamFlag, + kNoradGassedFlag, + kNoradFillingStationOnFlag, + kNoradN22MessagePlayedFlag, + kNoradArrivedFromSubFlag, + kNoradWaitingForLaserFlag, + kNoradRetScanGoodFlag, + kNoradPlayedGlobeGameFlag, + kNoradBeatRobotWithClawFlag, + kNoradBeatRobotWithDoorFlag, + kNumNoradFlags +}; + +enum { + kMarsSeenTimeStreamFlag, + kMarsHeardUpperPodMessageFlag, + kMarsRobotThrownPlayerFlag, + kMarsHeardCheckInMessageFlag, + kMarsPodAtUpperPlatformFlag, + kMarsSeenThermalScanFlag, + kMarsArrivedBelowFlag, + kMarsSeenRobotAtReactorFlag, + kMarsAvoidedReactorRobotFlag, + kMarsInAirlockFlag, + kMarsAirlockOpenFlag, + kMarsMaskOnFillerFlag, + kMarsLockFrozenFlag, + kMarsLockBrokenFlag, + kMarsMazeDoorPair1Flag, + kMarsMazeDoorPair2Flag, + kMarsMazeDoorPair3Flag, + kMarsSawRobotLeaveFlag, + kMarsSecurityDownFlag, + kMarsHitRobotWithCannonFlag, + kMarsReadyForShuttleTransportFlag, + kMarsFinishedCanyonChaseFlag, + kMarsThreadedMazeFlag, + kNumMarsFlags +}; + +enum { + kWSCSeenTimeStreamFlag, + kWSCPoisonedFlag, + kWSCAnsweredAboutDartFlag, + kWSCRemovedDartFlag, + kWSCAnalyzerOnFlag, + kWSCDartInAnalyzerFlag, + kWSCAnalyzedDartFlag, + kWSCSawMorphFlag, + kWSCDesignedAntidoteFlag, + kWSCPickedUpAntidoteFlag, + kWSCOfficeMessagesOpenFlag, + kWSCSeenNerdFlag, + kWSCHeardPage1Flag, + kWSCHeardPage2Flag, + kWSCHeardCheckInFlag, + kWSCDidPlasmaDodgeFlag, + kWSCSeenSinclairLectureFlag, + kWSCBeenAtWSC93Flag, + kWSCCatwalkDarkFlag, + kWSCRobotDeadFlag, + kWSCRobotGoneFlag, + kNumWSCFlags +}; + +class GameStateManager : public Common::Singleton<GameStateManager> { +public: + GameStateManager() { resetGameState(); } + + // Base game state + Common::Error writeGameState(Common::WriteStream *stream); + Common::Error readGameState(Common::ReadStream *stream); + + void resetGameState(); + + void getCurrentLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction); + void setCurrentLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction); + + tNeighborhoodID getCurrentNeighborhood(); + void setCurrentNeighborhood(const tNeighborhoodID neighborhood); + tRoomID getCurrentRoom(); + void setCurrentRoom(const tRoomID room); + tDirectionConstant getCurrentDirection(); + void setCurrentDirection(const tDirectionConstant direction); + + tRoomViewID getCurrentRoomAndView(); + + void getNextLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction); + void setNextLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction); + + tNeighborhoodID getNextNeighborhood(); + void setNextNeighborhood(const tNeighborhoodID neighborhood); + tRoomID getNextRoom(); + void setNextRoom(const tRoomID room); + tDirectionConstant getNextDirection(); + void setNextDirection(const tDirectionConstant direction); + + void getLastLocation(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction); + void setLastLocation(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction); + + tNeighborhoodID getLastNeighborhood(); + void setLastNeighborhood(const tNeighborhoodID neighborhood); + tRoomID getLastRoom(); + void setLastRoom(const tRoomID room); + tDirectionConstant getLastDirection(); + void setLastDirection(const tDirectionConstant direction); + + tRoomViewID getLastRoomAndView(); + + void getOpenDoorLocation(tRoomID &room, tDirectionConstant &direction); + void setOpenDoorLocation(const tRoomID room, const tDirectionConstant direction); + tRoomID getOpenDoorRoom(); + void setOpenDoorRoom(const tRoomID room); + tDirectionConstant getOpenDoorDirection(); + void setOpenDoorDirection(const tDirectionConstant direction); + + tRoomViewID getDoorOpenRoomAndView(); + + bool isCurrentDoorOpen(); + + // Pegasus Prime + + // Scoring... + // Scoring "Set" functions. + // Caldoria/TSA scoring + void setScoringSawINN(const bool = true); + void setScoringTookShower(const bool = true); + void setScoringFixedHair(const bool = true); + void setScoringGotKeyCard(const bool = true); + void setScoringReadPaper(const bool = true); + void setScoringLookThroughTelescope(const bool = true); + void setScoringSawCaldoriaKiosk(const bool = true); + void setScoringGoToTSA(const bool = true); + void setScoringEnterTSA(const bool = true); + void setScoringSawBust1(const bool = true); + void setScoringSawBust2(const bool = true); + void setScoringSawBust3(const bool = true); + void setScoringSawBust4(const bool = true); + void setScoringSawBust5(const bool = true); + void setScoringSawBust6(const bool = true); + void setScoringSawTheory(const bool = true); + void setScoringSawBackground(const bool = true); + void setScoringSawProcedure(const bool = true); + void setScoringGotJourneymanKey(const bool = true); + void setScoringGotPegasusBiochip(const bool = true); + void setScoringGotBiosuit(const bool = true); + void setScoringGoToPrehistoric(const bool = true); + void setScoringPutLogInReader(const bool = true); + void setScoringSawCaldoriaNormal(const bool = true); + void setScoringSawCaldoriaAltered(const bool = true); + void setScoringSawNoradNormal(const bool = true); + void setScoringSawNoradAltered(const bool = true); + void setScoringSawMarsNormal(const bool = true); + void setScoringSawMarsAltered(const bool = true); + void setScoringSawWSCNormal(const bool = true); + void setScoringSawWSCAltered(const bool = true); + void setScoringWentToReadyRoom2(const bool = true); + void setScoringWentAfterSinclair(const bool = true); + void setScoringUsedCardBomb(const bool = true); + void setScoringShieldedCardBomb(const bool = true); + void setScoringStunnedSinclair(const bool = true); + void setScoringDisarmedNuke(const bool = true); + + // Prehistoric scoring + void setScoringThrewBreaker(const bool = true); + void setScoringExtendedBridge(const bool = true); + void setScoringGotHistoricalLog(const bool = true); + void setScoringFinishedPrehistoric(const bool = true); + + // Mars scoring + void setScoringThrownByRobot(const bool = true); + void setScoringGotMarsCard(const bool = true); + void setScoringSawMarsKiosk(const bool = true); + void setScoringSawTransportMap(const bool = true); + void setScoringGotCrowBar(const bool = true); + void setScoringTurnedOnTransport(const bool = true); + void setScoringGotOxygenMask(const bool = true); + void setScoringAvoidedRobot(const bool = true); + void setScoringActivatedPlatform(const bool = true); + void setScoringUsedLiquidNitrogen(const bool = true); + void setScoringUsedCrowBar(const bool = true); + void setScoringFoundCardBomb(const bool = true); + void setScoringDisarmedCardBomb(const bool = true); + void setScoringGotCardBomb(const bool = true); + void setScoringThreadedMaze(const bool = true); + void setScoringThreadedGearRoom(const bool = true); + void setScoringEnteredShuttle(const bool = true); + void setScoringEnteredLaunchTube(const bool = true); + void setScoringStoppedRobotsShuttle(const bool = true); + void setScoringGotMarsOpMemChip(const bool = true); + void setScoringFinishedMars(const bool = true); + + // Norad scoring + void setScoringSawSecurityMonitor(const bool = true); + void setScoringFilledOxygenCanister(const bool = true); + void setScoringFilledArgonCanister(const bool = true); + void setScoringSawUnconsciousOperator(const bool = true); + void setScoringWentThroughPressureDoor(const bool = true); + void setScoringPreppedSub(const bool = true); + void setScoringEnteredSub(const bool = true); + void setScoringExitedSub(const bool = true); + void setScoringSawRobotAt54North(const bool = true); + void setScoringPlayedWithClaw(const bool = true); + void setScoringUsedRetinalChip(const bool = true); + void setScoringFinishedGlobeGame(const bool = true); + void setScoringStoppedNoradRobot(const bool = true); + void setScoringGotNoradOpMemChip(const bool = true); + void setScoringFinishedNorad(const bool = true); + + // WSC scoring + void setScoringRemovedDart(const bool = true); + void setScoringAnalyzedDart(const bool = true); + void setScoringBuiltAntidote(const bool = true); + void setScoringGotSinclairKey(const bool = true); + void setScoringGotArgonCanister(const bool = true); + void setScoringGotNitrogenCanister(const bool = true); + void setScoringPlayedWithMessages(const bool = true); + void setScoringSawMorphExperiment(const bool = true); + void setScoringEnteredSinclairOffice(const bool = true); + void setScoringSawBrochure(const bool = true); + void setScoringSawSinclairEntry1(const bool = true); + void setScoringSawSinclairEntry2(const bool = true); + void setScoringSawSinclairEntry3(const bool = true); + void setScoringSawWSCDirectory(const bool = true); + void setScoringUsedCrowBarInWSC(const bool = true); + void setScoringFinishedPlasmaDodge(const bool = true); + void setScoringOpenedCatwalk(const bool = true); + void setScoringStoppedWSCRobot(const bool = true); + void setScoringGotWSCOpMemChip(const bool = true); + void setScoringFinishedWSC(const bool = true); + + // Gandhi scoring + void setScoringMarsGandhi(const bool = true); + void setScoringNoradGandhi(const bool = true); + void setScoringWSCGandhi(const bool = true); + + // Scoring "Get" functions. + bool getScoringSawINN(); + bool getScoringTookShower(); + bool getScoringFixedHair(); + bool getScoringGotKeyCard(); + bool getScoringReadPaper(); + bool getScoringLookThroughTelescope(); + bool getScoringSawCaldoriaKiosk(); + bool getScoringGoToTSA(); + bool getScoringEnterTSA(); + bool getScoringSawBust1(); + bool getScoringSawBust2(); + bool getScoringSawBust3(); + bool getScoringSawBust4(); + bool getScoringSawBust5(); + bool getScoringSawBust6(); + bool getScoringSawTheory(); + bool getScoringSawBackground(); + bool getScoringSawProcedure(); + bool getScoringGotJourneymanKey(); + bool getScoringGotPegasusBiochip(); + bool getScoringGotBiosuit(); + bool getScoringGoToPrehistoric(); + bool getScoringPutLogInReader(); + bool getScoringSawCaldoriaNormal(); + bool getScoringSawCaldoriaAltered(); + bool getScoringSawNoradNormal(); + bool getScoringSawNoradAltered(); + bool getScoringSawMarsNormal(); + bool getScoringSawMarsAltered(); + bool getScoringSawWSCNormal(); + bool getScoringSawWSCAltered(); + bool getScoringWentToReadyRoom2(); + bool getScoringWentAfterSinclair(); + bool getScoringUsedCardBomb(); + bool getScoringShieldedCardBomb(); + bool getScoringStunnedSinclair(); + bool getScoringDisarmedNuke(); + bool getScoringThrewBreaker(); + bool getScoringExtendedBridge(); + bool getScoringGotHistoricalLog(); + bool getScoringFinishedPrehistoric(); + bool getScoringThrownByRobot(); + bool getScoringGotMarsCard(); + bool getScoringSawMarsKiosk(); + bool getScoringSawTransportMap(); + bool getScoringGotCrowBar(); + bool getScoringTurnedOnTransport(); + bool getScoringGotOxygenMask(); + bool getScoringAvoidedRobot(); + bool getScoringActivatedPlatform(); + bool getScoringUsedLiquidNitrogen(); + bool getScoringUsedCrowBar(); + bool getScoringFoundCardBomb(); + bool getScoringDisarmedCardBomb(); + bool getScoringGotCardBomb(); + bool getScoringThreadedMaze(); + bool getScoringThreadedGearRoom(); + bool getScoringEnteredShuttle(); + bool getScoringEnteredLaunchTube(); + bool getScoringStoppedRobotsShuttle(); + bool getScoringGotMarsOpMemChip(); + bool getScoringFinishedMars(); + bool getScoringSawSecurityMonitor(); + bool getScoringFilledOxygenCanister(); + bool getScoringFilledArgonCanister(); + bool getScoringSawUnconsciousOperator(); + bool getScoringWentThroughPressureDoor(); + bool getScoringPreppedSub(); + bool getScoringEnteredSub(); + bool getScoringExitedSub(); + bool getScoringSawRobotAt54North(); + bool getScoringPlayedWithClaw(); + bool getScoringUsedRetinalChip(); + bool getScoringFinishedGlobeGame(); + bool getScoringStoppedNoradRobot(); + bool getScoringGotNoradOpMemChip(); + bool getScoringFinishedNorad(); + bool getScoringRemovedDart(); + bool getScoringAnalyzedDart(); + bool getScoringBuiltAntidote(); + bool getScoringGotSinclairKey(); + bool getScoringGotArgonCanister(); + bool getScoringGotNitrogenCanister(); + bool getScoringPlayedWithMessages(); + bool getScoringSawMorphExperiment(); + bool getScoringEnteredSinclairOffice(); + bool getScoringSawBrochure(); + bool getScoringSawSinclairEntry1(); + bool getScoringSawSinclairEntry2(); + bool getScoringSawSinclairEntry3(); + bool getScoringSawWSCDirectory(); + bool getScoringUsedCrowBarInWSC(); + bool getScoringFinishedPlasmaDodge(); + bool getScoringOpenedCatwalk(); + bool getScoringStoppedWSCRobot(); + bool getScoringGotWSCOpMemChip(); + bool getScoringFinishedWSC(); + bool getScoringMarsGandhi(); + bool getScoringNoradGandhi(); + bool getScoringWSCGandhi(); + + tGameScoreType getCaldoriaTSAScore(); + tGameScoreType getPrehistoricScore(); + tGameScoreType getMarsScore(); + tGameScoreType getNoradScore(); + tGameScoreType getWSCScore(); + tGameScoreType getGandhiScore(); + tGameScoreType getTotalScore(); + + void writeCaldoriaState(Common::WriteStream *stream); + void readCaldoriaState(Common::ReadStream *stream); + void resetCaldoriaState(); + + void writeTSAState(Common::WriteStream *stream); + void readTSAState(Common::ReadStream *stream); + void resetTSAState(); + + void writePrehistoricState(Common::WriteStream *stream); + void readPrehistoricState(Common::ReadStream *stream); + void resetPrehistoricState(); + + void writeNoradState(Common::WriteStream *stream); + void readNoradState(Common::ReadStream *stream); + void resetNoradState(); + + void writeMarsState(Common::WriteStream *stream); + void readMarsState(Common::ReadStream *stream); + void resetMarsState(); + + void writeWSCState(Common::WriteStream *stream); + void readWSCState(Common::ReadStream *stream); + void resetWSCState(); + + // Globals. + void setWalkthroughMode(bool); + bool getWalkthroughMode(); + void setShieldOn(bool); + bool getShieldOn(); + void setEasterEgg(bool); + bool getEasterEgg(); + void setBeenToWSC(bool value); + bool getBeenToWSC(); + void setBeenToMars(bool value); + bool getBeenToMars(); + void setBeenToNorad(bool value); + bool getBeenToNorad(); + void setWSCFinished(bool); + bool getWSCFinished(); + void setMarsFinished(bool); + bool getMarsFinished(); + void setNoradFinished(bool); + bool getNoradFinished(); + bool allTimeZonesFinished(); + void setTakenItemID(tItemID, bool); + bool isTakenItemID(tItemID); + void setTakenItem(Item*, bool); + bool isTakenItem(Item*); + + // Caldoria + void setCaldoriaFuseTimeLimit(const TimeValue); + TimeValue getCaldoriaFuseTimeLimit(); + void setCaldoriaSeenPullback(bool); + bool getCaldoriaSeenPullback(); + void setCaldoriaMadeOJ(bool); + bool getCaldoriaMadeOJ(); + void setCaldoriaWokenUp(bool); + bool getCaldoriaWokenUp(); + void setCaldoriaDidRecalibration(bool); + bool getCaldoriaDidRecalibration(); + void setCaldoriaSeenSinclairInElevator(bool); + bool getCaldoriaSeenSinclairInElevator(); + void setCaldoriaINNAnnouncing(bool); + bool getCaldoriaINNAnnouncing(); + void setCaldoriaSeenINN(bool); + bool getCaldoriaSeenINN(); + void setCaldoriaSeenMessages(bool); + bool getCaldoriaSeenMessages(); + void setCaldoriaSinclairShot(bool); + bool getCaldoriaSinclairShot(); + void setCaldoriaBombDisarmed(bool); + bool getCaldoriaBombDisarmed(); + void setCaldoriaRoofDoorOpen(bool); + bool getCaldoriaRoofDoorOpen(); + void setCaldoriaDoneHygiene(bool); + bool getCaldoriaDoneHygiene(); + void setCaldoriaSawVoiceAnalysis(bool); + bool getCaldoriaSawVoiceAnalysis(); + void setCaldoriaDoorBombed(bool); + bool getCaldoriaDoorBombed(); + void setCaldoriaGunAimed(bool); + bool getCaldoriaGunAimed(); + + // TSA + void setRipTimerTime(TimeValue); + TimeValue getRipTimerTime(); + void setTSAFuseTimeLimit(TimeValue); + TimeValue getTSAFuseTimeLimit(); + void setT0BMonitorMode(byte); + byte getT0BMonitorMode(); + void setTSAState(byte); + byte getTSAState(); + void setT0BMonitorStart(TimeValue); + TimeValue getT0BMonitorStart(); + void setTSAIDedAtDoor(bool); + bool getTSAIDedAtDoor(); + void setTSA0BZoomedIn(bool); + bool getTSA0BZoomedIn(); + void setTSAFrontDoorUnlockedOutside(bool); + bool getTSAFrontDoorUnlockedOutside(); + void setTSAFrontDoorUnlockedInside(bool); + bool getTSAFrontDoorUnlockedInside(); + void setTSASeenRobotGreeting(bool); + bool getTSASeenRobotGreeting(); + void setTSASeenTheory(bool); + bool getTSASeenTheory(); + void setTSASeenBackground(bool); + bool getTSASeenBackground(); + void setTSASeenProcedure(bool); + bool getTSASeenProcedure(); + void setTSASeenAgent3AtDoor(bool); + bool getTSASeenAgent3AtDoor(); + void setTSACommandCenterLocked(bool); + bool getTSACommandCenterLocked(); + void setTSASeenCaldoriaNormal(bool); + bool getTSASeenCaldoriaNormal(); + void setTSASeenCaldoriaAltered(bool); + bool getTSASeenCaldoriaAltered(); + void setTSASeenNoradNormal(bool); + bool getTSASeenNoradNormal(); + void setTSASeenNoradAltered(bool); + bool getTSASeenNoradAltered(); + void setTSASeenMarsNormal(bool); + bool getTSASeenMarsNormal(); + void setTSASeenMarsAltered(bool); + bool getTSASeenMarsAltered(); + void setTSASeenWSCNormal(bool); + bool getTSASeenWSCNormal(); + void setTSASeenWSCAltered(bool); + bool getTSASeenWSCAltered(); + void setTSABiosuitOn(bool); + bool getTSABiosuitOn(); + + // Prehistoric + void setPrehistoricTriedToExtendBridge(bool); + bool getPrehistoricTriedToExtendBridge(); + void setPrehistoricSeenTimeStream(bool); + bool getPrehistoricSeenTimeStream(); + void setPrehistoricSeenFlyer1(bool); + bool getPrehistoricSeenFlyer1(); + void setPrehistoricSeenFlyer2(bool); + bool getPrehistoricSeenFlyer2(); + void setPrehistoricSeenBridgeZoom(bool); + bool getPrehistoricSeenBridgeZoom(); + void setPrehistoricBreakerThrown(bool); + bool getPrehistoricBreakerThrown(); + + // Norad + void setNoradSeenTimeStream(bool); + bool getNoradSeenTimeStream(); + void setNoradGassed(bool); + bool getNoradGassed(); + void setNoradFillingStationOn(bool); + bool getNoradFillingStationOn(); + void setNoradN22MessagePlayed(bool); + bool getNoradN22MessagePlayed(); + void setNoradPlayedGlobeGame(bool); + bool getNoradPlayedGlobeGame(); + void setNoradBeatRobotWithClaw(bool); + bool getNoradBeatRobotWithClaw(); + void setNoradBeatRobotWithDoor(bool); + bool getNoradBeatRobotWithDoor(); + void setNoradRetScanGood(bool); + bool getNoradRetScanGood(); + void setNoradWaitingForLaser(bool); + bool getNoradWaitingForLaser(); + void setNoradSubRoomPressure(uint16); + uint16 getNoradSubRoomPressure(); + void setNoradSubPrepState(tNoradSubPrepState); + tNoradSubPrepState getNoradSubPrepState(); + void setNoradArrivedFromSub(bool); + bool getNoradArrivedFromSub(); + + // Mars + void setMarsSeenTimeStream(bool); + bool getMarsSeenTimeStream(); + void setMarsHeardUpperPodMessage(bool); + bool getMarsHeardUpperPodMessage(); + void setMarsRobotThrownPlayer(bool); + bool getMarsRobotThrownPlayer(); + void setMarsHeardCheckInMessage(bool); + bool getMarsHeardCheckInMessage(); + void setMarsPodAtUpperPlatform(bool); + bool getMarsPodAtUpperPlatform(); + void setMarsSeenThermalScan(bool); + bool getMarsSeenThermalScan(); + void setMarsArrivedBelow(bool); + bool getMarsArrivedBelow(); + void setMarsSeenRobotAtReactor(bool); + bool getMarsSeenRobotAtReactor(); + void setMarsAvoidedReactorRobot(bool); + bool getMarsAvoidedReactorRobot(); + void setMarsInAirlock(bool); + bool getMarsInAirlock(); + void setMarsAirlockOpen(bool); + bool getMarsAirlockOpen(); + void setMarsMaskOnFiller(bool); + bool getMarsMaskOnFiller(); + void setMarsLockFrozen(bool); + bool getMarsLockFrozen(); + void setMarsLockBroken(bool); + bool getMarsLockBroken(); + void setMarsMazeDoorPair1(bool); + bool getMarsMazeDoorPair1(); + void setMarsMazeDoorPair2(bool); + bool getMarsMazeDoorPair2(); + void setMarsMazeDoorPair3(bool); + bool getMarsMazeDoorPair3(); + void setMarsSawRobotLeave(bool); + bool getMarsSawRobotLeave(); + void setMarsSecurityDown(bool); + bool getMarsSecurityDown(); + void setMarsFinishedCanyonChase(bool); + bool getMarsFinishedCanyonChase(); + void setMarsThreadedMaze(bool); + bool getMarsThreadedMaze(); + void setMarsHitRobotWithCannon(bool); + bool getMarsHitRobotWithCannon(); + void setMarsReadyForShuttleTransport(bool); + bool getMarsReadyForShuttleTransport(); + + // WSC + void setWSCSeenTimeStream(bool); + bool getWSCSeenTimeStream(); + void setWSCPoisoned(bool); + bool getWSCPoisoned(); + void setWSCAnsweredAboutDart(bool); + bool getWSCAnsweredAboutDart(); + void setWSCRemovedDart(bool); + bool getWSCRemovedDart(); + void setWSCAnalyzerOn(bool); + bool getWSCAnalyzerOn(); + void setWSCDartInAnalyzer(bool); + bool getWSCDartInAnalyzer(); + void setWSCAnalyzedDart(bool); + bool getWSCAnalyzedDart(); + void setWSCSawMorph(bool); + bool getWSCSawMorph(); + void setWSCDesignedAntidote(bool); + bool getWSCDesignedAntidote(); + void setWSCPickedUpAntidote(bool); + bool getWSCPickedUpAntidote(); + void setWSCOfficeMessagesOpen(bool); + bool getWSCOfficeMessagesOpen(); + void setWSCSeenNerd(bool); + bool getWSCSeenNerd(); + void setWSCHeardPage1(bool); + bool getWSCHeardPage1(); + void setWSCHeardPage2(bool); + bool getWSCHeardPage2(); + void setWSCHeardCheckIn(bool); + bool getWSCHeardCheckIn(); + void setWSCDidPlasmaDodge(bool); + bool getWSCDidPlasmaDodge(); + void setWSCSeenSinclairLecture(bool); + bool getWSCSeenSinclairLecture(); + void setWSCBeenAtWSC93(bool); + bool getWSCBeenAtWSC93(); + void setWSCCatwalkDark(bool); + bool getWSCCatwalkDark(); + void setWSCRobotDead(bool); + bool getWSCRobotDead(); + void setWSCRobotGone(bool); + bool getWSCRobotGone(); + +protected: + friend class Common::Singleton<SingletonBaseType>; + +private: + // Base + tNeighborhoodID _currentNeighborhood; + tRoomID _currentRoom; + tDirectionConstant _currentDirection; + tNeighborhoodID _nextNeighborhoodID; + tRoomID _nextRoomID; + tDirectionConstant _nextDirection; + tNeighborhoodID _lastNeighborhood; + tRoomID _lastRoom; + tDirectionConstant _lastDirection; + tRoomID _openDoorRoom; + tDirectionConstant _openDoorDirection; + + // Pegasus Prime + FlagsArray<byte, kNumGlobalFlags> _globalFlags; + FlagsArray<byte, kNumScoringFlags> _scoringFlags; + FlagsArray<uint32, kNumItems> _itemTakenFlags; + + FlagsArray<byte, kNumCaldoriaFlags> _caldoriaFlags; + TimeValue _caldoriaFuseTimeLimit; + + TimeValue _TSARipTimerTime; + TimeValue _TSAFuseTimeLimit; + byte _TSAState; + byte _T0BMonitorMode; + TimeValue _T0BMonitorStart; + FlagsArray<byte, kNumTSAFlags> _TSAFlags; + + FlagsArray<byte, kNumPrehistoricFlags> _prehistoricFlags; + + FlagsArray<byte, kNumNoradFlags> _noradFlags; + uint16 _noradSubRoomPressure; + tNoradSubPrepState _noradSubPrepState; + + FlagsArray<byte, kNumMarsFlags> _marsFlags; + + FlagsArray<byte, kNumWSCFlags> _WSCFlags; +}; + +} // End of namespace Pegasus + +#define GameState (::Pegasus::GameStateManager::instance()) + +#endif diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp new file mode 100644 index 0000000000..1e529d0e07 --- /dev/null +++ b/engines/pegasus/graphics.cpp @@ -0,0 +1,216 @@ +/* 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/events.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "engines/util.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" + +namespace Pegasus { + +GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) { + initGraphics(640, 480, true, NULL); + + if (_vm->_system->getScreenFormat().bytesPerPixel == 1) + error("No true color mode available"); + + _backLayer = kMinAvailableOrder; + _frontLayer = kMaxAvailableOrder; + _firstDisplayElement = _lastDisplayElement = 0; + _workArea.create(640, 480, _vm->_system->getScreenFormat()); + _modifiedScreen = false; +} + +GraphicsManager::~GraphicsManager() { + _workArea.free(); +} + +void GraphicsManager::invalRect(const Common::Rect &rect) { + // We're using a simpler algorithm for dirty rect handling than the original + // The original was way too overcomplicated for what we need here now. + + if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) { + // We have no dirty rect, so this is now our dirty rect + _dirtyRect = rect; + } else { + // Expand our dirty rect to include rect + _dirtyRect.extend(rect); + } + + // Sanity check: clip our rect to the screen + _dirtyRect.right = MIN<int>(640, _dirtyRect.right); + _dirtyRect.bottom = MIN<int>(480, _dirtyRect.bottom); +} + +void GraphicsManager::addDisplayElement(DisplayElement *newElement) { + newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder); + + if (_firstDisplayElement) { + DisplayElement *runner = _firstDisplayElement; + DisplayElement *lastRunner = 0; + + // Search for first element whose display order is greater than + // the new element's and add the new element just before it. + while (runner) { + if (newElement->_elementOrder < runner->_elementOrder) { + if (lastRunner) { + lastRunner->_nextElement = newElement; + newElement->_nextElement = runner; + } else { + newElement->_nextElement = _firstDisplayElement; + _firstDisplayElement = newElement; + } + break; + } + lastRunner = runner; + runner = runner->_nextElement; + } + + // If got here and runner == NULL, we ran through the whole list without + // inserting, so add at the end. + if (!runner) { + _lastDisplayElement->_nextElement = newElement; + _lastDisplayElement = newElement; + } + } else { + _firstDisplayElement = newElement; + _lastDisplayElement = newElement; + } + + newElement->_elementIsDisplaying = true; +} + +void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) { + if (!_firstDisplayElement) + return; + + if (oldElement == _firstDisplayElement) { + if (oldElement == _lastDisplayElement) { + _firstDisplayElement = 0; + _lastDisplayElement = 0; + } else { + _firstDisplayElement = oldElement->_nextElement; + } + + invalRect(oldElement->_bounds); + } else { + // Scan list for element. + // If we get here, we know that the list has at least one item, and it + // is not the first item, so we can skip it. + DisplayElement *runner = _firstDisplayElement->_nextElement; + DisplayElement *lastRunner = _firstDisplayElement; + + while (runner) { + if (runner == oldElement) { + lastRunner->_nextElement = runner->_nextElement; + + if (oldElement == _lastDisplayElement) + _lastDisplayElement = lastRunner; + + invalRect(oldElement->_bounds); + break; + } + + lastRunner = runner; + runner = runner->_nextElement; + } + } + + oldElement->_nextElement = 0; + oldElement->_elementIsDisplaying = false; +} + +void GraphicsManager::updateDisplay() { + bool screenDirty = false; + + if (!_dirtyRect.isEmpty()) { + for (DisplayElement *runner = _firstDisplayElement; runner != 0; runner = runner->_nextElement) { + Common::Rect bounds; + runner->getBounds(bounds); + + // TODO: Better logic; it does a bit more work than it probably needs to + // but it should work fine for now. + if (bounds.intersects(_dirtyRect) && runner->validToDraw(_backLayer, _frontLayer)) { + runner->draw(bounds); + screenDirty = true; + } + } + + // Copy only the dirty rect to the screen + if (screenDirty) + g_system->copyRectToScreen((byte *)_workArea.getBasePtr(_dirtyRect.left, _dirtyRect.top), _workArea.pitch, _dirtyRect.left, _dirtyRect.top, _dirtyRect.width(), _dirtyRect.height()); + + // Clear the dirty rect + _dirtyRect = Common::Rect(); + } + + if (screenDirty || _modifiedScreen) + g_system->updateScreen(); + + _modifiedScreen = false; +} + +void GraphicsManager::clearScreen() { + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); + g_system->unlockScreen(); + _modifiedScreen = true; +} + +DisplayElement *GraphicsManager::findDisplayElement(const tDisplayElementID id) { + DisplayElement *runner = _firstDisplayElement; + + while (runner) { + if (runner->getObjectID() == id) + return runner; + runner = runner->_nextElement; + } + + return 0; +} + +void GraphicsManager::doFadeOutSync(const TimeValue, const TimeValue, uint32 color) { + if (color == 0) + color = g_system->getScreenFormat().RGBToColor(0, 0, 0); + + // HACK: Until fading out is done, white-out the screen here + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), color); + g_system->unlockScreen(); + g_system->updateScreen(); +} + +void GraphicsManager::doFadeInSync(const TimeValue, const TimeValue, uint32) { + // TODO +} + +void GraphicsManager::markCursorAsDirty() { + _modifiedScreen = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h new file mode 100644 index 0000000000..e1b339843f --- /dev/null +++ b/engines/pegasus/graphics.h @@ -0,0 +1,80 @@ +/* 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_GRAPHICS_H +#define PEGASUS_GRAPHICS_H + +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/pict.h" +#include "graphics/surface.h" + +#include "pegasus/constants.h" +#include "pegasus/pegasus.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Cursor; +class DisplayElement; +class PegasusEngine; + +class GraphicsManager { +friend class Cursor; +public: + GraphicsManager(PegasusEngine *vm); + ~GraphicsManager(); + + void addDisplayElement(DisplayElement *element); + void removeDisplayElement(DisplayElement *element); + void invalRect(const Common::Rect &rect); + tDisplayOrder getBackOfActiveLayer() const { return _backLayer; } + tDisplayOrder getFrontOfActiveLayer() const { return _frontLayer; } + void updateDisplay(); + Graphics::Surface *getWorkArea() { return &_workArea; } + void clearScreen(); + DisplayElement *findDisplayElement(const tDisplayElementID id); + + // These default to black + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0); + void doFadeInSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0); + +protected: + void markCursorAsDirty(); + +private: + PegasusEngine *_vm; + + bool _modifiedScreen; + Common::Rect _dirtyRect; + tDisplayOrder _backLayer, _frontLayer; + DisplayElement *_firstDisplayElement, *_lastDisplayElement; + Graphics::Surface _workArea; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/hotspot.cpp b/engines/pegasus/hotspot.cpp new file mode 100755 index 0000000000..b8ff97a897 --- /dev/null +++ b/engines/pegasus/hotspot.cpp @@ -0,0 +1,327 @@ +/* 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/stream.h" + +#include "pegasus/hotspot.h" + +namespace Pegasus { + +HotspotList g_allHotspots; + +Region::Region(Common::ReadStream *stream) { + uint16 length = stream->readUint16BE(); + + assert(length >= 10); + + _bounds.top = stream->readUint16BE(); + _bounds.left = stream->readUint16BE(); + _bounds.bottom = stream->readUint16BE(); + _bounds.right = stream->readUint16BE(); + + _bounds.debugPrint(0, "Bounds:"); + + if (length == 10) + return; + + length -= 10; + + while (length > 0) { + Vector v; + v.y = stream->readUint16BE(); + length -= 2; + + if (v.y == 0x7fff) + break; + + debug(0, "y: %d", v.y); + + // Normalize y to _bounds + v.y -= _bounds.top; + + while (length > 0) { + Run run; + run.start = stream->readUint16BE(); + length -= 2; + + if (run.start == 0x7fff) + break; + + run.end = stream->readUint16BE(); + length -= 2; + + debug(0, "\t[%d, %d)", run.start, run.end); + + // Normalize to _bounds + run.start -= _bounds.left; + run.end -= _bounds.left; + + v.push_back(run); + } + + _vectors.push_back(v); + } +} + +Region::Region(const Common::Rect &rect) { + _bounds = rect; +} + +bool Region::pointInRegion(const Common::Point &point) const { + if (!_bounds.contains(point)) + return false; + + bool pixelActive = false; + + // Normalize the points to _bounds + uint16 x = point.x - _bounds.left; + uint16 y = point.y - _bounds.top; + + for (Common::List<Vector>::const_iterator v = _vectors.begin(); v != _vectors.end(); v++) { + if (v->y > y) + return pixelActive; + + for (Vector::const_iterator run = v->begin(); run != v->end(); run++) { + if (x >= run->start && x < run->end) { + pixelActive = !pixelActive; + break; + } + } + } + + // the case if the region is just a rect + return true; +} + +void Region::moveTo(tCoordType h, tCoordType v) { + _bounds.moveTo(h, v); +} + +void Region::moveTo(const Common::Point &point) { + _bounds.moveTo(point); +} + +void Region::translate(tCoordType h, tCoordType v) { + _bounds.translate(h, v); +} + +void Region::translate(const Common::Point &point) { + _bounds.translate(point.x, point.y); +} + +void Region::getCenter(tCoordType &h, tCoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void Region::getCenter(Common::Point &point) const { + getCenter(point.x, point.y); +} + +Hotspot::Hotspot(const tHotSpotID id) : IDObject(id) { + _spotFlags = kNoHotSpotFlags; + _spotActive = false; +} + +Hotspot::~Hotspot() { +} + +void Hotspot::setArea(const Common::Rect &area) { + _spotArea = Region(area); +} + +void Hotspot::setArea(const tCoordType left, const tCoordType top, const tCoordType right, const tCoordType bottom) { + _spotArea = Region(Common::Rect(left, top, right, bottom)); +} + +void Hotspot::getBoundingBox(Common::Rect &r) const { + r = _spotArea.getBoundingBox(); +} + +void Hotspot::getCenter(Common::Point &pt) const { + _spotArea.getCenter(pt); +} + +void Hotspot::getCenter(tCoordType &h, tCoordType &v) const { + _spotArea.getCenter(h, v); +} + +void Hotspot::setActive() { + _spotActive = true; +} + +void Hotspot::setInactive() { + _spotActive = false; +} + +void Hotspot::setHotspotFlags(const tHotSpotFlags flags) { + _spotFlags = flags; +} + +void Hotspot::setMaskedHotspotFlags(const tHotSpotFlags flags, const tHotSpotFlags mask) { + _spotFlags = (_spotFlags & ~mask) | flags; +} + +bool Hotspot::isSpotActive() const { + return _spotActive; +} + +void Hotspot::moveSpotTo(const tCoordType h, const tCoordType v) { + _spotArea.moveTo(h, v); +} + +void Hotspot::moveSpotTo(const Common::Point pt) { + _spotArea.moveTo(pt); +} + +void Hotspot::moveSpot(const tCoordType h, const tCoordType v) { + _spotArea.translate(h, v); +} + +void Hotspot::moveSpot(const Common::Point pt) { + _spotArea.translate(pt.x, pt.y); +} + +bool Hotspot::pointInSpot(const Common::Point where) const { + return _spotActive && _spotArea.pointInRegion(where); +} + +tHotSpotFlags Hotspot::getHotspotFlags() const { + return _spotFlags; +} + +HotspotList::HotspotList() { +} + +HotspotList::~HotspotList() { + // TODO: Should this call deleteHotspots()? +} + +void HotspotList::deleteHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + delete *it; + + clear(); +} + +Hotspot *HotspotList::findHotspot(const Common::Point where) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->pointInSpot(where)) + return *it; + + return 0; +} + +tHotSpotID HotspotList::findHotspotID(const Common::Point where) { + Hotspot *hotspot = findHotspot(where); + return hotspot ? hotspot->getObjectID() : kNoHotSpotID; +} + +Hotspot *HotspotList::findHotspotByID(const tHotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +Hotspot *HotspotList::findHotspotByMask(const tHotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) == flags) + return *it; + + return 0; +} + +void HotspotList::activateMaskedHotspots(const tHotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (flags == kNoHotSpotFlags || ((*it)->getHotspotFlags() & flags) != 0) + (*it)->setActive(); +} + +void HotspotList::deactivateAllHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + (*it)->setInactive(); +} + +void HotspotList::deactivateMaskedHotspots(const tHotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) != 0) + (*it)->setInactive(); +} + +void HotspotList::activateOneHotspot(const tHotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setActive(); + return; + } + } +} + +void HotspotList::deactivateOneHotspot(const tHotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setInactive(); + return; + } + } +} + +void HotspotList::removeOneHotspot(const tHotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + erase(it); + return; + } + } +} + +void HotspotList::removeMaskedHotspots(const tHotSpotFlags flags) { + if (flags != kNoHotSpotFlags) { + for (HotspotIterator it = begin(); it != end(); ) { + if (((*it)->getHotspotFlags() & flags) != 0) + it = erase(it); + else + it++; + } + } else { + clear(); + } +} + +void HotspotList::setHotspotRect(const tHotSpotID id, const Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->setArea(r); +} + +void HotspotList::getHotspotRect(const tHotSpotID id, Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->getBoundingBox(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/hotspot.h b/engines/pegasus/hotspot.h new file mode 100755 index 0000000000..aafb96cc19 --- /dev/null +++ b/engines/pegasus/hotspot.h @@ -0,0 +1,153 @@ +/* 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_HOTSPOT_H +#define PEGASUS_HOTSPOT_H + +#include "common/list.h" +#include "common/rect.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" +#include "pegasus/util.h" + +/* + + Hot spots combine a pixel area, an ID value and an active flag. + + A point is considered in a hot spot if the point is in the hot spot's pixel area and + the active flag is set. + + In addition, hot spots have a 32 bit word of bit flags for filtering use. + +*/ + +namespace Common { + class ReadStream; +} + +namespace Pegasus { + +// Our implementation of QuickDraw regions +class Region { +public: + Region() {} + Region(Common::ReadStream *stream); + Region(const Common::Rect &rect); + + Common::Rect getBoundingBox() const { return _bounds; } + + bool pointInRegion(const Common::Point &point) const; + + void moveTo(tCoordType h, tCoordType v); + void moveTo(const Common::Point &point); + void translate(tCoordType h, tCoordType v); + void translate(const Common::Point &point); + void getCenter(tCoordType &h, tCoordType &v) const; + void getCenter(Common::Point &point) const; + +private: + Common::Rect _bounds; + + struct Run { + uint16 start, end; + }; + + class Vector : public Common::List<Run> { + public: + uint16 y; + }; + + Common::List<Vector> _vectors; +}; + +class Hotspot : public IDObject { +public: + Hotspot(const tHotSpotID); + virtual ~Hotspot(); + + void setArea(const Region ®ion) { _spotArea = region; } + void setArea(const Common::Rect &); + void setArea(const tCoordType, const tCoordType, const tCoordType, const tCoordType); + void getBoundingBox(Common::Rect &) const; + void getArea(Region &) const; + void getCenter(Common::Point&) const; + void getCenter(tCoordType&, tCoordType&) const; + + void moveSpotTo(const tCoordType, const tCoordType); + void moveSpotTo(const Common::Point); + void moveSpot(const tCoordType, const tCoordType); + void moveSpot(const Common::Point); + + bool pointInSpot(const Common::Point) const; + + void setActive(); + void setInactive(); + bool isSpotActive() const; + + tHotSpotFlags getHotspotFlags() const; + void setHotspotFlags(const tHotSpotFlags); + void setMaskedHotspotFlags(const tHotSpotFlags flags, const tHotSpotFlags mask); + +protected: + Region _spotArea; + tHotSpotFlags _spotFlags; + bool _spotActive; +}; + +class HotspotList : public Common::List<Hotspot *> { +public: + HotspotList(); + virtual ~HotspotList(); + + void deleteHotspots(); + + Hotspot *findHotspot(const Common::Point); + tHotSpotID findHotspotID(const Common::Point); + Hotspot * findHotspotByID(const tHotSpotID); + Hotspot * findHotspotByMask(const tHotSpotFlags); + + void activateMaskedHotspots(const tHotSpotFlags = kNoHotSpotFlags); + void deactivateAllHotspots(); + void deactivateMaskedHotspots(const tHotSpotFlags); + + void activateOneHotspot(const tHotSpotID); + void deactivateOneHotspot(const tHotSpotID); + + void removeOneHotspot(const tHotSpotID); + void removeMaskedHotspots(const tHotSpotFlags = kNoHotSpotFlags); + + void setHotspotRect(const tHotSpotID, const Common::Rect&); + void getHotspotRect(const tHotSpotID, Common::Rect&); +}; + +typedef HotspotList::iterator HotspotIterator; + +// FIXME: Remove global construction +extern HotspotList g_allHotspots; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp new file mode 100755 index 0000000000..6906dd9bb5 --- /dev/null +++ b/engines/pegasus/input.cpp @@ -0,0 +1,315 @@ +/* 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/events.h" +#include "common/system.h" + +#include "pegasus/cursor.h" +#include "pegasus/input.h" +#include "pegasus/pegasus.h" + +namespace Pegasus { + +InputDevice::InputDevice() { + _lastRawBits = kAllUpBits; +} + +InputDevice::~InputDevice() { +} + +void InputDevice::getInput(Input &input, const tInputBits filter) { + // TODO: Save/Load keys + + tInputBits currentBits = 0; + bool consoleRequested = false; + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + // We only care about key down here + // We're mapping from ScummVM events to pegasus events, which + // are based on pippin events. + if (event.type == Common::EVENT_KEYDOWN) { + switch (event.kbd.keycode) { + case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: + currentBits |= (kRawButtonDown << kUpButtonShift); + break; + case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: + currentBits |= (kRawButtonDown << kLeftButtonShift); + break; + case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP5: + currentBits |= (kRawButtonDown << kDownButtonShift); + break; + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: + currentBits |= (kRawButtonDown << kRightButtonShift); + break; + case Common::KEYCODE_RETURN: + case Common::KEYCODE_SPACE: + currentBits |= (kRawButtonDown << kTwoButtonShift); + break; + case Common::KEYCODE_t: + case Common::KEYCODE_KP_EQUALS: + currentBits |= (kRawButtonDown << kThreeButtonShift); + break; + case Common::KEYCODE_i: + case Common::KEYCODE_KP_DIVIDE: + currentBits |= (kRawButtonDown << kFourButtonShift); + break; + case Common::KEYCODE_q: + currentBits |= (kRawButtonDown << kMod1ButtonShift); + break; + case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_p: + currentBits |= (kRawButtonDown << kMod3ButtonShift); + break; + case Common::KEYCODE_TILDE: + case Common::KEYCODE_BACKQUOTE: + case Common::KEYCODE_NUMLOCK: // Yes, the original uses Num Lock/Clear on the Mac... + currentBits |= (kRawButtonDown << kLeftFireButtonShift); + break; + case Common::KEYCODE_BACKSPACE: + case Common::KEYCODE_KP_MULTIPLY: + currentBits |= (kRawButtonDown << kRightFireButtonShift); + break; + case Common::KEYCODE_d: + if (event.kbd.flags & Common::KBD_CTRL) // Console! + consoleRequested = true; + break; + default: + break; + } + +#if 0 + // FIXME: This is disabled for now because it interferes with + // the ScummVM alt combinations. It's only used for one easter egg + // anyway, so I'll come up with something when I get around to that. + if (event.kbd.flags & Common::KBD_ALT) + currentBits |= (kRawButtonDown << kMod2ButtonShift); +#endif + } + } + + // Update mouse button state + // Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because + // they do not show if the button is being held down. We're treating + // both mouse buttons as the same for ease of use. + if (g_system->getEventManager()->getButtonState() != 0) + currentBits |= (kRawButtonDown << kTwoButtonShift); + + // Update the mouse position too + input.setInputLocation(g_system->getEventManager()->getMousePos()); + + // Set the outgoing bits + tInputBits filteredBits = currentBits & filter; + input.setInputBits((filteredBits & kAllButtonDownBits) | (filteredBits & _lastRawBits & kAllAutoBits)); + + // Update the last bits + _lastRawBits = currentBits; + + // Set the console to be requested or not + input.setConsoleRequested(consoleRequested); +} + +// Wait until the input device stops returning input allowed by filter... +void InputDevice::waitInput(const tInputBits filter) { + if (filter != 0) { + for (;;) { + Input input; + getInput(input, filter); + if (!input.anyInput()) + break; + } + } +} + +int operator==(const Input &arg1, const Input &arg2) { + return arg1._inputState == arg2._inputState; +} + +int operator!=(const Input &arg1, const Input &arg2) { + return !operator==(arg1, arg2); +} + +InputHandler *InputHandler::_inputHandler = 0; +InputDevice InputHandler::_inputDevice; +bool InputHandler::_invalHotspots = false; +tInputBits InputHandler::_lastFilter = kFilterNoInput; + +InputHandler *InputHandler::setInputHandler(InputHandler *currentHandler) { + InputHandler *result = 0; + + if (_inputHandler != currentHandler && (!_inputHandler || _inputHandler->releaseInputFocus())) { + result = _inputHandler; + _inputHandler = currentHandler; + if (_inputHandler) + _inputHandler->grabInputFocus(); + } + + return result; +} + +void InputHandler::pollForInput() { + if (_inputHandler) { + Input input; + Hotspot *cursorSpot; + + InputHandler::getInput(input, cursorSpot); + if (_inputHandler->isClickInput(input, cursorSpot)) + _inputHandler->clickInHotspot(input, cursorSpot); + else + _inputHandler->handleInput(input, cursorSpot); + } +} + +void InputHandler::getInput(Input &input, Hotspot *&cursorSpot) { + Cursor *cursor = ((PegasusEngine *)g_engine)->_cursor; + + if (_inputHandler) + _lastFilter = _inputHandler->getInputFilter(); + else + _lastFilter = kFilterAllInput; + + _inputDevice.getInput(input, _lastFilter); + + if (_inputHandler && _inputHandler->wantsCursor() && (_lastFilter & _inputHandler->getClickFilter()) != 0) { + if (cursor->isVisible()) { + g_allHotspots.deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + Common::Point cursorLocation; + cursor->getCursorLocation(cursorLocation); + cursorSpot = g_allHotspots.findHotspot(cursorLocation); + + if (_inputHandler) + _inputHandler->updateCursor(cursorLocation, cursorSpot); + } else { + cursor->hideUntilMoved(); + } + } else { + cursor->hide(); + } +} + +void InputHandler::readInputDevice(Input &input) { + _inputDevice.getInput(input, kFilterAllInput); +} + +InputHandler::InputHandler(InputHandler *nextHandler) { + _nextHandler = nextHandler; + allowInput(true); +} + +InputHandler::~InputHandler() { + if (_inputHandler == this) + setInputHandler(_nextHandler); +} + +void InputHandler::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->handleInput(input, cursorSpot); +} + +void InputHandler::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->clickInHotspot(input, cursorSpot); +} + +bool InputHandler::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + return _nextHandler->isClickInput(input, cursorSpot); + + return false; +} + +void InputHandler::activateHotspots() { + if (_nextHandler) + _nextHandler->activateHotspots(); +} + +tInputBits InputHandler::getInputFilter() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->getInputFilter(); + else + return kFilterAllInput; + } + + return kFilterNoInput; +} + +tInputBits InputHandler::getClickFilter() { + if (_allowInput && _nextHandler) + return _nextHandler->getClickFilter(); + + return kFilterNoInput; +} + +void InputHandler::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->updateCursor(cursorLocation, cursorSpot); +} + +bool InputHandler::wantsCursor() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->wantsCursor(); + else + return true; + } + + return false; +} + +Tracker *Tracker::_currentTracker = 0; + +void Tracker::handleInput(const Input &input, const Hotspot *) { + if (stopTrackingInput(input)) + stopTracking(input); + else if (isTracking()) + continueTracking(input); +} + +void Tracker::startTracking(const Input &) { + if (!isTracking()) { + _savedHandler = InputHandler::setInputHandler(this); + _currentTracker = this; + } +} + +void Tracker::stopTracking(const Input &) { + if (isTracking()) { + _currentTracker = NULL; + InputHandler::setInputHandler(_savedHandler); + } +} + +bool Tracker::isClickInput(const Input &input, const Hotspot *hotspot) { + return !isTracking() && InputHandler::isClickInput(input, hotspot); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h new file mode 100755 index 0000000000..96df249737 --- /dev/null +++ b/engines/pegasus/input.h @@ -0,0 +1,486 @@ +/* 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_INPUT_H +#define PEGASUS_INPUT_H + +#include "common/rect.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class Hotspot; +class Input; + +class InputDevice { +public: + InputDevice(); + ~InputDevice(); + + void getInput(Input&, const tInputBits); + + void waitInput(const tInputBits); + +protected: + tInputBits _lastRawBits; +}; + +enum { + kButtonDownBit = 0, + kAutoButtonBit = 1, + kBitsPerButton = 2, + + kButtonDownMask = 1 << kButtonDownBit, + kAutoButtonMask = 1 << kAutoButtonBit, + + kButtonStateBits = kButtonDownMask | kAutoButtonMask, + + kRawButtonUp = 0, + kRawButtonDown = kButtonDownMask | kAutoButtonMask, + + kButtonUp = 0, + kButtonDown = kButtonDownMask, + kButtonAutoUp = kAutoButtonMask, + kButtonAutoDown = kButtonDownMask | kAutoButtonMask +}; + +enum { + kUpButtonNum = 0, + kLeftButtonNum = 1, + kDownButtonNum = 2, + kRightButtonNum = 3, + kLeftFireButtonNum = 4, + kRightFireButtonNum = 5, + kOneButtonNum = 6, + kTwoButtonNum = 7, + kThreeButtonNum = 8, + kFourButtonNum = 9, + kMod1ButtonNum = 10, + kMod2ButtonNum = 11, + kMod3ButtonNum = 12 +}; + +enum { + kUpButtonShift = kUpButtonNum * kBitsPerButton, + kLeftButtonShift = kLeftButtonNum * kBitsPerButton, + kDownButtonShift = kDownButtonNum * kBitsPerButton, + kRightButtonShift = kRightButtonNum * kBitsPerButton, + kLeftFireButtonShift = kLeftFireButtonNum * kBitsPerButton, + kRightFireButtonShift = kRightFireButtonNum * kBitsPerButton, + kOneButtonShift = kOneButtonNum * kBitsPerButton, + kTwoButtonShift = kTwoButtonNum * kBitsPerButton, + kThreeButtonShift = kThreeButtonNum * kBitsPerButton, + kFourButtonShift = kFourButtonNum * kBitsPerButton, + kMod1ButtonShift = kMod1ButtonNum * kBitsPerButton, + kMod2ButtonShift = kMod2ButtonNum * kBitsPerButton, + kMod3ButtonShift = kMod3ButtonNum * kBitsPerButton +}; + +enum { + kAllUpBits = (kButtonUp << kUpButtonShift) | + (kButtonUp << kLeftButtonShift) | + (kButtonUp << kDownButtonShift) | + (kButtonUp << kRightButtonShift) | + (kButtonUp << kLeftFireButtonShift) | + (kButtonUp << kRightFireButtonShift) | + (kButtonUp << kOneButtonShift) | + (kButtonUp << kTwoButtonShift) | + (kButtonUp << kThreeButtonShift) | + (kButtonUp << kFourButtonShift) | + (kButtonUp << kMod1ButtonShift) | + (kButtonUp << kMod2ButtonShift) | + (kButtonUp << kMod3ButtonShift), + kDirectionBits = (kButtonDownMask << kUpButtonShift) | + (kButtonDownMask << kLeftButtonShift) | + (kButtonDownMask << kDownButtonShift) | + (kButtonDownMask << kRightButtonShift), + kButtonBits = (kButtonDownMask << kLeftFireButtonShift) | + (kButtonDownMask << kRightFireButtonShift) | + (kButtonDownMask << kOneButtonShift) | + (kButtonDownMask << kTwoButtonShift) | + (kButtonDownMask << kThreeButtonShift) | + (kButtonDownMask << kFourButtonShift) | + (kButtonDownMask << kMod1ButtonShift) | + (kButtonDownMask << kMod2ButtonShift) | + (kButtonDownMask << kMod3ButtonShift), + kAllButtonDownBits = kDirectionBits | kButtonBits, + kAllAutoBits = (kAutoButtonMask << kUpButtonShift) | + (kAutoButtonMask << kLeftButtonShift) | + (kAutoButtonMask << kDownButtonShift) | + (kAutoButtonMask << kRightButtonShift) | + (kAutoButtonMask << kLeftFireButtonShift) | + (kAutoButtonMask << kRightFireButtonShift) | + (kAutoButtonMask << kOneButtonShift) | + (kAutoButtonMask << kTwoButtonShift) | + (kAutoButtonMask << kThreeButtonShift) | + (kAutoButtonMask << kFourButtonShift) | + (kAutoButtonMask << kMod1ButtonShift) | + (kAutoButtonMask << kMod2ButtonShift) | + (kAutoButtonMask << kMod3ButtonShift), + + kFilterUpButton = kButtonDownMask << kUpButtonShift, + kFilterUpAuto = kAutoButtonMask << kUpButtonShift, + kFilterUpButtonAny = kFilterUpButton | kFilterUpAuto, + kFilterLeftButton = kButtonDownMask << kLeftButtonShift, + kFilterLeftAuto = kAutoButtonMask << kLeftButtonShift, + kFilterLeftButtonAny = kFilterLeftButton | kFilterLeftAuto, + kFilterDownButton = kButtonDownMask << kDownButtonShift, + kFilterDownAuto = kAutoButtonMask << kDownButtonShift, + kFilterDownButtonAny = kFilterDownButton | kFilterDownAuto, + kFilterRightButton = kButtonDownMask << kRightButtonShift, + kFilterRightAuto = kAutoButtonMask << kRightButtonShift, + kFilterRightButtonAny = kFilterRightButton | kFilterRightAuto, + kFilterLeftFireButton = kButtonDownMask << kLeftFireButtonShift, + kFilterLeftFireAuto = kAutoButtonMask << kLeftFireButtonShift, + kFilterLeftFireButtonAny = kFilterLeftFireButton | kFilterLeftFireAuto, + kFilterRightFireButton = kButtonDownMask << kRightFireButtonShift, + kFilterRightFireAuto = kAutoButtonMask << kRightFireButtonShift, + kFilterRightFireButtonAny = kFilterRightFireButton | kFilterRightFireAuto, + kFilterOneButton = kButtonDownMask << kOneButtonShift, + kFilterOneAuto = kAutoButtonMask << kOneButtonShift, + kFilterOneButtonAny = kFilterOneButton | kFilterOneAuto, + kFilterTwoButton = kButtonDownMask << kTwoButtonShift, + kFilterTwoAuto = kAutoButtonMask << kTwoButtonShift, + kFilterTwoButtonAny = kFilterTwoButton | kFilterTwoAuto, + kFilterThreeButton = kButtonDownMask << kThreeButtonShift, + kFilterThreeAuto = kAutoButtonMask << kThreeButtonShift, + kFilterThreeButtonAny = kFilterThreeButton | kFilterThreeAuto, + kFilterFourButton = kButtonDownMask << kFourButtonShift, + kFilterFourAuto = kAutoButtonMask << kFourButtonShift, + kFilterFourButtonAny = kFilterFourButton | kFilterFourAuto, + kFilterMod1Button = kButtonDownMask << kMod1ButtonShift, + kFilterMod1Auto = kAutoButtonMask << kMod1ButtonShift, + kFilterMod1ButtonAny = kFilterMod1Button | kFilterMod1Auto, + kFilterMod2Button = kButtonDownMask << kMod2ButtonShift, + kFilterMod2Auto = kAutoButtonMask << kMod2ButtonShift, + kFilterMod2ButtonAny = kFilterMod2Button | kFilterMod2Auto, + kFilterMod3Button = kButtonDownMask << kMod3ButtonShift, + kFilterMod3Auto = kAutoButtonMask << kMod3ButtonShift, + kFilterMod3ButtonAny = kFilterMod3Button | kFilterMod3Auto, + + kFilterNoInput = 0, + kFilterAllInput = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto | + kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllDirections = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto, + + kFilterButtons = kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto, + + kFilterFireButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto, + + kFilterAllButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllInputNoAuto = kFilterUpButton | + kFilterLeftButton | + kFilterDownButton | + kFilterRightButton | + kFilterLeftFireButton | + kFilterRightFireButton | + kFilterOneButton | + kFilterTwoButton | + kFilterThreeButton | + kFilterFourButton | + kFilterMod1Button | + kFilterMod2Button | + kFilterMod3Button +}; + +static const tInputBits kHintInterruption = kFilterAllInputNoAuto; +static const tInputBits kWarningInterruption = kFilterNoInput; +static const tInputBits kOpticalInterruption = kFilterAllInputNoAuto; + +/* + + Buttons are defined as: + up, left, down, right direction buttons. + fireLeft, fireRight: fire buttons. + mod1, mod2, mod3: modifier buttons, similar to shift, control, etc. + a, b, c, d: general purpose buttons. + + button state is held as bits in a long word, two bits per button. + + Filter bits: + for each button, two bits are assigned for filtering. If bit 0 is set, the + corresponding button is available for "button down" input. If bit 1 is set, + the corresponding button is available for "auto down" input. Note that bit + 1 is meaningful only if bit 0 is set. + +*/ + +class Input { +friend int operator==(const Input &, const Input &); +friend int operator!=(const Input &, const Input &); +friend class InputDevice; + +public: + Input() { clearInput(); } + + bool upButtonDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonDown << kUpButtonShift); } + bool upButtonAutoDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonAutoDown << kUpButtonShift); } + bool upButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kUpButtonShift)) != 0; } + + bool leftButtonDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonDown << kLeftButtonShift); } + bool leftButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonAutoDown << kLeftButtonShift); } + bool leftButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftButtonShift)) != 0; } + + bool downButtonDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonDown << kDownButtonShift); } + bool downButtonAutoDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonAutoDown << kDownButtonShift); } + bool downButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kDownButtonShift)) != 0; } + + bool rightButtonDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonDown << kRightButtonShift); } + bool rightButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonAutoDown << kRightButtonShift); } + bool rightButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightButtonShift)) != 0; } + + bool leftFireButtonDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonDown << kLeftFireButtonShift); } + bool leftFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonAutoDown << kLeftFireButtonShift); } + bool leftFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftFireButtonShift)) != 0; } + + bool rightFireButtonDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonDown << kRightFireButtonShift); } + bool rightFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonAutoDown << kRightFireButtonShift); } + bool rightFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightFireButtonShift)) != 0; } + + bool oneButtonDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonDown << kOneButtonShift); } + bool oneButtonAutoDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonAutoDown << kOneButtonShift); } + bool oneButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kOneButtonShift)) != 0; } + + bool twoButtonDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonDown << kTwoButtonShift); } + bool twoButtonAutoDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonAutoDown << kTwoButtonShift); } + bool twoButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kTwoButtonShift)) != 0; } + + bool threeButtonDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonDown << kThreeButtonShift); } + bool threeButtonAutoDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonAutoDown << kThreeButtonShift); } + bool threeButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kThreeButtonShift)) != 0; } + + bool fourButtonDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonDown << kFourButtonShift); } + bool fourButtonAutoDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonAutoDown << kFourButtonShift); } + bool fourButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kFourButtonShift)) != 0; } + + bool mod1ButtonDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonDown << kMod1ButtonShift); } + bool mod1ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonAutoDown << kMod1ButtonShift); } + bool mod1ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod1ButtonShift)) != 0; } + + bool mod2ButtonDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonDown << kMod2ButtonShift); } + bool mod2ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonAutoDown << kMod2ButtonShift); } + bool mod2ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod2ButtonShift)) != 0; } + + bool mod3ButtonDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonDown << kMod3ButtonShift); } + bool mod3ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonAutoDown << kMod3ButtonShift); } + bool mod3ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod3ButtonShift)) != 0; } + + bool allAutoInput() const { return (_inputState & kAllAutoBits) != 0; } + bool anyDirectionInput() const { return (_inputState & kDirectionBits) != 0; } + bool anyButtonInput() const { return (_inputState & kButtonBits) != 0; } + bool anyInput() const { return _inputState != 0; } + + void getInputLocation(Common::Point &where) const { where = _inputLocation; } + + bool anyInputBitSet(const tInputBits bits) const { return (_inputState & bits) != 0; } + + bool isConsoleRequested() const { return _consoleRequested; } + + void clearInput() { + _inputState = kAllUpBits; + _inputLocation.x = 0; + _inputLocation.y = 0; + _consoleRequested = false; + } + +protected: + void setInputBits(const tInputBits state) { _inputState = state; } + void setInputLocation(const Common::Point &where) { _inputLocation = where; } + void setConsoleRequested(bool consoleRequested) { _consoleRequested = consoleRequested; } + + tInputBits _inputState; + Common::Point _inputLocation; + bool _consoleRequested; +}; + +class InputHandler { +public: + static InputHandler *setInputHandler(InputHandler*); + static InputHandler *getCurrentHandler() { return _inputHandler; } + static InputDevice *getCurrentInputDevice() { return &_inputDevice; } + static void pollForInput(); + static void getInput(Input&, Hotspot*&); + static void readInputDevice(Input&); + static void invalHotspots() { _invalHotspots = true; } + static tInputBits getCurrentFilter() { return _lastFilter; } + + InputHandler(InputHandler*); + virtual ~InputHandler(); + + virtual void setNextHandler(InputHandler *nextHandler) { _nextHandler = nextHandler; } + virtual InputHandler *getNextHandler() { return _nextHandler; } + + virtual void handleInput(const Input&, const Hotspot*); + virtual void clickInHotspot(const Input&, const Hotspot*); + + virtual void activateHotspots(); + virtual void updateCursor(const Common::Point, const Hotspot*); + virtual bool isClickInput(const Input&, const Hotspot*); + virtual bool wantsCursor(); + + virtual bool releaseInputFocus() { return true; } + virtual void grabInputFocus() {} + + // This returns bits set for what kinds of input to accept. + virtual tInputBits getInputFilter(); + + // This returns bits defining what input constitutes a "click." + virtual tInputBits getClickFilter(); + + virtual void allowInput(const bool allow) { _allowInput = allow; } + +protected: + static InputHandler *_inputHandler; + static InputDevice _inputDevice; // TODO: Remove global constructor + static bool _invalHotspots; + static tInputBits _lastFilter; + + InputHandler *_nextHandler; + bool _allowInput; +}; + + +/* + + Tracker implements "dragging". A Tracker can receive a startTracking message, + which causes it to be the current tracker, as well as setting it up as the current + input handler. In addition, only one tracker can be tracking at a time, and no + other handler can be set up as the current handler until the track finishes. By + default, there is no next input handler for a Tracker, but this behavior can be + overridden if desired. + +*/ + +class Tracker : public InputHandler { +public: + Tracker() : InputHandler(0) {} + virtual ~Tracker() {} + + virtual void handleInput(const Input &, const Hotspot *); + virtual bool stopTrackingInput(const Input &) { return false; } + + virtual void startTracking(const Input &); + virtual void stopTracking(const Input &); + virtual void continueTracking(const Input &) {} + + bool isTracking() { return this == _currentTracker; } + bool isClickInput(const Input &, const Hotspot *); + + bool releaseInputFocus() { return !isTracking(); } + +protected: + static Tracker *_currentTracker; + + InputHandler *_savedHandler; +}; + +class JMPPPInput { +public: + static bool isMenuButtonPressInput(const Input &input) { return input.twoButtonDown(); } + + static tInputBits getClickInputFilter() { return kFilterTwoButton; } + static bool isClickInput(const Input &input) { return input.twoButtonDown(); } + static bool isDraggingInput(const Input &input) { return input.twoButtonAnyDown(); } + static bool isPressingInput(const Input &input) { return input.twoButtonAnyDown(); } + + static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); } + static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); } + static tInputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; } + + static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); } + + static bool isToggleInfoInput(const Input &input) { return input.fourButtonDown(); } + + // Hmmmmm.... + static bool isEasterEggModifierInput(const Input &input) { return input.mod2ButtonAnyDown(); } + + static bool isTogglePauseInput(const Input &input) { return input.mod3ButtonDown(); } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h new file mode 100755 index 0000000000..b54c59ea38 --- /dev/null +++ b/engines/pegasus/interaction.h @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-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_INTERACTION_H +#define PEGASUS_INTERACTION_H + +#include "pegasus/input.h" +#include "pegasus/util.h" + +namespace Pegasus { + +static const tInteractionID kNoInteractionID = -1; + +class Neighborhood; + +class GameInteraction : public IDObject, public InputHandler { +public: + GameInteraction(const tInteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) { + _isInteracting = false; + _savedHandler = 0; + _owner = nextHandler; + } + + // If the interaction is open (_isInteracting == true), it's too late to do anything + // about it here. + virtual ~GameInteraction() {} + + // startInteraction and stopInteraction are called by the outside world to + // start and stop the interaction sequence. + // isInteracting returns a bool indicating whether or not the interaction + // is going. + void startInteraction() { + if (!isInteracting()) { + openInteraction(); + initInteraction(); + _isInteracting = true; + _savedHandler = InputHandler::setInputHandler(this); + } + } + void stopInteraction() { + if (isInteracting()) { + closeInteraction(); + _isInteracting = false; + if (InputHandler::_inputHandler == this) + InputHandler::setInputHandler(_savedHandler); + } + } + void startOverInteraction() { + if (isInteracting()) + resetInteraction(); + } + bool isInteracting() const { return _isInteracting; } + Neighborhood *getOwner() const { return _owner; } + + virtual Common::String getBriefingMovie() { return ""; } + virtual Common::String getEnvScanMovie() { return ""; } + virtual long getNumHints() { return 0; } + virtual Common::String getHintMovie(uint) { return ""; } + virtual bool canSolve() { return false; } + + virtual void setSoundFXLevel(const uint16) {} + virtual void setAmbienceLevel(const uint16) {} + + virtual void doSolve() {} + +protected: + // Subclasses override openInteraction and closeInteraction to perform + // specific initialization and cleanup. Override resetInteraction to + // "start the interaction over." resetInteraction is called only when + // the interaction is already open. + // These functions are only called in pairs, never two opens or closes + // in a row. + virtual void openInteraction() {} + virtual void initInteraction() {} + virtual void closeInteraction() {} + virtual void resetInteraction() {} + + InputHandler *_savedHandler; + Neighborhood *_owner; + +private: + // Private so that only StartInteraction and StopInteraction can touch it. + bool _isInteracting; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp new file mode 100755 index 0000000000..e9daecbd07 --- /dev/null +++ b/engines/pegasus/interface.cpp @@ -0,0 +1,667 @@ +/* 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/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Interface *g_interface = 0; + +Interface::Interface() : InputHandler(0), _interfaceNotification(kInterfaceNotificationID, (NotificationManager *)((PegasusEngine *)g_engine)), + _currentItemSpot(kCurrentItemSpotID), _currentBiochipSpot(kCurrentBiochipSpotID), + _background1(kInterface1ID), _background2(kInterface2ID), _background3(kInterface3ID), + _background4(kInterface4ID), _datePicture(kDateID), _inventoryPush(kInventoryPushID), + _inventoryLid(kInventoryLidID, kNoDisplayElement), + _inventoryPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getItemsInventory()), + _biochipPush(kBiochipPushID), _biochipLid(kBiochipLidID, kNoDisplayElement), + _biochipPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getBiochipsInventory()) { + g_energyMonitor = 0; + _previousHandler = 0; + _inventoryRaised = false; + _biochipRaised = false; + _playingEndMessage = false; + g_interface = this; +} + +Interface::~Interface() { + throwAwayInterface(); + g_interface = 0; +} + +void Interface::throwAwayInterface() { + g_allHotspots.removeOneHotspot(kCurrentItemSpotID); + g_allHotspots.removeOneHotspot(kCurrentBiochipSpotID); + + throwAwayBackground(); + throwAwayDateMonitor(); + throwAwayEnergyMonitor(); + throwAwayAIArea(); + throwAwayCompass(); + throwAwayNotifications(); + throwAwayInventoryPanel(); + throwAwayBiochipPanel(); +} + +void Interface::validateBackground() { + if (!_background1.isSurfaceValid()) { + _background1.initFromPICTFile("Images/Interface/3DInterface Left"); + _background2.initFromPICTFile("Images/Interface/3DInterface Top"); + _background3.initFromPICTFile("Images/Interface/3DInterface Right"); + _background4.initFromPICTFile("Images/Interface/3DInterface Bottom"); + + _background1.setDisplayOrder(kBackground1Order); + _background1.startDisplaying(); + _background1.moveElementTo(kBackground1Left, kBackground1Top); + + _background2.setDisplayOrder(kBackground2Order); + _background2.startDisplaying(); + _background2.moveElementTo(kBackground2Left, kBackground2Top); + + _background3.setDisplayOrder(kBackground2Order); + _background3.startDisplaying(); + _background3.moveElementTo(kBackground3Left, kBackground3Top); + + _background4.setDisplayOrder(kBackground4Order); + _background4.startDisplaying(); + _background4.moveElementTo(kBackground4Left, kBackground4Top); + + _background1.show(); + _background2.show(); + _background3.show(); + _background4.show(); + } +} + +void Interface::throwAwayBackground() { + _background1.stopDisplaying(); + _background1.deallocateSurface(); + _background2.stopDisplaying(); + _background2.deallocateSurface(); + _background3.stopDisplaying(); + _background3.deallocateSurface(); + _background4.stopDisplaying(); + _background4.deallocateSurface(); +} + +void Interface::validateDateMonitor() { + if (!_datePicture.isSurfaceValid()) { + _datePicture.setDisplayOrder(kDateOrder); + _datePicture.startDisplaying(); + _datePicture.moveElementTo(kDateLeft, kDateTop); + _datePicture.show(); + } +} + +void Interface::throwAwayDateMonitor() { + _datePicture.stopDisplaying(); + _datePicture.deallocateSurface(); +} + +void Interface::setDate(const uint16 dateResID) { + validateDateMonitor(); + _datePicture.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, dateResID); + _datePicture.triggerRedraw(); +} + +void Interface::validateCompass() { + if (!g_compass) { + new Compass(); + g_compass->initCompass(); + g_compass->setDisplayOrder(kCompassOrder); + g_compass->startDisplaying(); + g_compass->moveElementTo(kCompassLeft, kCompassTop); + g_compass->show(); + } +} + +void Interface::throwAwayCompass() { + delete g_compass; +} + +void Interface::validateNotifications() { + _interfaceNotification.notifyMe(this, kInterfaceNotificationFlags, kInterfaceNotificationFlags); + _inventoryLidCallBack.setNotification(&_interfaceNotification); + _inventoryPushCallBack.setNotification(&_interfaceNotification); + _biochipLidCallBack.setNotification(&_interfaceNotification); + _biochipPushCallBack.setNotification(&_interfaceNotification); +} + +void Interface::throwAwayNotifications() { + _interfaceNotification.cancelNotification(this); +} + +void Interface::validateAIArea() { + if (!g_AIArea) { + new AIArea((InputHandler *)((PegasusEngine *)g_engine)); + if (g_AIArea) + g_AIArea->initAIArea(); + } +} + +void Interface::throwAwayAIArea() { + delete g_AIArea; +} + +void Interface::validateInventoryPanel() { + if (!_inventoryPanel.isSurfaceValid()) { + _inventoryPanel.initInventoryImage(&_inventoryPush); + _inventoryPanel.moveElementTo(kInventoryPushLeft, kInventoryPushTop); + _inventoryPush.setSlideDirection(kSlideUpMask); + _inventoryPush.setInAndOutElements(&_inventoryPanel, 0); + _inventoryPush.setDisplayOrder(kInventoryPushOrder); + _inventoryPush.startDisplaying(); + + _inventoryLid.useFileName("Images/Lids/Inventory Lid Sequence"); + _inventoryLid.useTransparent(true); + _inventoryLid.openFrameSequence(); + _inventoryLid.moveElementTo(kInventoryLidLeft, kInventoryLidTop); + _inventoryLid.setDisplayOrder(kInventoryLidOrder); + _inventoryLid.startDisplaying(); + + _inventoryPushCallBack.initCallBack(&_inventoryPush, kCallBackAtExtremes); + _inventoryLidCallBack.initCallBack(&_inventoryLid, kCallBackAtExtremes); + + _inventoryUp = false; + _inventoryRaised = false; + + Item *item = getCurrentInventoryItem(); + if (item) + item->select(); + } +} + +void Interface::throwAwayInventoryPanel() { + _inventoryPanel.stopDisplaying(); + _inventoryPanel.throwAwayInventoryImage(); + _inventoryPush.stopDisplaying(); + _inventoryLid.stopDisplaying(); + _inventoryLid.closeFrameSequence(); + _inventoryPushCallBack.releaseCallBack(); + _inventoryLidCallBack.releaseCallBack(); + + Item *item = getCurrentInventoryItem(); + if (item) + item->deselect(); + + _inventoryUp = false; + _inventoryRaised = false; +} + +void Interface::validateBiochipPanel() { + if (!_biochipPanel.isSurfaceValid()) { + _biochipPanel.initInventoryImage(&_biochipPush); + _biochipPanel.moveElementTo(kBiochipPushLeft, kBiochipPushTop); + _biochipPush.setSlideDirection(kSlideUpMask); + _biochipPush.setInAndOutElements(&_biochipPanel, 0); + _biochipPush.setDisplayOrder(kBiochipPushOrder); + _biochipPush.startDisplaying(); + + _biochipLid.useFileName("Images/Lids/Biochip Lid Sequence"); + _biochipLid.useTransparent(true); + _biochipLid.openFrameSequence(); + _biochipLid.moveElementTo(kBiochipLidLeft, kBiochipLidTop); + _biochipLid.setDisplayOrder(kBiochipLidOrder); + _biochipLid.startDisplaying(); + + _biochipPushCallBack.initCallBack(&_biochipPush, kCallBackAtExtremes); + _biochipLidCallBack.initCallBack(&_biochipLid, kCallBackAtExtremes); + + _biochipUp = false; + _biochipRaised = false; + + Item *item = getCurrentBiochip(); + if (item) + item->select(); + } +} + +void Interface::throwAwayBiochipPanel() { + _biochipPanel.stopDisplaying(); + _biochipPanel.throwAwayInventoryImage(); + _biochipPush.stopDisplaying(); + _biochipLid.stopDisplaying(); + _biochipLid.closeFrameSequence(); + _biochipPushCallBack.releaseCallBack(); + _biochipLidCallBack.releaseCallBack(); + + Item *item = getCurrentBiochip(); + if (item) + item->deselect(); + + _biochipUp = false; + _biochipRaised = false; +} + +void Interface::validateEnergyMonitor() { + if (!g_energyMonitor) + new EnergyMonitor(); +} + +void Interface::throwAwayEnergyMonitor() { + delete g_energyMonitor; +} + +void Interface::createInterface() { + validateBackground(); + validateDateMonitor(); + validateCompass(); + validateNotifications(); + validateAIArea(); + validateBiochipPanel(); + validateInventoryPanel(); + validateEnergyMonitor(); + + if (!g_allHotspots.findHotspotByID(kCurrentItemSpotID)) { + _currentItemSpot.setArea(Common::Rect(76, 334, 172, 430)); + _currentItemSpot.setHotspotFlags(kShellSpotFlag); + _currentItemSpot.setActive(); + g_allHotspots.push_back(&_currentItemSpot); + } + + if (!g_allHotspots.findHotspotByID(kCurrentBiochipSpotID)) { + _currentBiochipSpot.setArea(Common::Rect(364, 334, 460, 430)); + _currentBiochipSpot.setHotspotFlags(kShellSpotFlag); + _currentBiochipSpot.setActive(); + g_allHotspots.push_back(&_currentBiochipSpot); + } +} + +tInventoryResult Interface::addInventoryItem(InventoryItem *item) { + return _inventoryPanel.addInventoryItem(item); +} + +tInventoryResult Interface::removeInventoryItem(InventoryItem *item) { + return _inventoryPanel.removeInventoryItem(item); +} + +void Interface::removeAllItemsFromInventory() { + _inventoryPanel.removeAllItems(); +} + +InventoryItem *Interface::getCurrentInventoryItem() { + return (InventoryItem *)_inventoryPanel.getCurrentItem(); +} + +void Interface::setCurrentInventoryItem(InventoryItem *item) { + setCurrentInventoryItemID(item->getObjectID()); +} + +void Interface::setCurrentInventoryItemID(tItemID id) { + _inventoryPanel.setCurrentItemID(id); +} + +tInventoryResult Interface::addBiochip(BiochipItem *item) { + return _biochipPanel.addInventoryItem(item); +} + +void Interface::removeAllItemsFromBiochips() { + _biochipPanel.removeAllItems(); +} + +BiochipItem *Interface::getCurrentBiochip() { + return (BiochipItem *)_biochipPanel.getCurrentItem(); +} + +void Interface::setCurrentBiochip(BiochipItem *item) { + setCurrentBiochipID(item->getObjectID()); +} + +void Interface::setCurrentBiochipID(tItemID id) { + _biochipPanel.setCurrentItemID(id); +} + +void Interface::receiveNotification(Notification *notification, const tNotificationFlags flags) { + if (notification == &_interfaceNotification) { + switch (flags) { + case kInventoryLidOpenFlag: + inventoryLidOpen(true); + break; + case kInventoryLidClosedFlag: + inventoryLidClosed(); + break; + case kInventoryDrawerUpFlag: + inventoryDrawerUp(); + break; + case kInventoryDrawerDownFlag: + inventoryDrawerDown(true); + break; + case kBiochipLidOpenFlag: + biochipLidOpen(true); + break; + case kBiochipLidClosedFlag: + biochipLidClosed(); + break; + case kBiochipDrawerUpFlag: + biochipDrawerUp(); + break; + case kBiochipDrawerDownFlag: + biochipDrawerDown(true); + break; + } + } +} + +void Interface::raiseInventoryDrawer(const bool doCallBacks) { + if (!_biochipUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_inventoryPanel); + _inventoryUp = true; + _inventoryPanel.activateInventoryPicture(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidOpenFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _inventoryLid.show(); + _inventoryPush.show(); + _inventoryLid.start(); +} + +void Interface::playEndMessage() { + raiseInventoryDrawerForMessage(); + _playingEndMessage = true; + _inventoryPanel.playEndMessage(&_inventoryPush); + lowerInventoryDrawerForMessage(); + _playingEndMessage = false; +} + +void Interface::raiseInventoryDrawerForMessage() { + _inventoryPanel.disableLooping(); + raiseInventoryDrawerSync(); +} + +void Interface::lowerInventoryDrawerForMessage() { + lowerInventoryDrawerSync(); +} + +void Interface::inventoryLidOpen(const bool doCallBacks) { + _inventoryLid.stop(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerUpFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); + _inventoryPush.startFader(moveSpec); +} + +void Interface::inventoryDrawerUp() { + _inventoryPush.stopFader(); + _inventoryPanel.panelUp(); + _inventoryRaised = true; +} + +bool Interface::isInventoryUp() { + return _inventoryRaised; +} + +bool Interface::isInventoryDown() { + return !_inventoryUp; +} + +void Interface::lowerInventoryDrawer(const bool doCallBacks) { + if (_inventoryRaised) { + _inventoryRaised = false; + + if (!_playingEndMessage) + _inventoryPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerDownFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 15, 0); + _inventoryPush.startFader(moveSpec); + } +} + +void Interface::inventoryDrawerDown(const bool doCallBacks) { + _inventoryPush.stopFader(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidClosedFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _inventoryLid.setRate(-1); +} + +void Interface::inventoryLidClosed() { + _inventoryLid.stop(); + + if (!_biochipUp) + InputHandler::setInputHandler(_previousHandler); + + _inventoryLid.hide(); + _inventoryPush.hide(); + _inventoryUp = false; +} + +void Interface::raiseBiochipDrawer(const bool doCallBacks) { + if (!_inventoryUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_biochipPanel); + _biochipUp = true; + _biochipPanel.activateInventoryPicture(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidOpenFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _biochipLid.show(); + _biochipPush.show(); + _biochipLid.start(); +} + +void Interface::biochipLidOpen(const bool doCallBacks) { + _biochipLid.stop(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerUpFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 9, 1000); + _biochipPush.startFader(moveSpec); +} + +void Interface::biochipDrawerUp() { + _biochipPush.stopFader(); + _biochipPanel.panelUp(); + _biochipRaised = true; +} + +void Interface::lowerBiochipDrawer(const bool doCallBacks) { + if (_biochipRaised) { + _biochipRaised = false; + _biochipPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerDownFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 9, 0); + _biochipPush.startFader(moveSpec); + } +} + +void Interface::biochipDrawerDown(const bool doCallBacks) { + _biochipPush.stopFader(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidClosedFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _biochipLid.setRate(-1); +} + +void Interface::biochipLidClosed() { + _biochipLid.stop(); + + if (!_inventoryUp) + InputHandler::setInputHandler(_previousHandler); + + _biochipLid.hide(); + _biochipPush.hide(); + _biochipUp = false; +} + +void Interface::calibrateCompass() { + uint32 currentValue = g_compass->getFaderValue(); + FaderMoveSpec compassMove; + compassMove.makeTwoKnotFaderSpec(15, 0, currentValue, 30, currentValue + 360); + + g_compass->startFader(compassMove); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + while (g_compass->isFading()) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + g_compass->setFaderValue(currentValue); +} + +void Interface::calibrateEnergyBar() { + g_energyMonitor->calibrateEnergyBar(); +} + +void Interface::raiseInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseInventoryDrawer(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidOpen(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerUp(); +} + +void Interface::lowerInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerInventoryDrawer(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerDown(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidClosed(); +} + +void Interface::raiseBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseBiochipDrawer(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidOpen(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerUp(); +} + +void Interface::lowerBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerBiochipDrawer(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerDown(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidClosed(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/interface.h b/engines/pegasus/interface.h new file mode 100755 index 0000000000..f8ad714e97 --- /dev/null +++ b/engines/pegasus/interface.h @@ -0,0 +1,148 @@ +/* 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_INTERFACE_H +#define PEGASUS_INTERFACE_H + +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" + +namespace Pegasus { + +class BiochipItem; +class InventoryItem; + +class Interface : public InputHandler, public NotificationReceiver { +public: + Interface(); + virtual ~Interface(); + + void createInterface(); + + // Recalibration functions... + void calibrateCompass(); + void calibrateEnergyBar(); + void raiseInventoryDrawerSync(); + void lowerInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerBiochipDrawerSync(); + + void raiseInventoryDrawer(const bool doCallBacks = true); + void raiseBiochipDrawer(const bool doCallBacks = true); + void lowerInventoryDrawer(const bool doCallBacks = true); + void lowerBiochipDrawer(const bool doCallBacks = true); + + void raiseInventoryDrawerForMessage(); + void lowerInventoryDrawerForMessage(); + bool isInventoryUp(); + bool isInventoryDown(); + + tInventoryResult addInventoryItem(InventoryItem*); + tInventoryResult removeInventoryItem(InventoryItem*); + void removeAllItemsFromInventory(); + InventoryItem *getCurrentInventoryItem(); + void setCurrentInventoryItem(InventoryItem*); + void setCurrentInventoryItemID(tItemID); + tInventoryResult addBiochip(BiochipItem*); + void removeAllItemsFromBiochips(); + BiochipItem *getCurrentBiochip(); + void setCurrentBiochip(BiochipItem*); + void setCurrentBiochipID(tItemID); + + void setDate(const uint16); + + void playEndMessage(); + + void throwAwayInterface(); + +protected: + void validateBackground(); + void validateDateMonitor(); + void validateCompass(); + void validateNotifications(); + void validateAIArea(); + void validateInventoryPanel(); + void validateBiochipPanel(); + void validateEnergyMonitor(); + + void throwAwayBackground(); + void throwAwayDateMonitor(); + void throwAwayCompass(); + void throwAwayNotifications(); + void throwAwayAIArea(); + void throwAwayInventoryPanel(); + void throwAwayBiochipPanel(); + void throwAwayEnergyMonitor(); + + void receiveNotification(Notification *, const tNotificationFlags); + void inventoryLidOpen(const bool doCallBacks); + void inventoryLidClosed(); + void inventoryDrawerUp(); + void inventoryDrawerDown(const bool doCallBacks); + void biochipLidOpen(const bool doCallBacks); + void biochipLidClosed(); + void biochipDrawerUp(); + void biochipDrawerDown(const bool doCallBacks); + + Picture _background1; + Picture _background2; + Picture _background3; + Picture _background4; + + Picture _datePicture; + + InputHandler *_previousHandler; + + Push _inventoryPush; + SpriteSequence _inventoryLid; + NotificationCallBack _inventoryPushCallBack; + NotificationCallBack _inventoryLidCallBack; + InventoryItemsPicture _inventoryPanel; + bool _inventoryUp, _inventoryRaised; + + Push _biochipPush; + SpriteSequence _biochipLid; + NotificationCallBack _biochipPushCallBack; + NotificationCallBack _biochipLidCallBack; + BiochipPicture _biochipPanel; + bool _biochipUp, _biochipRaised; + + Hotspot _currentItemSpot; + Hotspot _currentBiochipSpot; + + Notification _interfaceNotification; + + bool _playingEndMessage; +}; + +extern Interface *g_interface; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/autodragger.cpp b/engines/pegasus/items/autodragger.cpp new file mode 100755 index 0000000000..de03f8118f --- /dev/null +++ b/engines/pegasus/items/autodragger.cpp @@ -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. + * + */ + +#include "pegasus/elements.h" +#include "pegasus/items/autodragger.h" + +namespace Pegasus { + +AutoDragger::AutoDragger() { + _draggingElement = NULL; + _lastTime = 0; + initCallBack(this, kCallBackAtExtremes); +} + +void AutoDragger::autoDrag(DisplayElement *dragElement, const Common::Point &startPoint, const Common::Point &stopPoint, + TimeValue dragTime, TimeScale dragScale) { + _draggingElement = dragElement; + + if (_draggingElement) { + _startLocation = startPoint; + _stopLocation = stopPoint; + _lastTime = 0; + _done = false; + _draggingElement->moveElementTo(_startLocation.x, _startLocation.y); + setScale(dragScale); + setSegment(0, dragTime); + setTime(0); + scheduleCallBack(kTriggerAtStop, 0, 0); + startIdling(); + start(); + } else { + stopDragging(); + } +} + +void AutoDragger::stopDragging() { + cancelCallBack(); + stopIdling(); + _draggingElement = 0; + _startLocation = Common::Point(); + _stopLocation = Common::Point(); + _lastTime = 0; + _done = true; +} + +bool AutoDragger::isDragging() { + return isIdling(); +} + +void AutoDragger::useIdleTime() { + TimeValue thisTime = getTime(); + + if (thisTime != _lastTime) { + int32 offsetX = (_stopLocation.x - _startLocation.x) * (int32)thisTime / (int32)getDuration(); + int32 offsetY = (_stopLocation.y - _startLocation.y) * (int32)thisTime / (int32)getDuration(); + _draggingElement->moveElementTo(_startLocation.x + offsetX, _startLocation.y + offsetY); + _lastTime = thisTime; + } + + if (_done) + stopDragging(); +} + +void AutoDragger::callBack() { + if (isIdling()) + _done = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/autodragger.h b/engines/pegasus/items/autodragger.h new file mode 100755 index 0000000000..6783fdf9a3 --- /dev/null +++ b/engines/pegasus/items/autodragger.h @@ -0,0 +1,57 @@ +/* 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_ITEMS_AUTODRAGGER_H +#define PEGASUS_ITEMS_AUTODRAGGER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class DisplayElement; + +class AutoDragger : private Idler, private TimeBase, private TimeBaseCallBack { +public: + AutoDragger(); + virtual ~AutoDragger() {} + + void autoDrag(DisplayElement *, const Common::Point &, const Common::Point &, TimeValue, TimeScale); + bool isDragging(); + void stopDragging(); + +protected: + void useIdleTime(); + void callBack(); + + DisplayElement *_draggingElement; + Common::Point _startLocation, _stopLocation; + TimeValue _lastTime; + bool _done; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp new file mode 100755 index 0000000000..d4e425921b --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.cpp @@ -0,0 +1,279 @@ +/* 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/biochips/aichip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// indexed by [number of hints][number of solves (0, 1, or 2)][which button to highlight] +static const tItemState s_highlightState[4][3][7] = { + { + {kAI000, -1, -1, -1, -1, kAI005, kAI006}, + {kAI010, -1, -1, -1, -1, kAI015, kAI016}, + {kAI020, -1, -1, -1, kAI024, -1, -1} + }, + { + {kAI100, kAI101, -1, -1, -1, kAI105, kAI106}, + {kAI110, kAI111, -1, -1, -1, kAI115, kAI116}, + {kAI120, kAI121, -1, -1, kAI124, kAI125, kAI126} + }, + { + {kAI200, kAI201, kAI202, -1, -1, kAI205, kAI206}, + {kAI210, kAI211, kAI212, -1, -1, kAI215, kAI216}, + {kAI220, kAI221, kAI222, -1, kAI224, kAI225, kAI226} + }, + { + {kAI300, kAI301, kAI302, kAI303, -1, kAI305, kAI306}, + {kAI310, kAI311, kAI312, kAI313, -1, kAI315, kAI316}, + {kAI320, kAI321, kAI322, kAI323, kAI324, kAI325, kAI326} + } +}; + +AIChip *g_AIChip = 0; + +AIChip::AIChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _briefingSpot(kAIBriefingSpotID), _scanSpot(kAIScanSpotID), + _hint1Spot(kAIHint1SpotID), _hint2Spot(kAIHint2SpotID), _hint3Spot(kAIHint3SpotID), _solveSpot(kAISolveSpotID) { + _briefingSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 10 + 81, kAIMiddleAreaTop + 27 + 31)); + _briefingSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_briefingSpot); + + _scanSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 100 + 81, kAIMiddleAreaTop + 27 + 31)); + _scanSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_scanSpot); + + _hint1Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 70, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 70 + 21, kAIMiddleAreaTop + 67 + 21)); + _hint1Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint1Spot); + + _hint2Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 91, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 91 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint2Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint2Spot); + + _hint3Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 111, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 111 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint3Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint3Spot); + + _solveSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 131, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 131 + 50, kAIMiddleAreaTop + 67 + 21)); + _solveSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_solveSpot); + + _playingMovie = false; + setItemState(kAI000); + + g_AIChip = this; +} + +AIChip::~AIChip() { + g_AIChip = NULL; + + g_allHotspots.removeOneHotspot(kAIBriefingSpotID); + g_allHotspots.removeOneHotspot(kAIScanSpotID); + g_allHotspots.removeOneHotspot(kAIHint1SpotID); + g_allHotspots.removeOneHotspot(kAIHint2SpotID); + g_allHotspots.removeOneHotspot(kAIHint3SpotID); + g_allHotspots.removeOneHotspot(kAISolveSpotID); +} + +void AIChip::select() { + BiochipItem::select(); + setUpAIChip(); +} + +void AIChip::takeSharedArea() { + setUpAIChip(); +} + +void AIChip::setUpAIChip() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + setItemState(s_highlightState[vm->getNumHints()][numSolves][0]); + } +} + +// Only does something when there are hints or solves available. +void AIChip::setUpAIChipRude() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + uint numHints = vm->getNumHints(); + if (numSolves == 2 || numHints != 0) + setItemState(s_highlightState[numHints][numSolves][0]); + } +} + +void AIChip::activateAIHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + _briefingSpot.setActive(); + _scanSpot.setActive(); + + switch (vm->getNumHints()) { + case 3: + _hint3Spot.setActive(); + // fall through + case 2: + _hint2Spot.setActive(); + // fall through + case 1: + _hint1Spot.setActive(); + break; + } + + if (GameState.getWalkthroughMode() && vm->canSolve()) + _solveSpot.setActive(); +} + +void AIChip::showBriefingClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + tItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIBriefingSpotID - kAIHint1SpotID + 1]; + if (newState != -1) + setItemState(newState); +} + +void AIChip::showEnvScanClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + tItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); +} + +void AIChip::clearClicked() { + _playingMovie = false; + setUpAIChip(); +} + +void AIChip::clickInAIHotspot(tHotSpotID id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::String movieName; + + switch (id) { + case kAIBriefingSpotID: + movieName = vm->getBriefingMovie(); + break; + case kAIScanSpotID: + movieName = vm->getEnvScanMovie(); + break; + case kAIHint1SpotID: + movieName = vm->getHintMovie(1); + break; + case kAIHint2SpotID: + movieName = vm->getHintMovie(2); + break; + case kAIHint3SpotID: + movieName = vm->getHintMovie(3); + break; + case kAISolveSpotID: + g_neighborhood->doSolve(); + break; + } + + tItemState state = getItemState(); + + if (!movieName.empty()) { + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + tItemState newState = s_highlightState[vm->getNumHints()][numSolves][id - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); + + if (g_AIArea) { + vm->prepareForAIHint(movieName); + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption); + vm->cleanUpAfterAIHint(movieName); + } + + if (newState != -1) + setItemState(state); + + _playingMovie = false; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/aichip.h b/engines/pegasus/items/biochips/aichip.h new file mode 100755 index 0000000000..96d2fb2c01 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.h @@ -0,0 +1,69 @@ +/* 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_ITEMS_BIOCHIPS_AICHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_AICHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class AIChip : public BiochipItem { +public: + AIChip(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~AIChip(); + + void select(); + + void setUpAIChip(); + + // Called to set up the AI chip when the AI chip is the current chip but does not + // own the center area. + void setUpAIChipRude(); + void activateAIHotspots(); + void clickInAIHotspot(tHotSpotID); + + void takeSharedArea(); + + void showBriefingClicked(); + void showEnvScanClicked(); + void clearClicked(); + +protected: + Hotspot _briefingSpot; + Hotspot _scanSpot; + Hotspot _hint1Spot; + Hotspot _hint2Spot; + Hotspot _hint3Spot; + Hotspot _solveSpot; + bool _playingMovie; +}; + +extern AIChip *g_AIChip; + +} // End of namespace Pegasus + +#endif
\ No newline at end of file diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp new file mode 100755 index 0000000000..99e6050c85 --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.cpp @@ -0,0 +1,95 @@ +/* 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/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +BiochipItem::BiochipItem(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *biochipInfo = vm->_resFork->getResource(MKTAG('B', 'i', 'o', 'I'), kItemBaseResID + id); + if (biochipInfo) { + _biochipInfoPanelTime = biochipInfo->readUint32BE(); + delete biochipInfo; + } else { + _biochipInfoPanelTime = 0; + } + + Common::SeekableReadStream *rightInfo = vm->_resFork->getResource(MKTAG('R', 'g', 'h', 't'), kItemBaseResID + id); + if (!rightInfo) + error("Could not find right info for biochip %d", id); + + _rightAreaInfo = readItemState(rightInfo); + delete rightInfo; + + setItemState(kNormalItem); +} + +BiochipItem::~BiochipItem() { + delete[] _rightAreaInfo.entries; +} + +tItemType BiochipItem::getItemType() { + return kBiochipItemType; +} + +TimeValue BiochipItem::getRightAreaTime() const { + if (!_rightAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + tItemState state; + + findItemStateEntryByState(_rightAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_rightAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in right area. +void BiochipItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime()); +} + +void BiochipItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/biochipitem.h b/engines/pegasus/items/biochips/biochipitem.h new file mode 100755 index 0000000000..904c23b392 --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.h @@ -0,0 +1,54 @@ +/* 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_ITEMS_BIOCHIPS_BIOCHIPITEM_H +#define PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +class BiochipItem : public Item { +public: + BiochipItem(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~BiochipItem(); + + virtual tItemType getItemType(); + + TimeValue getPanelTime() const { return _biochipInfoPanelTime; } + TimeValue getRightAreaTime() const; + + // Must affect images in right area. + virtual void select(); + virtual void deselect(); + +protected: + TimeValue _biochipInfoPanelTime; + ItemStateInfo _rightAreaInfo; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp new file mode 100755 index 0000000000..f6519c7f61 --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.cpp @@ -0,0 +1,189 @@ +/* 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/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" + +namespace Pegasus { + +OpticalChip *g_opticalChip = 0; + +OpticalChip::OpticalChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _ariesHotspot(kAriesSpotID), _mercuryHotspot(kMercurySpotID), + _poseidonHotspot(kPoseidonSpotID) { + _ariesHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 27 + 20)); + _ariesHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_ariesHotspot); + + _mercuryHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 47, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 47 + 20)); + _mercuryHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_mercuryHotspot); + + _poseidonHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 67 + 20)); + _poseidonHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_poseidonHotspot); + + setItemState(kOptical000); + + g_opticalChip = this; +} + +OpticalChip::~OpticalChip() { + g_allHotspots.removeOneHotspot(kAriesSpotID); + g_allHotspots.removeOneHotspot(kMercurySpotID); + g_allHotspots.removeOneHotspot(kPoseidonSpotID); +} + +void OpticalChip::writeToStream(Common::WriteStream *stream) { + BiochipItem::writeToStream(stream); + _opticalFlags.writeToStream(stream); +} + +void OpticalChip::readFromStream(Common::ReadStream *stream) { + BiochipItem::readFromStream(stream); + _opticalFlags.readFromStream(stream); +} + +void OpticalChip::addAries() { + _opticalFlags.setFlag(kOpticalAriesExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addMercury() { + _opticalFlags.setFlag(kOpticalMercuryExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addPoseidon() { + _opticalFlags.setFlag(kOpticalPoseidonExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::setUpOpticalChip() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical111); + else + setItemState(kOptical011); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical101); + else + setItemState(kOptical001); + } + } else { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical110); + else + setItemState(kOptical010); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical100); + else + setItemState(kOptical000); + } + } +} + +void OpticalChip::activateOpticalHotspots() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) + _ariesHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) + _mercuryHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + _poseidonHotspot.setActive(); +} + +void OpticalChip::clickInOpticalHotspot(tHotSpotID id) { + playOpMemMovie(id); +} + +void OpticalChip::playOpMemMovie(tHotSpotID id) { + Common::String movieName; + switch (id) { + case kAriesSpotID: + movieName = "Images/AI/Globals/OMAI"; + break; + case kMercurySpotID: + movieName = "Images/AI/Globals/OMMI"; + break; + case kPoseidonSpotID: + movieName = "Images/AI/Globals/OMPI"; + break; + } + + tItemState state = getItemState(), newState; + switch (state) { + case kOptical000: + // Can never happen. + break; + case kOptical001: + newState = kOptical002; + break; + case kOptical010: + newState = kOptical020; + break; + case kOptical011: + if (id == kAriesSpotID) + newState = kOptical012; + else + newState = kOptical021; + break; + case kOptical100: + newState = kOptical200; + break; + case kOptical101: + if (id == kAriesSpotID) + newState = kOptical102; + else + newState = kOptical201; + break; + case kOptical110: + if (id == kMercurySpotID) + newState = kOptical120; + else + newState = kOptical210; + break; + case kOptical111: + if (id == kAriesSpotID) + newState = kOptical112; + else if (id == kMercurySpotID) + newState = kOptical121; + else + newState = kOptical211; + break; + } + + setItemState(newState); + + if (g_AIArea) + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kOpticalInterruption); + + setItemState(state); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/opticalchip.h b/engines/pegasus/items/biochips/opticalchip.h new file mode 100755 index 0000000000..c44a5eaf63 --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.h @@ -0,0 +1,71 @@ +/* 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_ITEMS_BIOCHIPS_OPTICALCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/util.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class OpticalChip : public BiochipItem { +public: + OpticalChip(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~OpticalChip(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + void addAries(); + void addMercury(); + void addPoseidon(); + + void activateOpticalHotspots(); + void clickInOpticalHotspot(tHotSpotID); + void playOpMemMovie(tHotSpotID); + +protected: + enum { + kOpticalAriesExposed, + kOpticalMercuryExposed, + kOpticalPoseidonExposed, + kNumOpticalChipFlags + }; + + void setUpOpticalChip(); + + FlagsArray<byte, kNumOpticalChipFlags> _opticalFlags; + Hotspot _ariesHotspot; + Hotspot _mercuryHotspot; + Hotspot _poseidonHotspot; +}; + +extern OpticalChip *g_opticalChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/pegasuschip.cpp b/engines/pegasus/items/biochips/pegasuschip.cpp new file mode 100755 index 0000000000..8d369c1a17 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.cpp @@ -0,0 +1,180 @@ +/* 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/items/biochips/pegasuschip.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +PegasusChip::PegasusChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _recallSpot(kPegasusRecallSpotID) { + _recallSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 116, kAIMiddleAreaTop + 63, kAIMiddleAreaLeft + 184, kAIMiddleAreaTop + 91)); + _recallSpot.setHotspotFlags(kPegasusBiochipSpotFlag); + g_allHotspots.push_back(&_recallSpot); + setItemState(kPegasusTSA00); +} + +PegasusChip::~PegasusChip() { + g_allHotspots.removeOneHotspot(kPegasusRecallSpotID); +} + +void PegasusChip::select() { + BiochipItem::select(); + setUpPegasusChip(); +} + +void PegasusChip::setUpPegasusChip() { + switch (GameState.getCurrentNeighborhood()) { + case kCaldoriaID: + setItemState(kPegasusCaldoria); + break; + case kFullTSAID: + case kFinalTSAID: + case kTinyTSAID: + setItemState(kPegasusTSA10); + break; + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + else + setItemState(kPegasusPrehistoric10); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + else + setItemState(kPegasusMars10); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + else + setItemState(kPegasusWSC10); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + else + setItemState(kPegasusNorad10); + break; + } +} + +// Only does something if the chip should be announcing that the time zone is finished... +void PegasusChip::setUpPegasusChipRude() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + break; + } +} + +void PegasusChip::activatePegasusHotspots() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + case kMarsID: + case kWSCID: + case kNoradAlphaID: + case kNoradDeltaID: + _recallSpot.setActive(); + break; + } +} + +void PegasusChip::clickInPegasusHotspot() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + tItemState thisState = getItemState(); + tItemState hiliteState; + + switch (thisState) { + case kPegasusPrehistoric00: + hiliteState = kPegasusPrehistoric01; + break; + case kPegasusPrehistoric10: + hiliteState = kPegasusPrehistoric11; + break; + case kPegasusMars00: + hiliteState = kPegasusMars01; + break; + case kPegasusMars10: + hiliteState = kPegasusMars11; + break; + case kPegasusNorad00: + hiliteState = kPegasusNorad01; + break; + case kPegasusNorad10: + hiliteState = kPegasusNorad11; + break; + case kPegasusWSC00: + hiliteState = kPegasusWSC01; + break; + case kPegasusWSC10: + hiliteState = kPegasusWSC11; + break; + } + + setItemState(hiliteState); + + uint32 time = g_system->getMillis(); + while (g_system->getMillis() < time + 500) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + setItemState(thisState); + + if (!((Neighborhood *)g_neighborhood)->okayToJump()) + return; + + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + if (GameState.getTSAState() == kPlayerWentToPrehistoric || GameState.allTimeZonesFinished()) + vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); + else + vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h new file mode 100755 index 0000000000..1ee35b78a1 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.h @@ -0,0 +1,55 @@ +/* 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_ITEMS_BIOCHIPS_PEGASUSCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class PegasusChip : public BiochipItem { +public: + PegasusChip(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~PegasusChip(); + + void select(); + + void setUpPegasusChip(); + + // Called to set up the Pegasus chip when the Pegasus chip is the current chip but does not + // own the center area. + void setUpPegasusChipRude(); + void activatePegasusHotspots(); + void clickInPegasusHotspot(); + +protected: + Hotspot _recallSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/retscanchip.cpp b/engines/pegasus/items/biochips/retscanchip.cpp new file mode 100755 index 0000000000..e9904056c1 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.cpp @@ -0,0 +1,49 @@ +/* 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/ai/ai_area.h" +#include "pegasus/items/biochips/retscanchip.h" + +namespace Pegasus { + +RetScanChip::RetScanChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { +} + +void RetScanChip::searchForLaser() { + ItemExtraEntry entry; + findItemExtra(kRetinalScanSearching, entry); + + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + + findItemExtra(kRetinalScanActivated, entry); + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kRightAreaSignature, entry.extraStart, entry.extraStop); + + setItemState(kRetinalSimulating); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/retscanchip.h b/engines/pegasus/items/biochips/retscanchip.h new file mode 100755 index 0000000000..7d8fe6d401 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.h @@ -0,0 +1,43 @@ +/* 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_ITEMS_BIOCHIPS_RETSCANCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class RetScanChip : public BiochipItem { +public: + RetScanChip(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~RetScanChip() {} + + void searchForLaser(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/shieldchip.cpp b/engines/pegasus/items/biochips/shieldchip.cpp new file mode 100755 index 0000000000..4bd05728b4 --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.cpp @@ -0,0 +1,53 @@ +/* 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/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +ShieldChip *g_shield = 0; + +ShieldChip::ShieldChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { + g_shield = this; +} + +void ShieldChip::select() { + BiochipItem::select(); + GameState.setShieldOn(true); + if (g_neighborhood) + g_neighborhood->shieldOn(); +} + +void ShieldChip::deselect() { + BiochipItem::deselect(); + GameState.setShieldOn(false); + if (g_neighborhood) + g_neighborhood->shieldOff(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/shieldchip.h b/engines/pegasus/items/biochips/shieldchip.h new file mode 100755 index 0000000000..c0b9cc5183 --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.h @@ -0,0 +1,46 @@ +/* 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_ITEMS_BIOCHIPS_SHIELDCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class ShieldChip : public BiochipItem { +public: + ShieldChip(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~ShieldChip() {} + + void select(); + void deselect(); +}; + +extern ShieldChip *g_shield; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory.cpp b/engines/pegasus/items/inventory.cpp new file mode 100755 index 0000000000..8f7eb46fd4 --- /dev/null +++ b/engines/pegasus/items/inventory.cpp @@ -0,0 +1,175 @@ +/* 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/constants.h" +#include "pegasus/items/item.h" +#include "pegasus/items/inventory.h" + +namespace Pegasus { + +Inventory::Inventory() { + _weightLimit = 100; + _ownerID = kNoActorID; + _referenceCount = 0; +} + +Inventory::~Inventory() { +} + +void Inventory::setWeightLimit(tWeightType limit) { + _weightLimit = limit; + // *** What to do if the new weight limit is greater than the current weight? +} + +tWeightType Inventory::getWeight() { + tWeightType result = 0; + + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + result += (*it)->getItemWeight(); + + return result; +} + +// If the item already belongs, just return kInventoryOK. +tInventoryResult Inventory::addItem(Item *item) { + if (itemInInventory(item)) + return kInventoryOK; + + if (getWeight() + item->getItemWeight() > _weightLimit) + return kTooMuchWeight; + + _inventoryList.push_back(item); + item->setItemOwner(_ownerID); + + ++_referenceCount; + return kInventoryOK; +} + +tInventoryResult Inventory::removeItem(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) { + if (*it == item) { + _inventoryList.erase(it); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + } + + return kItemNotInInventory; +} + +tInventoryResult Inventory::removeItem(tItemID id) { + Item *item = findItemByID(id); + + if (item) { + _inventoryList.remove(item); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + + return kItemNotInInventory; +} + +void Inventory::removeAllItems() { + _inventoryList.clear(); + ++_referenceCount; +} + +bool Inventory::itemInInventory(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + if (*it == item) + return true; + + return false; +} + +bool Inventory::itemInInventory(tItemID id) { + return findItemByID(id) != NULL; +} + +Item *Inventory::getItemAt(int32 index) { + int32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (i == index) + return *it; + + return 0; +} + +tItemID Inventory::getItemIDAt(int32 index) { + Item *item = getItemAt(index); + + if (item) + return item->getObjectID(); + + return kNoItemID; +} + +Item *Inventory::findItemByID(tItemID id) { + return _inventoryList.findItemByID(id); +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(Item *item) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (*it == item) + return i; + + return -1; +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(tItemID id) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if ((*it)->getObjectID() == id) + return i; + + return -1; +} + +tWeightType Inventory::getWeightLimit() { + return _weightLimit; +} + +int32 Inventory::getNumItems() { + return _inventoryList.size(); +} + +void Inventory::setOwnerID(const tActorID id) { + _ownerID = id; +} + +tActorID Inventory::getOwnerID() const { + return _ownerID; +} + +} // End of namespae Pegasus diff --git a/engines/pegasus/items/inventory.h b/engines/pegasus/items/inventory.h new file mode 100755 index 0000000000..7da782f00e --- /dev/null +++ b/engines/pegasus/items/inventory.h @@ -0,0 +1,80 @@ +/* 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_ITEMS_INVENTORY_H +#define PEGASUS_ITEMS_INVENTORY_H + +#include "pegasus/types.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +class Item; + +// Inventories have a "current item". This item is the default item the player can +// use. In a text adventure system, the current item would be "it", as in +// "Hit the troll with it," where "it" would refer to some weapon which is the current +// item. In a graphic adventure, the current item would be the item the user selects +// to use with the mouse or other pointing device. + +class Inventory { +public: + Inventory(); + virtual ~Inventory(); + + tWeightType getWeightLimit(); + void setWeightLimit(tWeightType limit); + tWeightType getWeight(); + + virtual tInventoryResult addItem(Item *item); + virtual tInventoryResult removeItem(Item *item); + virtual tInventoryResult removeItem(tItemID id); + virtual bool itemInInventory(Item *item); + virtual bool itemInInventory(tItemID id); + virtual Item *getItemAt(int32 index); + virtual tItemID getItemIDAt(int32 index); + virtual Item *findItemByID(tItemID id); + virtual int32 findIndexOf(Item *item); + virtual int32 findIndexOf(tItemID id); + int32 getNumItems(); + virtual void removeAllItems(); + + void setOwnerID(const tActorID id); + tActorID getOwnerID() const; + + uint32 getReferenceCount() { return _referenceCount; } + +protected: + tWeightType _weightLimit; + tActorID _ownerID; + ItemList _inventoryList; + +private: + uint32 _referenceCount; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp new file mode 100755 index 0000000000..682927deaf --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.cpp @@ -0,0 +1,248 @@ +/* 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/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +AirMask *g_airMask = 0; + +// Based on full == 100, which is scale used by GetAirLeft(). +static const TimeValue kOxygenLowThreshold = 25; + +void AirMask::airMaskTimerExpired(FunctionPtr *, void *) { + if (g_neighborhood) + g_neighborhood->checkAirMask(); +} + +AirMask::AirMask(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction), _toggleSpot(kAirMaskToggleSpotID) { + g_airMask = this; + _toggleSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 17, kAIMiddleAreaLeft + 110, kAIMiddleAreaTop + 57)); + _toggleSpot.setHotspotFlags(kAirMaskSpotFlag); + g_allHotspots.push_back(&_toggleSpot); + setItemState(kAirMaskEmptyOff); + _oxygenTimer.primeFuse(0); + _oxygenTimer.setFunctionPtr(&airMaskTimerExpired, 0); +} + +AirMask::~AirMask() { + g_allHotspots.removeOneHotspot(kAirMaskToggleSpotID); + g_airMask = 0; +} + +void AirMask::writeToStream(Common::WriteStream *stream) { + InventoryItem::writeToStream(stream); + stream->writeUint32BE(_oxygenTimer.getTimeRemaining()); +} + +void AirMask::readFromStream(Common::ReadStream *stream) { + _oxygenTimer.stopFuse(); + InventoryItem::readFromStream(stream); + _oxygenTimer.primeFuse(stream->readUint32BE()); +} + +void AirMask::putMaskOn() { + tAirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom()); + else + airQuality = kAirQualityGood; + + uint airLevel = getAirLeft(); + tItemState newState = getItemState(); + tItemState oldState = newState; + + if (airLevel == 0) { + newState = kAirMaskEmptyFilter; + } else if (airLevel <= kOxygenLowThreshold) { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskLowOn; + else + newState = kAirMaskLowFilter; + } else { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskFullOn; + else + newState = kAirMaskFullFilter; + } + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::takeMaskOff() { + uint airLevel = getAirLeft(); + tItemState newState = getItemState(); + tItemState oldState = newState; + + if (airLevel == 0) + newState = kAirMaskEmptyOff; + else if (airLevel <= kOxygenLowThreshold) + newState = kAirMaskLowOff; + else + newState = kAirMaskFullOff; + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::toggleItemState() { + if (isAirMaskInUse()) + takeMaskOff(); + else + putMaskOn(); +} + +void AirMask::airQualityChanged() { + if (isAirMaskInUse()) + putMaskOn(); + else + takeMaskOff(); +} + +void AirMask::setItemState(const tItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + + switch (newState) { + case kAirMaskFullOn: + case kAirMaskLowOn: + if (!_oxygenTimer.isFuseLit()) { + _oxygenTimer.lightFuse(); + startIdling(); + } + break; + default: + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + stopIdling(); + } + break; + } + + if (g_neighborhood) + g_neighborhood->checkAirMask(); + + g_AIArea->checkMiddleArea(); + } +} + +void AirMask::useIdleTime() { + if (getAirLeft() == 0) + setItemState(kAirMaskEmptyOff); + else if (getAirLeft() <= kOxygenLowThreshold) + setItemState(kAirMaskLowOn); +} + +void AirMask::refillAirMask() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + setItemState(kAirMaskFullOff); + break; + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + setItemState(kAirMaskFullFilter); + break; + case kAirMaskLowOn: + setItemState(kAirMaskFullOn); + break; + } + + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + _oxygenTimer.primeFuse(kOxyMaskFullTime); + _oxygenTimer.lightFuse(); + } else { + _oxygenTimer.primeFuse(kOxyMaskFullTime); + } +} + +// Doesn't return 0 until the timer is actually at 0. +uint AirMask::getAirLeft() { + return CLIP<int>(((_oxygenTimer.getTimeRemaining() * 100) + kOxyMaskFullTime - 1) / kOxyMaskFullTime, 0, 100); +} + +bool AirMask::isAirMaskInUse() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + case kAirMaskFullOff: + return false; + break; + default: + return true; + break; + } +} + +bool AirMask::isAirMaskOn() { + switch (getItemState()) { + case kAirMaskLowOn: + case kAirMaskFullOn: + return true; + break; + default: + return false; + break; + } +} + +bool AirMask::isAirFilterOn() { + switch (getItemState()) { + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + case kAirMaskFullFilter: + return true; + break; + default: + return false; + break; + } +} + +void AirMask::addedToInventory() { + GameState.setMarsMaskOnFiller(false); +} + +void AirMask::removedFromInventory() { + if (isAirMaskInUse()) + toggleItemState(); +} + +void AirMask::activateAirMaskHotspots() { + _toggleSpot.setActive(); +} + +void AirMask::clickInAirMaskHotspot() { + toggleItemState(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/airmask.h b/engines/pegasus/items/inventory/airmask.h new file mode 100755 index 0000000000..0e71380baf --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.h @@ -0,0 +1,76 @@ +/* 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_ITEMS_INVENTORY_AIRMASK_H +#define PEGASUS_ITEMS_INVENTORY_AIRMASK_H + +#include "pegasus/hotspot.h" +#include "pegasus/timers.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class AirMask : public InventoryItem, private Idler { +public: + AirMask(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~AirMask(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + virtual void setItemState(const tItemState); + void putMaskOn(); + void takeMaskOff(); + void toggleItemState(); + void airQualityChanged(); + + bool isAirMaskInUse(); + bool isAirMaskOn(); + bool isAirFilterOn(); + + void refillAirMask(); + + // Returns a percentage + uint getAirLeft(); + + void activateAirMaskHotspots(); + void clickInAirMaskHotspot(); + +protected: + static void airMaskTimerExpired(FunctionPtr *, void *); + + virtual void removedFromInventory(); + virtual void addedToInventory(); + void useIdleTime(); + + Hotspot _toggleSpot; + FuseFunction _oxygenTimer; +}; + +extern AirMask *g_airMask; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/gascanister.cpp b/engines/pegasus/items/inventory/gascanister.cpp new file mode 100755 index 0000000000..b6cd883bec --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.cpp @@ -0,0 +1,46 @@ +/* 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/ai/ai_area.h" +#include "pegasus/items/inventory/gascanister.h" + +namespace Pegasus { + +GasCanister::GasCanister(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { +} + +void GasCanister::select() { + InventoryItem::select(); + takeSharedArea(); +} + +void GasCanister::takeSharedArea() { + ItemExtraEntry entry; + findItemExtra(kGasCanLoop, entry); + g_AIArea->loopAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/gascanister.h b/engines/pegasus/items/inventory/gascanister.h new file mode 100755 index 0000000000..437df1292f --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.h @@ -0,0 +1,44 @@ +/* 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_ITEMS_INVENTORY_GASCANISTER_H +#define PEGASUS_ITEMS_INVENTORY_GASCANISTER_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class GasCanister : public InventoryItem { +public: + GasCanister(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~GasCanister() {} + + void select(); + void takeSharedArea(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp new file mode 100755 index 0000000000..17f07050b3 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-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/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryItem::InventoryItem(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *leftInfo = vm->_resFork->getResource(MKTAG('L', 'e', 'f', 't'), kItemBaseResID + id); + if (leftInfo) { + _leftAreaInfo = readItemState(leftInfo); + delete leftInfo; + } else { + _leftAreaInfo.numEntries = 0; + _leftAreaInfo.entries = 0; + } + + Common::SeekableReadStream *inventoryInfo = vm->_resFork->getResource(MKTAG('I', 'n', 'v', 'I'), kItemBaseResID + id); + if (inventoryInfo) { + _inventoryInfo.panelStart = inventoryInfo->readUint32BE(); + _inventoryInfo.panelStop = inventoryInfo->readUint32BE(); + delete inventoryInfo; + } else { + _inventoryInfo.panelStart = _inventoryInfo.panelStop = 0; + } + + _itemAnimationTime = 0; +} + +InventoryItem::~InventoryItem() { + delete[] _leftAreaInfo.entries; +} + +tItemType InventoryItem::getItemType() { + return kInventoryItemType; +} + +TimeValue InventoryItem::getLeftAreaTime() const { + if (!_leftAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + tItemState state; + + findItemStateEntryByState(_leftAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_leftAreaInfo, 0, state, time); + + return time; +} + +void InventoryItem::setAnimationTime(const TimeValue time) { + _itemAnimationTime = time; +} + +TimeValue InventoryItem::getAnimationTime() const { + return _itemAnimationTime; +} + +// Must affect images in left area. +void InventoryItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime()); +} + +void InventoryItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff); +} + +void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const { + start = _inventoryInfo.panelStart; + stop = _inventoryInfo.panelStop; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/inventoryitem.h b/engines/pegasus/items/inventory/inventoryitem.h new file mode 100755 index 0000000000..d3247d651c --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.h @@ -0,0 +1,67 @@ +/* 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_ITEMS_INVENTORY_INVENTORYITEM_H +#define PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +// JMPInventoryInfo contains the resource data used by InventoryItems. + +struct JMPInventoryInfo { + TimeValue panelStart; + TimeValue panelStop; +}; + +class InventoryItem : public Item { +public: + InventoryItem(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~InventoryItem(); + + virtual tItemType getItemType(); + + void getPanelTimes(TimeValue&, TimeValue&) const; + TimeValue getLeftAreaTime() const; + + void setAnimationTime(const TimeValue); + TimeValue getAnimationTime() const; + + virtual void toggleItemState() {} + + // Must affect images in left area. + virtual void select(); + virtual void deselect(); + +protected: + JMPInventoryInfo _inventoryInfo; + ItemStateInfo _leftAreaInfo; + TimeValue _itemAnimationTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/keycard.cpp b/engines/pegasus/items/inventory/keycard.cpp new file mode 100755 index 0000000000..53a3f67e00 --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.cpp @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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/pegasus.h" +#include "pegasus/items/inventory/keycard.h" + +namespace Pegasus { + +KeyCard::KeyCard(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { + setItemState(kFlashlightOff); +} + +void KeyCard::toggleItemState() { + if (getItemState() == kFlashlightOff) + setItemState(kFlashlightOn); + else + setItemState(kFlashlightOff); +} + +void KeyCard::setItemState(const tItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + ((PegasusEngine *)g_engine)->checkFlashlight(); + } +} + +bool KeyCard::isFlashlightOn() { + return getItemState() == kFlashlightOn; +} + +void KeyCard::removedFromInventory() { + if (isFlashlightOn()) + setItemState(kFlashlightOff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/keycard.h b/engines/pegasus/items/inventory/keycard.h new file mode 100755 index 0000000000..7fdb905d58 --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.h @@ -0,0 +1,48 @@ +/* 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_ITEMS_INVENTORY_KEYCARD_H +#define PEGASUS_ITEMS_INVENTORY_KEYCARD_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class KeyCard : public InventoryItem { +public: + KeyCard(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~KeyCard() {} + + virtual void toggleItemState(); + virtual void setItemState(const tItemState); + bool isFlashlightOn(); + +protected: + virtual void removedFromInventory(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp new file mode 100755 index 0000000000..5eef7ef15c --- /dev/null +++ b/engines/pegasus/items/inventorypicture.cpp @@ -0,0 +1,367 @@ +/* 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/pegasus.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryPicture::InventoryPicture(const tDisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InputHandler(nextHandler), Picture(id), _panelMovie(kNoDisplayElement){ + _inventory = inventory; + _lastReferenceCount = 0xffffffff; + + if (_inventory->getNumItems() > 0) { + _currentItemIndex = 0; + _currentItem = (Item *)_inventory->getItemAt(0); + } else { + _currentItemIndex = -1; + _currentItem = 0; + } + + _active = false; + _shouldDrawHighlight = true; + _itemsPerRow = 1; + _numberOfRows = 1; + _itemWidth = 0; + _itemHeight = 0; + _itemX = 0; + _itemY = 0; +} + +void InventoryPicture::initInventoryImage(Transition *transition) { + initFromPICTFile(_pictName, true); + _panelMovie.shareSurface(this); + _panelMovie.initFromMovieFile(_movieName); + _panelMovie.getBounds(_highlightBounds); + _panelMovie.setTriggeredElement(transition); + _highlightImage.initFromPICTFile(_highlightName, true); +} + +void InventoryPicture::throwAwayInventoryImage() { + if (isSurfaceValid()) { + _panelMovie.releaseMovie(); + _highlightImage.deallocateSurface(); + deallocateSurface(); + } +} + +void InventoryPicture::getItemXY(uint32 index, tCoordType &x, tCoordType &y) { + x = (index % _itemsPerRow) * _itemWidth + _itemX; + y = (index / _itemsPerRow) * _itemHeight + _itemY; +} + +void InventoryPicture::drawItemHighlight(const Common::Rect &r) { + if (_highlightImage.isSurfaceValid()) { + Common::Rect r2 = _highlightBounds; + Common::Rect bounds; + getBounds(bounds); + + r2.translate(bounds.left, bounds.top); + r2 = r2.findIntersectingRect(r); + if (!r2.isEmpty()) { + Common::Rect r1 = r2; + r1.translate(-bounds.left - _highlightBounds.left, -bounds.top - _highlightBounds.top); + _highlightImage.drawImage(r1, r2); + } + } +} + +void InventoryPicture::draw(const Common::Rect &r) { + Picture::draw(r); + if (_inventory->getNumItems() != 0 && _shouldDrawHighlight) + drawItemHighlight(r); +} + +// Assumes index >= 0. +void InventoryPicture::setCurrentItemIndex(int32 index) { + if (index >= _inventory->getNumItems()) + index = _inventory->getNumItems() - 1; + + Item *currentItem = 0; + if (index >= 0) + currentItem = (Item *)_inventory->getItemAt(index); + + if (currentItem != _currentItem) { + if (_currentItem) { + if (_currentItem->isSelected()) + _currentItem->deselect(); + + if (_active) + unhighlightCurrentItem(); + } + + _currentItemIndex = index; + _currentItem = currentItem; + if (_currentItem) { + _currentItem->select(); + + if (_active) + highlightCurrentItem(); + } + + if (_active) + triggerRedraw(); + } +} + +void InventoryPicture::setCurrentItemID(tItemID id) { + int32 index = _inventory->findIndexOf(id); + if (index >= 0) + setCurrentItemIndex(index); +} + +tInventoryResult InventoryPicture::addInventoryItem(Item *item) { + tInventoryResult result = _inventory->addItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(_inventory->findIndexOf(item)); + + return result; +} + +tInventoryResult InventoryPicture::removeInventoryItem(Item *item) { + tInventoryResult result = _inventory->removeItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(getCurrentItemIndex()); + + return result; +} + +void InventoryPicture::removeAllItems() { + _inventory->removeAllItems(); + setCurrentItemIndex(0); +} + +bool InventoryPicture::itemInInventory(Item *item) { + return _inventory->itemInInventory(item); +} + +bool InventoryPicture::itemInInventory(const tItemID id) { + return _inventory->itemInInventory(id); +} + +void InventoryPicture::panelUp() { + allowInput(true); +} + +// Must ensure that the picture matches the _inventory member variable. +void InventoryPicture::activateInventoryPicture() { + if (_active) + return; + + allowInput(false); + + if (_lastReferenceCount != _inventory->getReferenceCount()) { + uint32 numItems = _inventory->getNumItems(); + + tCoordType x, y; + getItemXY(0, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.show(); + + for (uint32 i = 0; i < numItems; i++) { + Item *item = (Item *)_inventory->getItemAt(i); + if (item == _currentItem) + item->select(); + + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(item)); + _panelMovie.redrawMovieWorld(); + } + + _panelMovie.setTime(0); + uint32 numSlots = _itemsPerRow * _numberOfRows; + + for (uint32 i = numItems; i < numSlots; i++) { + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.redrawMovieWorld(); + } + + _lastReferenceCount = _inventory->getReferenceCount(); + } + + show(); // *** Do we really need this? + if (_currentItem) + highlightCurrentItem(); + + _active = true; +} + +void InventoryPicture::deactivateInventoryPicture() { + if (!_active) + return; + + _active = false; + allowInput(false); + _panelMovie.hide(); + hide(); + + if (_inventory->getNumItems() != 0) + if (!_currentItem->isActive()) + _currentItem->activate(); +} + +void InventoryPicture::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_active) { + if (input.upButtonDown()) { + if (_currentItemIndex - _itemsPerRow >= 0) + setCurrentItemIndex(_currentItemIndex - _itemsPerRow); + } else if (input.downButtonDown()) { + if (_currentItemIndex + _itemsPerRow < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + _itemsPerRow); + } else if (input.leftButtonDown()) { + if ((_currentItemIndex % _itemsPerRow) != 0) + setCurrentItemIndex(_currentItemIndex - 1); + } else if (input.rightButtonDown()) { + if (((_currentItemIndex + 1) % _itemsPerRow) != 0 && _currentItemIndex + 1 < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + 1); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void InventoryPicture::highlightCurrentItem() { + tCoordType x, y; + getItemXY(_currentItemIndex, x, y); + _highlightBounds.moveTo(x, y); +} + +InventoryItemsPicture::InventoryItemsPicture(const tDisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Inventory/Inventory Panel"; + _movieName = "Images/Items/Inventory/Inventory Panel Movie"; + _highlightName = "Images/Items/Inventory/Inventory Hilite"; + + _itemsPerRow = 3; + _numberOfRows = 3; + _itemWidth = 88; + _itemHeight = 64; + _itemX = 8; + _itemY = 26; + _isLooping = true; +} + +void InventoryItemsPicture::loopCurrentItem() { + if (_isLooping) { + tCoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _highlightBounds.moveTo(x, y); + + TimeValue start, stop; + ((InventoryItem *)_currentItem)->getPanelTimes(start, stop); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(start, stop); + _panelMovie.setFlags(kLoopTimeBase); + _panelMovie.setTime(((InventoryItem *)_currentItem)->getAnimationTime()); + _panelMovie.start(); + } +} + +void InventoryItemsPicture::highlightCurrentItem() { + InventoryPicture::highlightCurrentItem(); + loopCurrentItem(); +} + +void InventoryItemsPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + ((InventoryItem *)_currentItem)->setAnimationTime(_panelMovie.getTime()); +} + +TimeValue InventoryItemsPicture::getItemPanelTime(Item *item) { + TimeValue start, stop; + ((InventoryItem *)item)->getPanelTimes(start, stop); + ((InventoryItem *)item)->setAnimationTime(start); + return start; +} + +void InventoryItemsPicture::deactivateInventoryPicture() { + if (_active) { + InventoryPicture::deactivateInventoryPicture(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(0, _panelMovie.getDuration()); + _isLooping = true; + } +} + +void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) { + Movie endMessage(0); + + _shouldDrawHighlight = false; + endMessage.shareSurface(this); + endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats"); + endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop); + endMessage.setTriggeredElement(pushElement); + endMessage.start(); + + while (endMessage.isRunning()) { + ((PegasusEngine *)g_engine)->refreshDisplay(); + g_system->delayMillis(10); + } + + endMessage.stop(); +} + +BiochipPicture::BiochipPicture(const tDisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Biochips/Biochip Panel"; + _movieName = "Images/Items/Biochips/Biochip Panel Movie"; + _highlightName = "Images/Items/Biochips/BioChip Hilite"; + + _itemsPerRow = 4; + _numberOfRows = 2; + _itemWidth = 46; + _itemHeight = 46; + _itemX = 4; + _itemY = 24; +} + +void BiochipPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + tCoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.show(); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(_currentItem)); + _panelMovie.redrawMovieWorld(); +} + +TimeValue BiochipPicture::getItemPanelTime(Item *item) { + return ((BiochipItem *)item)->getPanelTime(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h new file mode 100755 index 0000000000..034354df09 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.h @@ -0,0 +1,125 @@ +/* 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_ITEMS_INVENTORYPICTURE_H +#define PEGASUS_ITEMS_INVENTORYPICTURE_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +class Inventory; +class Item; +class Input; +class Transition; + +class InventoryPicture : public InputHandler, public Picture { +public: + InventoryPicture(const tDisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryPicture() {} + + void initInventoryImage(Transition *); + void throwAwayInventoryImage(); + + void panelUp(); + void activateInventoryPicture(); + void deactivateInventoryPicture(); + void handleInput(const Input &, const Hotspot *); + bool wantsCursor() { return false; } + + tInventoryResult addInventoryItem(Item *); + tInventoryResult removeInventoryItem(Item *); + void removeAllItems(); + Item *getCurrentItem() { return _currentItem; } + void setCurrentItemIndex(int32); + void setCurrentItemID(tItemID); + int32 getCurrentItemIndex() { return _currentItemIndex; } + bool itemInInventory(Item *); + bool itemInInventory(const tItemID); + +protected: + void getItemXY(uint32, tCoordType &, tCoordType &); + void draw(const Common::Rect &); + void drawItemHighlight(const Common::Rect &); + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem() {} + virtual TimeValue getItemPanelTime(Item *) = 0; + + Inventory *_inventory; + uint32 _lastReferenceCount; + Frame _highlightImage; + Movie _panelMovie; + int32 _currentItemIndex; + Item *_currentItem; + Common::Rect _highlightBounds; + bool _active, _shouldDrawHighlight; + + Common::String _pictName; + Common::String _movieName; + Common::String _highlightName; + uint16 _itemsPerRow; + uint16 _numberOfRows; + uint16 _itemWidth; + uint16 _itemHeight; + uint16 _itemX; + uint16 _itemY; +}; + +class InventoryItemsPicture : public InventoryPicture { +public: + InventoryItemsPicture(const tDisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryItemsPicture() {} + + void deactivateInventoryPicture(); + + void disableLooping() { _isLooping = false; } + + void playEndMessage(DisplayElement *); + +protected: + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); + void loopCurrentItem(); + + InputHandler *_previousHandler; + bool _isLooping; +}; + +class BiochipPicture : public InventoryPicture { +public: + BiochipPicture(const tDisplayElementID, InputHandler *, Inventory *); + virtual ~BiochipPicture() {} + +protected: + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp new file mode 100755 index 0000000000..bcb85defae --- /dev/null +++ b/engines/pegasus/items/item.cpp @@ -0,0 +1,314 @@ +/* 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/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/elements.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Item::Item(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) : IDObject(id) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + _itemWeight = 1; + _itemOwnerID = kNoActorID; + _itemState = 0; + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *info = vm->_resFork->getResource(kItemInfoResType, kItemBaseResID + id); + if (info) { + _itemInfo.infoLeftTime = info->readUint32BE(); + _itemInfo.infoRightStart = info->readUint32BE(); + _itemInfo.infoRightStop = info->readUint32BE(); + _itemInfo.dragSpriteNormalID = info->readUint16BE(); + _itemInfo.dragSpriteUsedID = info->readUint16BE(); + + if (vm->isDemo()) { + // Adjust info right times to account for the stuff that was chopped out of the + // info right movies. + // Assumes time scale of 600. + + // Gap times in seconds + static const TimeValue kGap1 = 24; + static const TimeValue kGap2 = 34; + static const TimeValue kGap3 = 4; + static const TimeValue kGap4 = 4; + + static const TimeValue kGapForGroup1 = kGap1; + static const TimeValue kGapForGroup2 = kGapForGroup1 + kGap2; + static const TimeValue kGapForGroup3 = kGapForGroup2 + kGap3; + static const TimeValue kGapForGroup4 = kGapForGroup3 + kGap4; + + switch (id) { + case kHistoricalLog: + case kJourneymanKey: + case kKeyCard: + _itemInfo.infoRightStart -= 600 * kGapForGroup1; + _itemInfo.infoRightStop -= 600 * kGapForGroup1; + break; + case kAIBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup2; + _itemInfo.infoRightStop -= 600 * kGapForGroup2; + break; + case kMapBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup3; + _itemInfo.infoRightStop -= 600 * kGapForGroup3; + break; + case kPegasusBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup4; + _itemInfo.infoRightStop -= 600 * kGapForGroup4; + break; + } + } + + delete info; + } else { + memset(&_itemInfo, 0, sizeof(_itemInfo)); + } + + Common::SeekableReadStream *middleAreaInfo = vm->_resFork->getResource(kMiddleAreaInfoResType, kItemBaseResID + id); + if (middleAreaInfo) { + _sharedAreaInfo = readItemState(middleAreaInfo); + delete middleAreaInfo; + } else { + // Only kArgonPickup does not have this + memset(&_sharedAreaInfo, 0, sizeof(_sharedAreaInfo)); + } + + Common::SeekableReadStream *extraInfo = vm->_resFork->getResource(kItemExtraInfoResType, kItemBaseResID + id); + if (!extraInfo) + error("Extra info not found for item %d", id); + + _itemExtras.numEntries = extraInfo->readUint16BE(); + _itemExtras.entries = new ItemExtraEntry[_itemExtras.numEntries]; + for (uint16 i = 0; i < _itemExtras.numEntries; i++) { + _itemExtras.entries[i].extraID = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraArea = extraInfo->readUint16BE(); + _itemExtras.entries[i].extraStart = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraStop = extraInfo->readUint32BE(); + } + + delete extraInfo; + + g_allItems.push_back(this); +} + +Item::~Item() { + delete[] _sharedAreaInfo.entries; + delete[] _itemExtras.entries; +} + +void Item::writeToStream(Common::WriteStream *stream) { + stream->writeUint16BE(_itemNeighborhood); + stream->writeUint16BE(_itemRoom); + stream->writeByte(_itemDirection); + stream->writeUint16BE(_itemOwnerID); + stream->writeUint16BE(_itemState); +} + +void Item::readFromStream(Common::ReadStream *stream) { + _itemNeighborhood = stream->readUint16BE(); + _itemRoom = stream->readUint16BE(); + _itemDirection = stream->readByte(); + _itemOwnerID = stream->readUint16BE(); + _itemState = stream->readUint16BE(); +} + +tActorID Item::getItemOwner() const { + return _itemOwnerID; +} + +void Item::setItemOwner(const tActorID owner) { + _itemOwnerID = owner; + + if (owner == kNoActorID) { + if (isSelected()) + deselect(); + removedFromInventory(); + } else { + addedToInventory(); + } +} + +tWeightType Item::getItemWeight() { + return _itemWeight; +} + +tItemState Item::getItemState() const { + return _itemState; +} + +void Item::setItemState(const tItemState state) { + if (state != _itemState) { + _itemState = state; + + if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this) + select(); + else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this) + select(); + } +} + +void Item::getItemRoom(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction) const { + neighborhood = _itemNeighborhood; + room = _itemRoom; + direction = _itemDirection; +} + +void Item::setItemRoom(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + + if (neighborhood == kNoNeighborhoodID) + pickedUp(); + else + dropped(); +} + +tNeighborhoodID Item::getItemNeighborhood() const { + return _itemNeighborhood; +} + +TimeValue Item::getSharedAreaTime() const { + if (!_sharedAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + tItemState state; + + findItemStateEntryByState(_sharedAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_sharedAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in shared area. +void Item::select() { + _isSelected = true; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, getSharedAreaTime()); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, getSharedAreaTime()); + } +} + +void Item::deselect() { + _isActive = false; + _isSelected = false; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, 0xffffffff); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, 0xffffffff); + } +} + +void Item::getItemStateEntry(ItemStateInfo info, uint32 index, tItemState &state, TimeValue &time) { + if (index < info.numEntries) { + state = info.entries[index].itemState; + time = info.entries[index].itemTime; + } else { + state = kNoItemState; + time = 0xffffffff; + } +} + +void Item::findItemStateEntryByState(ItemStateInfo info, tItemState state, TimeValue &time) { + for (uint16 i = 0; i < info.numEntries; i++) { + if (info.entries[i].itemState == state) { + time = info.entries[i].itemTime; + return; + } + } + + time = 0xffffffff; +} + +ItemStateInfo Item::readItemState(Common::SeekableReadStream *stream) { + ItemStateInfo info; + + info.numEntries = stream->readUint16BE(); + info.entries = new ItemStateEntry[info.numEntries]; + for (uint16 i = 0; i < info.numEntries; i++) { + info.entries[i].itemState = stream->readSint16BE(); + info.entries[i].itemTime = stream->readUint32BE(); + } + + return info; +} + +Sprite *Item::getDragSprite(const tDisplayElementID id) const { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Sprite *result = new Sprite(id); + SpriteFrame *frame = new SpriteFrame(); + + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteNormalID, true); + result->addFrame(frame, 0, 0); + + if (_itemInfo.dragSpriteNormalID != _itemInfo.dragSpriteUsedID) { + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteUsedID, true); + } + + result->addFrame(frame, 0, 0); + result->setCurrentFrameIndex(0); + return result; +} + +TimeValue Item::getInfoLeftTime() const { + return _itemInfo.infoLeftTime; +} + +void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const { + start = _itemInfo.infoRightStart; + stop = _itemInfo.infoRightStop; +} + +void Item::findItemExtra(const uint32 extraID, ItemExtraEntry &entry) { + for (uint32 i = 0; i < _itemExtras.numEntries; i++) { + if (_itemExtras.entries[i].extraID == extraID) { + entry = _itemExtras.entries[i]; + return; + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h new file mode 100755 index 0000000000..b74dfc4e96 --- /dev/null +++ b/engines/pegasus/items/item.h @@ -0,0 +1,367 @@ +/* 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_ITEMS_ITEM_H +#define PEGASUS_ITEMS_ITEM_H + +#include "common/endian.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; + class SeekableReadStream; +} + +namespace Pegasus { + +// JMPItemInfo contains resource data used by all Items. + +struct JMPItemInfo { + TimeValue infoLeftTime; + TimeValue infoRightStart; + TimeValue infoRightStop; + uint32 dragSpriteNormalID; + uint32 dragSpriteUsedID; +}; + +// ItemStateEntry contains a single state/TimeValue pair. The TimeValue is +// the time value to set the shared area movie that corresponds with the given +// state of an inventory item. + +struct ItemStateEntry { + tItemState itemState; + TimeValue itemTime; +}; + +// ItemStateInfoHandle is an array of ItemStateEntry. + +struct ItemStateInfo { + uint16 numEntries; // For easy ResEdit access + ItemStateEntry *entries; +}; + +// ItemExtraEntry + +const short kLeftAreaExtra = 0; +const short kMiddleAreaExtra = 1; +const short kRightAreaExtra = 2; + +struct ItemExtraEntry { + uint32 extraID; + uint16 extraArea; + TimeValue extraStart; + TimeValue extraStop; +}; + +// tItemExtraInfoHandle is an array of tItemExtraEntry. + +struct ItemExtraInfo { + uint16 numEntries; // For easy ResEdit access + ItemExtraEntry *entries; +}; + +// Inventory info resource type and ID: +// Individual inventory items are stored in these resource types. +// Resource ID is item ID + kItemBaseResID. + +const uint32 kItemInfoResType = MKTAG('I', 't', 'e', 'm'); // JMPItemInfoHandle +const uint32 kLeftAreaInfoResType = MKTAG('L', 'e', 'f', 't'); // ItemStateInfoHandle +const uint32 kMiddleAreaInfoResType = MKTAG('M', 'i', 'd', 'l'); // ItemStateInfoHandle +const uint32 kRightAreaInfoResType = MKTAG('R', 'g', 'h', 't'); // ItemStateInfoHandle +const uint32 kItemExtraInfoResType = MKTAG('I', 'X', 't', 'r'); // ItemExtraInfoHandle + +const uint16 kItemBaseResID = 128; + +// Item IDs. + +const tItemID kAirMask = 7; +const tItemID kAntidote = 8; +const tItemID kArgonCanister = 9; +const tItemID kCardBomb = 10; +const tItemID kCrowbar = 11; +const tItemID kGasCanister = 12; +const tItemID kHistoricalLog = 13; +const tItemID kJourneymanKey = 14; +const tItemID kKeyCard = 15; +const tItemID kMachineGun = 16; +const tItemID kMarsCard = 17; +const tItemID kNitrogenCanister = 18; +const tItemID kOrangeJuiceGlassFull = 19; +const tItemID kOrangeJuiceGlassEmpty = 20; +const tItemID kPoisonDart = 21; +const tItemID kSinclairKey = 22; +const tItemID kStunGun = 23; +const tItemID kArgonPickup = 24; + +// Biochips. + +const tItemID kAIBiochip = 0; +const tItemID kInterfaceBiochip = 1; +const tItemID kMapBiochip = 2; +const tItemID kOpticalBiochip = 3; +const tItemID kPegasusBiochip = 4; +const tItemID kRetinalScanBiochip = 5; +const tItemID kShieldBiochip = 6; + +const tItemID kNumItems = 25; + +// Item States. + +const tItemState kAI000 = 0; +const tItemState kAI005 = 1; +const tItemState kAI006 = 2; +const tItemState kAI010 = 3; +const tItemState kAI015 = 4; +const tItemState kAI016 = 5; +const tItemState kAI020 = 6; +const tItemState kAI024 = 7; +const tItemState kAI100 = 8; +const tItemState kAI101 = 9; +const tItemState kAI105 = 10; +const tItemState kAI106 = 11; +const tItemState kAI110 = 12; +const tItemState kAI111 = 13; +const tItemState kAI115 = 14; +const tItemState kAI116 = 15; +const tItemState kAI120 = 16; +const tItemState kAI121 = 17; +const tItemState kAI124 = 18; +const tItemState kAI125 = 19; +const tItemState kAI126 = 20; +const tItemState kAI200 = 21; +const tItemState kAI201 = 22; +const tItemState kAI202 = 23; +const tItemState kAI205 = 24; +const tItemState kAI206 = 25; +const tItemState kAI210 = 26; +const tItemState kAI211 = 27; +const tItemState kAI212 = 28; +const tItemState kAI215 = 29; +const tItemState kAI216 = 30; +const tItemState kAI220 = 31; +const tItemState kAI221 = 32; +const tItemState kAI222 = 33; +const tItemState kAI224 = 34; +const tItemState kAI225 = 35; +const tItemState kAI226 = 36; +const tItemState kAI300 = 37; +const tItemState kAI301 = 38; +const tItemState kAI302 = 39; +const tItemState kAI303 = 40; +const tItemState kAI305 = 41; +const tItemState kAI306 = 42; +const tItemState kAI310 = 43; +const tItemState kAI311 = 44; +const tItemState kAI312 = 45; +const tItemState kAI313 = 46; +const tItemState kAI315 = 47; +const tItemState kAI316 = 48; +const tItemState kAI320 = 49; +const tItemState kAI321 = 50; +const tItemState kAI322 = 51; +const tItemState kAI323 = 52; +const tItemState kAI324 = 53; +const tItemState kAI325 = 54; +const tItemState kAI326 = 55; +const tItemState kNormalItem = 56; +const tItemState kMapUnavailable = 57; +const tItemState kMapEngaged = 58; +const tItemState kOptical000 = 59; +const tItemState kOptical001 = 60; +const tItemState kOptical002 = 61; +const tItemState kOptical010 = 62; +const tItemState kOptical011 = 63; +const tItemState kOptical012 = 64; +const tItemState kOptical020 = 65; +const tItemState kOptical021 = 66; +const tItemState kOptical100 = 67; +const tItemState kOptical101 = 68; +const tItemState kOptical102 = 69; +const tItemState kOptical110 = 70; +const tItemState kOptical111 = 71; +const tItemState kOptical112 = 72; +const tItemState kOptical120 = 73; +const tItemState kOptical121 = 74; +const tItemState kOptical200 = 75; +const tItemState kOptical201 = 76; +const tItemState kOptical210 = 77; +const tItemState kOptical211 = 78; +const tItemState kPegasusTSA00 = 79; +const tItemState kPegasusTSA10 = 80; +const tItemState kPegasusPrehistoric00 = 81; +const tItemState kPegasusPrehistoric01 = 82; +const tItemState kPegasusPrehistoric10 = 83; +const tItemState kPegasusPrehistoric11 = 84; +const tItemState kPegasusMars00 = 85; +const tItemState kPegasusMars01 = 86; +const tItemState kPegasusMars10 = 87; +const tItemState kPegasusMars11 = 88; +const tItemState kPegasusNorad00 = 89; +const tItemState kPegasusNorad01 = 90; +const tItemState kPegasusNorad10 = 91; +const tItemState kPegasusNorad11 = 92; +const tItemState kPegasusWSC00 = 93; +const tItemState kPegasusWSC01 = 94; +const tItemState kPegasusWSC10 = 95; +const tItemState kPegasusWSC11 = 96; +const tItemState kPegasusCaldoria = 97; +const tItemState kRetinalSimulating = 98; +const tItemState kShieldNormal = 99; +const tItemState kShieldRadiation = 100; +const tItemState kShieldPlasma = 101; +const tItemState kShieldCardBomb = 102; +const tItemState kShieldDraining = 103; +const tItemState kAirMaskEmptyOff = 104; +const tItemState kAirMaskEmptyFilter = 105; +const tItemState kAirMaskLowOff = 106; +const tItemState kAirMaskLowFilter = 107; +const tItemState kAirMaskLowOn = 108; +const tItemState kAirMaskFullOff = 109; +const tItemState kAirMaskFullFilter = 110; +const tItemState kAirMaskFullOn = 111; +const tItemState kArgonEmpty = 112; +const tItemState kArgonFull = 113; +const tItemState kFlashlightOff = 114; +const tItemState kFlashlightOn = 115; +const tItemState kNitrogenEmpty = 116; +const tItemState kNitrogenFull = 117; +const tItemState kFullGlass = 118; + +// Extra IDs. + +const uint32 kRetinalScanSearching = 0; +const uint32 kRetinalScanActivated = 1; +const uint32 kShieldIntro = 2; +const uint32 kRemoveAirMask = 3; +const uint32 kRemoveArgon = 4; +const uint32 kRemoveCrowbar = 5; +const uint32 kGasCanLoop = 6; +const uint32 kRemoveJourneymanKey = 7; +const uint32 kRemoveMarsCard = 8; +const uint32 kRemoveNitrogen = 9; +const uint32 kRemoveGlass = 10; +const uint32 kRemoveDart = 11; +const uint32 kRemoveSinclairKey = 12; + +enum tItemType { + kInventoryItemType, + kBiochipItemType +}; + +class Sprite; + +/* + + Item is an object which can be picked up and carried around. + Items have + a location + an ID. + weight + an owner (kNoActorID if no one is carrying the Item) + +*/ + +class Item : public IDObject { +public: + Item(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction); + virtual ~Item(); + + // WriteToStream writes everything EXCEPT the item's ID. + // It is assumed that the calling function will write and read the ID. + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + virtual tActorID getItemOwner() const; + virtual void setItemOwner(const tActorID owner); + + void getItemRoom(tNeighborhoodID &neighborhood, tRoomID &room, tDirectionConstant &direction) const; + void setItemRoom(const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction); + tNeighborhoodID getItemNeighborhood() const; + + virtual tWeightType getItemWeight(); + + virtual void setItemState(const tItemState state); + virtual tItemState getItemState() const; + + virtual tItemType getItemType() = 0; + + TimeValue getInfoLeftTime() const; + void getInfoRightTimes(TimeValue &, TimeValue &) const; + TimeValue getSharedAreaTime() const; + + Sprite *getDragSprite(const tDisplayElementID) const; + + /* + select -- called when this item becomes current. Also called when the inventory + panel holding this item is raised and this is the current item. + deselect -- called when this item is no longer current. + activate -- called on the current item when the panel is closed. + */ + // In an override of these three member functions, you must call the inherited + // member functions. + virtual void select(); + virtual void deselect(); + virtual bool isSelected() { return _isSelected; } + + virtual void activate() { _isActive = true; } + virtual bool isActive() { return _isActive; } + virtual void pickedUp() {} + virtual void addedToInventory() {} + virtual void removedFromInventory() {} + virtual void dropped() {} + + // Called when the shared area is taken by another item, but this item is still + // selected. + virtual void giveUpSharedArea() {} + virtual void takeSharedArea() {} + + void findItemExtra(const uint32 extraID, ItemExtraEntry &entry); + +protected: + tNeighborhoodID _itemNeighborhood; + tRoomID _itemRoom; + tDirectionConstant _itemDirection; + tActorID _itemOwnerID; + tWeightType _itemWeight; + tItemState _itemState; + + JMPItemInfo _itemInfo; + ItemStateInfo _sharedAreaInfo; + ItemExtraInfo _itemExtras; + bool _isActive; + bool _isSelected; + + static void getItemStateEntry(ItemStateInfo, uint32, tItemState&, TimeValue&); + static void findItemStateEntryByState(ItemStateInfo, tItemState, TimeValue&); + static ItemStateInfo readItemState(Common::SeekableReadStream *stream); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemdragger.cpp b/engines/pegasus/items/itemdragger.cpp new file mode 100755 index 0000000000..4e3a9f1419 --- /dev/null +++ b/engines/pegasus/items/itemdragger.cpp @@ -0,0 +1,190 @@ +/* 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/elements.h" +#include "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/itemdragger.h" + +namespace Pegasus { + +SpriteDragger::SpriteDragger() { + _draggingSprite = 0; + _limitRect = Common::Rect(-30000, -30000, 30000, 30000); + _slopRect = Common::Rect(-30000, -30000, 30000, 30000); + _dragOffset.x = 0; + _dragOffset.y = 0; + _lastHotspot = 0; +} + +void SpriteDragger::setDragSprite(Sprite *newSprite) { + if (!isTracking()) + _draggingSprite = newSprite; +} + +void SpriteDragger::setDragConstraints(const Common::Rect &limitRect, const Common::Rect &slopRect) { + if (!isTracking()) { + _rawLimitRect = limitRect; + _slopRect = slopRect; + } +} + +void SpriteDragger::getDragConstraints(Common::Rect &limitRect, Common::Rect &slopRect) const { + limitRect = _rawLimitRect; + slopRect = _slopRect; +} + +void SpriteDragger::startTracking(const Input &input) { + if (_draggingSprite) { + Tracker::startTracking(input); + + if (isTracking()) { + input.getInputLocation(_startPoint); + _lastRawPoint = _startRawPoint = _startPoint; + + Common::Rect r; + _draggingSprite->getBounds(r); + _dragOffset.x = _startPoint.x - r.left; + _dragOffset.y = _startPoint.y - r.top; + + _limitRect = _rawLimitRect; + _limitRect.left += _dragOffset.x; + _limitRect.top += _dragOffset.y; + _limitRect.right -= r.width() - _dragOffset.x; + _limitRect.bottom -= r.height() - _dragOffset.y; + pinPointInRect(_limitRect, _startPoint); + + _lastPoint = _startPoint; + if (_startPoint != _startRawPoint) { + Common::Point pt = _startPoint - _dragOffset; + _draggingSprite->moveElementTo(pt.x, pt.y); + } + + _lastHotspot = g_allHotspots.findHotspot(_lastRawPoint); + if (_lastHotspot) + enterHotspot(_lastHotspot); + } + } +} + +void SpriteDragger::continueTracking(const Input &input) { + if (_draggingSprite) { + Common::Point rawPoint; + input.getInputLocation(rawPoint); + + if (!_slopRect.contains(rawPoint)) + rawPoint = _startRawPoint; + + if (rawPoint != _lastRawPoint) { + Common::Point newPoint = rawPoint; + pinPointInRect(_limitRect, newPoint); + newPoint -= _dragOffset; + + if (newPoint != _lastPoint) { + _draggingSprite->moveElementTo(newPoint.x, newPoint.y); + _lastPoint = newPoint; + } + + Hotspot *newHotspot = g_allHotspots.findHotspot(rawPoint); + if (newHotspot != _lastHotspot) { + if (_lastHotspot) + exitHotspot(_lastHotspot); + if (newHotspot) + enterHotspot(newHotspot); + _lastHotspot = newHotspot; + } + + _lastRawPoint = rawPoint; + } + } +} + +void SpriteDragger::pinPointInRect(const Common::Rect &r, Common::Point &pt) { + pt.x = CLIP<int>(pt.x, r.left, r.right - 1); + pt.y = CLIP<int>(pt.y, r.top, r.bottom - 1); +} + +ItemDragger::ItemDragger(PegasusEngine *owner) : _inventoryDropSpot(kInventoryDropSpotID), _biochipDropSpot(kBiochipDropSpotID), + _inventoryHighlight(kInventoryDropHighlightID), _biochipHighlight(kBiochipDropHighlightID) { + _owner = owner; + + Common::Rect r(kInventoryDropLeft, kInventoryDropTop, kInventoryDropRight, kInventoryDropBottom); + _inventoryDropSpot.setArea(r); + _inventoryDropSpot.setHotspotFlags(kDropItemSpotFlag); + g_allHotspots.push_back(&_inventoryDropSpot); + + r = Common::Rect(kBiochipDropLeft, kBiochipDropTop, kBiochipDropRight, kBiochipDropBottom); + _biochipDropSpot.setArea(r); + _biochipDropSpot.setHotspotFlags(kDropBiochipSpotFlag); + g_allHotspots.push_back(&_biochipDropSpot); +} + +void ItemDragger::startTracking(const Input &input) { + _inventoryHighlight.setDisplayOrder(kInventoryHiliteOrder); + _inventoryHighlight.startDisplaying(); + + _biochipHighlight.setDisplayOrder(kBiochipHiliteOrder); + _biochipHighlight.startDisplaying(); + + SpriteDragger::startTracking(input); +} + +void ItemDragger::stopTracking(const Input &input) { + SpriteDragger::stopTracking(input); + _inventoryHighlight.hide(); + _biochipHighlight.hide(); + _inventoryHighlight.stopDisplaying(); + _biochipHighlight.stopDisplaying(); + _owner->dragTerminated(input); +} + +bool ItemDragger::stopTrackingInput(const Input &input) { + return !JMPPPInput::isDraggingInput(input); +} + +void ItemDragger::enterHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.show(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.show(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(1); +} + +void ItemDragger::exitHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.hide(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.hide(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(0); +} + +void ItemDragger::setHighlightBounds() { + _inventoryHighlight.setBounds(Common::Rect(76, 334, 172, 430)); + _biochipHighlight.setBounds(Common::Rect(364, 334, 460, 430)); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemdragger.h b/engines/pegasus/items/itemdragger.h new file mode 100755 index 0000000000..69612316f3 --- /dev/null +++ b/engines/pegasus/items/itemdragger.h @@ -0,0 +1,96 @@ +/* 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_ITEMS_ITEMDRAGGER_H +#define PEGASUS_ITEMS_ITEMDRAGGER_H + +#include "pegasus/elements.h" +#include "pegasus/input.h" + +namespace Pegasus { + +// TODO: Merge SpriteDragger and ItemDragger + +class Hotspot; +class Sprite; + +class SpriteDragger : public Tracker { +public: + SpriteDragger(); + virtual ~SpriteDragger() {} + + void setDragSprite(Sprite *); + Sprite *getDragSprite() { return _draggingSprite; } + + void setDragConstraints(const Common::Rect &, const Common::Rect &); + void getDragConstraints(Common::Rect &, Common::Rect &) const; + + void startTracking(const Input &); + void continueTracking(const Input&); + + Hotspot *getLastHotspot() const { return _lastHotspot; } + +protected: + virtual void enterHotspot(Hotspot *) {} + virtual void exitHotspot(Hotspot *) {} + + Sprite *_draggingSprite; + Common::Point _startPoint, _lastPoint, _dragOffset; + Common::Point _startRawPoint, _lastRawPoint; + Common::Rect _rawLimitRect; + Common::Rect _limitRect; + Common::Rect _slopRect; + Hotspot *_lastHotspot; + + // This is a replica of QuickDraw's PinPointInRect function + void pinPointInRect(const Common::Rect &, Common::Point &); +}; + +class PegasusEngine; + +class ItemDragger : public SpriteDragger { +public: + ItemDragger(PegasusEngine *); + virtual ~ItemDragger() {} + + void setHighlightBounds(); + void startTracking(const Input &); + void stopTracking(const Input &); + bool stopTrackingInput(const Input &); + +protected: + virtual void enterHotspot(Hotspot *); + virtual void exitHotspot(Hotspot *); + + PegasusEngine *_owner; + DropHighlight _inventoryHighlight; + Hotspot _inventoryDropSpot; + DropHighlight _biochipHighlight; + Hotspot _biochipDropSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp new file mode 100755 index 0000000000..cbe48285e9 --- /dev/null +++ b/engines/pegasus/items/itemlist.cpp @@ -0,0 +1,69 @@ +/* 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/error.h" +#include "common/stream.h" + +#include "engines/pegasus/items/item.h" +#include "engines/pegasus/items/itemlist.h" + +namespace Pegasus { + +// TODO: Don't use global construction! +ItemList g_allItems; + +ItemList::ItemList() { +} + +ItemList::~ItemList() { +} + +void ItemList::writeToStream(Common::WriteStream *stream) { + stream->writeUint32BE(size()); + + for (ItemIterator it = begin(); it != end(); it++) { + stream->writeUint16BE((*it)->getObjectID()); + (*it)->writeToStream(stream); + } +} + +void ItemList::readFromStream(Common::ReadStream *stream) { + uint32 itemCount = stream->readUint32BE(); + + for (uint32 i = 0; i < itemCount; i++) { + tItemID itemID = stream->readUint16BE(); + g_allItems.findItemByID(itemID)->readFromStream(stream); + } +} + +Item *ItemList::findItemByID(const tItemID id) { + for (ItemIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h new file mode 100755 index 0000000000..df31b5c262 --- /dev/null +++ b/engines/pegasus/items/itemlist.h @@ -0,0 +1,60 @@ +/* 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_ITEMS_ITEMLIST_H +#define PEGASUS_ITEMS_ITEMLIST_H + +#include "common/list.h" + +#include "pegasus/types.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class Item; + +class ItemList : public Common::List<Item *> { +public: + ItemList(); + virtual ~ItemList(); + + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + Item *findItemByID(const tItemID id); +}; + +typedef ItemList::iterator ItemIterator; + +// TODO: Don't use global construction! +extern ItemList g_allItems; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp new file mode 100755 index 0000000000..dfef67c27b --- /dev/null +++ b/engines/pegasus/menu.cpp @@ -0,0 +1,1215 @@ +/* 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/menu.h" +#include "pegasus/pegasus.h" +#include "pegasus/scoring.h" + +namespace Pegasus { + +GameMenu::GameMenu(const uint32 id) : IDObject(id), InputHandler((InputHandler *)((PegasusEngine *)g_engine)) { + _previousHandler = 0; + _lastCommand = kMenuCmdNoCommand; +} + +void GameMenu::becomeCurrentHandler() { + _previousHandler = InputHandler::setInputHandler(this); +} + +void GameMenu::restorePreviousHandler() { + InputHandler::setInputHandler(_previousHandler); +} + +void GameMenu::drawScore(tGameScoreType score, tGameScoreType total, const Common::Rect &scoreBounds, Surface *numbers) { + tCoordType x = scoreBounds.right; + drawNumber(total, x, scoreBounds.top, numbers); + + x -= 12; + Common::Rect r1(120, 0, 132, 12); // The slash. + Common::Rect r2 = r1; + r2.moveTo(x, scoreBounds.top); + numbers->copyToCurrentPort(r1, r2); + drawNumber(score, x, scoreBounds.top, numbers); +} + +void GameMenu::drawNumber(tGameScoreType number, tCoordType &x, tCoordType y, Surface *numbers) { + Common::Rect r1(0, 0, 12, 12); // Width, height of one digit + Common::Rect r2 = r1; + r2.moveTo(x - 12, y); + + do { + uint16 digit = number % 10; + number /= 10; + r1.moveTo(digit * 12, 0); + numbers->copyToCurrentPort(r1, r2); + r2.translate(-12, 0); + } while (number != 0); + + x = r2.right; +} + +enum { + kMainMenuStartDemo = 0, + kMainMenuCreditsDemo, + kMainMenuQuitDemo, + kFirstSelectionDemo = kMainMenuStartDemo, + kLastSelectionDemo = kMainMenuQuitDemo, + + kMainMenuOverview = 0, + kMainMenuStart, + kMainMenuRestore, + kMainMenuDifficulty, + kMainMenuCredits, + kMainMenuQuit, + kFirstSelection = kMainMenuOverview, + kLastSelection = kMainMenuQuit +}; + +static const tCoordType kStartLeftDemo = 44; +static const tCoordType kStartTopDemo = 336; + +static const tCoordType kStartSelectLeftDemo = 40; +static const tCoordType kStartSelectTopDemo = 331; + +static const tCoordType kCreditsLeftDemo = 44; +static const tCoordType kCreditsTopDemo = 372; + +static const tCoordType kCreditsSelectLeftDemo = 40; +static const tCoordType kCreditsSelectTopDemo = 367; + +static const tCoordType kMainMenuQuitLeftDemo = 32; +static const tCoordType kMainMenuQuitTopDemo = 412; + +static const tCoordType kMainMenuQuitSelectLeftDemo = 28; +static const tCoordType kMainMenuQuitSelectTopDemo = 408; + +static const tCoordType kOverviewLeft = 200; +static const tCoordType kOverviewTop = 208; + +static const tCoordType kOverviewSelectLeft = 152; +static const tCoordType kOverviewSelectTop = 204; + +static const tCoordType kStartLeft = 212; +static const tCoordType kStartTop = 256; + +static const tCoordType kStartSelectLeft = 152; +static const tCoordType kStartSelectTop = 252; + +static const tCoordType kRestoreLeft = 212; +static const tCoordType kRestoreTop = 296; + +static const tCoordType kRestoreSelectLeft = 152; +static const tCoordType kRestoreSelectTop = 292; + +static const tCoordType kDifficultyLeft = 320; +static const tCoordType kDifficultyTop = 340; + +static const tCoordType kDifficultySelectLeft = 152; +static const tCoordType kDifficultySelectTop = 336; + +static const tCoordType kCreditsLeft = 212; +static const tCoordType kCreditsTop = 388; + +static const tCoordType kCreditsSelectLeft = 152; +static const tCoordType kCreditsSelectTop = 384; + +static const tCoordType kMainMenuQuitLeft = 212; +static const tCoordType kMainMenuQuitTop = 428; + +static const tCoordType kMainMenuQuitSelectLeft = 152; +static const tCoordType kMainMenuQuitSelectTop = 424; + +// Never set the current input handler to the MainMenu. +MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButton(0), + _restoreButton(0), _adventureButton(0), _walkthroughButton(0), _startButton(0), + _creditsButton(0), _quitButton(0), _largeSelect(0), _smallSelect(0) { + + bool isDemo = ((PegasusEngine *)g_engine)->isDemo(); + + if (isDemo) + _menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict"); + else + _menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + if (!isDemo) { + _overviewButton.initFromPICTFile("Images/Main Menu/pbOvervi.pict"); + _overviewButton.setDisplayOrder(1); + _overviewButton.moveElementTo(kOverviewLeft, kOverviewTop); + _overviewButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Main Menu/pbRestor.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeft, kRestoreTop); + _restoreButton.startDisplaying(); + + _adventureButton.initFromPICTFile("Images/Main Menu/BtnAdv.pict"); + _adventureButton.setDisplayOrder(1); + _adventureButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _adventureButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Main Menu/BtnWlk.pict"); + _walkthroughButton.setDisplayOrder(1); + _walkthroughButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _walkthroughButton.startDisplaying(); + } + + if (isDemo) + _startButton.initFromPICTFile("Images/Demo/Start.pict"); + else + _startButton.initFromPICTFile("Images/Main Menu/pbStart.pict"); + _startButton.setDisplayOrder(1); + _startButton.moveElementTo(isDemo ? kStartLeftDemo : kStartLeft, isDemo ? kStartTopDemo : kStartTop); + _startButton.startDisplaying(); + + if (isDemo) + _creditsButton.initFromPICTFile("Images/Demo/Credits.pict"); + else + _creditsButton.initFromPICTFile("Images/Main Menu/pbCredit.pict"); + _creditsButton.setDisplayOrder(1); + _creditsButton.moveElementTo(isDemo ? kCreditsLeftDemo : kCreditsLeft, isDemo ? kCreditsTopDemo : kCreditsTop); + _creditsButton.startDisplaying(); + + if (isDemo) + _quitButton.initFromPICTFile("Images/Demo/Quit.pict"); + else + _quitButton.initFromPICTFile("Images/Main Menu/pbQuit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(isDemo ? kMainMenuQuitLeftDemo : kMainMenuQuitLeft, isDemo ? kMainMenuQuitTopDemo : kMainMenuQuitTop); + _quitButton.startDisplaying(); + + if (isDemo) + _largeSelect.initFromPICTFile("Images/Demo/SelectL.pict", true); + else + _largeSelect.initFromPICTFile("Images/Main Menu/SelectL.pict", true); + _largeSelect.setDisplayOrder(1); + _largeSelect.startDisplaying(); + + if (isDemo) + _smallSelect.initFromPICTFile("Images/Demo/SelectS.pict", true); + else + _smallSelect.initFromPICTFile("Images/Main Menu/SelectS.pict", true); + _smallSelect.setDisplayOrder(1); + _smallSelect.startDisplaying(); + + _menuSelection = isDemo ? kFirstSelectionDemo : kFirstSelection; + + _adventureMode = true; + + _menuLoop.attachFader(&_menuFader); + _menuLoop.initFromAIFFFile("Sounds/Main Menu.aiff"); + + updateDisplay(); +} + +MainMenu::~MainMenu() { + if (_menuLoop.isPlaying()) + stopMainMenuLoop(); +} + +void MainMenu::startMainMenuLoop() { + FaderMoveSpec spec; + + _menuLoop.loopSound(); + spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255); + + // FIXME: Should be sync, but it's a pain to use the main menu right now + // with this one. + _menuFader.startFader(spec); +} + +void MainMenu::stopMainMenuLoop() { + FaderMoveSpec spec; + + spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0); + _menuFader.startFaderSync(spec); + _menuLoop.stopSound(); +} + +void MainMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + if (input.upButtonDown()) { + if (_menuSelection > (isDemo ? kFirstSelectionDemo : kFirstSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown()) { + if (_menuSelection < (isDemo ? kLastSelectionDemo : kLastSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (!isDemo && (input.leftButtonDown() || input.rightButtonDown())) { + if (_menuSelection == kMainMenuDifficulty) { + _adventureMode = !_adventureMode; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (isDemo) { + switch (_menuSelection) { + case kMainMenuCreditsDemo: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStartDemo: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + setLastCommand(kMenuCmdStartAdventure); + break; + case kMainMenuQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _overviewButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _overviewButton.hide(); + setLastCommand(kMenuCmdOverview); + break; + case kMainMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdRestore); + break; + case kMainMenuCredits: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStart: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + if (_adventureMode) + setLastCommand(kMenuCmdStartAdventure); + else + setLastCommand(kMenuCmdStartWalkthrough); + break; + case kMainMenuDifficulty: + _adventureMode = !_adventureMode; + updateDisplay(); + break; + case kMainMenuQuit: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void MainMenu::updateDisplay() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (vm->isDemo()) { + switch (_menuSelection) { + case kMainMenuStartDemo: + _smallSelect.moveElementTo(kStartSelectLeftDemo, kStartSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCreditsDemo: + _smallSelect.moveElementTo(kCreditsSelectLeftDemo, kCreditsSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuitDemo: + _largeSelect.moveElementTo(kMainMenuQuitSelectLeftDemo, kMainMenuQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _largeSelect.moveElementTo(kOverviewSelectLeft, kOverviewSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuRestore: + _smallSelect.moveElementTo(kRestoreSelectLeft, kRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuDifficulty: + if (_adventureMode) { + _adventureButton.show(); + _walkthroughButton.hide(); + } else { + _walkthroughButton.show(); + _adventureButton.hide(); + } + + _largeSelect.moveElementTo(kDifficultySelectLeft, kDifficultySelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuStart: + _smallSelect.moveElementTo(kStartSelectLeft, kStartSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCredits: + _smallSelect.moveElementTo(kCreditsSelectLeft, kCreditsSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuit: + _smallSelect.moveElementTo(kMainMenuQuitSelectLeft, kMainMenuQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + + vm->resetIntroTimer(); + } +} + +enum { + kCreditsMenuCoreTeam, + kCreditsMenuSupportTeam, + kCreditsMenuOriginalTeam, + kCreditsMenuTalent, + kCreditsMenuOtherTitles, + kCreditsMenuMainMenu, + + kCreditsFirstSelection = kCreditsMenuCoreTeam, + kCreditsLastSelection = kCreditsMenuMainMenu +}; + +static const tCoordType kCreditsMovieLeft = 288; +static const tCoordType kCreditsMovieTop = 0; + +static const tCoordType kCoreTeamSelectLeft = 40; +static const tCoordType kCoreTeamSelectTop = 223; + +static const tCoordType kSupportTeamSelectLeft = 40; +static const tCoordType kSupportTeamSelectTop = 259; + +static const tCoordType kOriginalTeamSelectLeft = 40; +static const tCoordType kOriginalTeamSelectTop = 295; + +static const tCoordType kTalentSelectLeft = 40; +static const tCoordType kTalentSelectTop = 331; + +static const tCoordType kOtherTitlesSelectLeft = 40; +static const tCoordType kOtherTitlesSelectTop = 367; + +static const tCoordType kCreditsMainMenuLeft = 32; +static const tCoordType kCreditsMainMenuTop = 412; + +static const tCoordType kCreditsMainMenuSelectLeft = 30; +static const tCoordType kCreditsMainMenuSelectTop = 408; + +static const TimeValue kCoreTeamTime = 0; +static const TimeValue kSupportTeamTime = 1920; +static const TimeValue kOriginalTeamTime = 3000; +static const TimeValue kTalentTime = 4440; +static const TimeValue kOtherTitlesTime = 4680; + +static const TimeValue kFrameIncrement = 120; // Three frames... + +// Never set the current input handler to the CreditsMenu. +CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0), + _mainMenuButton(0), _largeSelect(0), _smallSelect(0) { + + _menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + _creditsMovie.initFromMovieFile("Images/Credits/Credits.movie"); + _creditsMovie.setDisplayOrder(1); + _creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop); + _creditsMovie.startDisplaying(); + _creditsMovie.show(); + _creditsMovie.redrawMovieWorld(); + + _mainMenuButton.initFromPICTFile("Images/Credits/MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kCreditsMainMenuLeft, kCreditsMainMenuTop); + _mainMenuButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Credits/SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.moveElementTo(kCreditsMainMenuSelectLeft, kCreditsMainMenuSelectTop); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Credits/SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.show(); + _smallSelect.startDisplaying(); + + _menuSelection = -1; + + newMenuSelection(kCreditsMenuCoreTeam); +} + +// Assumes the new selection is never more than one away from the old... +void CreditsMenu::newMenuSelection(const int newSelection) { + if (newSelection != _menuSelection) { + switch (newSelection) { + case kCreditsMenuCoreTeam: + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _creditsMovie.setTime(kCoreTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuSupportTeam: + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _creditsMovie.setTime(kSupportTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOriginalTeam: + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _creditsMovie.setTime(kOriginalTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuTalent: + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _creditsMovie.setTime(kTalentTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOtherTitles: + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _creditsMovie.setTime(kOtherTitlesTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuMainMenu: + _smallSelect.hide(); + _largeSelect.show(); + break; + } + + _menuSelection = newSelection; + } +} + +void CreditsMenu::newMovieTime(const TimeValue newTime) { + if (newTime < kSupportTeamTime) { + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _menuSelection = kCreditsMenuCoreTeam; + } else if (newTime < kOriginalTeamTime) { + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _menuSelection = kCreditsMenuSupportTeam; + } else if (newTime < kTalentTime) { + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _menuSelection = kCreditsMenuOriginalTeam; + } else if (newTime < kOtherTitlesTime) { + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuTalent; + } else if ((int)newTime == -120) { + // HACK: Avoid getting sent to the bottom button in the default case + return; + } else { + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuOtherTitles; + } + + _creditsMovie.setTime(newTime); + _creditsMovie.redrawMovieWorld(); +} + +void CreditsMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.upButtonDown()) { + if (_menuSelection > kCreditsFirstSelection) + newMenuSelection(_menuSelection - 1); + } else if (input.downButtonDown()) { + if (_menuSelection < kCreditsLastSelection) + newMenuSelection(_menuSelection + 1); + } else if (input.leftButtonDown()) { + newMovieTime(_creditsMovie.getTime() - kFrameIncrement); + } else if (input.rightButtonDown()) { + newMovieTime(_creditsMovie.getTime() + kFrameIncrement); + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (_menuSelection == kCreditsMenuMainMenu) { + _mainMenuButton.show(); + ((PegasusEngine *)g_engine)->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdCreditsMainMenu); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +static const tCoordType kContinueLeft = 44; +static const tCoordType kContinueTop = 336; + +static const tCoordType kContinueSelectLeft = 40; +static const tCoordType kContinueSelectTopDemo = 331; +static const tCoordType kContinueSelectTop = 332; + +static const tCoordType kMainMenuLeftDemo = 44; +static const tCoordType kMainMenuTopDemo = 372; + +static const tCoordType kMainMenuSelectLeftDemo = 40; +static const tCoordType kMainMenuSelectTopDemo = 367; + +static const tCoordType kQuitLeftDemo = 32; +static const tCoordType kQuitTopDemo = 412; + +static const tCoordType kQuitSelectLeftDemo = 28; +static const tCoordType kQuitSelectTopDemo = 408; + +static const tCoordType kRestoreLeftDeath = 44; +static const tCoordType kRestoreTopDeath = 372; + +static const tCoordType kRestoreSelectLeftDeath = 40; +static const tCoordType kRestoreSelectTopDeath = 368; + +static const tCoordType kMainMenuLeft = 32; +static const tCoordType kMainMenuTop = 412; + +static const tCoordType kMainMenuSelectLeft = 28; +static const tCoordType kMainMenuSelectTop = 408; + +enum { + kDeathScreenContinueDemo = 0, + kDeathScreenMainMenuDemo, + kDeathScreenQuitDemo, + + kFirstDeathSelectionDemo = kDeathScreenContinueDemo, + kLastDeathSelectionDemo = kDeathScreenQuitDemo, + + kDeathScreenContinue = 0, + kDeathScreenRestore, + kDeathScreenMainMenu, + + kFirstDeathSelection = kDeathScreenContinue, + kLastDeathSelection = kDeathScreenMainMenu +}; + +// Never set the current input handler to the DeathMenu. +DeathMenu::DeathMenu(const tDeathReason deathReason) : GameMenu(kDeathMenuID), _deathBackground(0), _continueButton(0), + _mainMenuButton(0), _quitButton(0), _restoreButton(0), _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + _playerWon = (deathReason == kPlayerWonGame); + + Common::String prefix = "Images/"; + Common::String imageName; + if (isDemo) { + prefix += "Demo/"; + imageName = prefix; + + switch (deathReason) { + case kDeathFallOffCliff: + imageName += "dPfall"; + break; + case kDeathEatenByDinosaur: + imageName += "dPdino"; + break; + case kDeathStranded: + imageName += "dPstuck"; + break; + default: + imageName += "dPdemowin"; + break; + } + + imageName += ".pict"; + } else { + prefix += "Death Screens/"; + imageName = prefix; + + static const char *fileNames[] = { + "dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked", + "dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck", + "dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1", + "dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot", + "dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2", + "dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2", + "dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale" + }; + + imageName += fileNames[deathReason - 1]; + imageName += ".pict"; + } + + _deathBackground.initFromPICTFile(imageName); + _deathReason = deathReason; + + if (!isDemo) + drawAllScores(); + + _deathBackground.setDisplayOrder(0); + _deathBackground.startDisplaying(); + _deathBackground.show(); + + if (isDemo) { + if (_playerWon) // Make credits button... + _continueButton.initFromPICTFile(prefix + "Credits.pict"); + else // Make continue button... + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeftDemo, kMainMenuTopDemo); + _mainMenuButton.startDisplaying(); + + _quitButton.initFromPICTFile(prefix + "Quit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(kQuitLeftDemo, kQuitTopDemo); + _quitButton.startDisplaying(); + + _menuSelection = kDeathScreenContinueDemo; + } else { + if (!_playerWon) { + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeft, kMainMenuTop); + _mainMenuButton.startDisplaying(); + + _restoreButton.initFromPICTFile(prefix + "Restore.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeftDeath, kRestoreTopDeath); + _restoreButton.startDisplaying(); + } + + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _menuSelection = kDeathScreenContinue; + } + + _smallSelect.initFromPICTFile(prefix + "SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.startDisplaying(); + + _continueButton.setDisplayOrder(1); + _continueButton.moveElementTo(kContinueLeft, kContinueTop); + _continueButton.startDisplaying(); + + if (isDemo || !_playerWon) { + _largeSelect.initFromPICTFile(prefix + "SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.startDisplaying(); + } else { + _triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph"); + _triumphSound.playSound(); + } + + updateDisplay(); +} + +void DeathMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (_menuSelection > (vm->isDemo() ? kFirstDeathSelectionDemo : kFirstDeathSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown() && (vm->isDemo() || !_playerWon)) { + if (_menuSelection < (vm->isDemo() ? kLastDeathSelectionDemo : kLastDeathSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (vm->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdDeathQuitDemo); + break; + case kDeathScreenMainMenuDemo: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenuDemo); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdDeathRestore); + break; + case kDeathScreenMainMenu: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenu); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void DeathMenu::updateDisplay() { + if (((PegasusEngine *)g_engine)->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenQuitDemo: + _largeSelect.moveElementTo(kQuitSelectLeftDemo, kQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kDeathScreenMainMenuDemo: + _smallSelect.moveElementTo(kMainMenuSelectLeftDemo, kMainMenuSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenRestore: + _smallSelect.moveElementTo(kRestoreSelectLeftDeath, kRestoreSelectTopDeath); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenMainMenu: + _largeSelect.moveElementTo(kMainMenuSelectLeft, kMainMenuSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } +} + +void DeathMenu::drawAllScores() { + Surface numbers; + numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict"); + + Common::Rect scoreBounds; + tGameScoreType caldoriaTotal = 0; + + switch (_deathReason) { + case kDeathCardBomb: + case kDeathShotBySinclair: + case kDeathSinclairShotDelegate: + case kDeathNuclearExplosion: + case kDeathGassedInNorad: + case kDeathWokeUpNorad: + case kDeathArrestedInNorad: + case kDeathSubDestroyed: + case kDeathRobotThroughNoradDoor: + case kDeathRobotSubControlRoom: + case kDeathWrongShuttleLock: + case kDeathArrestedInMars: + case kDeathRunOverByPod: + case kDeathDidntGetOutOfWay: + case kDeathReactorBurn: + case kDeathDidntFindMarsBomb: + case kDeathDidntDisarmMarsBomb: + case kDeathNoMaskInMaze: + case kDeathNoAirInMaze: + case kDeathGroundByMazebot: + case kDeathMissedOreBucket: + case kDeathDidntLeaveBucket: + case kDeathRanIntoCanyonWall: + case kDeathRanIntoSpaceJunk: + case kDeathDidntStopPoison: + case kDeathArrestedInWSC: + case kDeathHitByPlasma: + case kDeathShotOnCatwalk: + case kPlayerWonGame: + caldoriaTotal += kMaxCaldoriaTSAScoreAfter; + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight); + drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers); + // fall through + case kDeathFallOffCliff: + case kDeathEatenByDinosaur: + case kDeathStranded: + case kDeathShotByTSARobots: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight); + drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers); + // fall through + case kDeathUncreatedInCaldoria: + case kDeathUncreatedInTSA: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth, + kDeathScreenScoreTop + kDeathScreenScoreHeight); + caldoriaTotal += kMaxCaldoriaTSAScoreBefore; + drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers); + + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight); + + drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers); + break; + } +} + +enum { + kPauseMenuSave, + kPauseMenuContinue, + kPauseMenuRestore, + kPauseMenuSoundFX, + kPauseMenuAmbience, + kPauseMenuWalkthru, + kPauseMenuQuitToMainMenu, + + kFirstPauseSelection = kPauseMenuSave, + kLastPauseSelection = kPauseMenuQuitToMainMenu +}; + +const tCoordType kPauseLeft = 194; +const tCoordType kPauseTop = 68; + +const tCoordType kSaveGameLeft = kPauseLeft + 6; +const tCoordType kSaveGameTop = kPauseTop + 56; + +const tCoordType kSaveGameSelectLeft = kPauseLeft - 44; +const tCoordType kSaveGameSelectTop = kPauseTop + 52; + +const tCoordType kPauseContinueLeft = kPauseLeft + 18; +const tCoordType kPauseContinueTop = kPauseTop + 100; + +const tCoordType kPauseContinueSelectLeft = kPauseLeft - 44; +const tCoordType kPauseContinueSelectTop = kPauseTop + 95; + +const tCoordType kPauseRestoreLeft = kPauseLeft + 18; +const tCoordType kPauseRestoreTop = kPauseTop + 136; + +const tCoordType kPauseRestoreSelectLeft = kPauseLeft - 44; +const tCoordType kPauseRestoreSelectTop = kPauseTop + 131; + +const tCoordType kSoundFXLeft = kPauseLeft + 128; +const tCoordType kSoundFXTop = kPauseTop + 187; +const tCoordType kSoundFXRight = kSoundFXLeft + 96; +const tCoordType kSoundFXBottom = kSoundFXTop + 14; + +const tCoordType kSoundFXSelectLeft = kPauseLeft - 44; +const tCoordType kSoundFXSelectTop = kPauseTop + 172; + +const tCoordType kAmbienceLeft = kPauseLeft + 128; +const tCoordType kAmbienceTop = kPauseTop + 227; +const tCoordType kAmbienceRight = kAmbienceLeft + 96; +const tCoordType kAmbienceBottom = kAmbienceTop + 14; + +const tCoordType kAmbienceSelectLeft = kPauseLeft - 44; +const tCoordType kAmbienceSelectTop = kPauseTop + 212; + +const tCoordType kWalkthruLeft = kPauseLeft + 128; +const tCoordType kWalkthruTop = kPauseTop + 264; + +const tCoordType kWalkthruSelectLeft = kPauseLeft - 44; +const tCoordType kWalkthruSelectTop = kPauseTop + 255; + +const tCoordType kQuitLeft = kPauseLeft + 18; +const tCoordType kQuitTop = kPauseTop + 302; + +const tCoordType kQuitSelectLeft = kPauseLeft - 44; +const tCoordType kQuitSelectTop = kPauseTop + 297; + +// These are relative to the pause background. +const tCoordType kPauseScoreLeft = 130; +const tCoordType kPauseScoreTop = 34; +const tCoordType kPauseScoreRight = kPauseScoreLeft + 108; +const tCoordType kPauseScoreBottom = kPauseScoreTop + 12; + +// Never set the current input handler to the CPauseMenu. +PauseMenu::PauseMenu() : GameMenu(kPauseMenuID), _pauseBackground(0), _saveButton(0), _restoreButton(0), + _walkthroughButton(0), _continueButton(0), _soundFXLevel(0), _ambienceLevel(0), _quitButton(0), + _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _pauseBackground.initFromPICTFile("Images/Pause Screen/PausScrn.pict", true); + + if (!vm->isDemo()) { + Surface numbers; + numbers.getImageFromPICTFile("Images/Pause Screen/Numbers.pict"); + drawScore(GameState.getTotalScore(), kMaxTotalScore, + Common::Rect(kPauseScoreLeft, kPauseScoreTop, kPauseScoreRight, kPauseScoreBottom), &numbers); + } + + _pauseBackground.setDisplayOrder(kPauseMenuOrder); + _pauseBackground.moveElementTo(kPauseLeft, kPauseTop); + _pauseBackground.startDisplaying(); + _pauseBackground.show(); + + if (!vm->isDemo()) { + _saveButton.initFromPICTFile("Images/Pause Screen/SaveGame.pict"); + _saveButton.setDisplayOrder(kSaveGameOrder); + _saveButton.moveElementTo(kSaveGameLeft, kSaveGameTop); + _saveButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Pause Screen/Restore.pict"); + _restoreButton.setDisplayOrder(kRestoreOrder); + _restoreButton.moveElementTo(kPauseRestoreLeft, kPauseRestoreTop); + _restoreButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Pause Screen/Walkthru.pict"); + _walkthroughButton.setDisplayOrder(kWalkthruOrder); + _walkthroughButton.moveElementTo(kWalkthruLeft, kWalkthruTop); + _walkthroughButton.startDisplaying(); + + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + } + + _continueButton.initFromPICTFile("Images/Pause Screen/Continue.pict"); + _continueButton.setDisplayOrder(kContinueOrder); + _continueButton.moveElementTo(kPauseContinueLeft, kPauseContinueTop); + _continueButton.startDisplaying(); + + _soundFXLevel.setDisplayOrder(kSoundFXOrder); + _soundFXLevel.setBounds(Common::Rect(kSoundFXLeft, kSoundFXTop, kSoundFXRight, kSoundFXBottom)); + _soundFXLevel.startDisplaying(); + _soundFXLevel.show(); + _soundFXLevel.setSoundLevel(vm->getSoundFXLevel()); + + _ambienceLevel.setDisplayOrder(kAmbienceOrder); + _ambienceLevel.setBounds(Common::Rect(kAmbienceLeft, kAmbienceTop, kAmbienceRight, kAmbienceBottom)); + _ambienceLevel.startDisplaying(); + _ambienceLevel.show(); + _ambienceLevel.setSoundLevel(vm->getAmbienceLevel()); + + _quitButton.initFromPICTFile("Images/Pause Screen/Quit2MM.pict"); + _quitButton.setDisplayOrder(kQuitToMainMenuOrder); + _quitButton.moveElementTo(kQuitLeft, kQuitTop); + _quitButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Pause Screen/SelectL.pict", true); + _largeSelect.setDisplayOrder(kPauseLargeHiliteOrder); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Pause Screen/SelectS.pict", true); + _smallSelect.setDisplayOrder(kPauseSmallHiliteOrder); + _smallSelect.startDisplaying(); + + _menuSelection = (vm->isDemo()) ? kPauseMenuContinue : kPauseMenuSave; + + updateDisplay(); +} + +void PauseMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection > kPauseMenuContinue) { + switch (_menuSelection) { + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuContinue; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuQuitToMainMenu: + _menuSelection = kPauseMenuAmbience; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection > kFirstPauseSelection) { + _menuSelection--; + updateDisplay(); + } + } + } else if (input.downButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection < kPauseMenuQuitToMainMenu) { + switch (_menuSelection) { + case kPauseMenuContinue: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuAmbience; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuQuitToMainMenu; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection < kLastPauseSelection) { + _menuSelection++; + updateDisplay(); + } + } + } else if (input.leftButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.decrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.decrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (input.rightButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.incrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.incrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + switch (_menuSelection) { + case kPauseMenuSave: + _saveButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _saveButton.hide(); + setLastCommand(kMenuCmdPauseSave); + break; + case kPauseMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdPauseRestore); + break; + case kPauseMenuContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdPauseContinue); + break; + case kPauseMenuWalkthru: + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + break; + case kPauseMenuQuitToMainMenu: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdPauseQuit); + break; + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void PauseMenu::updateDisplay() { + switch (_menuSelection) { + case kPauseMenuSave: + _largeSelect.moveElementTo(kSaveGameSelectLeft, kSaveGameSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuContinue: + _smallSelect.moveElementTo(kPauseContinueSelectLeft, kPauseContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuRestore: + _smallSelect.moveElementTo(kPauseRestoreSelectLeft, kPauseRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuSoundFX: + _largeSelect.moveElementTo(kSoundFXSelectLeft, kSoundFXSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuAmbience: + _largeSelect.moveElementTo(kAmbienceSelectLeft, kAmbienceSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuWalkthru: + _largeSelect.moveElementTo(kWalkthruSelectLeft, kWalkthruSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuQuitToMainMenu: + _smallSelect.moveElementTo(kQuitSelectLeft, kQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } +} + + +} // End of namespace Pegasus diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h new file mode 100755 index 0000000000..741662336d --- /dev/null +++ b/engines/pegasus/menu.h @@ -0,0 +1,171 @@ +/* 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_MENU_H +#define PEGASUS_MENU_H + +#include "pegasus/constants.h" +#include "pegasus/fader.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/sound.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class GameMenu : public IDObject, public InputHandler { +public: + GameMenu(const uint32); + virtual ~GameMenu() {} + + virtual void becomeCurrentHandler(); + virtual void restorePreviousHandler(); + + tGameMenuCommand getLastCommand() { return _lastCommand; } + void clearLastCommand() { _lastCommand = kMenuCmdNoCommand; } + +protected: + void setLastCommand(const tGameMenuCommand command) { _lastCommand = command; } + + InputHandler *_previousHandler; + tGameMenuCommand _lastCommand; + + void drawScore(tGameScoreType, tGameScoreType, const Common::Rect &, Surface *); + +private: + void drawNumber(tGameScoreType, tCoordType &, tCoordType, Surface *); +}; + +class Hotspot; + +class MainMenu : public GameMenu { +public: + MainMenu(); + virtual ~MainMenu(); + + virtual void handleInput(const Input &input, const Hotspot *); + void startMainMenuLoop(); + void stopMainMenuLoop(); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + + // Full and Demo + Picture _menuBackground; + Picture _startButton; + Picture _creditsButton; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; + + // Full only + bool _adventureMode; + Picture _overviewButton; + Picture _restoreButton; + Picture _adventureButton; + Picture _walkthroughButton; + + Sound _menuLoop; + SoundFader _menuFader; +}; + +class CreditsMenu : public GameMenu { +public: + CreditsMenu(); + virtual ~CreditsMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void newMenuSelection(const int); + void newMovieTime(const TimeValue); + + int _menuSelection; + Picture _menuBackground; + Movie _creditsMovie; + Picture _mainMenuButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +class DeathMenu : public GameMenu { +public: + DeathMenu(const tDeathReason); + virtual ~DeathMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + + bool playerWon() { return _playerWon; } + +protected: + void drawAllScores(); + + void updateDisplay(); + + bool _playerWon; + int _menuSelection; + tDeathReason _deathReason; + + Picture _deathBackground; + Picture _continueButton; + Picture _restoreButton; + Picture _mainMenuButton; + Picture _quitButton; + + Picture _largeSelect; + Picture _smallSelect; + + Sound _triumphSound; +}; + +class PauseMenu : public GameMenu { +public: + PauseMenu(); + virtual ~PauseMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + Picture _pauseBackground; + Picture _saveButton; + Picture _restoreButton; + Picture _walkthroughButton; + Picture _continueButton; + SoundLevel _soundFXLevel; + SoundLevel _ambienceLevel; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk new file mode 100644 index 0000000000..9f1265cd01 --- /dev/null +++ b/engines/pegasus/module.mk @@ -0,0 +1,69 @@ +MODULE := engines/pegasus + +MODULE_OBJS = \ + compass.o \ + console.o \ + cursor.o \ + detection.o \ + elements.o \ + energymonitor.o \ + fader.o \ + gamestate.o \ + graphics.o \ + hotspot.o \ + input.o \ + interface.o \ + menu.o \ + movie.o \ + notification.o \ + pegasus.o \ + sound.o \ + surface.o \ + timers.o \ + transition.o \ + util.o \ + ai/ai_action.o \ + ai/ai_area.o \ + ai/ai_condition.o \ + ai/ai_rule.o \ + items/autodragger.o \ + items/inventory.o \ + items/inventorypicture.o \ + items/item.o \ + items/itemdragger.o \ + items/itemlist.o \ + items/biochips/aichip.o \ + items/biochips/biochipitem.o \ + items/biochips/opticalchip.o \ + items/biochips/pegasuschip.o \ + items/biochips/retscanchip.o \ + items/biochips/shieldchip.o \ + items/inventory/airmask.o \ + items/inventory/gascanister.o \ + items/inventory/inventoryitem.o \ + items/inventory/keycard.o \ + neighborhood/door.o \ + neighborhood/exit.o \ + neighborhood/extra.o \ + neighborhood/hotspotinfo.o \ + neighborhood/neighborhood.o \ + neighborhood/spot.o \ + neighborhood/turn.o \ + neighborhood/view.o \ + neighborhood/zoom.o \ + neighborhood/caldoria/caldoria.o \ + neighborhood/caldoria/caldoria4dsystem.o \ + neighborhood/caldoria/caldoriamessages.o \ + neighborhood/caldoria/caldoriamirror.o \ + neighborhood/prehistoric/prehistoric.o \ + neighborhood/tsa/fulltsa.o \ + neighborhood/tsa/tinytsa.o + + +# This module can be built as a plugin +ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp new file mode 100755 index 0000000000..be3819efb3 --- /dev/null +++ b/engines/pegasus/movie.cpp @@ -0,0 +1,207 @@ +/* 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/system.h" +#include "graphics/surface.h" +#include "video/qt_decoder.h" +#include "video/video_decoder.h" + +#include "pegasus/movie.h" + +namespace Pegasus { + +Movie::Movie(const tDisplayElementID id) : Animation(id) { + _video = 0; + _directDraw = false; + setScale(600); +} + +Movie::~Movie() { + releaseMovie(); +} + +// *** Make sure this will stop displaying the movie. + +void Movie::releaseMovie() { + if (_video) { + delete _video; + _video = 0; + disposeAllCallBacks(); + deallocateSurface(); + // TODO: Decrease global direct draw counter + } +} + +void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) { + _transparent = transparent; + + releaseMovie(); + _video = new Video::QuickTimeDecoder(); + if (!_video->loadFile(fileName)) + error("Could not load video '%s'", fileName.c_str()); + + _video->pauseVideo(true); + + Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight()); + sizeElement(_video->getWidth(), _video->getHeight()); + _movieBox = bounds; + + if (!isSurfaceValid()) + allocateSurface(bounds); + + setStart(0, getScale()); + setStop(_video->getDuration() * getScale() / 1000, getScale()); +} + +void Movie::setDirectDraw(const bool flag) { + _directDraw = flag; + // TODO: Increase/decrease the global direct draw counter +} + +void Movie::redrawMovieWorld() { + if (_video) { + const Graphics::Surface *frame = _video->decodeNextFrame(); + + if (!frame) + return; + + if (_directDraw) { + // Copy to the screen + Common::Rect bounds; + getBounds(bounds); + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, bounds.left, bounds.top, frame->w, frame->h); + } else { + // Copy to the surface using _movieBox + uint16 width = MIN<int>(frame->w, _movieBox.width()); + uint16 height = MIN<int>(frame->h, _movieBox.height()); + + for (uint16 y = 0; y < height; y++) + memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel); + } + + triggerRedraw(); + } +} + +void Movie::draw(const Common::Rect &r) { + Common::Rect worldBounds = _movieBox; + Common::Rect elementBounds; + getBounds(elementBounds); + + worldBounds.moveTo(elementBounds.left, elementBounds.top); + Common::Rect r1 = r.findIntersectingRect(worldBounds); + + Common::Rect r2 = r1; + r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top); + drawImage(r2, r1); +} + +void Movie::moveMovieBoxTo(const tCoordType h, const tCoordType v) { + _movieBox.moveTo(h, v); +} + +void Movie::setVolume(uint16 volume) { + // TODO +} + +void Movie::setTime(const TimeValue time, const TimeScale scale) { + if (_video) { + // Don't go past the ends of the movie + Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale)); + + if (timeFrac < Common::Rational(_startTime, _startScale)) + timeFrac = Common::Rational(_startTime, _startScale); + else if (timeFrac >= Common::Rational(_stopTime, _stopScale)) + return; + + _video->seekToTime(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); + _time = timeFrac; + _lastMillis = 0; + } +} + +void Movie::setRate(const Common::Rational rate) { + if (rate != 1 && rate != 0) + error("Cannot set movie rate"); + + TimeBase::setRate(rate); +} + +void Movie::start() { + if (_video && _video->isPaused()) + _video->pauseVideo(false); + + TimeBase::start(); +} + +void Movie::stop() { + if (_video && !_video->isPaused()) + _video->pauseVideo(true); + + TimeBase::stop(); +} + +void Movie::resume() { + if (_video) + _video->pauseVideo(false); + + TimeBase::resume(); +} + +void Movie::pause() { + if (_video) + _video->pauseVideo(true); + + TimeBase::pause(); +} + +void Movie::checkCallBacks() { + TimeBase::checkCallBacks(); + + // The reason why we overrode TimeBase's checkCallBacks(): + // Again, avoiding timers and handling it here + if (_video && !_video->isPaused()) { + if (_video->needsUpdate()) + redrawMovieWorld(); + + uint32 startTime = _startTime * getScale() / _startScale; + uint32 stopTime = _stopTime * getScale() / _stopScale; + uint32 actualTime = CLIP<int>(_video->getElapsedTime() * getScale() / 1000, startTime, stopTime); + _time = Common::Rational(actualTime, getScale()); + + // Stop the video when we go past our end + // TODO: Check if this should really be -1 + if (actualTime >= stopTime - 1) { + // HACK: Handle looping here as well + // Should be handled like the rest of TimeBases + if (getFlags() & kLoopTimeBase) + setTime(_startTime, _startScale); + else + stop(); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h new file mode 100755 index 0000000000..3983a6f942 --- /dev/null +++ b/engines/pegasus/movie.h @@ -0,0 +1,81 @@ +/* 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_MOVIE_H +#define PEGASUS_MOVIE_H + +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/surface.h" + +namespace Video { + class SeekableVideoDecoder; +} + +namespace Pegasus { + +class Movie : public Animation, public PixelImage { +public: + Movie(const tDisplayElementID); + virtual ~Movie(); + + virtual void initFromMovieFile(const Common::String &fileName, bool transparent = false); + + bool isMovieValid() { return _video != 0; } + + virtual void releaseMovie(); + + virtual void draw(const Common::Rect &); + virtual void redrawMovieWorld(); + + void setDirectDraw(const bool); + + virtual void setTime(const TimeValue, const TimeScale = 0); + + virtual void setRate(const Common::Rational); + + virtual void start(); + virtual void stop(); + virtual void resume(); + virtual void pause(); + + virtual void moveMovieBoxTo(const tCoordType, const tCoordType); + + // *** HACK ALERT + Video::SeekableVideoDecoder *getMovie() { return _video; } + void setVolume(uint16); + + virtual void checkCallBacks(); + +protected: + Video::SeekableVideoDecoder *_video; + bool _directDraw; + Common::Rect _movieBox; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp new file mode 100755 index 0000000000..9e8717b317 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -0,0 +1,1923 @@ +/* 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/system.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" + +namespace Pegasus { + +const short kVidPhoneAngle = 30; +const short kReplicatorAngle = 50; +const short kDrawersAngle = -30; +const short kCaldoria53Angle = 45; +const short kCaldoria55Angle = -45; + +const TimeValue kSinclairInterruptionTime1 = 2955; +const TimeValue kSinclairInterruptionTime2 = 6835; +const TimeValue kSinclairInterruptionTime3 = 9835; +const TimeValue kSinclairInterruptionTime4 = 12555; + +const tInputBits kPullbackInterruptFilter = kFilterAllInput; +const tInputBits kRecalibrationInterruptFilter = kFilterAllInput; + +const TimeValue kCaldoriaReplicatorIntroIn = 4933; +const TimeValue kCaldoriaReplicatorIntroOut = 6557; + +const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557; +const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586; + +const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586; +const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687; + +const TimeValue kCaldoriaMessagesIntroIn = 11687; +const TimeValue kCaldoriaMessagesIntroOut = 13641; + +const TimeValue kCaldoriaFirstMessageIn = 13641; +const TimeValue kCaldoriaFirstMessageOut = 14203; + +const TimeValue kCaldoriaSecondMessageIn = 14203; +const TimeValue kCaldoriaSecondMessageOut = 14750; + +const TimeValue kCaldoriaDoorCloseIn = 14750; +const TimeValue kCaldoriaDoorCloseOut = 15472; + +const TimeValue kCaldoriaElevatorCloseIn = 15472; +const TimeValue kCaldoriaElevatorCloseOut = 16336; + +const TimeValue kCaldoriaShowerCloseIn = 16336; +const TimeValue kCaldoriaShowerCloseOut = 17101; + +const TimeValue kCaldoriaGTDoorCloseIn = 17101; +const TimeValue kCaldoriaGTDoorCloseOut = 18523; + +const TimeValue kCaldoriaNobodyHomeIn = 18523; +const TimeValue kCaldoriaNobodyHomeOut = 21469; + +const TimeValue kCaldoriaNoOtherFloorIn = 21469; +const TimeValue kCaldoriaNoOtherFloorOut = 28013; + +const TimeValue kCaldoria4DInstructionsIn = 28013; +const TimeValue kCaldoria4DInstructionsOut = 29730; + +const TimeValue kCaldoriaDrinkOJIn = 33910; +const TimeValue kCaldoriaDrinkOJOut = 35846; + +const TimeValue kCaldoriaNoOtherDestinationIn = 35846; +const TimeValue kCaldoriaNoOtherDestinationOut = 37877; + +const TimeValue kCaldoriaUhghIn = 37877; +const TimeValue kCaldoriaUhghOut = 38025; + +const TimeValue kCaldoriaSinclairShootsOSIn = 38025; +const TimeValue kCaldoriaSinclairShootsOSOut = 40649; + +const TimeValue kCaldoriaScreamingAfterIn = 40649; +const TimeValue kCaldoriaScreamingAfterOut = 47661; + +const TimeValue k4FloorTime = 0; + +const TimeValue k4To1Start = 40; +const TimeValue k4To1Stop = 7720; + +const TimeValue k4To5Start = 7720; +const TimeValue k4To5Stop = 10280; + +const TimeValue k4To2Time = 10280; + +const TimeValue k4To3Time = 10320; + +const TimeValue k1FloorTime = 10360; + +const TimeValue k1To4Start = 10400; +const TimeValue k1To4Stop = 18080; + +const TimeValue k1To5Start = 18080; +const TimeValue k1To5Stop = 28320; + +const TimeValue k1To2Time = 28320; + +const TimeValue k1To3Time = 28360; + +const TimeValue k5FloorTime = 28400; + +const TimeValue k5To1Start = 28440; +const TimeValue k5To1Stop = 38680; + +const TimeValue k5To4Start = 38680; +const TimeValue k5To4Stop = 41240; + +const TimeValue k5To2Time = 41240; + +const TimeValue k5To3Time = 41280; + +// MMFuseFunction functions... + +const tNotificationFlags kSinclairLoopDoneFlag = kLastNeighborhoodNotificationFlag << 1; + +void doorBombTimerExpiredFunction(FunctionPtr *, void *caldoria) { + ((Caldoria *)caldoria)->doorBombTimerExpired(); +} + +void sinclairTimerExpiredFunction(FunctionPtr *, void *caldoria) { + ((Caldoria *)caldoria)->sinclairTimerExpired(); +} + +Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID) { + setIsItemTaken(kKeyCard); + setIsItemTaken(kOrangeJuiceGlassEmpty); + GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty)); + _zoomOutSpot = 0; + _gunSprite = 0; +} + +Caldoria::~Caldoria() { +} + +void Caldoria::init() { + Neighborhood::init(); + + // We need this notification flag as well. + _neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag); + + forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown); + forceStridingStop(kCaldoria50, kNorth, kAltCaldoriaSinclairDown); +} + +void Caldoria::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getCaldoriaSeenPullback()) { + _vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + g_system->delayMillis(2 * 1000); + + Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder(); + + if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie")) + error("Could not load pullback movie"); + + bool skipped = false; + Input input; + + _vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) { + if (pullbackMovie->needsUpdate()) { + const Graphics::Surface *frame = pullbackMovie->decodeNextFrame(); + + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); + g_system->updateScreen(); + } + } + + InputHandler::getCurrentInputDevice()->getInput(input, kPullbackInterruptFilter); + if (input.anyInput()) { // TODO: Save/Quit requests + skipped = true; + break; + } + + g_system->delayMillis(10); + } + + delete pullbackMovie; + + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); + + ExtraTable::Entry entry; + + if (!skipped) { + uint32 white = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); + + _vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, white); + + g_system->delayMillis(3 * 1000 / 2); + + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + _vm->refreshDisplay(); + _vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, white); + } else { + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + } + + GameState.setCaldoriaSeenPullback(true); + } + + Neighborhood::start(); +} + +void Caldoria::flushGameState() { + GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +void Caldoria::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (GameState.allTimeZonesFinished()) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X49NB1", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria49, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + +#if 0 + // TODO + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X56EH1", false); + AIBombActiveCondition *activeCondition = new AIBombActiveCondition; + rule = new AIRule(activeCondition, messageAction); + g_AIArea->addAIRule(rule); +#endif + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB2", false); + AITimerCondition *timerCondition = new AITimerCondition(kLateWarning3TimeLimit, 1, true); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + AINotCondition *notCondition = new AINotCondition(locCondition); + AIAndCondition *andCondition = new AIAndCondition(timerCondition, notCondition); + AIRule *rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB1", false); + timerCondition = new AITimerCondition(kLateWarning2TimeLimit, 1, true); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + notCondition = new AINotCondition(locCondition); + andCondition = new AIAndCondition(timerCondition, notCondition); + rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XA44EB", false); + locCondition = new AILocationCondition(3); + locCondition->addLocation(MakeRoomView(kCaldoria01, kNorth)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kEast)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X42WH1", false); + AICondition *condition = makeLocationAndDoesntHaveItemCondition(kCaldoria44, kEast, kKeyCard); + rule = new AIRule(condition, messageAction); + g_AIArea->addAIRule(rule); + + AIActivateRuleAction *ruleAction = new AIActivateRuleAction(rule); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria42, kEast)); + rule = new AIRule(locCondition, ruleAction); + g_AIArea->addAIRule(rule); + } + } +} + +uint16 Caldoria::getDateResID() const { + return kDate2318ID; +} + +TimeValue Caldoria::getViewTime(const tRoomID room, const tDirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (room) { + case kCaldoria00: + if (direction == kEast && _privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + extraID = k4DEnvironOpenView; + break; + case kCaldoriaDrawers: + if (direction == kNorth && _privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + if (GameState.isTakenItemID(kKeyCard)) + extraID = kRightDrawerOpenViewNoKeys; + else + extraID = kRightDrawerOpenViewWithKeys; + } + break; + case kCaldoria16: + if (direction == kSouth && GameState.getCaldoriaSeenSinclairInElevator()) + extraID = kCaldoria16SouthViewWithElevator; + break; + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ() && !(GameState.isTakenItemID(kOrangeJuiceGlassEmpty) || GameState.isTakenItemID(kOrangeJuiceGlassFull))) + extraID = kReplicatorNorthViewWithOJ; + break; + case kCaldoriaKiosk: + case kCaldoriaBinoculars: + return 0xffffffff; + case kCaldoria48: + if (direction == kNorth && GameState.getCaldoriaRoofDoorOpen()) + extraID = kCa48NorthExplosion; + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + +void Caldoria::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria13, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen13CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen13CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria14, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen14CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen14CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria18, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen18CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen18CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria23, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen23CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen23CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria33, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen33CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen33CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria36, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen36CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen36CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41NorthCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41NorthCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41EastCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41EastCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41WestCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41WestCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + default: + Neighborhood::startSpotOnceOnly(startTime, stopTime); + break; + } +} + +void Caldoria::findSpotEntry(const tRoomID room, const tDirectionConstant direction, tSpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (room) { + case kCaldoria00: + if (direction == kEast && (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN())) + entry.clear(); + break; + case kCaldoriaVidPhone: + if (direction == kNorth && GameState.getCaldoriaSeenMessages()) + entry.clear(); + break; + case kCaldoria44: + if (direction == kEast && GameState.getLastRoom() != kCaldoria42) + entry.clear(); + break; + } +} + +void Caldoria::startExitMovie(const ExitTable::Entry &exitEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + // fall through + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria13: + case kCaldoria14: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startExitMovie(exitEntry); +} + +void Caldoria::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria12: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startZoomMovie(zoomEntry); +} + +void Caldoria::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + if (GameState.getCurrentRoom() == kCaldoria27 || GameState.getCurrentRoom() == kCaldoria28 || GameState.getCurrentRoom() == kCaldoria45) + // Must be opening elevator door. + closeCroppedMovie(); + + if (GameState.getCurrentRoom() == kCaldoria44 && GameState.getLastRoom() != kCaldoria42) + startExtraSequence(kArriveAtCaldoriaFromTSA, kDoorOpenCompletedFlag, false); + else + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +void Caldoria::startTurnPush(const tTurnDirection turnDirection, const TimeValue newViewTime, const tDirectionConstant destDirection) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + break; + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria12: + case kCaldoria13: + case kCaldoria14: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + case kCaldoria48: + if (_croppedMovie.isSurfaceValid()) + closeCroppedMovie(); + break; + } + + Neighborhood::startTurnPush(turnDirection, newViewTime, destDirection); +} + +void Caldoria::bumpIntoWall() { + requestSpotSound(kCaldoriaUhghIn, kCaldoriaUhghOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void Caldoria::closeDoorOffScreen(const tRoomID room, const tDirectionConstant direction) { + switch (room) { + case kCaldoria08: + if (direction == kNorth) + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + else + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + case kCaldoria09: + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + break; + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + playSpotSoundSync(kCaldoriaElevatorCloseIn, kCaldoriaElevatorCloseOut); + break; + case kCaldoria44: + case kCaldoria42: + if (GameState.getCurrentRoom() == kCaldoria42) + playSpotSoundSync(kCaldoriaGTDoorCloseIn, kCaldoriaGTDoorCloseOut); + break; + default: + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + } +} + +int16 Caldoria::getStaticCompassAngle(const tRoomID room, const tDirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kCaldoriaVidPhone: + result += kVidPhoneAngle; + break; + case kCaldoriaReplicator: + result += kReplicatorAngle; + break; + case kCaldoriaDrawers: + result += kDrawersAngle; + break; + case kCaldoria53: + result += kCaldoria53Angle; + break; + case kCaldoria55: + result += kCaldoria55Angle; + break; + } + + return result; +} + +void Caldoria::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kCaldoria08, kNorth): + case MakeRoomView(kCaldoria09, kSouth): + compassMove.insertFaderKnot((exitEntry.movieStart + exitEntry.movieEnd) >> 1, compassMove.getNthKnotValue(0) + 30); + break; + case MakeRoomView(kCaldoria10, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + break; + case MakeRoomView(kCaldoria42, kWest): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, -90, exitEntry.movieEnd, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 3 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(exitEntry.movieStart + 33 * kCaldoriaFrameDuration, 90); + break; + case MakeRoomView(kCaldoria54, kEast): + if (getCurrentAlternate() != kAltCaldoriaSinclairDown) { + compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kCaldoriaFrameDuration, 135); + compassMove.insertFaderKnot(exitEntry.movieEnd, 135); + } + break; + case MakeRoomView(kCaldoria55, kNorth): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, 315, exitEntry.movieEnd, 270); + break; + } +} + +void Caldoria::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { + Neighborhood::getZoomCompassMove(zoomEntry, compassMove); + + switch (zoomEntry.hotspot) { + case kCaBathroomToiletSpotID: + compassMove.insertFaderKnot(zoomEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(zoomEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(zoomEntry.movieEnd, -90); + break; + } +} + +void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kCaldoria00WakeUp1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 180); + compassMove.insertFaderKnot(entry.movieStart + 1000, 90); + compassMove.insertFaderKnot(entry.movieStart + 1640, 120); + compassMove.insertFaderKnot(entry.movieStart + 2240, 135); + compassMove.insertFaderKnot(entry.movieStart + 2640, 180); + break; + case kCaldoria00WakeUp2: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 180, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 560, 90); + break; + case kCaldoria56BombStage1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 10); + compassMove.insertFaderKnot(entry.movieStart + 31 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 49 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 66 * kCaldoriaFrameDuration, 10); + break; + case kCaldoria56BombStage7: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 10, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 131 * kCaldoriaFrameDuration, 10); + compassMove.insertFaderKnot(entry.movieStart + 148 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 165 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieEnd - 5 * kCaldoriaFrameDuration, 90); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + break; + } +} + +void Caldoria::loadAmbientLoops() { + tRoomID room = GameState.getCurrentRoom(); + + if (room == kCaldoria00 && GameState.getCaldoriaWokenUp()) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room >= kCaldoria01 && room <= kCaldoria14) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45) + loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5); + else if (room == kCaldoria44) + loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF"); + else if (room >= kCaldoria15 && room <= kCaldoria48) + loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3); + else if (room >= kCaldoria49 && room <= kCaldoria56) + loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4); +} + +void Caldoria::checkContinuePoint(const tRoomID room, const tDirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kCaldoria06, kSouth): + case MakeRoomView(kCaldoria13, kNorth): + case MakeRoomView(kCaldoria16, kSouth): + case MakeRoomView(kCaldoria38, kEast): + case MakeRoomView(kCaldoria38, kWest): + case MakeRoomView(kCaldoria40, kNorth): + case MakeRoomView(kCaldoria44, kEast): + case MakeRoomView(kCaldoria48, kNorth): + case MakeRoomView(kCaldoria49, kNorth): + makeContinuePoint(); + break; + } +} + +void Caldoria::spotCompleted() { + Neighborhood::spotCompleted(); + if (GameState.getCurrentRoom() == kCaldoriaBinoculars) + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); +} + +void Caldoria::arriveAt(const tRoomID room, const tDirectionConstant direction) { + switch (room) { + case kCaldoria56: + if (!GameState.getCaldoriaGunAimed()) + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (GameState.getCaldoriaSinclairShot()) + setCurrentAlternate(kAltCaldoriaSinclairDown); + break; + } + + Neighborhood::arriveAt(room, direction); + Input dummy; + + switch (room) { + case kCaldoria00: + arriveAtCaldoria00(); + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoriaToilet: + GameState.setScoringReadPaper(true); + break; + case kCaldoriaReplicator: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaReplicatorIntroIn, kCaldoriaReplicatorIntroOut, kFilterNoInput, 0); + break; + case kCaldoria11: + setCurrentAlternate(kAltCaldoriaNormal); + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoriaDrawers: + setCurrentActivation(kActivateDrawersClosed); + break; + case kCaldoria13: + GameState.setCaldoriaINNAnnouncing(true); + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + // Fall through... + case kCaldoria15: + GameState.setCaldoriaINNAnnouncing(true); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + openDoor(); + break; + case kCaldoriaBinoculars: + GameState.setScoringLookThroughTelescope(true); + break; + case kCaldoriaKiosk: + GameState.setScoringSawCaldoriaKiosk(true); + startExtraSequenceSync(kCaldoriaKioskVideo, kFilterAllInput); + downButton(dummy); + break; + case kCaldoria44: + arriveAtCaldoria44(); + break; + case kCaldoria49: + arriveAtCaldoria49(); + break; + case kCaldoria53: + if (direction == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + arriveAtCaldoria56(); + break; + case kCaldoriaDeathRoom: + arriveAtCaldoriaDeath(); + break; + } + + checkSinclairShootsOS(); + setUpRoofTop(); +} + +void Caldoria::doAIRecalibration() { + GameState.setCaldoriaDidRecalibration(true); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter)) + return; + + g_interface->calibrateEnergyBar(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter)) + return; + + g_interface->raiseInventoryDrawerSync(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) { + g_interface->lowerInventoryDrawerSync(); + return; + } + + g_interface->lowerInventoryDrawerSync(); + g_interface->raiseBiochipDrawerSync(); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) { + g_interface->lowerBiochipDrawerSync(); + return; + } + + g_interface->lowerBiochipDrawerSync(); + + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter); +} + +void Caldoria::arriveAtCaldoria00() { + if (GameState.getCurrentDirection() == kEast) { + if (GameState.getCaldoriaWokenUp()) { + if (!GameState.getCaldoriaDidRecalibration()) + doAIRecalibration(); + setCurrentActivation(kActivate4DClosed); + } else { + // Good morning, sleeping beauty + ExtraTable::Entry extra; + getExtraEntry(kCaldoria00WakeUp1, extra); + + if (_navMovie.getTime() != extra.movieStart) { + _navMovie.setTime(extra.movieStart); + _navMovie.redrawMovieWorld(); + } + + startExtraSequenceSync(kCaldoria00WakeUp1, kFilterNoInput); + GameState.setCaldoriaWokenUp(true); + playCroppedMovieOnce("Images/Caldoria/VidPhone.movie", kCaldoriaVidPhoneLeft, kCaldoriaVidPhoneTop, kFilterAllInput); + startExtraSequence(kCaldoria00WakeUp2, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +bool Caldoria::wantsCursor() { + return GameState.getCaldoriaDidRecalibration(); +} + +void Caldoria::arriveAtCaldoria44() { + if (GameState.getLastNeighborhood() != kCaldoriaID) { + openDoor(); + } else { + setCurrentActivation(kActivateReadyForCard); + loopExtraSequence(kCaldoriaTransporterArrowLoop, 0); + } +} + +void Caldoria::arriveAtCaldoria49() { + if (GameState.getLastRoom() == kCaldoria48) + setCurrentAlternate(kAltCaldoriaNormal); + + // Need to force the loop to play. + if (GameState.getCurrentDirection() == kNorth) { + GameState.setCaldoriaFuseTimeLimit(kSinclairShootsTimeLimit); + startExtraSequence(kCa49NorthVoiceAnalysis, kExtraCompletedFlag, kFilterNoInput); + } +} + +void Caldoria::arriveAtCaldoria56() { + if (!GameState.getCaldoriaBombDisarmed()) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, true); + + if (GameState.getCurrentDirection() == kNorth) { + turnRight(); + } else if (GameState.getCurrentDirection() == kSouth) { + turnLeft(); + } else if (GameState.getCurrentDirection() == kEast) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } + } +} + +void Caldoria::arriveAtCaldoriaDeath() { + if (GameState.getLastRoom() == kCaldoria49) { + if (GameState.getCaldoriaSinclairShot()) { + die(kDeathNuclearExplosion); + } else { + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + } + } else { + die(kDeathShotBySinclair); + } +} + +void Caldoria::setUpRoofTop() { + switch (GameState.getCurrentRoom()) { + case kCaldoria48: + if (GameState.getCurrentDirection() == kNorth) { + if (GameState.getCaldoriaRoofDoorOpen()) { + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + } else if (GameState.getCaldoriaDoorBombed()) { + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctionPtr(&doorBombTimerExpiredFunction, (void *)this); + _utilityFuse.lightFuse(); + + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + } else { + setCurrentActivation(kActivateRoofSlotEmpty); + } + } + break; + case kCaldoria56: + if (GameState.getCurrentDirection() == kEast && GameState.getCaldoriaGunAimed()) + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + else + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (!GameState.getCaldoriaSinclairShot()) { + if (GameState.getCaldoriaSawVoiceAnalysis() && !_utilityFuse.isFuseLit()) { + _utilityFuse.primeFuse(GameState.getCaldoriaFuseTimeLimit()); + _utilityFuse.setFunctionPtr(&sinclairTimerExpiredFunction, (void *)this); + _utilityFuse.lightFuse(); + } + } else { + setCurrentAlternate(kAltCaldoriaSinclairDown); + } + break; + } +} + +void Caldoria::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria01, kEast): + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + break; + } +} + +void Caldoria::turnTo(const tDirectionConstant direction) { + Neighborhood::turnTo(direction); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (direction == kEast) + setCurrentActivation(kActivate4DClosed); + break; + case kCaldoria01: + if (direction == kEast) { + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoria11: + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoria13: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (direction == kNorth) + openElevatorMovie(); + else + closeCroppedMovie(); + break; + case kCaldoria48: + if (direction == kNorth && !GameState.getCaldoriaDoorBombed()) + setCurrentActivation(kActivateRoofSlotEmpty); + break; + case kCaldoria53: + if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + if (_privateFlags.getFlag(kCaldoriaPrivateZoomingToBombFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } else if (GameState.getCaldoriaBombDisarmed()) { + _vm->playEndMessage(); + } + break; + } + + checkSinclairShootsOS(); +} + +void Caldoria::zoomTo(const Hotspot *zoomOutSpot) { + // Need to set _zoomOutSpot here because we may come through + // this function another way, say by pressing the down arrow, + // that doesn't involve the ClickInHotSpot function. + _zoomOutSpot = zoomOutSpot; + + if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) { + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + } else if (_privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, false); + else + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, false); + } else { + Neighborhood::zoomTo(zoomOutSpot); + } + } else { + Neighborhood::zoomTo(zoomOutSpot); + } +} + +void Caldoria::setUpSinclairLoops() { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _sinclairLoopCount = 0; + _numSinclairLoops = 2; + _navMovie.start(); +} + +void Caldoria::zoomToSinclair() { + // TODO +} + +void Caldoria::receiveNotification(Notification *notification, const tNotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + InventoryItem *item; + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kCaldoria00WakeUp2: + makeContinuePoint(); + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNoRoomID); + arriveAt(kCaldoria00, kEast); + break; + case k4DEnvironOpenToINN: + GameState.setCaldoriaSeenINN(true); + GameState.setScoringSawINN(true); + // Fall through to k4DEnvironOpen... + case k4DEnvironOpen: + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, true); + setCurrentActivation(kActivate4DOpen); + newInteraction(kCaldoria4DInteractionID); + break; + case kCaldoriaShowerUp: + GameState.setScoringTookShower(true); + GameState.setCaldoriaDoneHygiene(true); + break; + case kLeftDrawerClose: + case kRightDrawerCloseNoKeys: + case kRightDrawerCloseWithKeys: + if (_zoomOutSpot && _zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + Input input; + clickInHotspot(input, _zoomOutSpot); + } + break; + case kCreateOrangeJuice: + setCurrentActivation(kActivateOJOnThePad); + requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0); + break; + case kCaldoria00SitDown: + arriveAt(kCaldoria00, kEast); + break; + case kCaldoria16ElevatorUp: + startExtraSequence(kCaldoria16ElevatorDown, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria16ElevatorDown: + GameState.setCaldoriaSeenSinclairInElevator(true); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthToGround: + case kCaldoriaRoofToGround: + arriveAt(kCaldoria28, GameState.getCurrentDirection()); + break; + case kCaldoriaFourthToRoof: + case kCaldoriaGroundToRoof: + arriveAt(kCaldoria45, GameState.getCurrentDirection()); + break; + case kCaldoriaGroundToFourth: + case kCaldoriaRoofToFourth: + arriveAt(kCaldoria27, GameState.getCurrentDirection()); + break; + case kCaGTCardSwipe: + item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateReadyToTransport); + break; + case kCaGTFryTheFly: + case kCaGTGoToTSA: + _vm->jumpToNewEnvironment(kFullTSAID, kTSA00, kNorth); + break; + case kCaGTGoToTokyo: + playDeathExtra(kCaGTArriveAtTokyo, kDeathUncreatedInCaldoria); + break; + case kCaGTGoToBeach: + playDeathExtra(kCaGTArriveAtBeach, kDeathUncreatedInCaldoria); + break; + case kCa48NorthExplosion: + // Current biochip must be the shield if we got here. + _vm->getCurrentBiochip()->setItemState(kShieldNormal); + break; + case kBinocularsZoomInOnShip: + setCurrentActivation(kActivateFocusedOnShip); + break; + case kCa49NorthVoiceAnalysis: + _utilityFuse.primeFuse(kSinclairShootsTimeLimit); + _utilityFuse.setFunctionPtr(&sinclairTimerExpiredFunction, (void*) this); + _utilityFuse.lightFuse(); + GameState.setCaldoriaSawVoiceAnalysis(true); + break; + case kCa53EastZoomToSinclair: + if (GameState.getCaldoriaSinclairShot()) { + delete _gunSprite; + _gunSprite = 0; + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + } else { + playDeathExtra(kCa53EastDeath2, kDeathSinclairShotDelegate); + } + break; + case kCa53EastShootSinclair: + _vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kStunGun)); + startExtraSequence(kCa53EastZoomOutFromSinclair, kExtraCompletedFlag, false); + GameState.setScoringStunnedSinclair(true); + break; + case kCa53EastZoomOutFromSinclair: + setCurrentAlternate(kAltCaldoriaSinclairDown); + updateViewFrame(); + makeContinuePoint(); + break; + } + } else if ((flags & kSpotSoundCompletedFlag) != 0) { + switch (GameState.getCurrentRoom()) { + case kCaldoria20: + case kCaldoria21: + case kCaldoria26: + case kCaldoria29: + case kCaldoria34: + case kCaldoria35: + updateViewFrame(); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + updateElevatorMovie(); + break; + case kCaldoriaReplicator: + emptyOJGlass(); + break; + } + } else if ((flags & kSinclairLoopDoneFlag) != 0) { + if (++_sinclairLoopCount == _numSinclairLoops) { + switch (GameState.getCurrentRoom()) { + case kCaldoria50: + playDeathExtra(kCa50SinclairShoots, kDeathShotBySinclair); + break; + case kCaldoria54: + playDeathExtra(kCa54SouthDeath, kDeathShotBySinclair); + break; + } + } else { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _navMovie.start(); + } + } + + g_AIArea->checkMiddleArea(); +} + +tInputBits Caldoria::getInputFilter() { + tInputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + result &= ~kFilterAllDirections; + break; + case kCaldoriaBinoculars: + if (getCurrentActivation() == kActivateNotFocusedOnShip) + result &= ~(kFilterDownButton | kFilterDownAuto); + break; + case kCaldoria53: + if (_privateFlags.getFlag(kCaldoriaPrivateReadyToShootFlag) && !GameState.getCaldoriaSinclairShot()) + result &= ~kFilterAllDirections; + break; + case kCaldoria48: + if (GameState.getCaldoriaDoorBombed()) + result &= ~kFilterAllDirections; + } + + return result; +} + +void Caldoria::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoom()) { + case kCaldoriaDrawers: + if (getCurrentActivation() == kActivateRightOpen) + if (GameState.isTakenItemID(kKeyCard)) { + g_allHotspots.activateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + g_allHotspots.deactivateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + } else { + g_allHotspots.activateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + g_allHotspots.deactivateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + } + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ()) + g_allHotspots.deactivateOneHotspot(kCaldoriaMakeOJSpotID); + break; + case kCaldoria27: + if (GameState.isCurrentDoorOpen()) { + g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator1); + g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator2); + g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator3); + g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator4); + g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator5); + } + break; + case kCaldoria28: + if (GameState.isCurrentDoorOpen()) { + g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator1); + g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator2); + g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator3); + g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator4); + g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator5); + } + break; + case kCaldoria45: + if (GameState.isCurrentDoorOpen()) { + g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator1); + g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator2); + g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator3); + g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator4); + g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator5); + } + break; + } +} + +void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DEnvironOpenSpotID: + if (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()) { + startExtraSequence(k4DEnvironOpen, kExtraCompletedFlag, kFilterNoInput); + } else { + // This trick depends on the following sequences being in order in the + // world movie: + // k4DEnvironOpenToINN + // k4DINNInterruption + // k4DINNIntro + // k4DINNMarkJohnson + // k4DINNMeganLove + // k4DINNFadeOut + // k4DEnvironOpenFromINN + loadLoopSound1(""); + loadLoopSound2(""); + startExtraLongSequence(k4DEnvironOpenToINN, k4DEnvironOpenFromINN, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCa4DEnvironCloseSpotID: + ((Caldoria4DSystem *)_currentInteraction)->shutDown4DSystem(); + break; + case kCaBathroomMirrorSpotID: + newInteraction(kCaldoriaMirrorInteractionID); + break; + case kCaShowerSpotID: + requestExtraSequence(kCaldoriaShowerTitle, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerButton, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerDown, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerUp, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerOpenSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, true); + setCurrentActivation(kActivateLeftOpen); + startExtraSequence(kLeftDrawerOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerCloseSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerOpenSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, true); + setCurrentActivation(kActivateRightOpen); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerOpenNoKeys, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kRightDrawerOpenWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerWithKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerNoKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaMakeStickyBunsSpotID: + requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0); + break; + case kCaldoriaMakeOJSpotID: + GameState.setCaldoriaMadeOJ(true); + startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBedroomVidPhoneActivationSpotID: + newInteraction(kCaldoriaMessagesInteractionID); + break; + case kCaldoriaFourthFloorElevatorSpotID: + if (!GameState.getCaldoriaSeenSinclairInElevator()) { + startExtraSequence(kCaldoria16ElevatorUp, kExtraCompletedFlag, kFilterNoInput); + } else { + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + } + break; + case kCaldoriaGroundElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaRoofElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthFloorElevator1: + case kCaldoriaFourthFloorElevator2: + case kCaldoriaFourthFloorElevator3: + case kCaldoriaFourthFloorElevator4: + case kCaldoriaFourthFloorElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(4, spot->getObjectID() - kCaldoriaFourthFloorElevator1 + 1); + break; + case kCaldoriaGroundElevator1: + case kCaldoriaGroundElevator2: + case kCaldoriaGroundElevator3: + case kCaldoriaGroundElevator4: + case kCaldoriaGroundElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(1, spot->getObjectID() - kCaldoriaGroundElevator1 + 1); + break; + case kCaldoriaRoofElevator1: + case kCaldoriaRoofElevator2: + case kCaldoriaRoofElevator3: + case kCaldoriaRoofElevator4: + case kCaldoriaRoofElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(5, spot->getObjectID() - kCaldoriaRoofElevator1 + 1); + break; + case kCaldoriaGTTokyoSpotID: + startExtraSequence(kCaGTGoToTokyo, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTTSASpotID: + GameState.setScoringGoToTSA(true); + startExtraLongSequence(kCaGTFryTheFly, kCaGTGoToTSA, kExtraCompletedFlag, false); + break; + case kCaldoriaGTBeachSpotID: + startExtraSequence(kCaGTGoToBeach, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTOtherSpotID: + showExtraView(kCaGTOtherChoice); + playSpotSoundSync(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut); + showExtraView(kCaGTCardSwipe); + break; + case kCaldoriaZoomInOnShipSpotID: + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRoofDoorSpotID: + startExtraSequence(kCa48NorthRooftopClosed, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria20DoorbellSpotID: + case kCaldoria21DoorbellSpotID: + case kCaldoria26DoorbellSpotID: + case kCaldoria29DoorbellSpotID: + case kCaldoria34DoorbellSpotID: + case kCaldoria35DoorbellSpotID: + clickOnDoorbell(spot->getObjectID()); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Caldoria::clickOnDoorbell(const tHotSpotID doorBellSpotID) { + uint32 extra; + ExtraTable::Entry entry; + + switch (doorBellSpotID) { + case kCaldoria20DoorbellSpotID: + extra = kCaldoria20Doorbell; + break; + case kCaldoria21DoorbellSpotID: + extra = kCaldoria21Doorbell; + break; + case kCaldoria26DoorbellSpotID: + extra = kCaldoria26Doorbell; + break; + case kCaldoria29DoorbellSpotID: + extra = kCaldoria29Doorbell; + break; + case kCaldoria34DoorbellSpotID: + extra = kCaldoria34Doorbell; + break; + case kCaldoria35DoorbellSpotID: + extra = kCaldoria35Doorbell; + break; + } + + getExtraEntry(extra, entry); + showViewFrame(entry.movieStart); + requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag); +} + +tCanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + if (GameState.getCurrentDirection() == kSouth && !_privateFlags.getFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag)) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void Caldoria::doorOpened() { + Neighborhood::doorOpened(); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, false); +} + +GameInteraction *Caldoria::makeInteraction(const tInteractionID interactionID) { + switch (interactionID) { + case kCaldoria4DInteractionID: + return new Caldoria4DSystem(this); + break; + case kCaldoriaBombInteractionID: + // TODO + error("STUB: Bomb game"); + break; + case kCaldoriaMessagesInteractionID: + return new CaldoriaMessages(this, kCaldoriaMessagesNotificationID, _vm); + break; + case kCaldoriaMirrorInteractionID: + return new CaldoriaMirror(this); + break; + } + + return 0; +} + +void Caldoria::newInteraction(const tInteractionID interactionID) { + Neighborhood::newInteraction(interactionID); + + if (!_currentInteraction) { + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, false); + setCurrentActivation(kActivate4DClosed); + startExtraSequence(k4DEnvironClose, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getCaldoriaBombDisarmed()) { + turnLeft(); + } + } +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Caldoria::getItemScreenSpot(Item *item, DisplayElement *element) { + tHotSpotID destSpotID = kNoHotSpotID; + + switch (item->getObjectID()) { + case kKeyCard: + destSpotID = kCaldoriaKeyCardSpotID; + break; + case kOrangeJuiceGlassEmpty: + case kOrangeJuiceGlassFull: + destSpotID = kCaldoriaOrangeJuiceSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return g_allHotspots.findHotspotByID(destSpotID); +} + +void Caldoria::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kKeyCard: + GameState.setScoringGotKeyCard(true); + break; + case kOrangeJuiceGlassFull: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaDrinkOJIn, kCaldoriaDrinkOJOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(false); + break; + } +} + +void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kKeyCard: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaGTCardDropSpotID) + startExtraSequence(kCaGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kOrangeJuiceGlassEmpty: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaOrangeJuiceDropSpotID) { + GameState.setCaldoriaMadeOJ(false); + startExtraSequence(kDisposeOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCardBomb: + GameState.setCaldoriaDoorBombed(true); + setCurrentActivation(kActivateHotSpotAlways); + Neighborhood::dropItemIntoRoom(item, dropSpot); + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctionPtr(&doorBombTimerExpiredFunction, (void *)this); + _utilityFuse.lightFuse(); + GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime); + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + GameState.setScoringUsedCardBomb(true); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(true); + GameState.setCaldoriaSinclairShot(true); + _gunSprite = item->getDragSprite(0); + _gunSprite->setCurrentFrameIndex(1); + _gunSprite->setDisplayOrder(kDragSpriteOrder); + _gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop); + _gunSprite->startDisplaying(); + _gunSprite->show(); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Caldoria::takeElevator(uint startFloor, uint endFloor) { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + + switch (startFloor) { + case 1: + switch (endFloor) { + case 1: + // Do nothing. + break; + case 2: + _croppedMovie.setTime(k1To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k1To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k1To4Start, k1To4Stop); + _croppedMovie.setTime(k1To4Start); + startExtraSequence(kCaldoriaGroundToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + _croppedMovie.setSegment(k1To5Start, k1To5Stop); + _croppedMovie.setTime(k1To5Start); + startExtraSequence(kCaldoriaGroundToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 4: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k4To1Start, k4To1Stop); + _croppedMovie.setTime(k4To1Start); + startExtraSequence(kCaldoriaFourthToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k4To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k4To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + // Do nothing. + break; + case 5: + _croppedMovie.setSegment(k4To5Start, k4To5Stop); + _croppedMovie.setTime(k4To5Start); + startExtraSequence(kCaldoriaFourthToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 5: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k5To1Start, k5To1Stop); + _croppedMovie.setTime(k5To1Start); + startExtraSequence(kCaldoriaRoofToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k5To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k5To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k5To4Start, k5To4Stop); + _croppedMovie.setTime(k5To4Start); + startExtraSequence(kCaldoriaRoofToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + // Do nothing. + break; + } + break; + }; +} + +void Caldoria::updateElevatorMovie() { + TimeValue time = 0xffffffff; + + if (GameState.getCurrentDirection() == kNorth) { + switch (GameState.getCurrentRoom()) { + case kCaldoria27: + time = k4FloorTime; + break; + case kCaldoria28: + time = k1FloorTime; + break; + case kCaldoria45: + time = k5FloorTime; + break; + } + } + + _croppedMovie.stop(); + + if (time == 0xffffffff) { + _croppedMovie.hide(); + } else { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + _croppedMovie.setTime(time); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.show(); + + // *** Why do I need this? + // clone2727: "don't ask me!" + _navMovie.redrawMovieWorld(); + } +} + +void Caldoria::openElevatorMovie() { + if (!_croppedMovie.isSurfaceValid()) + openCroppedMovie("Images/Caldoria/Caldoria Elevator.movie", kCaldoriaElevatorLeft, kCaldoriaElevatorTop); + + updateElevatorMovie(); +} + +void Caldoria::emptyOJGlass() { + GameState.setTakenItemID(kOrangeJuiceGlassFull, false); + GameState.setTakenItemID(kOrangeJuiceGlassEmpty, true); + _vm->removeItemFromInventory((InventoryItem *)g_allItems.findItemByID(kOrangeJuiceGlassFull)); + _vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kOrangeJuiceGlassEmpty)); +} + +void Caldoria::doorBombTimerExpired() { + closeCroppedMovie(); + + if (GameState.getShieldOn()) { + _vm->getCurrentBiochip()->setItemState(kShieldCardBomb); + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + startExtraSequence(kCa48NorthExplosion, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringShieldedCardBomb(true); + GameState.setCaldoriaDoorBombed(false); + GameState.setCaldoriaRoofDoorOpen(true); + } else { + playDeathExtra(kCa48NorthExplosionDeath, kDeathCardBomb); + } +} + +void Caldoria::sinclairTimerExpired() { + _privateFlags.setFlag(kCaldoriaPrivateSinclairTimerExpiredFlag, true); + checkSinclairShootsOS(); +} + +void Caldoria::checkSinclairShootsOS() { + if (_privateFlags.getFlag(kCaldoriaPrivateSinclairTimerExpiredFlag)) + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria49, kNorth): + case MakeRoomView(kCaldoria49, kSouth): + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria49, kWest): + case MakeRoomView(kCaldoria50, kSouth): + case MakeRoomView(kCaldoria50, kEast): + case MakeRoomView(kCaldoria50, kWest): + case MakeRoomView(kCaldoria51, kNorth): + case MakeRoomView(kCaldoria51, kSouth): + case MakeRoomView(kCaldoria51, kWest): + case MakeRoomView(kCaldoria52, kNorth): + case MakeRoomView(kCaldoria52, kSouth): + case MakeRoomView(kCaldoria52, kWest): + case MakeRoomView(kCaldoria53, kNorth): + case MakeRoomView(kCaldoria53, kSouth): + case MakeRoomView(kCaldoria53, kWest): + case MakeRoomView(kCaldoria54, kNorth): + case MakeRoomView(kCaldoria54, kEast): + case MakeRoomView(kCaldoria54, kWest): + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + break; + } +} + +void Caldoria::checkInterruptSinclair() { + if (GameState.getCaldoriaSinclairShot()) { + _navMovie.stop(); + _neighborhoodNotification.setNotificationFlags(kExtraCompletedFlag, kExtraCompletedFlag); + g_AIArea->unlockAI(); + } else { + // TODO + } +} + +Common::String Caldoria::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + if (GameState.allTimeZonesFinished()) + return "Images/AI/Caldoria/XA02"; + + return "Images/AI/Caldoria/XA01"; + } + + return movieName; +} + +Common::String Caldoria::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + tRoomID room = GameState.getCurrentRoom(); + + if (room >= kCaldoria00 && room <= kCaldoria14) { + // Inside apartment. + if (GameState.getCaldoriaDoneHygiene()) + return "Images/AI/Caldoria/XAE2"; + + return "Images/AI/Caldoria/XAE1"; + } else if (room >= kCaldoria15 && room <= kCaldoria48) { + // Wandering the halls... + return "Images/AI/Caldoria/XAE3"; + } else { + // Must be the roof. + return "Images/AI/Caldoria/XAEH2"; + } + } + + return movieName; +} + +uint Caldoria::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + if (!GameState.isTakenItemID(kKeyCard) && GameState.getOpenDoorRoom() == kNoRoomID) + numHints = 1; + break; + case MakeRoomView(kCaldoria48, kNorth): + if (!GameState.getCaldoriaRoofDoorOpen()) { + if (_croppedMovie.isRunning()) // Bomb must be looping. + numHints = 3; + else if (GameState.isTakenItemID(kCardBomb)) + numHints = 1; + } + break; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + numHints = 1; + break; + case MakeRoomView(kCaldoria49, kNorth): + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Caldoria::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + return "Images/AI/Caldoria/X42WH2"; + case MakeRoomView(kCaldoria48, kNorth): + if (_croppedMovie.isRunning()) { // Bomb must be looping. + if (hintNum == 1) + return "Images/AI/Caldoria/X48ND1"; + else if (hintNum == 2) + return "Images/AI/Caldoria/X48ND2"; + else if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Caldoria/X48ND3"; + + // *** Doesn't work yet, need global movies. + break; + } + + return "Images/AI/Globals/XGLOB1A"; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + return "Images/AI/Caldoria/X49E"; + case MakeRoomView(kCaldoria49, kNorth): + return "Images/AI/Caldoria/X49NB2"; + } + } + + return movieName; +} + +void Caldoria::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kCa4DEnvironCloseSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + case kCaldoriaKioskSpotID: + _vm->_cursor->setCurrentFrameIndex(3); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String Caldoria::getNavMovieName() { + return "Images/Caldoria/Caldoria.movie"; +} + +Common::String Caldoria::getSoundSpotsName() { + return "Sounds/Caldoria/Caldoria Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h new file mode 100755 index 0000000000..f2e3775168 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.h @@ -0,0 +1,509 @@ +/* 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_CALDORIA_CALDORIA_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +const TimeScale kCaldoriaMovieScale = 600; +const TimeScale kCaldoriaFramesPerSecond = 15; +const TimeScale kCaldoriaFrameDuration = 40; + +// Alternate IDs. + +const tAlternateID kAltCaldoriaNormal = 0; +const tAlternateID kAltCaldoriaRoofDoorBlown = 2; +const tAlternateID kAltCaldoriaSinclairDown = 3; + +// Room IDs. + +const tRoomID kCaldoria00 = 1; +const tRoomID kCaldoria01 = 2; +const tRoomID kCaldoria02 = 3; +const tRoomID kCaldoria03 = 4; +const tRoomID kCaldoria04 = 5; +const tRoomID kCaldoria05 = 6; +const tRoomID kCaldoria06 = 7; +const tRoomID kCaldoria07 = 8; +const tRoomID kCaldoria08 = 9; +const tRoomID kCaldoria09 = 10; +const tRoomID kCaldoria10 = 11; +const tRoomID kCaldoriaToilet = 12; +const tRoomID kCaldoria11 = 13; +const tRoomID kCaldoria12 = 14; +const tRoomID kCaldoriaVidPhone = 15; +const tRoomID kCaldoriaReplicator = 16; +const tRoomID kCaldoriaDrawers = 17; +const tRoomID kCaldoria13 = 18; +const tRoomID kCaldoria14 = 19; +const tRoomID kCaldoria15 = 20; +const tRoomID kCaldoria16 = 21; +const tRoomID kCaldoria17 = 22; +const tRoomID kCaldoria18 = 23; +const tRoomID kCaldoria19 = 24; +const tRoomID kCaldoria20 = 25; +const tRoomID kCaldoria21 = 26; +const tRoomID kCaldoria22 = 27; +const tRoomID kCaldoria23 = 28; +const tRoomID kCaldoria24 = 29; +const tRoomID kCaldoria25 = 30; +const tRoomID kCaldoria26 = 31; +const tRoomID kCaldoria27 = 32; +const tRoomID kCaldoria28 = 33; +const tRoomID kCaldoria29 = 34; +const tRoomID kCaldoria30 = 35; +const tRoomID kCaldoria31 = 36; +const tRoomID kCaldoria32 = 37; +const tRoomID kCaldoria33 = 38; +const tRoomID kCaldoria34 = 39; +const tRoomID kCaldoria35 = 40; +const tRoomID kCaldoria36 = 41; +const tRoomID kCaldoria37 = 42; +const tRoomID kCaldoria38 = 43; +const tRoomID kCaldoria39 = 44; +const tRoomID kCaldoria40 = 45; +const tRoomID kCaldoria41 = 46; +const tRoomID kCaldoriaBinoculars = 47; +const tRoomID kCaldoria42 = 48; +const tRoomID kCaldoriaKiosk = 49; +const tRoomID kCaldoria44 = 50; +const tRoomID kCaldoria45 = 51; +const tRoomID kCaldoria46 = 52; +const tRoomID kCaldoria47 = 53; +const tRoomID kCaldoria48 = 54; +const tRoomID kCaldoria49 = 55; +const tRoomID kCaldoria50 = 56; +const tRoomID kCaldoria51 = 57; +const tRoomID kCaldoria52 = 58; +const tRoomID kCaldoria53 = 59; +const tRoomID kCaldoria54 = 60; +const tRoomID kCaldoria55 = 61; +const tRoomID kCaldoria56 = 62; +const tRoomID kCaldoriaDeathRoom = 0; + +// Hot Spot Activation IDs. + +const tHotSpotActivationID kActivate4DClosed = 1; +const tHotSpotActivationID kActivate4DOpen = 2; +const tHotSpotActivationID kActivateMirrorReady = 3; +const tHotSpotActivationID kActivateStylistReady = 4; +const tHotSpotActivationID kActivateReplicatorReady = 5; +const tHotSpotActivationID kActivateOJOnThePad = 6; +const tHotSpotActivationID kActivateDrawersClosed = 7; +const tHotSpotActivationID kActivateRightOpen = 8; +const tHotSpotActivationID kActivateLeftOpen = 9; +const tHotSpotActivationID kActivateFocusedOnShip = 10; +const tHotSpotActivationID kActivateNotFocusedOnShip = 11; +const tHotSpotActivationID kActivateReadyForCard = 12; +const tHotSpotActivationID kActivateReadyToTransport = 13; +const tHotSpotActivationID kActivateRoofSlotEmpty = 14; +const tHotSpotActivationID kActivateZoomedOnSinclair = 15; + +// Hot Spot IDs. + +const tHotSpotID kCa4DEnvironOpenSpotID = 5000; +const tHotSpotID kCa4DEnvironCloseSpotID = 5001; +const tHotSpotID kCa4DVisualSpotID = 5002; +const tHotSpotID kCa4DAudioSpotID = 5003; +const tHotSpotID kCa4DChoice1SpotID = 5004; +const tHotSpotID kCa4DChoice2SpotID = 5005; +const tHotSpotID kCa4DChoice3SpotID = 5006; +const tHotSpotID kCa4DChoice4SpotID = 5007; +const tHotSpotID kCaBathroomMirrorSpotID = 5008; +const tHotSpotID kCaHairStyle1SpotID = 5009; +const tHotSpotID kCaHairStyle2SpotID = 5010; +const tHotSpotID kCaHairStyle3SpotID = 5011; +const tHotSpotID kCaShowerSpotID = 5012; +const tHotSpotID kCaBathroomToiletSpotID = 5013; +const tHotSpotID kCaldoriaVidPhoneSpotID = 5014; +const tHotSpotID kCaldoriaReplicatorSpotID = 5015; +const tHotSpotID kCaldoriaDrawersSpotID = 5016; +const tHotSpotID kCaldoriaVidPhoneOutSpotID = 5017; +const tHotSpotID kCaBedroomVidPhoneActivationSpotID = 5018; +const tHotSpotID kCaldoriaReplicatorOutSpotID = 5019; +const tHotSpotID kCaldoriaMakeOJSpotID = 5020; +const tHotSpotID kCaldoriaMakeStickyBunsSpotID = 5021; +const tHotSpotID kCaldoriaOrangeJuiceSpotID = 5022; +const tHotSpotID kCaldoriaOrangeJuiceDropSpotID = 5023; +const tHotSpotID kCaldoriaDrawersOutSpotID = 5024; +const tHotSpotID kCaldoriaLeftDrawerOpenSpotID = 5025; +const tHotSpotID kCaldoriaRightDrawerOpenSpotID = 5026; +const tHotSpotID kCaldoriaKeyCardSpotID = 5027; +const tHotSpotID kCaldoriaLeftDrawerCloseSpotID = 5028; +const tHotSpotID kCaldoriaRightDrawerWithKeysCloseSpotID = 5029; +const tHotSpotID kCaldoriaRightDrawerNoKeysCloseSpotID = 5030; +const tHotSpotID kCaldoriaFourthFloorElevatorSpotID = 5031; +const tHotSpotID kCaldoria20DoorbellSpotID = 5032; +const tHotSpotID kCaldoria21DoorbellSpotID = 5033; +const tHotSpotID kCaldoria26DoorbellSpotID = 5034; +const tHotSpotID kCaldoriaFourthFloorElevator1 = 5035; +const tHotSpotID kCaldoriaFourthFloorElevator2 = 5036; +const tHotSpotID kCaldoriaFourthFloorElevator3 = 5037; +const tHotSpotID kCaldoriaFourthFloorElevator4 = 5038; +const tHotSpotID kCaldoriaFourthFloorElevator5 = 5039; +const tHotSpotID kCaldoriaGroundElevator1 = 5040; +const tHotSpotID kCaldoriaGroundElevator2 = 5041; +const tHotSpotID kCaldoriaGroundElevator3 = 5042; +const tHotSpotID kCaldoriaGroundElevator4 = 5043; +const tHotSpotID kCaldoriaGroundElevator5 = 5044; +const tHotSpotID kCaldoria29DoorbellSpotID = 5045; +const tHotSpotID kCaldoria34DoorbellSpotID = 5046; +const tHotSpotID kCaldoria35DoorbellSpotID = 5047; +const tHotSpotID kCaldoriaGroundElevatorSpotID = 5048; +const tHotSpotID kCaldoriaBinocularZoomInSpotID = 5049; +const tHotSpotID kCaldoriaBinocularsOutSpotID = 5050; +const tHotSpotID kCaldoriaZoomInOnShipSpotID = 5051; +const tHotSpotID kCaldoriaKioskSpotID = 5052; +const tHotSpotID kCaldoriaKioskOutSpotID = 5053; +const tHotSpotID kCaldoriaKioskInfoSpotID = 5054; +const tHotSpotID kCaldoriaGTCardDropSpotID = 5055; +const tHotSpotID kCaldoriaGTTokyoSpotID = 5056; +const tHotSpotID kCaldoriaGTTSASpotID = 5057; +const tHotSpotID kCaldoriaGTBeachSpotID = 5058; +const tHotSpotID kCaldoriaGTOtherSpotID = 5059; +const tHotSpotID kCaldoriaRoofElevator1 = 5060; +const tHotSpotID kCaldoriaRoofElevator2 = 5061; +const tHotSpotID kCaldoriaRoofElevator3 = 5062; +const tHotSpotID kCaldoriaRoofElevator4 = 5063; +const tHotSpotID kCaldoriaRoofElevator5 = 5064; +const tHotSpotID kCaldoriaRoofElevatorSpotID = 5065; +const tHotSpotID kCaldoriaRoofDoorSpotID = 5066; +const tHotSpotID kCaldoriaRoofCardDropSpotID = 5067; +const tHotSpotID kCaldoria53EastSinclairTargetSpotID = 5068; + +// Extra sequence IDs. + +const tExtraID kCaldoriaWakeUpView1 = 0; +const tExtraID kCaldoria00WakeUp1 = 1; +const tExtraID kCaldoria00WakeUp2 = 2; +const tExtraID kCaldoria00SitDown = 3; +const tExtraID k4DEnvironOpenToINN = 4; +const tExtraID k4DINNInterruption = 5; +const tExtraID k4DINNIntro = 6; +const tExtraID k4DINNMarkJohnson = 7; +const tExtraID k4DINNMeganLove = 8; +const tExtraID k4DINNFadeOut = 9; +const tExtraID k4DEnvironOpenFromINN = 10; +const tExtraID k4DEnvironOpen = 11; +const tExtraID k4DEnvironOpenView = 12; +const tExtraID k4DEnvironClose = 13; +const tExtraID k4DIslandLoop = 14; +const tExtraID k4DDesertLoop = 15; +const tExtraID k4DMountainLoop = 16; +const tExtraID k4DIsland1ToIsland0 = 17; +const tExtraID k4DIsland2ToIsland0 = 18; +const tExtraID k4DIsland0ToDesert0 = 19; +const tExtraID k4DIsland1ToDesert0 = 20; +const tExtraID k4DIsland2ToDesert0 = 21; +const tExtraID k4DIsland0ToMountain0 = 22; +const tExtraID k4DIsland1ToMountain0 = 23; +const tExtraID k4DIsland2ToMountain0 = 24; +const tExtraID k4DDesert0ToIsland0 = 25; +const tExtraID k4DDesert1ToIsland0 = 26; +const tExtraID k4DDesert2ToIsland0 = 27; +const tExtraID k4DDesert0ToMountain0 = 28; +const tExtraID k4DDesert1ToMountain0 = 29; +const tExtraID k4DDesert2ToMountain0 = 30; +const tExtraID k4DMountain0ToIsland0 = 31; +const tExtraID k4DMountain1ToIsland0 = 32; +const tExtraID k4DMountain2ToIsland0 = 33; +const tExtraID k4DMountain0ToDesert0 = 34; +const tExtraID k4DMountain1ToDesert0 = 35; +const tExtraID k4DMountain2ToDesert0 = 36; +const tExtraID kCaBathroomGreeting = 37; +const tExtraID kCaBathroomBodyFat = 38; +const tExtraID kCaBathroomStylistIntro = 39; +const tExtraID kCaBathroomRetrothrash = 40; +const tExtraID kCaBathroomRetrothrashReturn = 41; +const tExtraID kCaBathroomGeoWave = 42; +const tExtraID kCaBathroomGeoWaveReturn = 43; +const tExtraID kCaBathroomAgencyStandard = 44; +const tExtraID kCaldoriaShowerTitle = 45; +const tExtraID kCaldoriaShowerButton = 46; +const tExtraID kCaldoriaShowerDown = 47; +const tExtraID kCaldoriaShowerUp = 48; +const tExtraID kCaBedroomVidPhone = 49; +const tExtraID kCaBedroomMessage1 = 50; +const tExtraID kCaBedroomMessage2 = 51; +const tExtraID kCreateOrangeJuice = 52; +const tExtraID kDisposeOrangeJuice = 53; +const tExtraID kReplicatorNorthViewWithOJ = 54; +const tExtraID kLeftDrawerOpen = 55; +const tExtraID kLeftDrawerClose = 56; +const tExtraID kRightDrawerOpenWithKeys = 57; +const tExtraID kRightDrawerCloseWithKeys = 58; +const tExtraID kRightDrawerOpenNoKeys = 59; +const tExtraID kRightDrawerCloseNoKeys = 60; +const tExtraID kRightDrawerOpenViewWithKeys = 61; +const tExtraID kRightDrawerOpenViewNoKeys = 62; +const tExtraID kCaldoria16ElevatorUp = 63; +const tExtraID kCaldoria16ElevatorDown = 64; +const tExtraID kCaldoria16SouthViewWithElevator = 65; +const tExtraID kCaldoria20Doorbell = 66; +const tExtraID kCaldoria21Doorbell = 67; +const tExtraID kCaldoria26Doorbell = 68; +const tExtraID kCaldoriaFourthToGround = 69; +const tExtraID kCaldoriaRoofToFourth = 70; +const tExtraID kCaldoriaRoofToGround = 71; +const tExtraID kCaldoriaGroundToFourth = 72; +const tExtraID kCaldoriaGroundToRoof = 73; +const tExtraID kCaldoriaFourthToRoof = 74; +const tExtraID kCaldoria29Doorbell = 75; +const tExtraID kCaldoria34Doorbell = 76; +const tExtraID kCaldoria35Doorbell = 77; +const tExtraID kBinocularsZoomInOnShip = 78; +const tExtraID kCaldoriaKioskVideo = 79; +const tExtraID kCaldoriaTransporterArrowLoop = 80; +const tExtraID kArriveAtCaldoriaFromTSA = 81; +const tExtraID kCaGTOtherChoice = 82; +const tExtraID kCaGTCardSwipe = 83; +const tExtraID kCaGTSelectTSA = 84; +const tExtraID kCaGTFryTheFly = 85; +const tExtraID kCaGTGoToTSA = 86; +const tExtraID kCaGTSelectBeach = 87; +const tExtraID kCaGTGoToBeach = 88; +const tExtraID kCaGTArriveAtBeach = 89; +const tExtraID kCaGTSelectTokyo = 90; +const tExtraID kCaGTGoToTokyo = 91; +const tExtraID kCaGTArriveAtTokyo = 92; +const tExtraID kCa48NorthRooftopClosed = 93; +const tExtraID kCa48NorthExplosion = 94; +const tExtraID kCa48NorthExplosionDeath = 95; +const tExtraID kCa49NorthVoiceAnalysis = 96; +const tExtraID kCa50SinclairShoots = 97; +const tExtraID kCa53EastZoomToSinclair = 98; +const tExtraID kCa53EastDeath2 = 99; +const tExtraID kCa53EastShootSinclair = 100; +const tExtraID kCa53EastZoomOutFromSinclair = 101; +const tExtraID kCa54SouthDeath = 102; +const tExtraID kCaldoria56BombStage1 = 103; +const tExtraID kCaldoria56BombStage2 = 104; +const tExtraID kCaldoria56BombStage3 = 105; +const tExtraID kCaldoria56BombStage4 = 106; +const tExtraID kCaldoria56BombStage5 = 107; +const tExtraID kCaldoria56BombStage6 = 108; +const tExtraID kCaldoria56BombStage7 = 109; +const tExtraID kCaldoria56BombExplodes = 110; + +// Caldoria interactions. + +const tInteractionID kCaldoria4DInteractionID = 0; +const tInteractionID kCaldoriaBombInteractionID = 1; +const tInteractionID kCaldoriaMessagesInteractionID = 2; +const tInteractionID kCaldoriaMirrorInteractionID = 3; + +// Caldoria: + +const tDisplayOrder kVidPhoneOrder = kMonitorLayer; +const tDisplayOrder k4DSpritesOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaMessagesOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaElevatorOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaA05LightLoopOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaA07LightLoopOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaBombGridOrder = kMonitorLayer; +const tDisplayOrder kCaldoriaBombTimerOrder = kCaldoriaBombGridOrder + 1; + +///////////////////////////////////////////// +// +// Caldoria + +const tCoordType kCaldoriaVidPhoneLeft = kNavAreaLeft + 105; +const tCoordType kCaldoriaVidPhoneTop = kNavAreaTop + 28; + +const tCoordType kCaldoria4DSpritesLeft = kNavAreaLeft + 10; +const tCoordType kCaldoria4DSpritesTop = kNavAreaTop + 142; + +const tCoordType kCaldoriaMessageLeft = kNavAreaLeft + 202; +const tCoordType kCaldoriaMessageTop = kNavAreaTop + 26; + +const tCoordType kCaldoriaElevatorLeft = kNavAreaLeft + 407; +const tCoordType kCaldoriaElevatorTop = kNavAreaTop + 138; + +const tCoordType kCaldoriaA05LightLoopLeft = kNavAreaLeft + 213; +const tCoordType kCaldoriaA05LightLoopTop = kNavAreaTop + 215; + +const tCoordType kCaldoriaA07LightLoopLeft = kNavAreaLeft + 414; +const tCoordType kCaldoriaA07LightLoopTop = kNavAreaTop + 215; + +const tCoordType kCaldoriaGunSpriteLeft = kNavAreaLeft + 276; +const tCoordType kCaldoriaGunSpriteTop = kNavAreaTop + 115; + +const tCoordType kCaldoria11MessageLoopLeft = kNavAreaLeft + 135; +const tCoordType kCaldoria11MessageLoopTop = kNavAreaTop + 214; + +const tCoordType kCaldoria12MessageLoopLeft = kNavAreaLeft + 209; +const tCoordType kCaldoria12MessageLoopTop = kNavAreaTop + 170; + +const tCoordType kCaldoria13MessageLoopLeft = kNavAreaLeft + 480; +const tCoordType kCaldoria13MessageLoopTop = kNavAreaTop + 191; + +const tCoordType kCaldoria14MessageLoopLeft = kNavAreaLeft + 248; +const tCoordType kCaldoria14MessageLoopTop = kNavAreaTop + 191; + +const tCoordType kCaldoria48CardBombLoopLeft = kNavAreaLeft + 337; +const tCoordType kCaldoria48CardBombLoopTop = kNavAreaTop + 205; + +const tCoordType kCaldoriaBombGridLeft = kNavAreaLeft + 290; +const tCoordType kCaldoriaBombGridTop = kNavAreaTop + 58; + +const tCoordType kCaldoriaBombTimerLeft = kNavAreaLeft + 58; +const tCoordType kCaldoriaBombTimerTop = kNavAreaTop + 204; + +// Caldoria display IDs. + +const tDisplayElementID kCaldoriaVidPhoneID = kNeighborhoodDisplayID; +const tDisplayElementID kCaldoria4DSpritesID = kCaldoriaVidPhoneID + 1; +const tDisplayElementID kCaldoriaMessagesID = kCaldoria4DSpritesID + 1; +const tDisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1; +const tDisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1; +const tDisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1; + +const TimeValue kCaldoria4DBlankChoiceIn = 29730; +const TimeValue kCaldoria4DBlankChoiceOut = 33910; + +class Caldoria : public Neighborhood { +friend void doorBombTimerExpiredFunction(FunctionPtr *, void *); +friend void sinclairTimerExpiredFunction(FunctionPtr *, void *); + +public: + Caldoria(InputHandler *, PegasusEngine *); + virtual ~Caldoria(); + + virtual uint16 getDateResID() const; + + void pickedUpItem(Item *); + + virtual GameInteraction *makeInteraction(const tInteractionID); + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + void loadAmbientLoops(); + bool wantsCursor(); + void flushGameState(); + + void checkContinuePoint(const tRoomID, const tDirectionConstant); + +protected: + enum { + kCaldoriaPrivate4DSystemOpenFlag, + kCaloriaPrivateLeftDrawerOpenFlag, + kCaldoriaPrivateRightDrawerOpenFlag, + kCaldoriaPrivateReadyToShootFlag, + kCaldoriaPrivateZoomingToBombFlag, + kCaldoriaPrivateCanOpenElevatorDoorFlag, + kCaldoriaPrivateSinclairTimerExpiredFlag, + kCaldoriaPrivateSeen13CarFlag, + kCaldoriaPrivateSeen14CarFlag, + kCaldoriaPrivateSeen18CarFlag, + kCaldoriaPrivateSeen23CarFlag, + kCaldoriaPrivateSeen33CarFlag, + kCaldoriaPrivateSeen36CarFlag, + kCaldoriaPrivateSeen41NorthCarFlag, + kCaldoriaPrivateSeen41EastCarFlag, + kCaldoriaPrivateSeen41WestCarFlag, + kNumCaldoriaPrivateFlags + }; + + void init(); + void start(); + + void setUpRoofTop(); + + void setUpAIRules(); + void doAIRecalibration(); + TimeValue getViewTime(const tRoomID, const tDirectionConstant); + void findSpotEntry(const tRoomID, const tDirectionConstant, tSpotFlags, SpotTable::Entry &); + void startSpotOnceOnly(TimeValue, TimeValue); + void startExitMovie(const ExitTable::Entry &); + void startZoomMovie(const ZoomTable::Entry &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + void startTurnPush(const tTurnDirection, const TimeValue, const tDirectionConstant); + void bumpIntoWall(); + int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec &); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + void spotCompleted(); + void arriveAt(const tRoomID, const tDirectionConstant); + void arriveAtCaldoria00(); + void arriveAtCaldoriaToilet(); + void arriveAtCaldoria44(); + void arriveAtCaldoria49(); + void arriveAtCaldoria56(); + void arriveAtCaldoriaDeath(); + void turnTo(const tDirectionConstant); + void zoomTo(const Hotspot *); + void downButton(const Input &); + void receiveNotification(Notification *, const tNotificationFlags); + tInputBits getInputFilter(); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void newInteraction(const tInteractionID); + + void clickOnDoorbell(const tHotSpotID); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void takeElevator(uint, uint); + void updateElevatorMovie(); + void openElevatorMovie(); + void emptyOJGlass(); + void closeDoorOffScreen(const tRoomID, const tDirectionConstant); + void doorBombTimerExpired(); + void sinclairTimerExpired(); + void checkSinclairShootsOS(); + void setUpSinclairLoops(); + void zoomToSinclair(); + void playEndMessage(); + void checkInterruptSinclair(); + + tCanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void doorOpened(); + + void updateCursor(const Common::Point, const Hotspot *); + + FlagsArray<uint16, kNumCaldoriaPrivateFlags> _privateFlags; + + const Hotspot *_zoomOutSpot; + + FuseFunction _utilityFuse; + + long _sinclairLoopCount; + long _numSinclairLoops; + + Sprite *_gunSprite; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp new file mode 100755 index 0000000000..f6fa399ed2 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp @@ -0,0 +1,369 @@ +/* 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/ai/ai_area.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" + +namespace Pegasus { + +const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration; +// Two seconds - some slop +const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop; +// Twelve frames + some slop +const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop; + +const TimeValue kSwitchable1Start = 0; +const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration; + +const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration; +const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration; + +const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration; +const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration; + +const tNotificationFlags kVidPhoneDoneFlag = 1; + +const TimeValue kRockMusicLoopIn = 0; +const TimeValue kRockMusicLoopOut = 2088; + +const TimeValue kOrchestralMusicLoopIn = 2088; +const TimeValue kOrchestralMusicLoopOut = 4985; + +const TimeValue kRhythmsMusicLoopIn = 4985; +const TimeValue kRhythmsMusicLoopOut = 6824; + +const TimeValue kAcousticMusicLoopIn = 6824; +const TimeValue kAcousticMusicLoopOut = 9387; + +enum { + k4DVideoMenu, + k4DAudioMenu, + k4DShuttingDown, + + // These constants are the exact frame numbers of the sprite movie. + k4DRockChoice = 0, + k4DOrchestralChoice, + k4DRhythmsChoice, + k4DAcousticChoice, + k4DIslandChoice, + k4DDesertChoice, + k4DMountainChoice, + + k4DFirstVideoChoice = k4DIslandChoice +}; + +tExtraID s_transitionExtras0[3][3] = { + {0xffffffff, k4DIsland0ToDesert0, k4DIsland0ToMountain0}, + {k4DDesert0ToIsland0, 0xffffffff, k4DDesert0ToMountain0}, + {k4DMountain0ToIsland0, k4DMountain0ToDesert0, 0xffffffff} +}; + +tExtraID s_transitionExtras1[3][3] = { + {0xffffffff, k4DIsland1ToDesert0, k4DIsland1ToMountain0}, + {k4DDesert1ToIsland0, 0xffffffff, k4DDesert1ToMountain0}, + {k4DMountain1ToIsland0, k4DMountain1ToDesert0, 0xffffffff} +}; + +tExtraID s_transitionExtras2[3][3] = { + {0xffffffff, k4DIsland2ToDesert0, k4DIsland2ToMountain0}, + {k4DDesert2ToIsland0, 0xffffffff, k4DDesert2ToMountain0}, + {k4DMountain2ToIsland0, k4DMountain2ToDesert0, 0xffffffff} +}; + +tExtraID s_shutDownExtras[3][3] = { + {0xffffffff, k4DIsland1ToIsland0, k4DIsland2ToIsland0}, + {k4DDesert0ToIsland0, k4DDesert1ToIsland0, k4DDesert2ToIsland0}, + {k4DMountain0ToIsland0, k4DMountain1ToIsland0, k4DMountain2ToIsland0} +}; + +Caldoria4DSystem::Caldoria4DSystem(Neighborhood *owner) : GameInteraction(kCaldoria4DInteractionID, owner), + _4DSpritesMovie(kCaldoria4DSpritesID) { + g_AIArea->lockAIOut(); +} + +Caldoria4DSystem::~Caldoria4DSystem() { + g_AIArea->unlockAI(); +} + +void Caldoria4DSystem::openInteraction() { + _whichMenu = k4DVideoMenu; + _videoChoice = k4DIslandChoice; + _audioChoice = k4DRockChoice; + _clickedHotspotID = kNoHotSpotID; + + _4DSpritesMovie.initFromMovieFile("Images/Caldoria/4D Sprites", true); + _4DSpritesMovie.moveElementTo(kCaldoria4DSpritesLeft, kCaldoria4DSpritesTop); + _4DSpritesMovie.setDisplayOrder(k4DSpritesOrder); + _4DSpritesMovie.startDisplaying(); + _4DSpritesMovie.show(); + + _4DSpritesScale = _4DSpritesMovie.getScale(); + + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + + startIdling(); +} + +void Caldoria4DSystem::loopExtra(const tExtraID extraID) { + ExtraTable::Entry extraEntry; + + _owner->getExtraEntry(extraID, extraEntry); + _loopStart = extraEntry.movieStart; + _owner->loopExtraSequence(extraID); +} + +void Caldoria4DSystem::useIdleTime() { + if (_whichMenu == k4DShuttingDown) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + tExtraID extraID; + + if (movieTime < kSwitchable1Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][0]; + else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][1]; + else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][2]; + else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } else if (_clickedHotspotID != kNoHotSpotID) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + tExtraID extraID; + + if (movieTime < kSwitchable1Stop) { + extraID = s_transitionExtras0[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) { + extraID = s_transitionExtras1[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) { + extraID = s_transitionExtras2[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + switch (extraID) { + case k4DDesert0ToIsland0: + case k4DMountain0ToIsland0: + case k4DDesert1ToIsland0: + case k4DMountain1ToIsland0: + case k4DDesert2ToIsland0: + case k4DMountain2ToIsland0: + _videoChoice = k4DIslandChoice; + break; + case k4DIsland0ToDesert0: + case k4DMountain0ToDesert0: + case k4DIsland1ToDesert0: + case k4DMountain1ToDesert0: + case k4DIsland2ToDesert0: + case k4DMountain2ToDesert0: + _videoChoice = k4DDesertChoice; + break; + case k4DDesert0ToMountain0: + case k4DIsland0ToMountain0: + case k4DIsland1ToMountain0: + case k4DDesert1ToMountain0: + case k4DIsland2ToMountain0: + case k4DDesert2ToMountain0: + _videoChoice = k4DMountainChoice; + break; + } + + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +void Caldoria4DSystem::initInteraction() { + setSpritesMovie(); + + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + loopExtra(k4DIslandLoop); +} + +void Caldoria4DSystem::closeInteraction() { + stopIdling(); + _neighborhoodNotification->cancelNotification(this); + _4DSpritesMovie.releaseMovie(); + _owner->loadAmbientLoops(); +} + +void Caldoria4DSystem::setSpritesMovie() { + if (_whichMenu == k4DShuttingDown) + _4DSpritesMovie.setTime(_4DSpritesScale * k4DIslandChoice); + else if (_whichMenu == k4DVideoMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _videoChoice); + else if (_whichMenu == k4DAudioMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _audioChoice); + _4DSpritesMovie.triggerRedraw(); +} + +void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.downButtonAnyDown()) + return; + if (input.anyDirectionInput()) + shutDown4DSystem(); + else + GameInteraction::handleInput(input, cursorSpot); +} + +void Caldoria4DSystem::activateHotspots() { + GameInteraction::activateHotspots(); + if (_whichMenu == k4DAudioMenu) + g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID); +} + +void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DVisualSpotID: + if (_whichMenu == k4DAudioMenu) { + _whichMenu = k4DVideoMenu; + setSpritesMovie(); + } + break; + case kCa4DAudioSpotID: + if (_whichMenu == k4DVideoMenu) { + _whichMenu = k4DAudioMenu; + setSpritesMovie(); + } + break; + case kCa4DChoice1SpotID: + if (_whichMenu == k4DVideoMenu) + makeIslandChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRockChoice(); + break; + case kCa4DChoice2SpotID: + if (_whichMenu == k4DVideoMenu) + makeDesertChoice(); + else if (_whichMenu == k4DAudioMenu) + makeOrchestralChoice(); + break; + case kCa4DChoice3SpotID: + if (_whichMenu == k4DVideoMenu) + makeMountainChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRhythmsChoice(); + break; + case kCa4DChoice4SpotID: + if (_whichMenu == k4DAudioMenu) + makeAcousticChoice(); + else + _owner->playSpotSoundSync(kCaldoria4DBlankChoiceIn, kCaldoria4DBlankChoiceOut); + break; + default: + GameInteraction::clickInHotspot(input, spot); + } +} + +void Caldoria4DSystem::receiveNotification(Notification *, const tNotificationFlags) { + if (_whichMenu == k4DShuttingDown) { + _owner->requestDeleteCurrentInteraction(); + } else { + uint32 extraID; + + switch (_videoChoice) { + case k4DIslandChoice: + extraID = k4DIslandLoop; + break; + case k4DDesertChoice: + extraID = k4DDesertLoop; + break; + case k4DMountainChoice: + extraID = k4DMountainLoop; + break; + default: + extraID = 0xffffffff; + break; + } + + if (extraID != 0xffffffff) + loopExtra(extraID); + } +} + +void Caldoria4DSystem::makeIslandChoice() { + if (_videoChoice != k4DIslandChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice1SpotID; +} + +void Caldoria4DSystem::makeDesertChoice() { + if (_videoChoice != k4DDesertChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice2SpotID; +} + +void Caldoria4DSystem::makeMountainChoice() { + if (_videoChoice != k4DMountainChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice3SpotID; +} + +void Caldoria4DSystem::makeRockChoice() { + if (_audioChoice != k4DRockChoice) { + _audioChoice = k4DRockChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + } +} + +void Caldoria4DSystem::makeOrchestralChoice() { + if (_audioChoice != k4DOrchestralChoice) { + _audioChoice = k4DOrchestralChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff"); + } +} + +void Caldoria4DSystem::makeRhythmsChoice() { + if (_audioChoice != k4DRhythmsChoice) { + _audioChoice = k4DRhythmsChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff"); + } +} + +void Caldoria4DSystem::makeAcousticChoice() { + if (_audioChoice != k4DAcousticChoice) { + _audioChoice = k4DAcousticChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff"); + } +} + +void Caldoria4DSystem::shutDown4DSystem() { + _whichMenu = k4DShuttingDown; + +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h new file mode 100755 index 0000000000..e0217918fa --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h @@ -0,0 +1,78 @@ +/* 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_CALDORIA_CALDORIA4DSYSTEM_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class Caldoria4DSystem : public GameInteraction, private Idler, public NotificationReceiver { +public: + Caldoria4DSystem(Neighborhood *); + virtual ~Caldoria4DSystem(); + + void shutDown4DSystem(); + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const tNotificationFlags); + void setSpritesMovie(); + void makeIslandChoice(); + void makeRockChoice(); + void makeMountainChoice(); + void makeOrchestralChoice(); + void makeDesertChoice(); + void makeRhythmsChoice(); + void makeAcousticChoice(); + + void useIdleTime(); + void loopExtra(const tExtraID); + + Movie _4DSpritesMovie; + TimeScale _4DSpritesScale; + uint _whichMenu; + uint _videoChoice; + uint _audioChoice; + Notification *_neighborhoodNotification; + TimeValue _loopStart; + tHotSpotID _clickedHotspotID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp new file mode 100755 index 0000000000..bd5480de0e --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp @@ -0,0 +1,115 @@ +/* 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/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" + +namespace Pegasus { + +const tNotificationFlags kMessageDoneFlag = 1; + +CaldoriaMessages::CaldoriaMessages(Neighborhood *owner, const tNotificationID id, NotificationManager *manager) : + GameInteraction(kCaldoriaMessagesInteractionID, owner), Notification(id, manager), _messageMovie(kCaldoriaMessagesID) { +} + +void CaldoriaMessages::openInteraction() { + _neighborhoodNotification = GameInteraction::_owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + _messageCallBack.setNotification(this); + notifyMe(this, kMessageDoneFlag, kMessageDoneFlag); + _messageCallBack.setCallBackFlag(kMessageDoneFlag); + _messageNumber = 1; +} + +void CaldoriaMessages::initInteraction() { + GameInteraction::_owner->startExtraSequence(kCaBedroomVidPhone, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMessages::closeInteraction() { + cancelNotification(this); + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMessages::receiveNotification(Notification *notification, const tNotificationFlags) { + if (notification == _neighborhoodNotification) { + switch (GameInteraction::_owner->getLastExtra()) { + case kCaBedroomVidPhone: + GameInteraction::_owner->showExtraView(kCaBedroomMessage1); + break; + case kCaBedroomMessage1: + play1Message(1); + break; + case kCaBedroomMessage2: + play1Message(2); + break; + } + } else { + _messageCallBack.releaseCallBack(); + _messageMovie.releaseMovie(); + + uint32 extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->showExtraView(extraID); + allowInput(true); + } +} + +void CaldoriaMessages::clickInHotspot(const Input &input, const Hotspot *spot) { + uint32 extraID; + + switch (spot->getObjectID()) { + case kCaBedroomVidPhoneActivationSpotID: + extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMessages::play1Message(uint messageNumber) { + if (messageNumber == 1) { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVA.movie"); + _messageNumber = 2; + } else { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVB.movie"); + _messageNumber = 1; + GameState.setCaldoriaSeenMessages(true); + } + + _messageMovie.moveElementTo(kCaldoriaMessageLeft, kCaldoriaMessageTop); + _messageMovie.setDisplayOrder(kCaldoriaMessagesOrder); + _messageMovie.startDisplaying(); + _messageCallBack.initCallBack(&_messageMovie, kCallBackAtExtremes); + _messageCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + allowInput(false); + _messageMovie.show(); + _messageMovie.redrawMovieWorld(); + _messageMovie.start(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.h b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h new file mode 100755 index 0000000000..1ef0ab0692 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h @@ -0,0 +1,60 @@ +/* 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_CALDORIA_CALDORIAMESSAGES_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H + +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class CaldoriaMessages : public GameInteraction, public Notification, public NotificationReceiver { +public: + CaldoriaMessages(Neighborhood *, const tNotificationID, NotificationManager *); + virtual ~CaldoriaMessages() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + void receiveNotification(Notification *, const tNotificationFlags); + void clickInHotspot(const Input &, const Hotspot *); + void play1Message(uint); + + Movie _messageMovie; + NotificationCallBack _messageCallBack; + Notification *_neighborhoodNotification; + uint _messageNumber; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp new file mode 100755 index 0000000000..4d6dc6b758 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp @@ -0,0 +1,134 @@ +/* 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/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" + +namespace Pegasus { + +CaldoriaMirror::CaldoriaMirror(Neighborhood *owner) : GameInteraction(kCaldoriaMirrorInteractionID, owner) { +} + +void CaldoriaMirror::openInteraction() { + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaMirror::initInteraction() { + _owner->setCurrentActivation(kActivateMirrorReady); + _owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMirror::closeInteraction() { + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMirror::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_owner->getLastExtra() == (uint32)kCaBathroomAgencyStandard || !input.anyDirectionInput()) + GameInteraction::handleInput(input, cursorSpot); +} + +void CaldoriaMirror::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + case kCaBathroomBodyFat: + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + g_allHotspots.activateOneHotspot(kCaBathroomMirrorSpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle3SpotID); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + g_allHotspots.activateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle3SpotID); + g_allHotspots.deactivateOneHotspot(kCaBathroomMirrorSpotID); + break; + } +} + +void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCaBathroomMirrorSpotID: + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + _owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomBodyFat: + _owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomRetrothrash: + _owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomGeoWave: + _owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kCaHairStyle1SpotID: + _owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle2SpotID: + _owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle3SpotID: + _owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMirror::receiveNotification(Notification *, const tNotificationFlags) { + switch (_owner->getLastExtra()) { + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + _owner->setCurrentActivation(kActivateMirrorReady); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + _owner->setCurrentActivation(kActivateStylistReady); + break; + case kCaBathroomAgencyStandard: + _owner->setCurrentActivation(kActivateHotSpotAlways); + _owner->requestDeleteCurrentInteraction(); + GameState.setScoringFixedHair(true); + GameState.setCaldoriaDoneHygiene(true); + break; + } + + allowInput(true); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.h b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h new file mode 100755 index 0000000000..6b4339e69f --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h @@ -0,0 +1,54 @@ +/* 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_CALDORIA_CALDORIAMIRROR_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class CaldoriaMirror : public GameInteraction, public NotificationReceiver { +public: + CaldoriaMirror(Neighborhood *); + virtual ~CaldoriaMirror() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const tNotificationFlags); + + Notification *_neighborhoodNotification; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/door.cpp b/engines/pegasus/neighborhood/door.cpp new file mode 100755 index 0000000000..82c83f19b1 --- /dev/null +++ b/engines/pegasus/neighborhood/door.cpp @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 "common/textconsole.h" + +#include "pegasus/neighborhood/door.h" + +namespace Pegasus { + +void DoorTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].flags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Door[%d]: %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, + _entries[i].flags); + } +} + +void DoorTable::clear() { + _entries.clear(); +} + +DoorTable::Entry DoorTable::findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/door.h b/engines/pegasus/neighborhood/door.h new file mode 100755 index 0000000000..c670c6b956 --- /dev/null +++ b/engines/pegasus/neighborhood/door.h @@ -0,0 +1,90 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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_DOOR_H +#define PEGASUS_NEIGHBORHOOD_DOOR_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef tMM8BitFlags tDoorFlags; + +enum { + kDoorPresentBit, // Bit set if there is a door here. + kDoorLockedBit // Bit set if door is locked, clear if unlocked. +}; + +const tDoorFlags kNoDoorFlags = 0; +const tDoorFlags kDoorPresentMask = 1 << kDoorPresentBit; +const tDoorFlags kDoorLockedMask = 1 << kDoorLockedBit; + +class DoorTable { +public: + DoorTable() {} + ~DoorTable() {} + + static const uint32 getResTag() { return MKTAG('D', 'o', 'o', 'r'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + flags = kNoDoorFlags; + } + + tRoomID room; + tDirectionConstant direction; + tAlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + tDoorFlags flags; + }; + + Entry findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/neighborhood/exit.cpp b/engines/pegasus/neighborhood/exit.cpp new file mode 100755 index 0000000000..0ef12cc7e3 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.cpp @@ -0,0 +1,70 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/exit.h" + +namespace Pegasus { + +void ExitTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].exitEnd = stream->readUint32BE(); + _entries[i].exitLoop = stream->readUint32BE(); + _entries[i].exitRoom = stream->readUint16BE(); + _entries[i].exitDirection = stream->readByte(); + stream->readByte(); // alignment + + _entries[i].originalEnd = _entries[i].exitEnd; + + debug(0, "Exit[%d]: %d %d %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, _entries[i].exitEnd, + _entries[i].exitLoop, _entries[i].exitRoom, _entries[i].exitDirection); + } +} + +void ExitTable::clear() { + _entries.clear(); +} + +ExitTable::Entry ExitTable::findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/exit.h b/engines/pegasus/neighborhood/exit.h new file mode 100755 index 0000000000..d3e8d50446 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.h @@ -0,0 +1,93 @@ +/* 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_EXIT_H +#define PEGASUS_NEIGHBORHOOD_EXIT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExitTable { +public: + ExitTable() {} + ~ExitTable() {} + + static const uint32 getResTag() { return MKTAG('E', 'x', 'i', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + exitEnd = 0xffffffff; + originalEnd = 0xffffffff; + exitLoop = 0xffffffff; + exitRoom = kNoRoomID; + exitDirection = kNoDirection; + } + + tRoomID room; + tDirectionConstant direction; + tAlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + // fExitEnd is the end of the optimized run of walks. + TimeValue exitEnd; + TimeValue originalEnd; + // fExitLoop is the loop start time of the optimized run of walks if the run + // loops back on itself (so far, only in TSA). + TimeValue exitLoop; + tRoomID exitRoom; + tDirectionConstant exitDirection; + }; + + Entry findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/extra.cpp b/engines/pegasus/neighborhood/extra.cpp new file mode 100755 index 0000000000..44235ffa96 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.cpp @@ -0,0 +1,58 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/extra.h" + +namespace Pegasus { + +void ExtraTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].extra = stream->readUint32BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + debug(0, "Extra[%d]: %d %d %d", i, _entries[i].extra, _entries[i].movieStart, _entries[i].movieEnd); + } +} + +void ExtraTable::clear() { + _entries.clear(); +} + +ExtraTable::Entry ExtraTable::findEntry(tExtraID extra) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].extra == extra) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/extra.h b/engines/pegasus/neighborhood/extra.h new file mode 100755 index 0000000000..3320f51289 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.h @@ -0,0 +1,67 @@ +/* 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_EXTRA_H +#define PEGASUS_NEIGHBORHOOD_EXTRA_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExtraTable { +public: + ExtraTable() {} + ~ExtraTable() {} + + static const uint32 getResTag() { return MKTAG('X', 't', 'r', 'a'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { movieStart = 0xffffffff; } + bool isEmpty() { return movieStart == 0xffffffff; } + + tExtraID extra; + TimeValue movieStart; + TimeValue movieEnd; + }; + + Entry findEntry(tExtraID extra); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/hotspotinfo.cpp b/engines/pegasus/neighborhood/hotspotinfo.cpp new file mode 100755 index 0000000000..1cd241612c --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.cpp @@ -0,0 +1,65 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/hotspotinfo.h" + +namespace Pegasus { + +void HotspotInfoTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].hotspotActivation = stream->readSByte(); + stream->readByte(); // alignment + _entries[i].hotspotRoom = stream->readUint16BE(); + _entries[i].hotspotDirection = stream->readByte(); + stream->readByte(); // alignment + _entries[i].hotspotExtra = stream->readUint32BE(); + _entries[i].hotspotItem = stream->readUint16BE(); + debug(0, "Hotspot[%d]: %d %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].hotspotActivation, + _entries[i].hotspotRoom, _entries[i].hotspotDirection, _entries[i].hotspotExtra, + _entries[i].hotspotItem); + } +} + +void HotspotInfoTable::clear() { + _entries.clear(); +} + +HotspotInfoTable::Entry HotspotInfoTable::findEntry(tHotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/hotspotinfo.h b/engines/pegasus/neighborhood/hotspotinfo.h new file mode 100755 index 0000000000..3c958c9e35 --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.h @@ -0,0 +1,77 @@ +/* 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_HOTSPOTINFO_H +#define PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class HotspotInfoTable { +public: + HotspotInfoTable() {} + ~HotspotInfoTable() {} + + static const uint32 getResTag() { return MKTAG('H', 'S', 'I', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { hotspotRoom = kNoRoomID; } + bool isEmpty() { return hotspotRoom == kNoRoomID; } + + tHotSpotID hotspot; + tHotSpotActivationID hotspotActivation; + // Location hot spot lives in: + tRoomID hotspotRoom; + tDirectionConstant hotspotDirection; + // Extra to play if this is a "play extra" hot spot. + tExtraID hotspotExtra; + // Item corresponding to this hot spot if it is an item-related hot spot. + tItemID hotspotItem; + }; + + Entry findEntry(tHotSpotID hotspot); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp new file mode 100644 index 0000000000..e17e1008e6 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.cpp @@ -0,0 +1,1759 @@ +/* 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/input.h" +#include "pegasus/interaction.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.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, tNeighborhoodID 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++) + g_allHotspots.remove(*it); + + _neighborhoodHotspots.deleteHotspots(); + g_neighborhood = 0; +} + +void Neighborhood::init() { + _neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags); + _navMovieCallBack.setNotification(&_neighborhoodNotification); + _turnPushCallBack.setNotification(&_neighborhoodNotification); + _delayCallBack.setNotification(&_neighborhoodNotification); + + // TODO + //_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); + + // TODO + //_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 tNotificationFlags 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(tRoomID room, tDirectionConstant direction) { + // TODO: Map + + 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 tRoomID room, const tDirectionConstant direction, ExitTable::Entry &entry) { + entry = _exitTable.findEntry(room, direction, _currentAlternate); + + if (entry.isEmpty()) + entry = _exitTable.findEntry(room, direction, kNoAlternateID); +} + +TimeValue Neighborhood::getViewTime(const tRoomID room, const tDirectionConstant 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 tRoomID room, const tDirectionConstant direction, DoorTable::Entry &doorEntry) { + doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + + if (doorEntry.isEmpty()) + doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); +} + +tDirectionConstant Neighborhood::getTurnEntry(const tRoomID room, const tDirectionConstant direction, const tTurnDirection 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 tRoomID room, const tDirectionConstant direction, tSpotFlags 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 tHotSpotID id, ZoomTable::Entry &zoomEntry) { + zoomEntry = _zoomTable.findEntry(id); +} + +void Neighborhood::getHotspotEntry(const tHotSpotID 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 + +tCanMoveForwardReason 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; +} + +tCanTurnReason Neighborhood::canTurn(tTurnDirection turnDirection, tDirectionConstant &nextDir) { + nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection); + + if (nextDir == kNoDirection) + return kCantTurnNoTurn; + + return kCanTurn; +} + +tCanOpenDoorReason 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); + + g_allHotspots.push_back(hotspot); + _neighborhoodHotspots.push_back(hotspot); + } + + delete hotspotList; +} + +void Neighborhood::popActionQueue() { + if (!_actionQueue.empty()) { + tQueueRequest 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()) { + tQueueRequest &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; + // TODO: stop trigger + 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 tQueueRequestType requestType, const tExtraID extra, const TimeValue in, const TimeValue out, + const tInputBits interruptionFilter, const tNotificationFlags flags) { + + tQueueRequest 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 tExtraID whichExtra, const tNotificationFlags flags, const tInputBits interruptionFilter) { + requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags); +} + +void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const tInputBits interruptionFilter, const tNotificationFlags 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()) { + _vm->refreshDisplay(); + _vm->checkNotifications(); + _vm->_system->delayMillis(10); + } + + _spotSounds.stopSound(); + _spotSounds.playSoundSegment(in, out); + + while (_spotSounds.isPlaying()) { + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } +} + +void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const tInputBits interruptionFilter, const tNotificationFlags flags) { + requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags); +} + +bool operator==(const tQueueRequest &arg1, const tQueueRequest &arg2) { + return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra && + arg1.start == arg2.start && arg1.stop == arg2.stop; +} + +bool operator!=(const tQueueRequest &arg1, const tQueueRequest &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); +} + +tAirQuality Neighborhood::getAirQuality(const tRoomID) { + 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; + + // TODO: Map + 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 tRoomID, const tDirectionConstant 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(tNotificationFlags flags) { + _navMovieCallBack.cancelCallBack(); + + if (flags != 0) { + _navMovieCallBack.setCallBackFlag(flags); + _navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } +} + +void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, tNotificationFlags flags) { + _stridingCallBack.cancelCallBack(); + + if (flags != 0) + _stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale()); +} + +void Neighborhood::moveNavTo(const tCoordType h, const tCoordType v) { + tCoordType oldH, oldV; + _navMovie.getLocation(oldH, oldV); + + tCoordType offH = h - oldH; + tCoordType 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 = g_allHotspots.findHotspotByID(entry.hotspot); + if (hotspot) + activateOneHotspot(entry, hotspot); + } + } +} + +void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + tHotSpotFlags flags = clickedSpot->getHotspotFlags(); + + if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) { + tItemID 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 = g_allItems.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(tCanMoveForwardReason reason) { + switch (reason) { + case kCantMoveDoorClosed: + case kCantMoveDoorLocked: + openDoor(); + break; + case kCantMoveBlocked: + zoomUpOrBump(); + break; + default: + bumpIntoWall(); + break; + } +} + +void Neighborhood::cantOpenDoor(tCanOpenDoorReason) { + bumpIntoWall(); +} + +void Neighborhood::turnTo(const tDirectionConstant direction) { + // TODO: Map + + _pushIn.copyToCurrentPort(); + + // 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(); + + // TODO: Map + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Neighborhood::moveForward() { + ExitTable::Entry exitEntry; + tCanMoveForwardReason moveReason = canMoveForward(exitEntry); + + if (moveReason == kCanMoveForward) + startExitMovie(exitEntry); + else + cantMoveThatWay(moveReason); +} + +void Neighborhood::turn(const tTurnDirection turnDirection) { + tDirectionConstant nextDir; + tCanTurnReason 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; + tCanOpenDoorReason 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, tNotificationFlags 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 tExtraID extraID, const tNotificationFlags flags, const tInputBits interruptionFilter) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieStart != 0xffffffff) + playExtraMovie(entry, flags, interruptionFilter); +} + +bool Neighborhood::startExtraSequenceSync(const tExtraID extraID, const tInputBits interruptionFilter) { + InputHandler::getCurrentInputDevice()->waitInput(interruptionFilter); + return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter); +} + +void Neighborhood::loopExtraSequence(const uint32 extraID, tNotificationFlags 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(tExtraID extra, tDeathReason deathReason) { + _extraDeathReason = deathReason; + startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput); +} + +void Neighborhood::die(const tDeathReason 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 tRoomID room, const tDirectionConstant direction, const tAlternateID 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 tRoomID room, const tDirectionConstant direction, const tAlternateID 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 tHotSpotID 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 tTurnDirection turnDirection, const TimeValue newView, const tDirectionConstant 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 { + _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 tNotificationFlags flags, const tInputBits 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 tRoomID room, const tDirectionConstant direction, tSpotFlags 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 = g_allItems.findItemByID(entry.hotspotItem); + if (item && item->getItemNeighborhood() == getObjectID()) + hotspot->setActive(); + } else { + tHotSpotFlags 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, tNotificationFlags flags, bool loopSequence, + const tInputBits 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++) + g_allHotspots.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 tExtraID 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 tInputBits interruptionFilter) { + Input input; + bool result = true; + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + while (movie->isRunning()) { + InputHandler::getCurrentInputDevice()->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; +} + +tInputBits 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 (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 tInteractionID interactionID) { + if (interactionID == kNoInteractionID) + return 0; + + return new GameInteraction(interactionID, this); +} + +void Neighborhood::newInteraction(const tInteractionID 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() { + // TODO + warning("bump"); +} + +void Neighborhood::zoomUpOrBump() { + Hotspot *zoomSpot = 0; + + for (HotspotList::iterator it = g_allHotspots.begin(); it != g_allHotspots.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(); + _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, tNotificationFlags flags, + const tInputBits 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, tCoordType left, tCoordType top) { + if (_croppedMovie.isMovieValid()) + closeCroppedMovie(); + + _croppedMovie.initFromMovieFile(movieName); + _croppedMovie.moveElementTo(left, top); + _croppedMovie.startDisplaying(); + _croppedMovie.show(); +} + +void Neighborhood::loopCroppedMovie(const Common::String &movieName, tCoordType left, tCoordType top) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.setFlags(kLoopTimeBase); + _croppedMovie.start(); +} + +void Neighborhood::closeCroppedMovie() { + _croppedMovie.releaseMovie(); +} + +void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, tCoordType left, tCoordType top, const tInputBits interruptionFilter) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.start(); + + tInputBits 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(); + InputHandler::getCurrentInputDevice()->getInput(input, interruptionFilter); + if (input.anyInput() || _vm->shouldQuit()) // TODO: Save/Load request + 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()) { + _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 tHotSpotID id, const tHotSpotFlags flags) { + Hotspot *hotspot = g_allHotspots.findHotspotByID(id); + hotspot->setMaskedHotspotFlags(flags, flags); +} + +void Neighborhood::setIsItemTaken(const tItemID 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()) { + g_allHotspots.deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + for (HotspotList::iterator it = g_allHotspots.begin(); it != g_allHotspots.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, tDisplayOrder order, tCoordType left, tCoordType 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, tDisplayOrder order, tCoordType left, tCoordType top, bool show) { + movie->initFromMovieFile(movieName); + movie->setDisplayOrder(order); + movie->moveElementTo(left, top); + movie->startDisplaying(); + + if (show) + movie->show(); + + movie->redrawMovieWorld(); +} + +void Neighborhood::reinstateMonocleInterface() { + // TODO: Disable erase? + + _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 timerFunction(FunctionPtr *, void *neighborhood) { + ((Neighborhood *)neighborhood)->timerExpired(((Neighborhood *)neighborhood)->getTimerEvent()); +} + +void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) { + _eventTimer.stopFuse(); + _eventTimer.primeFuse(time, scale); + _timerEvent = eventType; + _eventTimer.setFunctionPtr(&timerFunction, this); + _eventTimer.lightFuse(); +} + +void Neighborhood::cancelEvent() { + _eventTimer.stopFuse(); +} + +void Neighborhood::pauseTimer() { + _eventTimer.pauseFuse(); +} + +void Neighborhood::resumeTimer() { + // NOTE: Yes, this function calls pauseFuse! + // Looks like an original game bug, will need + // to investigate how this affects gameplay. + _eventTimer.pauseFuse(); +} + +bool Neighborhood::timerPaused() { + return _eventTimer.isFusePaused(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h new file mode 100644 index 0000000000..e1aa814864 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.h @@ -0,0 +1,408 @@ +/* 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_H +#define PEGASUS_NEIGHBORHOOD_H + +#include "common/queue.h" +#include "common/str.h" + +#include "pegasus/fader.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/sound.h" +#include "pegasus/timers.h" +#include "pegasus/transition.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/door.h" +#include "pegasus/neighborhood/exit.h" +#include "pegasus/neighborhood/extra.h" +#include "pegasus/neighborhood/hotspotinfo.h" +#include "pegasus/neighborhood/spot.h" +#include "pegasus/neighborhood/turn.h" +#include "pegasus/neighborhood/view.h" +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +class PegasusEngine; + +// Pegasus Prime neighborhood id's +const tNeighborhoodID kCaldoriaID = 0; +const tNeighborhoodID kFullTSAID = 1; +const tNeighborhoodID kFinalTSAID = 2; +const tNeighborhoodID kTinyTSAID = 3; +const tNeighborhoodID kPrehistoricID = 4; +const tNeighborhoodID kMarsID = 5; +const tNeighborhoodID kWSCID = 6; +const tNeighborhoodID kNoradAlphaID = 7; +const tNeighborhoodID kNoradDeltaID = 8; +// The sub chase is not really a neighborhood, but we define a constant that is used +// to allow an easy transition out of Norad Alpha. +const tNeighborhoodID kNoradSubChaseID = 1000; + +const TimeScale kDefaultLoopFadeScale = kThirtyTicksPerSecond; +const TimeValue kDefaultLoopFadeOut = kHalfSecondPerThirtyTicks; +const TimeValue kDefaultLoopFadeIn = kHalfSecondPerThirtyTicks; + +enum tQueueRequestType { + kNavExtraRequest, + kSpotSoundRequest, + kDelayRequest +}; + +// For delay requests, start is interpreted as the total delay and stop is interpreted +// as the scale the delay is in. +// For extra requests, start and stop are not used. +struct tQueueRequest { + tQueueRequestType requestType; + tExtraID extra; + TimeValue start, stop; + tInputBits interruptionFilter; + bool playing; + tNotificationFlags flags; + Notification *notification; +}; + +bool operator==(const tQueueRequest &arg1, const tQueueRequest &arg2); +bool operator!=(const tQueueRequest &arg1, const tQueueRequest &arg2); + +class GameInteraction; +class Item; +class Neighborhood; + +class StriderCallBack : public TimeBaseCallBack { +public: + StriderCallBack(Neighborhood *); + virtual ~StriderCallBack() {} + +protected: + virtual void callBack(); + + Neighborhood *_neighborhood; +}; + +typedef Common::Queue<tQueueRequest> NeighborhoodActionQueue; + +class Neighborhood : public IDObject, public NotificationReceiver, public InputHandler, public Idler { +friend class StriderCallBack; +friend void timerFunction(FunctionPtr *, void *); + +public: + Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, tNeighborhoodID id); + virtual ~Neighborhood(); + + virtual void init(); + virtual void start(); + virtual void moveNavTo(const tCoordType, const tCoordType); + virtual void checkContinuePoint(const tRoomID, const tDirectionConstant) = 0; + void makeContinuePoint(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + tCanMoveForwardReason canMoveForward(ExitTable::Entry &entry); + tCanTurnReason canTurn(tTurnDirection turn, tDirectionConstant &nextDir); + tCanOpenDoorReason canOpenDoor(DoorTable::Entry &entry); + + virtual void cantMoveThatWay(tCanMoveForwardReason); + virtual void cantTurnThatWay(tCanTurnReason) {} + virtual void cantOpenDoor(tCanOpenDoorReason); + virtual void arriveAt(tRoomID room, tDirectionConstant direction); + virtual void turnTo(const tDirectionConstant); + virtual void spotCompleted(); + virtual void doorOpened(); + virtual void closeDoorOffScreen(const tRoomID, const tDirectionConstant) {} + + virtual void moveForward(); + virtual void turn(const tTurnDirection); + virtual void turnLeft(); + virtual void turnRight(); + virtual void turnUp(); + virtual void turnDown(); + virtual void openDoor(); + virtual void zoomTo(const Hotspot *); + + virtual void updateViewFrame(); + + void requestExtraSequence(const tExtraID, const tNotificationFlags, const tInputBits interruptionFilter); + void requestSpotSound(const TimeValue, const TimeValue, const tInputBits interruptionFilter, const tNotificationFlags); + void playSpotSoundSync(const TimeValue in, const TimeValue out); + void requestDelay(const TimeValue, const TimeScale, const tInputBits interruptionFilter, const tNotificationFlags); + + Notification *getNeighborhoodNotification() { return &_neighborhoodNotification; } + + virtual void getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry); + virtual void startSpotLoop(TimeValue, TimeValue, tNotificationFlags = 0); + virtual bool actionQueueEmpty() { return _actionQueue.empty(); } + virtual void showViewFrame(TimeValue); + virtual void findSpotEntry(const tRoomID room, const tDirectionConstant direction, tSpotFlags flags, SpotTable::Entry &spotEntry); + virtual void startExtraSequence(const tExtraID, const tNotificationFlags, const tInputBits interruptionFilter); + bool startExtraSequenceSync(const tExtraID, const tInputBits); + virtual void loopExtraSequence(const uint32, tNotificationFlags = 0); + int32 getLastExtra() const { return _lastExtra; } + virtual void scheduleNavCallBack(tNotificationFlags); + + Movie *getNavMovie() { return &_navMovie; } + bool navMoviePlaying(); + + void setCurrentAlternate(const tAlternateID alt) { _currentAlternate = alt; } + tAlternateID getCurrentAlternate() const { return _currentAlternate; } + + void setCurrentActivation(const tHotSpotActivationID a) { _currentActivation = a; } + tHotSpotActivationID getCurrentActivation() { return _currentActivation; } + + virtual void playDeathExtra(tExtraID, tDeathReason); + virtual void die(const tDeathReason); + + virtual void setSoundFXLevel(const uint16); + virtual void setAmbienceLevel(const uint16); + + void forceStridingStop(const tRoomID, const tDirectionConstant, const tAlternateID); + void restoreStriding(const tRoomID, const tDirectionConstant, const tAlternateID); + + HotspotInfoTable::Entry *findHotspotEntry(const tHotSpotID); + + Push *getTurnPush() { return &_turnPush; } + Picture *getTurnPushPicture() { return &_pushIn; } + + void hideNav(); + void showNav(); + + virtual void loadAmbientLoops() {} + + virtual void flushGameState() {} + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + virtual bool canSolve(); + virtual void prepareForAIHint(const Common::String &) {} + virtual void cleanUpAfterAIHint(const Common::String &) {} + virtual void doSolve(); + + virtual bool okayToJump(); + + virtual tAirQuality getAirQuality(const tRoomID); + virtual void checkAirMask() {} + virtual void checkFlashlight() {} + virtual void shieldOn() {} + virtual void shieldOff() {} + + virtual void loadLoopSound1(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + virtual void loadLoopSound2(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + bool loop1Loaded(const Common::String &soundName) { return _loop1SoundString == soundName; } + bool loop2Loaded(const Common::String &soundName) { return _loop2SoundString == soundName; } + void startLoop1Fader(const FaderMoveSpec &); + void startLoop2Fader(const FaderMoveSpec &); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + virtual Hotspot *getItemScreenSpot(Item *, DisplayElement *) { return 0; } + + virtual GameInteraction *makeInteraction(const tInteractionID); + virtual void requestDeleteCurrentInteraction() { _doneWithInteraction = true; } + + virtual uint16 getDateResID() const = 0; + + virtual void showExtraView(uint32); + virtual void startExtraLongSequence(const uint32, const uint32, tNotificationFlags, const tInputBits interruptionFilter); + + void openCroppedMovie(const Common::String &, tCoordType, tCoordType); + void loopCroppedMovie(const Common::String &, tCoordType, tCoordType); + void closeCroppedMovie(); + void playCroppedMovieOnce(const Common::String &, tCoordType, tCoordType, const tInputBits interruptionFilter = kFilterNoInput); + + void playMovieSegment(Movie *, TimeValue = 0, TimeValue = 0xffffffff); + + virtual void recallToTSASuccess(); + virtual void recallToTSAFailure(); + + virtual void pickedUpItem(Item *) {} + + virtual void handleInput(const Input &, const Hotspot *); +protected: + PegasusEngine *_vm; + Common::String _resName; + + virtual Common::String getSoundSpotsName() = 0; + virtual Common::String getNavMovieName() = 0; + + // Notification function. + virtual void receiveNotification(Notification *, const tNotificationFlags); + + // Map info functions. + virtual void getExitEntry(const tRoomID room, const tDirectionConstant direction, ExitTable::Entry &entry); + virtual TimeValue getViewTime(const tRoomID room, const tDirectionConstant direction); + virtual void getDoorEntry(const tRoomID room, const tDirectionConstant direction, DoorTable::Entry &doorEntry); + virtual tDirectionConstant getTurnEntry(const tRoomID room, const tDirectionConstant direction, const tTurnDirection turn); + virtual void getZoomEntry(const tHotSpotID id, ZoomTable::Entry &zoomEntry); + virtual void getHotspotEntry(const tHotSpotID id, HotspotInfoTable::Entry &hotspotEntry); + + // Nav movie sequences. + virtual void startExitMovie(const ExitTable::Entry &); + virtual void keepStriding(ExitTable::Entry &); + virtual void stopStriding(); + virtual void checkStriding(); + virtual bool stillMoveForward(); + virtual void scheduleStridingCallBack(const TimeValue, tNotificationFlags flags); + virtual void startZoomMovie(const ZoomTable::Entry &); + virtual void startDoorOpenMovie(const TimeValue, const TimeValue); + virtual void startTurnPush(const tTurnDirection, const TimeValue, const tDirectionConstant); + virtual void playExtraMovie(const ExtraTable::Entry &, const tNotificationFlags, const tInputBits interruptionFilter); + + virtual void activateCurrentView(const tRoomID, const tDirectionConstant, tSpotFlags); + + virtual void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + + virtual void startSpotOnceOnly(TimeValue, TimeValue); + + virtual void startMovieSequence(const TimeValue, const TimeValue, tNotificationFlags, + bool loopSequence, const tInputBits interruptionFilter, const TimeValue strideStop = 0xffffffff); + + virtual void createNeighborhoodSpots(); + + void resetLastExtra() { _lastExtra = -1; } + + virtual void throwAwayInterface(); + + // Action queue stuff + void popActionQueue(); + void serviceActionQueue(); + void requestAction(const tQueueRequestType, const tExtraID, const TimeValue, const TimeValue, const tInputBits, const tNotificationFlags); + + virtual bool prepareExtraSync(const tExtraID); + virtual bool waitMovieFinish(Movie *, const tInputBits); + + virtual tInputBits getInputFilter(); + + // Misc. + virtual int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant dir); + virtual void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec&); + virtual void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec&); + + virtual void setUpAIRules(); + virtual void setHotspotFlags(const tHotSpotID, const tHotSpotFlags); + virtual void setIsItemTaken(const tItemID); + + virtual void upButton(const Input &); + virtual void leftButton(const Input &); + virtual void rightButton(const Input &); + virtual void downButton(const Input &); + + void initOnePicture(Picture *, const Common::String &, tDisplayOrder, tCoordType, tCoordType, bool); + void initOneMovie(Movie *, const Common::String &, tDisplayOrder, tCoordType, tCoordType, bool); + + void reinstateMonocleInterface(); + + virtual void newInteraction(const tInteractionID); + virtual void useIdleTime(); + virtual void bumpIntoWall(); + virtual void zoomUpOrBump(); + + void scheduleEvent(const TimeValue, const TimeScale, const uint32); + void cancelEvent(); + virtual void timerExpired(const uint32) {} + bool isEventTimerRunning() { return _eventTimer.isFuseLit(); } + uint32 getTimerEvent() { return _timerEvent; } + + void pauseTimer(); + void resumeTimer(); + bool timerPaused(); + + // Navigation Data + DoorTable _doorTable; + ExitTable _exitTable; + ExtraTable _extraTable; + HotspotInfoTable _hotspotInfoTable; + SpotTable _spotTable; + TurnTable _turnTable; + ViewTable _viewTable; + ZoomTable _zoomTable; + tAlternateID _currentAlternate; + tHotSpotActivationID _currentActivation; + + int32 _lastExtra; + tDeathReason _extraDeathReason; + + // Graphics + Movie _navMovie; + Picture _pushIn; + Push _turnPush; + + // Callbacks + Notification _neighborhoodNotification; + NotificationCallBack _navMovieCallBack; + StriderCallBack _stridingCallBack; + NotificationCallBack _turnPushCallBack; + NotificationCallBack _spotSoundCallBack; + NotificationCallBack _delayCallBack; + + // Hotspots + HotspotList _neighborhoodHotspots; + + // Sounds + Sound _spotSounds; + + // Action queue + NeighborhoodActionQueue _actionQueue; + TimeBase _delayTimer; + + // Interruptibility... + tInputBits _interruptionFilter; + + // Nav hiding (for info support...) + bool _isRunning; + + GameInteraction *_currentInteraction; + bool _doneWithInteraction; + Movie _croppedMovie; + + Sound _soundLoop1; + Common::String _loop1SoundString; + SoundFader _loop1Fader; + + Sound _soundLoop2; + Common::String _loop2SoundString; + SoundFader _loop2Fader; + + // The event timer... + FuseFunction _eventTimer; + uint32 _timerEvent; +}; + +extern Neighborhood *g_neighborhood; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp new file mode 100755 index 0000000000..50b6d87e90 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp @@ -0,0 +1,689 @@ +/* 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/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" + +namespace Pegasus { + +static const int16 s_prehistoricCompass[kPrehistoric25 + 1][4] = { + { 0, 170, 90, 270 }, // kPrehistoric01 + { 0, 180, 90, 270 }, // kPrehistoric02 + { 10, 180, 90, 270 }, // kPrehistoric03 + { 10, 190, 90, 270 }, // kPrehistoric04 + { 10, 195, 90, 270 }, // kPrehistoric05 + { 20, 210, 90, 270 }, // kPrehistoric06 + { 20, 200, 130, 276 }, // kPrehistoric07 + { 20, 176, 110, 260 }, // kPrehistoric08 + { 20, 200, 100, 270 }, // kPrehistoric09 + { 14, 186, 100, 280 }, // kPrehistoric10 + { 26, 206, 116, 296 }, // kPrehistoric11 + { 60, 226, 140, 320 }, // kPrehistoric12 + { 0, 180, 80, 270 }, // kPrehistoric13 + { 14, 200, 106, 286 }, // kPrehistoric14 + { -10, 174, 80, 260 }, // kPrehistoric15 + { 54, 236, 140, 210 }, // kPrehistoric16 + { -24, 160, 70, 250 }, // kPrehistoric17 + { 26, 206, 140, 296 }, // kPrehistoric18 + { -16, 160, 70, 250 }, // kPrehistoric19 + { -16, 160, 70, 250 }, // kPrehistoric20 + { -10, 160, 90, 250 }, // kPrehistoric21 + { -20, 160, 70, 244 }, // kPrehistoric22 + { -20, 160, 70, 244 }, // kPrehistoric22North + { 60, 234, 150, 330 }, // kPrehistoric23 + { 50, 230, 140, 320 }, // kPrehistoric24 + { 60, 240, 140, 330 } // kPrehistoric25 +}; + +static const TimeValue kPrehistoricFlashlightClickIn = 0; +static const TimeValue kPrehistoricFlashlightClickOut = 138; + +static const TimeValue kPrehistoricBumpIntoWallIn = 138; +static const TimeValue kPrehistoricBumpIntoWallOut = 291; + +static const TimeValue kBridgeRetractIn = 291; +static const TimeValue kBridgeRetractOut = 1499; + +static const TimeValue kPrehistoricWarningTimeLimit = kTenMinutes; + +Prehistoric::Prehistoric(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Prehistoric", kPrehistoricID) { + setIsItemTaken(kHistoricalLog); +} + +uint16 Prehistoric::getDateResID() const { + return kDatePrehistoricID; +} + +void Prehistoric::init() { + Neighborhood::init(); + + // Forces a stop so the flashlight can turn off... + forceStridingStop(kPrehistoric12, kSouth, kNoAlternateID); +} + +void Prehistoric::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + Neighborhood::start(); +} + +class FinishPrehistoricAction : public AIPlayMessageAction { +public: + FinishPrehistoricAction() : AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false) {} + ~FinishPrehistoricAction() {} + + void performAIAction(AIRule *); + +}; + +void FinishPrehistoricAction::performAIAction(AIRule *rule) { + AIPlayMessageAction::performAIAction(rule); + ((PegasusEngine *)g_engine)->die(kPlayerWonGame); +} + +void Prehistoric::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (_vm->isDemo()) { + FinishPrehistoricAction *doneAction = new FinishPrehistoricAction(); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + AIRule *rule = new AIRule(hasLogCondition, doneAction); + g_AIArea->addAIRule(rule); + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP1NB", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric16, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric08, kEast)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric25, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP16NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric23, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP18NB", false); + AITimerCondition *timerCondition = new AITimerCondition(kPrehistoricWarningTimeLimit, 1, true); + rule = new AIRule(timerCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + rule = new AIRule(hasLogCondition, messageAction); + g_AIArea->addAIRule(rule); + } + } +} + +TimeValue Prehistoric::getViewTime(const tRoomID room, const tDirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + getExtraEntry(kPreArrivalFromTSA, extra); + return extra.movieStart; + } + break; + case MakeRoomView(kPrehistoric25, kEast): + if (_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) { + if (_vm->itemInLocation(kHistoricalLog, kPrehistoricID, kPrehistoric25, kEast)) + extraID = kPre25EastViewWithLog; + else + extraID = kPre25EastViewNoLog; + } + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + + +void Prehistoric::findSpotEntry(const tRoomID room, const tDirectionConstant direction, tSpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric25, kSouth): + entry.clear(); + break; + case MakeRoomView(kPrehistoric01, kEast): + if (GameState.getPrehistoricSeenFlyer1()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer1(true); + break; + case MakeRoomView(kPrehistoric08, kEast): + if (GameState.getPrehistoricSeenFlyer2()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer2(true); + break; + } +} + +int16 Prehistoric::getStaticCompassAngle(const tRoomID room, const tDirectionConstant dir) { + if (room == kPrehistoricDeath) + return g_compass->getFaderValue(); + + return s_prehistoricCompass[room][dir]; +} + +void Prehistoric::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + uint32 angle; + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kPrehistoric01, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 2, -10); + break; + case MakeRoomView(kPrehistoric06, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4, 95); + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4 * 1, 100); + break; + case MakeRoomView(kPrehistoric18, kEast): + if (getCurrentAlternate() == kAltPrehistoricBridgeSet) { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 11, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 26, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 39, 148); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 114, 140); + } else { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 10, 140); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 16, 145); + compassMove.insertFaderKnot(exitEntry.movieEnd, 145); + } + break; + case MakeRoomView(kPrehistoric23, kWest): + angle = compassMove.getNthKnotValue(0); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 17, angle); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 32, angle - 90); + compassMove.insertFaderKnot(exitEntry.movieEnd, angle - 90); + break; + } +} + +void Prehistoric::turnTo(const tDirectionConstant newDirection) { + setCurrentAlternate(kAltPrehistoricNormal); + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, false); + Neighborhood::turnTo(newDirection); + + Item *keyCard; + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric18, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + loadAmbientLoops(); + } + // fall through + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kWest): + keyCard = g_allItems.findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric16, kEast): + case MakeRoomView(kPrehistoric16, kWest): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric21, kSouth): + keyCard = g_allItems.findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + } +} + +void Prehistoric::zoomToVault() { + if (!GameState.getPrehistoricSeenBridgeZoom()) + startExtraSequence(kPre18EastZoom, kExtraCompletedFlag, kFilterNoInput); +} + +void Prehistoric::checkContinuePoint(const tRoomID room, const tDirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric08, kEast): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric25, kNorth): + makeContinuePoint(); + break; + } +} + +void Prehistoric::arriveAt(const tRoomID room, const tDirectionConstant direction) { + Item *keyCard; + + if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) && + _privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + _navMovie.stop(); + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + } + + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoricDeath, kNorth): + case MakeRoomView(kPrehistoricDeath, kSouth): + case MakeRoomView(kPrehistoricDeath, kEast): + case MakeRoomView(kPrehistoricDeath, kWest): + if (GameState.getLastRoom() == kPrehistoric23) + die(kDeathEatenByDinosaur); + else + die(kDeathFallOffCliff); + break; + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + GameState.setPrehistoricTriedToExtendBridge(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); + } + break; + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric16, kNorth): + keyCard = g_allItems.findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric23, kNorth): + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric08, kSouth): + case MakeRoomView(kPrehistoric10, kSouth): + case MakeRoomView(kPrehistoric12, kSouth): + case MakeRoomView(kPrehistoric13, kNorth): + case MakeRoomView(kPrehistoric14, kSouth): + case MakeRoomView(kPrehistoric15, kNorth): + case MakeRoomView(kPrehistoric16, kSouth): + case MakeRoomView(kPrehistoric17, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric19, kNorth): + case MakeRoomView(kPrehistoric20, kNorth): + case MakeRoomView(kPrehistoric21, kEast): + keyCard = g_allItems.findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + } +} + +void Prehistoric::loadAmbientLoops() { + tRoomID room = GameState.getCurrentRoom(); + + switch (room) { + case kPrehistoric02: + // 1/4 volume. + if (GameState.getPrehistoricSeenTimeStream()) + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric01: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric09: + case kPrehistoric11: + case kPrehistoric13: + case kPrehistoric15: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + // 1/4 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric08: + case kPrehistoric10: + case kPrehistoric12: + case kPrehistoric14: + case kPrehistoric16: + case kPrehistoric18: + case kPrehistoric21: + // 3/16 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48); + break; + case kPrehistoric25: + // 1/8 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32); + break; + case kPrehistoric22: + case kPrehistoric22North: + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoricDeath: + // 0 volume. + loadLoopSound1(""); + break; + } + + switch (room) { + case kPrehistoric02: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric08: + case kPrehistoric09: + case kPrehistoric10: + case kPrehistoric11: + case kPrehistoric12: + case kPrehistoric13: + case kPrehistoric14: + case kPrehistoric15: + case kPrehistoric16: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + case kPrehistoric21: + case kPrehistoricDeath: + loadLoopSound2(""); + break; + case kPrehistoric01: + case kPrehistoric25: + loadLoopSound2("Sounds/Prehistoric/VolcLoop.22K.AIFF", 64); + break; + case kPrehistoric18: + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0); + else + loadLoopSound2(""); + break; + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoric22: + case kPrehistoric22North: + loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64); + break; + } +} + +void Prehistoric::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + g_allHotspots.activateOneHotspot(kPre18EastSpotID); + break; + case MakeRoomView(kPrehistoric22North, kNorth): + g_allHotspots.activateOneHotspot(kPre22NorthBreakerSpotID); + break; + } +} + +void Prehistoric::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kPre18EastSpotID: + if (GameState.getPrehistoricBreakerThrown()) + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kPre18EastBridgeOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre22NorthBreakerSpotID: + startExtraSequence(kPre22ThrowBreaker, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Prehistoric::receiveNotification(Notification *notification, const tNotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kPreArrivalFromTSA: + GameState.setPrehistoricSeenTimeStream(true); + loadAmbientLoops(); + makeContinuePoint(); + break; + case kPre18EastZoom: + startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre18EastZoomOut: + GameState.setPrehistoricSeenBridgeZoom(true); + break; + case kPre18EastBridgeOn: + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, true); + setCurrentAlternate(kAltPrehistoricBridgeSet); + GameState.setPrehistoricTriedToExtendBridge(false); + loadAmbientLoops(); + GameState.setScoringExtendedBridge(true); + break; + case kPre18EastBridgeOut: + GameState.setPrehistoricTriedToExtendBridge(true); + if (g_AIArea) + g_AIArea->checkMiddleArea(); + break; + case kPre22ThrowBreaker: + GameState.setPrehistoricBreakerThrown(true); + GameState.setScoringThrewBreaker(true); + break; + case kPre25EastUnlockingVaultNoLog: + case kPre25EastUnlockingVaultWithLog: + _vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kJourneymanKey)); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +Common::String Prehistoric::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) + movieName = "Images/AI/Prehistoric/XPE"; + + return movieName; +} + +Common::String Prehistoric::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + if (!_vm->isDemo()) { + switch (GameState.getCurrentRoom()) { + case kPrehistoric16: + case kPrehistoric23: + case kPrehistoric24: + return "Images/AI/Prehistoric/XP7WB"; + } + } + + return "Images/AI/Prehistoric/XP17NB"; + } + + return movieName; +} + +uint Prehistoric::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!GameState.getPrehistoricBreakerThrown() && GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + numHints = 1; + break; + case MakeRoomView(kPrehistoric25, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Prehistoric::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + return "Images/AI/Prehistoric/XP18WD"; + case MakeRoomView(kPrehistoric25, kEast): + return "Images/AI/Globals/XGLOB1A"; + } + } + + return movieName; +} + +bool Prehistoric::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric18, kEast) && + !GameState.getPrehistoricBreakerThrown() && + GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag); +} + +void Prehistoric::doSolve() { + GameState.setPrehistoricBreakerThrown(true); + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); +} + +Hotspot *Prehistoric::getItemScreenSpot(Item *item, DisplayElement *element) { + if (item->getObjectID() == kHistoricalLog) + return g_allHotspots.findHotspotByID(kPrehistoricHistoricalLogSpotID); + + return Neighborhood::getItemScreenSpot(item, element); +} + +void Prehistoric::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kHistoricalLog: + GameState.setScoringGotHistoricalLog(true); + break; + } + + Neighborhood::pickedUpItem(item); +} + +void Prehistoric::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kJourneymanKey: + Neighborhood::dropItemIntoRoom(item, dropSpot); + + if (GameState.isTakenItemID(kHistoricalLog)) + startExtraLongSequence(kPre25EastUnlockingVaultNoLog, kPre25EastVaultOpenNoLog, kExtraCompletedFlag, kFilterNoInput); + else + startExtraLongSequence(kPre25EastUnlockingVaultWithLog, kPre25EastVaultOpenWithLog, kExtraCompletedFlag, kFilterNoInput); + + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, true); + setCurrentActivation(kActivationVaultOpen); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Prehistoric::bumpIntoWall() { + requestSpotSound(kPrehistoricBumpIntoWallIn, kPrehistoricBumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +Common::String Prehistoric::getNavMovieName() { + return "Images/Prehistoric/Prehistoric.movie"; +} + +Common::String Prehistoric::getSoundSpotsName() { + return "Sounds/Prehistoric/Prehistoric Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h new file mode 100755 index 0000000000..44084f5110 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h @@ -0,0 +1,158 @@ +/* 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_PREHISTORIC_H +#define PEGASUS_NEIGHBORHOOD_PREHISTORIC_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +const TimeScale kPrehistoricMovieScale = 600; +const TimeScale kPrehistoricFramesPerSecond = 15; +const TimeScale kPrehistoricFrameDuration = 40; + +// Alternate IDs. + +const tAlternateID kAltPrehistoricNormal = 0; +const tAlternateID kAltPrehistoricBridgeSet = 1; + +// Room IDs. + +const tRoomID kPrehistoric01 = 0; +const tRoomID kPrehistoric02 = 1; +const tRoomID kPrehistoric03 = 2; +const tRoomID kPrehistoric04 = 3; +const tRoomID kPrehistoric05 = 4; +const tRoomID kPrehistoric06 = 5; +const tRoomID kPrehistoric07 = 6; +const tRoomID kPrehistoric08 = 7; +const tRoomID kPrehistoric09 = 8; +const tRoomID kPrehistoric10 = 9; +const tRoomID kPrehistoric11 = 10; +const tRoomID kPrehistoric12 = 11; +const tRoomID kPrehistoric13 = 12; +const tRoomID kPrehistoric14 = 13; +const tRoomID kPrehistoric15 = 14; +const tRoomID kPrehistoric16 = 15; +const tRoomID kPrehistoric17 = 16; +const tRoomID kPrehistoric18 = 17; +const tRoomID kPrehistoric19 = 18; +const tRoomID kPrehistoric20 = 19; +const tRoomID kPrehistoric21 = 20; +const tRoomID kPrehistoric22 = 21; +const tRoomID kPrehistoric22North = 22; +const tRoomID kPrehistoric23 = 23; +const tRoomID kPrehistoric24 = 24; +const tRoomID kPrehistoric25 = 25; +const tRoomID kPrehistoricDeath = 26; + +// Hot Spot Activation IDs. + +const tHotSpotActivationID kActivationVaultClosed = 1; +const tHotSpotActivationID kActivationVaultOpen = 2; + +// Hot Spot IDs. + +const tHotSpotID kPre18EastSpotID = 5000; +const tHotSpotID kPre22NorthSpotID = 5001; +const tHotSpotID kPre22NorthOutSpotID = 5002; +const tHotSpotID kPre22NorthBreakerSpotID = 5003; +const tHotSpotID kPrehistoricKeyDropSpotID = 5004; +const tHotSpotID kPrehistoricHistoricalLogSpotID = 5005; + +// Extra sequence IDs. + +const tExtraID kPreArrivalFromTSA = 0; +const tExtraID kPre18EastBridgeOut = 1; +const tExtraID kPre18EastBridgeOn = 2; +const tExtraID kPre18EastZoom = 3; +const tExtraID kPre18EastZoomOut = 4; +const tExtraID kPre22ThrowBreaker = 5; +const tExtraID kPre25EastUnlockingVaultWithLog = 6; +const tExtraID kPre25EastVaultOpenWithLog = 7; +const tExtraID kPre25EastViewWithLog = 8; +const tExtraID kPre25EastUnlockingVaultNoLog = 9; +const tExtraID kPre25EastVaultOpenNoLog = 10; +const tExtraID kPre25EastViewNoLog = 11; + +class PegasusEngine; + +class Prehistoric : public Neighborhood { +public: + Prehistoric(InputHandler *, PegasusEngine *); + virtual ~Prehistoric() {} + + virtual uint16 getDateResID() const; + virtual void init(); + + virtual void arriveAt(const tRoomID, const tDirectionConstant); + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void pickedUpItem(Item *); + + void start(); + + void bumpIntoWall(); + + void checkContinuePoint(const tRoomID, const tDirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + enum { + kPrehistoricPrivateVaultOpenFlag, + kPrehistoricPrivateExtendedBridgeFlag, + kNumPrehistoricPrivateFlags + }; + + void setUpAIRules(); + int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void receiveNotification(Notification *, const tNotificationFlags); + void turnTo(const tDirectionConstant); + void zoomToVault(); + TimeValue getViewTime(const tRoomID, const tDirectionConstant); + void findSpotEntry(const tRoomID, const tDirectionConstant, tSpotFlags, SpotTable::Entry &); + + void loadAmbientLoops(); + + FlagsArray<byte, kNumPrehistoricPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/spot.cpp b/engines/pegasus/neighborhood/spot.cpp new file mode 100755 index 0000000000..9ccdfd2de6 --- /dev/null +++ b/engines/pegasus/neighborhood/spot.cpp @@ -0,0 +1,70 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/spot.h" + +namespace Pegasus { + +void SpotTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].srcFlags = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].dstFlags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Spot[%d]: %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].srcFlags, _entries[i].altCode, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].dstFlags); + } +} + +void SpotTable::clear() { + _entries.clear(); +} + +// Two CSpotIndices are equal if +// In addition to having their rooms, directions and alt codes identical... +// They are both either loops or once only animations AND +// They overlap in at least one of the on arrival, on turn and on door open bits. +SpotTable::Entry SpotTable::findEntry(tRoomID room, tDirectionConstant direction, tSpotFlags srcFlags, tAlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode && (_entries[i].srcFlags & kSpotLoopsMask) == (srcFlags & kSpotLoopsMask) && ((_entries[i].srcFlags & srcFlags) & kSpotTriggers) != 0) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/spot.h b/engines/pegasus/neighborhood/spot.h new file mode 100755 index 0000000000..38cc81d581 --- /dev/null +++ b/engines/pegasus/neighborhood/spot.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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_SPOT_H +#define PEGASUS_NEIGHBORHOOD_SPOT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef tMM8BitFlags tSpotFlags; + +enum { + kSpotLoopsBit, // Loop or once only? + kSpotOnArrivalBit, + kSpotOnTurnBit, + kSpotOnDoorOpenBit +}; + +static const tSpotFlags kNoSpotFlags = 0; +static const tSpotFlags kSpotLoopsMask = 1 << kSpotLoopsBit; +static const tSpotFlags kSpotOnArrivalMask = 1 << kSpotOnArrivalBit; +static const tSpotFlags kSpotOnTurnMask = 1 << kSpotOnTurnBit; +static const tSpotFlags kSpotOnDoorOpenMask = 1 << kSpotOnDoorOpenBit; + +static const tSpotFlags kSpotTriggers = kSpotOnArrivalMask | kSpotOnTurnMask | kSpotOnDoorOpenMask; + +class SpotTable { +public: + SpotTable() {} + ~SpotTable() {} + + static const uint32 getResTag() { return MKTAG('S', 'p', 'o', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + srcFlags = kNoSpotFlags; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + dstFlags = kNoSpotFlags; + } + + tRoomID room; + tDirectionConstant direction; + tSpotFlags srcFlags; + tAlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + tSpotFlags dstFlags; + }; + + Entry findEntry(tRoomID room, tDirectionConstant direction, tSpotFlags srcFlags, tAlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp new file mode 100755 index 0000000000..1656e9f068 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -0,0 +1,3016 @@ +/* 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/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" + +namespace Pegasus { + +// TSA PICTs: + +const tResIDType kTBPCloseBoxPICTID = 800; +const tResIDType kTBPRewindPICTID = 801; +const tResIDType kUnresolvedPICTID = 802; +const tResIDType kResolvedPICTID = 803; +const tResIDType kJumpMenuPICTID = 804; +const tResIDType kJumpMenuHilitedPICTID = 805; +const tResIDType kExitPICTID = 806; +const tResIDType kExitHilitedPICTID = 807; +const tResIDType kLeftRipPICTID = 808; +const tResIDType kComparisonCloseBoxPICTID = 809; +const tResIDType kComparisonLeftRewindPICTID = 810; +const tResIDType kComparisonRightRewindPICTID = 811; +const tResIDType kComparisonHiliteNoradPICTID = 812; +const tResIDType kComparisonHiliteMarsPICTID = 813; +const tResIDType kComparisonHiliteCaldoriaPICTID = 814; +const tResIDType kComparisonHiliteWSCPICTID = 815; +const tResIDType kComparisonChancesNoradPICTID = 816; +const tResIDType kComparisonChancesMarsPICTID = 817; +const tResIDType kComparisonChancesCaldoriaPICTID = 818; +const tResIDType kComparisonChancesWSCPICTID = 819; +const tResIDType kRedirectionCCRolloverPICTID = 820; +const tResIDType kRedirectionRRRolloverPICTID = 821; +const tResIDType kRedirectionFDRolloverPICTID = 822; +const tResIDType kRedirectionCCDoorPICTID = 823; +const tResIDType kRedirectionRRDoorPICTID = 824; +const tResIDType kRedirectionFDDoorPICTID = 825; +const tResIDType kRedirectionSecuredPICTID = 826; +const tResIDType kRedirectionNewTargetPICTID = 827; +const tResIDType kRedirectionClosePICTID = 828; + +const short kCompassShift = 15; + +const TimeScale kFullTSAMovieScale = 600; +const TimeScale kFullTSAFramesPerSecond = 15; +const TimeScale kFullTSAFrameDuration = 40; + +// Alternate IDs. + +const tAlternateID kAltTSANormal = 0; +const tAlternateID kAltTSARobotsAtReadyRoom = 1; +const tAlternateID kAltTSARobotsAtFrontDoor = 2; +const tAlternateID kAltTSARedAlert = 3; + +// Room IDs. + +const tRoomID kTSA01 = 1; +const tRoomID kTSA02 = 2; +const tRoomID kTSA03 = 3; +const tRoomID kTSA04 = 4; +const tRoomID kTSA05 = 5; +const tRoomID kTSA0A = 6; +const tRoomID kTSA06 = 7; +const tRoomID kTSA07 = 8; +const tRoomID kTSA08 = 9; +const tRoomID kTSA09 = 10; +const tRoomID kTSA10 = 11; +const tRoomID kTSA11 = 12; +const tRoomID kTSA12 = 13; +const tRoomID kTSA13 = 14; +const tRoomID kTSA14 = 15; +const tRoomID kTSA15 = 16; +const tRoomID kTSA16 = 17; +const tRoomID kTSA17 = 18; +const tRoomID kTSA18 = 19; +const tRoomID kTSA19 = 20; +const tRoomID kTSA0B = 21; +const tRoomID kTSA21Cyan = 22; +const tRoomID kTSA22Cyan = 23; +const tRoomID kTSA23Cyan = 24; +const tRoomID kTSA24Cyan = 25; +const tRoomID kTSA25Cyan = 26; +const tRoomID kTSA21Red = 27; +const tRoomID kTSA22Red = 28; +const tRoomID kTSA23Red = 29; +const tRoomID kTSA24Red = 30; +const tRoomID kTSA25Red = 31; +const tRoomID kTSA26 = 32; +const tRoomID kTSA27 = 33; +const tRoomID kTSA28 = 34; +const tRoomID kTSA29 = 35; +const tRoomID kTSA30 = 36; +const tRoomID kTSA31 = 37; +const tRoomID kTSA32 = 38; +const tRoomID kTSA33 = 39; +const tRoomID kTSA34 = 40; +const tRoomID kTSA35 = 41; +const tRoomID kTSADeathRoom = 43; + +// Hot Spot Activation IDs. + +const tHotSpotActivationID kActivateTSAReadyForCard = 1; +const tHotSpotActivationID kActivateTSAReadyToTransport = 2; +const tHotSpotActivationID kActivateTSARobotsAwake = 3; +const tHotSpotActivationID kActivateTSA0BZoomedOut = 4; +const tHotSpotActivationID kActivateTSA0BZoomedIn = 5; +const tHotSpotActivationID kActivateTSA0BComparisonVideo = 6; +const tHotSpotActivationID kActivationLogReaderOpen = 7; +const tHotSpotActivationID kActivateTSA0BTBPVideo = 8; +const tHotSpotActivationID kActivationDoesntHaveKey = 9; +const tHotSpotActivationID kActivationKeyVaultOpen = 10; +const tHotSpotActivationID kActivationDoesntHaveChips = 11; +const tHotSpotActivationID kActivationChipVaultOpen = 12; +const tHotSpotActivationID kActivationJumpToPrehistoric = 13; +const tHotSpotActivationID kActivationJumpToNorad = 14; +const tHotSpotActivationID kActivationJumpToMars = 15; +const tHotSpotActivationID kActivationJumpToWSC = 16; +const tHotSpotActivationID kActivationReadyToExit = 17; +const tHotSpotActivationID kActivationReadyForJumpMenu = 18; +const tHotSpotActivationID kActivationMainJumpMenu = 19; + +// Hot Spot IDs. + +const tHotSpotID kTSAGTCardDropSpotID = 5000; +const tHotSpotID kTSAGTTokyoSpotID = 5001; +const tHotSpotID kTSAGTCaldoriaSpotID = 5002; +const tHotSpotID kTSAGTBeachSpotID = 5003; +const tHotSpotID kTSAGTOtherSpotID = 5004; +const tHotSpotID kTSA02DoorSpotID = 5005; +const tHotSpotID kTSA03EastJimenezSpotID = 5006; +const tHotSpotID kTSA03WestCrenshawSpotID = 5007; +const tHotSpotID kTSA04EastMatsumotoSpotID = 5008; +const tHotSpotID kTSA04WestCastilleSpotID = 5009; +const tHotSpotID kTSA05EastSinclairSpotID = 5010; +const tHotSpotID kTSA05WestWhiteSpotID = 5011; +const tHotSpotID kTSA0AEastSpotID = 5012; +const tHotSpotID kTSA0AWastSpotID = 5013; +const tHotSpotID kTSA0BEastMonitorSpotID = 5014; +const tHotSpotID kTSA0BEastMonitorOutSpotID = 5015; +const tHotSpotID kTSA0BEastCompareNoradSpotID = 5016; +const tHotSpotID kTSA0BEastCompareMarsSpotID = 5017; +const tHotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018; +const tHotSpotID kTSA0BEastCompareWSCSpotID = 5019; +const tHotSpotID kTSA0BEastLeftRewindSpotID = 5020; +const tHotSpotID kTSA0BEastLeftPlaySpotID = 5021; +const tHotSpotID kTSA0BEastRightRewindSpotID = 5022; +const tHotSpotID kTSA0BEastRightPlaySpotID = 5023; +const tHotSpotID kTSA0BEastCloseVideoSpotID = 5024; +const tHotSpotID kTSA0BNorthMonitorSpotID = 5025; +const tHotSpotID kTSA0BNorthMonitorOutSpotID = 5026; +const tHotSpotID kTSA0BNorthHistLogSpotID = 5027; +const tHotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028; +const tHotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029; +const tHotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030; +const tHotSpotID kTSA0BWestMonitorSpotID = 5031; +const tHotSpotID kTSA0BWestMonitorOutSpotID = 5032; +const tHotSpotID kTSA0BWestTheorySpotID = 5033; +const tHotSpotID kTSA0BWestBackgroundSpotID = 5034; +const tHotSpotID kTSA0BWestProcedureSpotID = 5035; +const tHotSpotID kTSA0BWestCloseVideoSpotID = 5036; +const tHotSpotID kTSA0BWestPlayVideoSpotID = 5037; +const tHotSpotID kTSA0BWestRewindVideoSpotID = 5038; +const tHotSpotID kTSA22EastMonitorSpotID = 5039; +const tHotSpotID kTSA22EastKeySpotID = 5040; +const tHotSpotID kTSA23WestMonitorSpotID = 5041; +const tHotSpotID kTSA23WestChipsSpotID = 5042; +const tHotSpotID kTSA34NorthDoorSpotID = 5043; +const tHotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044; +const tHotSpotID kTSA37NorthJumpToNoradSpotID = 5045; +const tHotSpotID kTSA37NorthCancelNoradSpotID = 5046; +const tHotSpotID kTSA37NorthJumpToMarsSpotID = 5047; +const tHotSpotID kTSA37NorthCancelMarsSpotID = 5048; +const tHotSpotID kTSA37NorthJumpToWSCSpotID = 5049; +const tHotSpotID kTSA37NorthCancelWSCSpotID = 5050; +const tHotSpotID kTSA37NorthExitSpotID = 5051; +const tHotSpotID kTSA37NorthJumpMenuSpotID = 5052; +const tHotSpotID kTSA37NorthNoradMenuSpotID = 5053; +const tHotSpotID kTSA37NorthMarsMenuSpotID = 5054; +const tHotSpotID kTSA37NorthWSCMenuSpotID = 5055; + +// Extra sequence IDs. + +const tExtraID kTSATransporterArrowLoop = 0; +const tExtraID kTSAArriveFromCaldoria = 1; +const tExtraID kTSAGTOtherChoice = 2; +const tExtraID kTSAGTCardSwipe = 3; +const tExtraID kTSAGTSelectCaldoria = 4; +const tExtraID kTSAGTGoToCaldoria = 5; +const tExtraID kTSAGTSelectBeach = 6; +const tExtraID kTSAGTGoToBeach = 7; +const tExtraID kTSAGTArriveAtBeach = 8; +const tExtraID kTSAGTSelectTokyo = 9; +const tExtraID kTSAGTGoToTokyo = 10; +const tExtraID kTSAGTArriveAtTokyo = 11; +const tExtraID kTSA02NorthZoomIn = 12; +const tExtraID kTSA02NorthTenSecondDoor = 13; +const tExtraID kTSA02NorthZoomOut = 14; +const tExtraID kTSA02NorthDoorWithAgent3 = 15; +const tExtraID kTSA03JimenezZoomIn = 16; +const tExtraID kTSA03JimenezSpeech = 17; +const tExtraID kTSA03JimenezZoomOut = 18; +const tExtraID kTSA03CrenshawZoomIn = 19; +const tExtraID kTSA03CrenshawSpeech = 20; +const tExtraID kTSA03CrenshawZoomOut = 21; +const tExtraID kTSA03SouthRobotDeath = 22; +const tExtraID kTSA04NorthRobotGreeting = 23; +const tExtraID kTSA04MatsumotoZoomIn = 24; +const tExtraID kTSA04MatsumotoSpeech = 25; +const tExtraID kTSA04MatsumotoZoomOut = 26; +const tExtraID kTSA04CastilleZoomIn = 27; +const tExtraID kTSA04CastilleSpeech = 28; +const tExtraID kTSA04CastilleZoomOut = 29; +const tExtraID kTSA05SinclairZoomIn = 30; +const tExtraID kTSA05SinclairSpeech = 31; +const tExtraID kTSA05SinclairZoomOut = 32; +const tExtraID kTSA05WhiteZoomIn = 33; +const tExtraID kTSA05WhiteSpeech = 34; +const tExtraID kTSA05WhiteZoomOut = 35; +const tExtraID kTSA0AEastRobot = 36; +const tExtraID kTSA0AWestRobot = 37; +const tExtraID kTSA16NorthRobotDeath = 38; +const tExtraID kTSA0BEastZoomIn = 39; +const tExtraID kTSA0BEastZoomedView = 40; +const tExtraID kTSA0BEastZoomOut = 41; +const tExtraID kTSA0BEastTurnLeft = 42; +const tExtraID kTSA0BComparisonStartup = 43; +const tExtraID kTSA0BComparisonView0000 = 44; +const tExtraID kTSA0BComparisonView0002 = 45; +const tExtraID kTSA0BComparisonView0020 = 46; +const tExtraID kTSA0BComparisonView0022 = 47; +const tExtraID kTSA0BComparisonView0200 = 48; +const tExtraID kTSA0BComparisonView0202 = 49; +const tExtraID kTSA0BComparisonView0220 = 50; +const tExtraID kTSA0BComparisonView0222 = 51; +const tExtraID kTSA0BComparisonView2000 = 52; +const tExtraID kTSA0BComparisonView2002 = 53; +const tExtraID kTSA0BComparisonView2020 = 54; +const tExtraID kTSA0BComparisonView2022 = 55; +const tExtraID kTSA0BComparisonView2200 = 56; +const tExtraID kTSA0BComparisonView2202 = 57; +const tExtraID kTSA0BComparisonView2220 = 58; +const tExtraID kTSA0BComparisonView2222 = 59; +const tExtraID kTSA0BNoradComparisonView = 60; +const tExtraID kTSA0BNoradUnaltered = 61; +const tExtraID kTSA0BNoradAltered = 62; +const tExtraID kTSA0BMarsComparisonView = 63; +const tExtraID kTSA0BMarsUnaltered = 64; +const tExtraID kTSA0BMarsAltered = 65; +const tExtraID kTSA0BWSCComparisonView = 66; +const tExtraID kTSA0BWSCUnaltered = 67; +const tExtraID kTSA0BWSCAltered = 68; +const tExtraID kTSA0BCaldoriaComparisonView = 69; +const tExtraID kTSA0BCaldoriaUnaltered = 70; +const tExtraID kTSA0BCaldoriaAltered = 71; +const tExtraID kTSA0BNorthZoomIn = 72; +const tExtraID kTSA0BNorthZoomedView = 73; +const tExtraID kTSA0BNorthZoomOut = 74; +const tExtraID kTSA0BNorthTurnLeft = 75; +const tExtraID kTSA0BNorthTurnRight = 76; +const tExtraID kTSA0BNorthHistLogOpen = 77; +const tExtraID kTSA0BNorthHistLogClose = 78; +const tExtraID kTSA0BNorthHistLogCloseWithLog = 79; +const tExtraID kTSA0BNorthCantChangeHistory = 80; +const tExtraID kTSA0BNorthYoureBusted = 81; +const tExtraID kTSA0BNorthFinallyHappened = 82; +const tExtraID kTSA0BShowRip1 = 83; +const tExtraID kTSA0BNorthRipView1 = 84; +const tExtraID kTSA0BShowRip2 = 85; +const tExtraID kTSA0BShowGuardRobots = 86; +const tExtraID kTSA0BAIInterruption = 87; +const tExtraID kTSA0BRobotsToCommandCenter = 88; +const tExtraID kTSA0BNorthRobotsAtCCView = 89; +const tExtraID kTSA0BNorthRobotsAtRRView = 90; +const tExtraID kTSA0BNorthRobotsAtFDView = 91; +const tExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92; +const tExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93; +const tExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94; +const tExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95; +const tExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96; +const tExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97; +const tExtraID kTSA0BWestZoomIn = 98; +const tExtraID kTSA0BWestZoomedView = 99; +const tExtraID kTSA0BWestZoomOut = 100; +const tExtraID kTSA0BWestTurnRight = 101; +const tExtraID kTSA0BTBPTheoryHighlight = 102; +const tExtraID kTSA0BTBPBackgroundHighlight = 103; +const tExtraID kTSA0BTBPProcedureHighlight = 104; +const tExtraID kTSA0BTBPTheory = 105; +const tExtraID kTSA0BTBPBackground = 106; +const tExtraID kTSA0BTBPProcedure = 107; +const tExtraID kTSA0BRipAlarmScreen = 108; +const tExtraID kTSA22RedEastZoomInSequence = 109; +const tExtraID kTSA22RedEastVaultViewWithKey = 110; +const tExtraID kTSA22RedEastVaultViewNoKey = 111; +const tExtraID kTSA23RedWestVaultZoomInSequence = 112; +const tExtraID kTSA23RedWestVaultViewWithChips = 113; +const tExtraID kTSA23RedWestVaultViewNoChips = 114; +const tExtraID kTSA25NorthDeniedNoKey = 115; +const tExtraID kTSA25NorthDeniedNoChip = 116; +const tExtraID kTSA25NorthPutOnSuit = 117; +const tExtraID kTSA25NorthAlreadyHaveSuit = 118; +const tExtraID kTSA25NorthDescending1 = 119; +const tExtraID kTSA25NorthDescending2 = 120; +const tExtraID kTSA37HorseToAI1 = 121; +const tExtraID kTSA37PegasusAI1 = 122; +const tExtraID kTSA37AI1ToCommissioner1 = 123; +const tExtraID kTSA37Commissioner1 = 124; +const tExtraID kTSA37Commissioner1ToZoom = 125; +const tExtraID kTSA37ZoomToPrehistoric = 126; +const tExtraID kTSA37PrehistoricToAI2 = 127; +const tExtraID kTSA37PegasusAI2 = 128; +const tExtraID kTSA37AI2ToPrehistoric = 129; +const tExtraID kTSA37PrehistoricToDepart = 130; +const tExtraID kTSA37PegasusDepart = 131; +const tExtraID kTSA37TimeJumpToPegasus = 132; +const tExtraID kTSA37RecallToDownload = 133; +const tExtraID kTSA37DownloadToColonel1 = 134; +const tExtraID kTSA37Colonel1 = 135; +const tExtraID kTSA37Colonel1ToReviewRequired = 136; +const tExtraID kTSA37ReviewRequiredToExit = 137; +const tExtraID kTSA37ExitHilited = 138; +const tExtraID kTSA37ExitToHorse = 139; +const tExtraID kTSA37HorseToColonel2 = 140; +const tExtraID kTSA37Colonel2 = 141; +const tExtraID kTSA37PegasusAI3 = 142; +const tExtraID kTSA37AI3ToHorse = 143; +const tExtraID kTSA37HorseToZoom = 144; +const tExtraID kTSA37ZoomToMainMenu = 145; +const tExtraID kTSA37MainMenuToAI4 = 146; +const tExtraID kTSA37PegasusAI4 = 147; +const tExtraID kTSA37AI4ToMainMenu = 148; +const tExtraID kTSA37JumpMenu000 = 149; +const tExtraID kTSA37JumpMenu001 = 150; +const tExtraID kTSA37JumpMenu010 = 151; +const tExtraID kTSA37JumpMenu011 = 152; +const tExtraID kTSA37JumpMenu100 = 153; +const tExtraID kTSA37JumpMenu101 = 154; +const tExtraID kTSA37JumpMenu110 = 155; +const tExtraID kTSA37JumpMenu111 = 156; +const tExtraID kTSA37JumpToWSCMenu = 157; +const tExtraID kTSA37CancelWSC = 158; +const tExtraID kTSA37JumpToWSC = 159; +const tExtraID kTSA37WSCToAI5 = 160; +const tExtraID kTSA37PegasusAI5 = 161; +const tExtraID kTSA37AI5ToWSC = 162; +const tExtraID kTSA37WSCToDepart = 163; +const tExtraID kTSA37JumpToMarsMenu = 164; +const tExtraID kTSA37CancelMars = 165; +const tExtraID kTSA37JumpToMars = 166; +const tExtraID kTSA37MarsToAI6 = 167; +const tExtraID kTSA37PegasusAI6 = 168; +const tExtraID kTSA37AI6ToMars = 169; +const tExtraID kTSA37MarsToDepart = 170; +const tExtraID kTSA37JumpToNoradMenu = 171; +const tExtraID kTSA37CancelNorad = 172; +const tExtraID kTSA37JumpToNorad = 173; +const tExtraID kTSA37NoradToAI7 = 174; +const tExtraID kTSA37PegasusAI7 = 175; +const tExtraID kTSA37AI7ToNorad = 176; +const tExtraID kTSA37NoradToDepart = 177; +const tExtraID kTSA37EnvironmentalScan = 178; +const tExtraID kTSA37DownloadToMainMenu = 179; +const tExtraID kTSA37DownloadToOpMemReview = 180; +const tExtraID kTSA37OpMemReviewToMainMenu = 181; +const tExtraID kTSA37OpMemReviewToAllClear = 182; +const tExtraID kTSA37AllClearToCongratulations = 183; +const tExtraID kTSA37Congratulations = 184; +const tExtraID kTSA37CongratulationsToExit = 185; + +const tDisplayOrder kRipTimerOrder = kMonitorLayer; + + +const tCoordType kUnresolvedLeft = kNavAreaLeft + 14; +const tCoordType kUnresolvedTop = kNavAreaTop + 236; + +const tCoordType kResolvedLeft = kNavAreaLeft + 36; +const tCoordType kResolvedTop = kNavAreaTop + 236; + +const tCoordType kJumpMenuLeft = kNavAreaLeft + 360; +const tCoordType kJumpMenuTop = kNavAreaTop + 202; + +const tCoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354; +const tCoordType kJumpMenuHilitedTop = kNavAreaTop + 196; + +const tCoordType kExitLeft = kNavAreaLeft + 360; +const tCoordType kExitTop = kNavAreaTop + 216; + +const tCoordType kExitHilitedLeft = kNavAreaLeft + 354; +const tCoordType kExitHilitedTop = kNavAreaTop + 210; + +const tCoordType kRipTimerLeft = kNavAreaLeft + 95; +const tCoordType kRipTimerTop = kNavAreaTop + 87; + +const tCoordType kTBPCloseLeft = kNavAreaLeft + 30; +const tCoordType kTBPCloseTop = kNavAreaTop + 16; + +const tCoordType kTBPRewindLeft = kNavAreaLeft + 86; +const tCoordType kTBPRewindTop = kNavAreaTop + 218; + +const tCoordType kComparisonCloseLeft = kNavAreaLeft + 50; +const tCoordType kComparisonCloseTop = kNavAreaTop + 14; + +const tCoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96; +const tCoordType kComparisonLeftRewindTop = kNavAreaTop + 190; + +const tCoordType kComparisonRightRewindLeft = kNavAreaLeft + 282; +const tCoordType kComparisonRightRewindTop = kNavAreaTop + 190; + +const tCoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45; +const tCoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65; + +const tCoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45; +const tCoordType kComparisonHiliteNoradTop = kNavAreaTop + 65; + +const tCoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4; +const tCoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23; + +const tCoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7; +const tCoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46; + +const tCoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11; +const tCoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68; + +const tCoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148; +const tCoordType kComparisonChancesSpriteTop = kNavAreaTop + 162; + +const tCoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148; +const tCoordType kComparisonChancesNoradTop = kNavAreaTop + 162; + +const tCoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148; +const tCoordType kComparisonChancesMarsTop = kNavAreaTop + 162; + +const tCoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148; +const tCoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1; + +const tCoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148; +const tCoordType kComparisonChancesWSCTop = kNavAreaTop + 162; + +const tCoordType kRedirectionSprite1Left = kNavAreaLeft + 58; +const tCoordType kRedirectionSprite1Top = kNavAreaTop + 16; + +const tCoordType kRedirectionSprite2Left = kNavAreaLeft + 36; +const tCoordType kRedirectionSprite2Top = kNavAreaTop + 166; + +const tCoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58; +const tCoordType kRedirectionCCRolloverTop = kNavAreaTop + 16; + +const tCoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430; +const tCoordType kRedirectionRRRolloverTop = kNavAreaTop + 30; + +const tCoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278; +const tCoordType kRedirectionFDRolloverTop = kNavAreaTop + 160; + +const tCoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174; +const tCoordType kRedirectionCCDoorTop = kNavAreaTop + 36; + +const tCoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418; +const tCoordType kRedirectionRRDoorTop = kNavAreaTop + 32; + +const tCoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298; +const tCoordType kRedirectionFDDoorTop = kNavAreaTop + 240; + +const tCoordType kRedirectionSecuredLeft = kNavAreaLeft + 36; +const tCoordType kRedirectionSecuredTop = kNavAreaTop + 166; + +const tCoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36; +const tCoordType kRedirectionNewTargetTop = kNavAreaTop + 166; + +const tCoordType kRedirectionCloseLeft = kNavAreaLeft + 56; +const tCoordType kRedirectionCloseTop = kNavAreaTop + 220; + +const TimeValue kTSABumpIntoWallIn = 0; +const TimeValue kTSABumpIntoWallOut = 148; + +const TimeValue kTSAGTDoorCloseIn = 148; +const TimeValue kTSAGTDoorCloseOut = 1570; + +const TimeValue kTSANoOtherDestinationIn = 1570; +const TimeValue kTSANoOtherDestinationOut = 3601; + +const TimeValue kTSAEntryDoorCloseIn = 3601; +const TimeValue kTSAEntryDoorCloseOut = 4200; + +const TimeValue kTSAInsideDoorCloseIn = 4200; +const TimeValue kTSAInsideDoorCloseOut = 4800; + +const TimeValue kTSAVaultCloseIn = 4800; +const TimeValue kTSAVaultCloseOut = 5388; + +const TimeValue kTSAPegasusDoorCloseIn = 5388; +const TimeValue kTSAPegasusDoorCloseOut = 6457; + +const bool kPegasusUnresolved = false; +const bool kPegasusResolved = true; +const bool kPegasusCantExit = false; +const bool kPegasusCanExit = true; + +// Monitor modes +enum { + kMonitorNeutral = 0, + kMonitorTheory = 1, + kMonitorProcedure = 2, + kMonitorBackground = 3, + kMonitorNoradComparison = 4, + kMonitorMarsComparison = 5, + kMonitorCaldoriaComparison = 6, + kMonitorWSCComparison = 7, + + kRawModeMask = 0x0F, + kPlayingTBPMask = 0x10, + kPlayingLeftComparisonMask = 0x20, + kPlayingRightComparisonMask = 0x40, + + kPlayingAnyMask = kPlayingTBPMask | + kPlayingLeftComparisonMask | + kPlayingRightComparisonMask, + + kMonitorPlayingTheory = kMonitorTheory | kPlayingTBPMask, + kMonitorPlayingProcedure = kMonitorProcedure | kPlayingTBPMask, + kMonitorPlayingBackground = kMonitorBackground | kPlayingTBPMask, + + kMonitorPlayingLeftNoradComparison = kMonitorNoradComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightNoradComparison = kMonitorNoradComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftMarsComparison = kMonitorMarsComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightMarsComparison = kMonitorMarsComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftWSCComparison = kMonitorWSCComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightWSCComparison = kMonitorWSCComparison | + kPlayingRightComparisonMask +}; + +static const tExtraID s_historicalLogViews[16] = { + kTSA0BComparisonView0000, + kTSA0BComparisonView0002, + kTSA0BComparisonView0020, + kTSA0BComparisonView0022, + kTSA0BComparisonView0200, + kTSA0BComparisonView0202, + kTSA0BComparisonView0220, + kTSA0BComparisonView0222, + kTSA0BComparisonView2000, + kTSA0BComparisonView2002, + kTSA0BComparisonView2020, + kTSA0BComparisonView2022, + kTSA0BComparisonView2200, + kTSA0BComparisonView2202, + kTSA0BComparisonView2220, + kTSA0BComparisonView2222 +}; + +const long kRedirectionCCRolloverSprite = 0; +const long kRedirectionRRRolloverSprite = 1; +const long kRedirectionFDRolloverSprite = 2; +const long kRedirectionCCDoorSprite = 3; +const long kRedirectionRRDoorSprite = 4; +const long kRedirectionFDDoorSprite = 5; +const long kRedirectionCloseSprite = 6; +const long kRedirectionSecuredSprite = 0; +const long kRedirectionNewTargetSprite = 1; + +void RipTimer::initImage() { + _middle = -1; + + _timerImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLeftRipPICTID); + + Common::Rect r; + _timerImage.getSurfaceBounds(r); + setBounds(r); +} + +void RipTimer::releaseImage() { + _timerImage.deallocateSurface(); +} + +void RipTimer::draw(const Common::Rect &updateRect) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r1 = bounds; + r1.right = _middle; + r1 = updateRect.findIntersectingRect(r1); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - _bounds.left, r1.top - bounds.top); + _timerImage.copyToCurrentPort(r2, r1); + } +} + +void RipTimer::timeChanged(const TimeValue newTime) { + Common::Rect bounds; + getBounds(bounds); + + tCoordType newMiddle = bounds.left + bounds.width() * newTime / getDuration(); + + if (newMiddle != _middle) { + _middle = newMiddle; + triggerRedraw(); + } + + if (newTime == getStop()) + ((PegasusEngine *)g_engine)->die(kDeathUncreatedInTSA); +} + +FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID), + _ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) { + setIsItemTaken(kJourneymanKey); + setIsItemTaken(kPegasusBiochip); + setIsItemTaken(kMapBiochip); +} + +void FullTSA::init() { + Neighborhood::init(); + _ripTimer.setDisplayOrder(kRipTimerOrder); + _ripTimer.startDisplaying(); + + if (!GameState.getTSASeenRobotGreeting()) + forceStridingStop(kTSA03, kNorth, kNoAlternateID); + + _sprite1.setDisplayOrder(kMonitorLayer); + _sprite1.startDisplaying(); + _sprite2.setDisplayOrder(kMonitorLayer); + _sprite2.startDisplaying(); + _sprite3.setDisplayOrder(kMonitorLayer); + _sprite3.startDisplaying(); + + // Fix a mistake in the world builder tables. + HotspotInfoTable::Entry *entry = findHotspotEntry(kTSA23WestChipsSpotID); + entry->hotspotItem = kPegasusBiochip; +} + +void uncreatedInTSAFunction(FunctionPtr *, void *tsa) { + ((FullTSA *)tsa)->die(kDeathUncreatedInTSA); +} + +void FullTSA::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getScoringEnterTSA()) { + _utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit()); + _utilityFuse.setFunctionPtr(&uncreatedInTSAFunction, (void *)this); + _utilityFuse.lightFuse(); + } else if (GameState.getTSAState() == kTSAPlayerDetectedRip || GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) { + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.setTime(GameState.getRipTimerTime()); + _ripTimer.start(); + } + + Neighborhood::start(); +} + +void FullTSA::flushGameState() { + GameState.setRipTimerTime(_ripTimer.getTime()); + GameState.setTSAFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +Common::String FullTSA::getBriefingMovie() { + Common::String movieName = getBriefingMovie(); + + if (movieName.empty()) { + tRoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + if (room >= kTSA16 && room <= kTSA0B) + return "Images/AI/TSA/XT01A"; + + return "Images/AI/TSA/XT01"; + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XT02"; + case kTSAPlayerGotHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + return "Images/AI/TSA/XT03"; + default: + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI2, kHintInterruption); + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + break; + default: + if (GameState.allTimeZonesFinished()) + return "Images/AI/TSA/XT05"; + + return "Images/AI/TSA/XT04"; + } + break; + } + } + + return movieName; +} + +Common::String FullTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XTE1"; + default: + if (GameState.getCurrentRoom() == kTSA37) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + break; + case kActivationJumpToNorad: + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTSA37JumpToNoradMenu); + break; + case kActivationJumpToMars: + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTSA37JumpToMarsMenu); + break; + case kActivationJumpToWSC: + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTSA37JumpToWSCMenu); + break; + default: + startExtraSequenceSync(kTSA37AI4ToMainMenu, kFilterNoInput); + break; + } + + g_AIChip->clearClicked(); + } else if (GameState.allTimeZonesFinished()) { + return "Images/AI/TSA/XTE1"; + } else { + return "Images/AI/TSA/XTE2"; + } + break; + } + } + + return movieName; +} + +uint FullTSA::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getCurrentRoom() == kTSA0B && GameState.getTSA0BZoomedIn()) + numHints = 3; + break; + } + } + + return numHints; +} + +Common::String FullTSA::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) + movieName = Common::String::format("Images/AI/TSA/XT20NH%d", hintNum); + + return movieName; +} + +void FullTSA::loadAmbientLoops() { + tRoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red)) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0); + else if (room == kTSA25Cyan || room == kTSA25Red) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0); + else + loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0); + break; + default: + if (room >= kTSA00 && room <= kTSA02) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA03 && room <= kTSA15) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA16 && room <= kTSA0B) + loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF"); + else if (room >= kTSA21Cyan && room <= kTSA25Red) + loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF"); + else if (room >= kTSA26 && room <= kTSA37) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + break; + } +} + +short FullTSA::getStaticCompassAngle(const tRoomID room, const tDirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kTSA08: + result += kCompassShift; + break; + case kTSA09: + result -= kCompassShift; + break; + case kTSA10: + result += kCompassShift * 2; + break; + case kTSA11: + case kTSA22Cyan: + case kTSA22Red: + result -= kCompassShift * 2; + break; + case kTSA12: + result += kCompassShift * 3; + break; + case kTSA13: + result -= kCompassShift * 3; + break; + case kTSA14: + case kTSA16: + case kTSA17: + case kTSA18: + case kTSA19: + result += kCompassShift * 4; + break; + case kTSA0B: + result += kCompassShift * 4; + + if (dir == kWest) + result += 30; + else if (dir == kEast) + result -= 30; + break; + case kTSA33: + result += kCompassShift * 4; + break; + case kTSA15: + case kTSA21Cyan: + case kTSA24Cyan: + case kTSA25Cyan: + case kTSA21Red: + case kTSA24Red: + case kTSA25Red: + case kTSA26: + case kTSA27: + case kTSA28: + case kTSA29: + case kTSA30: + result -= kCompassShift * 4; + break; + case kTSA23Cyan: + case kTSA23Red: + result -= kCompassShift * 6; + break; + case kTSA32: + result -= kCompassShift * 8; + break; + case kTSA34: + result -= kCompassShift * 12; + break; + case kTSA35: + result += kCompassShift * 8; + break; + case kTSA37: + result -= kCompassShift * 2; + break; + } + + return result; +} + +void FullTSA::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kTSA01, kSouth): + compassMove.insertFaderKnot(exitEntry.movieStart, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 3, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 33, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA11, kEast): + if (getCurrentAlternate() == kAltTSARobotsAtReadyRoom) { + compassMove.makeTwoKnotFaderSpec(kFullTSAMovieScale, exitEntry.movieStart, + getStaticCompassAngle(kTSA11, kEast), exitEntry.movieEnd, + getStaticCompassAngle(kTSA13, kEast)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 13, compassMove.getNthKnotValue(1)); + } + break; + case MakeRoomView(kTSA34, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 48, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 68, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA37, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 38, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 64, + getStaticCompassAngle(exitEntry.room, exitEntry.direction) + kCompassShift * 3 / 2); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 105, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + } +} + +void FullTSA::getExtraCompassMove(const ExtraTable::Entry &extraEntry, FaderMoveSpec &compassMove) { + int16 angle; + + switch (extraEntry.extra) { + case kTSA0BEastTurnLeft: + case kTSA0BNorthTurnLeft: + angle =getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle - 60); + break; + case kTSA0BNorthTurnRight: + case kTSA0BWestTurnRight: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle + 60); + break; + case kTSA22RedEastZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 8160, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 9840, angle); + break; + case kTSA23RedWestVaultZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 10100, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 11880, angle); + break; + default: + Neighborhood::getExtraCompassMove(extraEntry, compassMove); + break; + } +} + +uint16 FullTSA::getDateResID() const { + return kDate2318ID; +} + +TimeValue FullTSA::getViewTime(const tRoomID room, const tDirectionConstant direction) { + tExtraID extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + extraID = s_historicalLogViews[getHistoricalLogIndex()]; + break; + default: + extraID = kTSA0BEastZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + extraID = kTSA0BNorthRipView1; + break; + default: + extraID = kTSA0BNorthZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + extraID = kTSA0BWestZoomedView; + break; + case MakeRoomView(kTSA22Red, kEast): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + if (_vm->itemInLocation(kJourneymanKey, kFullTSAID, kTSA22Red, kEast)) + extraID = kTSA22RedEastVaultViewWithKey; + else + extraID = kTSA22RedEastVaultViewNoKey; + } + break; + case MakeRoomView(kTSA23Red, kWest): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + if (_vm->itemInLocation(kPegasusBiochip, kFullTSAID, kTSA23Red, kWest)) + extraID = kTSA23RedWestVaultViewWithChips; + else + extraID = kTSA23RedWestVaultViewNoChips; + } + break; + case MakeRoomView(kTSA37, kNorth): + switch (GameState.getTSAState()) { + case kTSAPlayerGotHistoricalLog: + extraID = kTSA37ReviewRequiredToExit; + break; + case kPlayerFinishedWithTSA: + extraID = kTSA37CongratulationsToExit; + break; + default: + extraID = kTSA37AI3ToHorse; + break; + } + break; + } + + if (extraID != 0xffffffff) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + return entry.movieEnd - 1; + } + + return Neighborhood::getViewTime(room, direction); +} + +void FullTSA::findSpotEntry(const tRoomID room, const tDirectionConstant direction, tSpotFlags flags, SpotTable::Entry &entry) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kNorth): + case MakeRoomView(kTSA0B, kEast): + case MakeRoomView(kTSA0B, kWest): + if (!GameState.getTSA0BZoomedIn()) + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + default: + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + } +} + +void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + Neighborhood::getExtraEntry(id, extraEntry); + + if (id == kTSA0BShowGuardRobots) + extraEntry.movieStart += kFullTSAFrameDuration * 3; +} + +void FullTSA::pickedUpItem(Item *item) { + BiochipItem *biochip; + + switch (item->getObjectID()) { + case kJourneymanKey: + GameState.setScoringGotJourneymanKey(true); + break; + case kPegasusBiochip: + biochip = (BiochipItem *)g_allItems.findItemByID(kMapBiochip); + _vm->addItemToBiochips(biochip); + GameState.setScoringGotPegasusBiochip(true); + break; + } +} + +void FullTSA::playExtraMovie(const ExtraTable::Entry &extraEntry, const tNotificationFlags flags, const tInputBits interruptionInput) { + switch (extraEntry.extra) { + case kTSA0BNorthZoomIn: + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + requestExtraSequence(kTSA0BNorthHistLogClose, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + } + break; + case kTSA0BNorthZoomOut: + if (_ripTimer.isVisible()) + _ripTimer.hide(); + + shutDownRobotMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + case kTSA0BEastZoomOut: + shutDownComparisonMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + default: + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + } +} + +void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + startExtraSequence(kTSAArriveFromCaldoria, kDoorOpenCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAIDedAtDoor()) { + GameState.setTSAIDedAtDoor(true); + requestExtraSequence(kTSA02NorthZoomIn, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthTenSecondDoor, 0, kFilterNoInput); + + if (GameState.getTSASeenAgent3AtDoor()) { + requestExtraSequence(kTSA02NorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + } else { + GameState.setTSASeenAgent3AtDoor(true); + requestExtraSequence(kTSA02NorthZoomOut, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthDoorWithAgent3, kDoorOpenCompletedFlag, kFilterNoInput); + } + return; + } + break; + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) { + playDeathExtra(kTSA03SouthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSAState() == kRobotsAtCommandCenter) { + playDeathExtra(kTSA16NorthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + } + + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +tInputBits FullTSA::getInputFilter() { + tInputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kTSA0B: + if (GameState.getT0BMonitorMode() != kMonitorNeutral) + // Only allow a click. + result &= JMPPPInput::getClickInputFilter(); + break; + case kTSA37: + // Can't move forward in Pegasus. Only press the exit button. + result &= ~(kFilterUpButton | kFilterUpAuto); + break; + } + + return result; +} + +void FullTSA::turnLeft() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kNorth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnLeft(); +} + +void FullTSA::turnRight() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnRight(); +} + +void FullTSA::openDoor() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor) + setCurrentAlternate(kAltTSARedAlert); + break; + } + + Neighborhood::openDoor(); +} + +tCanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth)) + return kCantMoveBlocked; + + return Neighborhood::canMoveForward(entry); +} + +tCanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + return kCantOpenLocked; + case MakeRoomView(kTSA03, kSouth): + if (!GameState.getTSAFrontDoorUnlockedInside()) + return kCantOpenLocked; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSACommandCenterLocked()) + return kCantOpenLocked; + } + + return Neighborhood::canOpenDoor(entry); +} + +void FullTSA::bumpIntoWall() { + requestSpotSound(kTSABumpIntoWallIn, kTSABumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +void FullTSA::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn() && GameState.getT0BMonitorMode() == kMonitorNeutral) + startExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + } +} + +void FullTSA::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *spot) { + switch (spot->getObjectID()) { + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastLeftPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingRightComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + case kTSA0BEastRightRewindSpotID: + case kTSA0BEastRightPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingLeftComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + default: + Neighborhood::activateOneHotspot(entry, spot); + break; + } +} + +void FullTSA::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + g_allHotspots.activateOneHotspot(kTSA02DoorSpotID); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (getCurrentActivation() != kActivateTSA0BComparisonVideo) { + g_allHotspots.activateOneHotspot(kTSA0BEastCompareNoradSpotID); + g_allHotspots.activateOneHotspot(kTSA0BEastCompareMarsSpotID); + g_allHotspots.activateOneHotspot(kTSA0BEastCompareCaldoriaSpotID); + g_allHotspots.activateOneHotspot(kTSA0BEastCompareWSCSpotID); + } + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToCommandCenterSpotID); + g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToReadyRoomSpotID); + g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToFrontDoorSpotID); + break; + } + break; + } +} + +void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTSAGTOtherSpotID: + showExtraView(kTSAGTOtherChoice); + playSpotSoundSync(kTSANoOtherDestinationIn, kTSANoOtherDestinationOut); + showExtraView(kTSAGTCardSwipe); + break; + case kTSA02DoorSpotID: + GameState.setTSAFrontDoorUnlockedOutside(true); + Neighborhood::clickInHotspot(input, clickedSpot); + break; + case kTSA03EastJimenezSpotID: + startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA03WestCrenshawSpotID: + startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04EastMatsumotoSpotID: + startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04WestCastilleSpotID: + startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05EastSinclairSpotID: + startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05WestWhiteSpotID: + startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BEastCompareNoradSpotID: + initializeComparisonMonitor(kMonitorNoradComparison, kTSA0BNoradComparisonView); + break; + case kTSA0BEastCompareMarsSpotID: + initializeComparisonMonitor(kMonitorMarsComparison, kTSA0BMarsComparisonView); + break; + case kTSA0BEastCompareCaldoriaSpotID: + initializeComparisonMonitor(kMonitorCaldoriaComparison, kTSA0BCaldoriaComparisonView); + break; + case kTSA0BEastCompareWSCSpotID: + initializeComparisonMonitor(kMonitorWSCComparison, kTSA0BWSCComparisonView); + break; + case kTSA0BEastCloseVideoSpotID: + _navMovie.stop(); + _sprite3.show(); + _vm->delayShell(1, 2); + _sprite3.hide(); + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + case kTSA0BEastLeftPlaySpotID: + playLeftComparison(); + break; + case kTSA0BEastRightPlaySpotID: + playRightComparison(); + break; + + // Command center + case kTSA0BWestTheorySpotID: + initializeTBPMonitor(kMonitorTheory, kTSA0BTBPTheoryHighlight); + break; + case kTSA0BWestBackgroundSpotID: + initializeTBPMonitor(kMonitorBackground, kTSA0BTBPBackgroundHighlight); + break; + case kTSA0BWestProcedureSpotID: + initializeTBPMonitor(kMonitorProcedure, kTSA0BTBPProcedureHighlight); + break; + case kTSA0BWestCloseVideoSpotID: + _navMovie.stop(); + _sprite2.show(); + _vm->delayShell(1, 2); + _sprite2.hide(); + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestPlayVideoSpotID: + playTBPMonitor(); + break; + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastRightRewindSpotID: + case kTSA0BWestRewindVideoSpotID: + if ((GameState.getT0BMonitorMode() & kPlayingAnyMask) != 0) { + bool playing = _navMovie.isRunning(); + if (playing) + _navMovie.stop(); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.show(); + else + _sprite1.show(); + + _vm->delayShell(1, 2); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.hide(); + else + _sprite1.hide(); + + _navMovie.setTime(GameState.getT0BMonitorStart()); + + if (playing) { + _navMovie.start(); + } else { + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + } + } + break; + case kTSA22EastMonitorSpotID: + requestExtraSequence(kTSA22RedEastZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA23WestMonitorSpotID: + requestExtraSequence(kTSA23RedWestVaultZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + // Nothing + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + // Nothing + break; + } + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + + // Pegasus + case kTSA37NorthJumpToPrehistoricSpotID: + startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthExitSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + moveForward(); + break; + case kTSA37NorthJumpMenuSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + break; + case kTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTSA37JumpToNorad, 0, kFilterNoInput); + + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTSA37JumpToMars, 0, kFilterNoInput); + + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTSA37JumpToWSC, 0, kFilterNoInput); + + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } +} + +void FullTSA::showMainJumpMenu() { + tExtraID jumpMenuView = kTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationMainJumpMenu); +} + +void FullTSA::playTBPMonitor() { + InputHandler::getCurrentInputDevice()->waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingTBPMask) == 0) { + tExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorTheory: + GameState.setTSASeenTheory(true); + extra = kTSA0BTBPTheory; + GameState.setScoringSawTheory(true); + break; + case kMonitorBackground: + GameState.setTSASeenBackground(true); + extra = kTSA0BTBPBackground; + GameState.setScoringSawBackground(true); + break; + case kMonitorProcedure: + GameState.setTSASeenProcedure(true); + extra = kTSA0BTBPProcedure; + GameState.setScoringSawProcedure(true); + break; + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingTBPMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, kExtraCompletedFlag, false, kFilterAllInput); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::initializeTBPMonitor(const int newMode, const tExtraID highlightExtra) { + GameState.setT0BMonitorMode(newMode); + + if (newMode != kMonitorNeutral) { + showExtraView(highlightExtra); + _vm->delayShell(1, 2); + setCurrentActivation(kActivateTSA0BTBPVideo); + _sprite1.addPICTResourceFrame(kTBPRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kTBPRewindLeft, kTBPRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kTBPCloseBoxPICTID, false, 0, 0); + _sprite2.moveElementTo(kTBPCloseLeft, kTBPCloseTop); + _sprite2.setCurrentFrameIndex(0); + playTBPMonitor(); + } else { + if (GameState.getTSAState() == kTSAPlayerForcedReview && GameState.getTSASeenTheory() && + GameState.getTSASeenBackground() && GameState.getTSASeenProcedure()) { + setOffRipAlarm(); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + updateViewFrame(); + } + + releaseSprites(); + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::startUpComparisonMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kComparisonHiliteNoradPICTID, false, + kComparisonHiliteNoradLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteNoradTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteMarsPICTID, false, + kComparisonHiliteMarsLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteMarsTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteCaldoriaPICTID, false, + kComparisonHiliteCaldoriaLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteCaldoriaTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteWSCPICTID, false, + kComparisonHiliteWSCLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteWSCTop - kComparisonHiliteSpriteTop); + + _sprite1.setCurrentFrameIndex(0); + _sprite1.moveElementTo(kComparisonHiliteSpriteLeft, kComparisonHiliteSpriteTop); + + _sprite2.addPICTResourceFrame(kComparisonChancesNoradPICTID, false, + kComparisonChancesNoradLeft - kComparisonChancesSpriteLeft, + kComparisonChancesNoradTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesMarsPICTID, false, + kComparisonChancesMarsLeft - kComparisonChancesSpriteLeft, + kComparisonChancesMarsTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesCaldoriaPICTID, false, + kComparisonChancesCaldoriaLeft - kComparisonChancesSpriteLeft, + kComparisonChancesCaldoriaTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesWSCPICTID, false, + kComparisonChancesWSCLeft - kComparisonChancesSpriteLeft, + kComparisonChancesWSCTop - kComparisonChancesSpriteTop); + + _sprite2.setCurrentFrameIndex(0); + _sprite2.moveElementTo(kComparisonChancesSpriteLeft, kComparisonChancesSpriteTop); + updateViewFrame(); +} + +void FullTSA::shutDownComparisonMonitor() { + releaseSprites(); +} + +void FullTSA::initializeComparisonMonitor(const int newMode, const tExtraID comparisonView) { + GameState.setT0BMonitorMode(newMode); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + + if (newMode != kMonitorNeutral) { + shutDownComparisonMonitor(); + setCurrentActivation(kActivateTSA0BComparisonVideo); + _sprite1.addPICTResourceFrame(kComparisonLeftRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kComparisonLeftRewindLeft, kComparisonLeftRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kComparisonRightRewindPICTID, false, 0, 0); + _sprite2.moveElementTo(kComparisonRightRewindLeft, kComparisonRightRewindTop); + _sprite2.setCurrentFrameIndex(0); + _sprite3.addPICTResourceFrame(kComparisonCloseBoxPICTID, false, 0, 0); + _sprite3.moveElementTo(kComparisonCloseLeft, kComparisonCloseTop); + _sprite3.setCurrentFrameIndex(0); + showExtraView(comparisonView); + } else { + if (GameState.getTSAState() == kTSAPlayerInstalledHistoricalLog && + GameState.getTSASeenNoradNormal() && + GameState.getTSASeenNoradAltered() && + GameState.getTSASeenMarsNormal() && + GameState.getTSASeenMarsAltered() && + GameState.getTSASeenCaldoriaNormal() && + GameState.getTSASeenCaldoriaAltered() && + GameState.getTSASeenWSCNormal() && + GameState.getTSASeenWSCAltered()) { + GameState.setTSAState(kTSABossSawHistoricalLog); + requestExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BEastTurnLeft, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + releaseSprites(); + startUpComparisonMonitor(); + } + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::playLeftComparison() { + InputHandler::getCurrentInputDevice()->waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingLeftComparisonMask) == 0) { + tExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradAltered(true); + extra = kTSA0BNoradAltered; + GameState.setScoringSawNoradAltered(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsAltered(true); + extra = kTSA0BMarsAltered; + GameState.setScoringSawMarsAltered(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaAltered(true); + extra = kTSA0BCaldoriaAltered; + GameState.setScoringSawCaldoriaAltered(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCAltered(true); + extra = kTSA0BWSCAltered; + GameState.setScoringSawWSCAltered(true); + break; + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingLeftComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::playRightComparison() { + InputHandler::getCurrentInputDevice()->waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingRightComparisonMask) == 0) { + tExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradNormal(true); + extra = kTSA0BNoradUnaltered; + GameState.setScoringSawNoradNormal(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsNormal(true); + extra = kTSA0BMarsUnaltered; + GameState.setScoringSawMarsNormal(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaNormal(true); + extra = kTSA0BCaldoriaUnaltered; + GameState.setScoringSawCaldoriaNormal(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCNormal(true); + extra = kTSA0BWSCUnaltered; + GameState.setScoringSawWSCNormal(true); + break; + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingRightComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +// When this function is called, the player is zoomed up on the center monitor, and the +// TSA state is kTSABossSawHistoricalLog. +void FullTSA::startRobotGame() { + requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput); + requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput); + requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::startUpRobotMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kRedirectionCCRolloverPICTID, true, + kRedirectionCCRolloverLeft - kRedirectionSprite1Left, + kRedirectionCCRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true, + kRedirectionRRRolloverLeft - kRedirectionSprite1Left, + kRedirectionRRRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false, + kRedirectionFDRolloverLeft - kRedirectionSprite1Left, + kRedirectionFDRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true, + kRedirectionCCDoorLeft - kRedirectionSprite1Left, + kRedirectionCCDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true, + kRedirectionRRDoorLeft - kRedirectionSprite1Left, + kRedirectionRRDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false, + kRedirectionFDDoorLeft - kRedirectionSprite1Left, + kRedirectionFDDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false, + kRedirectionCloseLeft - kRedirectionSprite1Left, + kRedirectionCloseTop - kRedirectionSprite1Top); + _sprite1.moveElementTo(kRedirectionSprite1Left, kRedirectionSprite1Top); + + _sprite2.addPICTResourceFrame(kRedirectionSecuredPICTID, false, + kRedirectionSecuredLeft - kRedirectionSprite2Left, + kRedirectionSecuredTop - kRedirectionSprite2Top); + _sprite2.addPICTResourceFrame(kRedirectionNewTargetPICTID, false, + kRedirectionNewTargetLeft - kRedirectionSprite2Left, + kRedirectionNewTargetTop - kRedirectionSprite2Top); + _sprite2.moveElementTo(kRedirectionSprite2Left, kRedirectionSprite2Top); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + showExtraView(kTSA0BNorthRobotsAtCCView); + break; + case kRobotsAtFrontDoor: + showExtraView(kTSA0BNorthRobotsAtFDView); + break; + case kRobotsAtReadyRoom: + showExtraView(kTSA0BNorthRobotsAtRRView); + break; + } +} + +void FullTSA::shutDownRobotMonitor() { + releaseSprites(); +} + +// Assume this is called only when zoomed in at T0B west +void FullTSA::setOffRipAlarm() { + GameState.setTSAState(kTSAPlayerDetectedRip); + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.start(); + loadAmbientLoops(); + startExtraSequenceSync(kTSA0BRipAlarmScreen, kFilterNoInput); + _vm->delayShell(2, 1); // Two seconds.. + requestExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BWestTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthFinallyHappened, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::checkContinuePoint(const tRoomID room, const tDirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA04, kNorth): + case MakeRoomView(kTSA14, kEast): + case MakeRoomView(kTSA15, kWest): + case MakeRoomView(kTSA16, kNorth): + case MakeRoomView(kTSA16, kSouth): + case MakeRoomView(kTSA21Cyan, kSouth): + case MakeRoomView(kTSA21Red, kSouth): + case MakeRoomView(kTSA26, kNorth): + makeContinuePoint(); + break; + } +} + +void FullTSA::arriveAt(const tRoomID room, const tDirectionConstant direction) { + checkRobotLocations(room, direction); + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSADeathRoom, kNorth): + case MakeRoomView(kTSADeathRoom, kSouth): + case MakeRoomView(kTSADeathRoom, kEast): + case MakeRoomView(kTSADeathRoom, kWest): + die(kDeathShotByTSARobots); + break; + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + makeContinuePoint(); + openDoor(); + } else { + setCurrentActivation(kActivateTSAReadyForCard); + loopExtraSequence(kTSATransporterArrowLoop, 0); + } + break; + case MakeRoomView(kTSA03, kNorth): + case MakeRoomView(kTSA05, kNorth): + case MakeRoomView(kTSA0A, kNorth): + case MakeRoomView(kTSA06, kNorth): + case MakeRoomView(kTSA07, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + GameState.setScoringEnterTSA(true); + break; + case MakeRoomView(kTSA04, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + if (!GameState.getTSASeenRobotGreeting()) + startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA03, kSouth): + GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + if (GameState.getTSAState() == kTSAPlayerNotArrived) + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + _ripTimer.show(); + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpRobotMonitor(); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthYoureBusted, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnLeft, 0, kFilterNoInput); + requestExtraSequence(kTSA0BWestZoomIn, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerGotHistoricalLog: + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + } + } + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + initializeTBPMonitor(kMonitorNeutral, 0); + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA21Red, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) + GameState.setScoringWentToReadyRoom2(true); + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA25Red, kNorth): + arriveAtTSA25Red(); + break; + case MakeRoomView(kTSA34, kSouth): + if (GameState.getLastRoom() == kTSA37) + closeDoorOffScreen(kTSA37, kNorth); + break; + case MakeRoomView(kTSA37, kNorth): + arriveAtTSA37(); + break; + } +} + +void FullTSA::checkRobotLocations(const tRoomID room, const tDirectionConstant dir) { + switch (room) { + case kTSA03: + case kTSA04: + case kTSA05: + case kTSA06: + case kTSA0A: + case kTSA07: + case kTSA08: + case kTSA09: + case kTSA10: + case kTSA11: + case kTSA12: + case kTSA13: + case kTSA14: + case kTSA15: + switch (GameState.getTSAState()) { + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case kTSA16: + if (dir == kNorth) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + } + break; + } +} + +void FullTSA::arriveAtTSA25Red() { + if (!_vm->playerHasItemID(kJourneymanKey)) + startExtraSequence(kTSA25NorthDeniedNoKey, kExtraCompletedFlag, kFilterNoInput); + else if (!_vm->playerHasItemID(kPegasusBiochip)) + startExtraSequence(kTSA25NorthDeniedNoChip, kExtraCompletedFlag, kFilterNoInput); + else if (GameState.getTSABiosuitOn()) + startExtraSequence(kTSA25NorthAlreadyHaveSuit, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kTSA25NorthPutOnSuit, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::arriveAtTSA37() { + _ripTimer.stop(); + _ripTimer.releaseImage(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraLongSequence(kTSA37HorseToAI1, kTSA37AI2ToPrehistoric, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerOnWayToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + showExtraView(kTSA37AI2ToPrehistoric); + break; + case kTSAPlayerGotHistoricalLog: + initializePegasusButtons(false, true); + break; + case kPlayerWentToPrehistoric: + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + startExtraLongSequence(kTSA37HorseToColonel2, kTSA37AI4ToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + case kPlayerFinishedWithTSA: + initializePegasusButtons(true, true); + break; + } +} + +void FullTSA::turnTo(const tDirectionConstant newDirection) { + Neighborhood::turnTo(newDirection); + + switch (MakeRoomView(GameState.getCurrentRoom(), newDirection)) { + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()) + GameState.setTSAFrontDoorUnlockedInside(true); + else + GameState.setTSAFrontDoorUnlockedInside(false); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startUpComparisonMonitor(); + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + if (GameState.getTSA0BZoomedIn()) + _ripTimer.show(); + break; + case kTSAPlayerGotHistoricalLog: + if (!GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerInstalledHistoricalLog: + if (GameState.getTSA0BZoomedIn()) { + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + if (GameState.getTSA0BZoomedIn()) + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA16, kNorth): + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA22Red, kNorth): + case MakeRoomView(kTSA22Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA23Red, kNorth): + case MakeRoomView(kTSA23Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + } + + // Make sure the TBP monitor is forced neutral. + GameState.setT0BMonitorMode(kMonitorNeutral); +} + +void FullTSA::closeDoorOffScreen(const tRoomID room, const tDirectionConstant) { + switch (room) { + case kTSA00: + case kTSA01: + if (GameState.getCurrentRoom() == kTSA01 || GameState.getCurrentRoom() == kTSA02) + playSpotSoundSync(kTSAGTDoorCloseIn, kTSAGTDoorCloseOut); + break; + case kTSA02: + case kTSA03: + playSpotSoundSync(kTSAEntryDoorCloseIn, kTSAEntryDoorCloseOut); + break; + case kTSA14: + case kTSA15: + case kTSA16: + case kTSA21Cyan: + case kTSA21Red: + playSpotSoundSync(kTSAInsideDoorCloseIn, kTSAInsideDoorCloseOut); + break; + case kTSA34: + case kTSA37: + playSpotSoundSync(kTSAPegasusDoorCloseIn, kTSAPegasusDoorCloseOut); + break; + } +} + +void FullTSA::receiveNotification(Notification *notification, const tNotificationFlags flags) { + tExtraID lastExtra = _lastExtra; + + if ((flags & kExtraCompletedFlag) != 0) { + switch (lastExtra) { + case kTSA0BEastTurnLeft: + // Need to check this here because turnTo will call _navMovie.stop, + // so it has to happen before Neighborhood::receiveNotification, + // which may end up starting another sequence... + turnTo(kNorth); + break; + } + } + + Neighborhood::receiveNotification(notification, flags); + + InventoryItem *item; + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTSAGTCardSwipe: + item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateTSAReadyToTransport); + break; + case kTSAGTGoToCaldoria: + _vm->jumpToNewEnvironment(kCaldoriaID, kCaldoria44, kEast); + + if (GameState.allTimeZonesFinished()) + GameState.setScoringWentAfterSinclair(true); + break; + case kTSAGTGoToTokyo: + case kTSAGTGoToBeach: + if (GameState.allTimeZonesFinished()) + die(kDeathSinclairShotDelegate); + else + die(kDeathUncreatedInTSA); + break; + case kTSA02NorthZoomOut: + openDoor(); + break; + + // Hall of suspects. + case kTSA04NorthRobotGreeting: + GameState.setTSASeenRobotGreeting(true); + restoreStriding(kTSA03, kNorth, kNoAlternateID); + break; + case kTSA03JimenezZoomIn: + GameState.setScoringSawBust1(true); + break; + case kTSA03CrenshawZoomIn: + GameState.setScoringSawBust2(true); + break; + case kTSA04MatsumotoZoomIn: + GameState.setScoringSawBust3(true); + break; + case kTSA04CastilleZoomIn: + GameState.setScoringSawBust4(true); + break; + case kTSA05SinclairZoomIn: + GameState.setScoringSawBust5(true); + break; + case kTSA05WhiteZoomIn: + GameState.setScoringSawBust6(true); + break; + + // Command center + // Historical comparison... + case kTSA0BEastZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpComparisonMonitor(); + break; + } + break; + case kTSA0BEastZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSABossSawHistoricalLog: + // Prevent current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BComparisonStartup: + if ((flags & kActionRequestCompletedFlag) != 0) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + GameState.setTSAState(kTSAPlayerInstalledHistoricalLog); + turnTo(kEast); + } + + startUpComparisonMonitor(); + break; + case kTSA0BNoradAltered: + case kTSA0BMarsAltered: + case kTSA0BCaldoriaAltered: + case kTSA0BWSCAltered: + case kTSA0BNoradUnaltered: + case kTSA0BMarsUnaltered: + case kTSA0BCaldoriaUnaltered: + case kTSA0BWSCUnaltered: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + + // Center monitor. + case kTSA0BNorthZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSABossSawHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case kTSA0BShowRip1: + GameState.setTSAState(kTSAPlayerNeedsHistoricalLog); + GameState.setTSACommandCenterLocked(false); + + if ((flags & kActionRequestCompletedFlag) != 0) + turnTo(kNorth); + + _ripTimer.show(); + break; + case kTSA0BNorthHistLogOpen: + setCurrentActivation(kActivationLogReaderOpen); + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, true); + break; + case kTSA0BRobotsToCommandCenter: + GameState.setTSAState(kRobotsAtCommandCenter); + // Fall through + case kTSA0BShowGuardRobots: + startUpRobotMonitor(); + // Fall through + case kTSA0BRobotsFromCommandCenterToReadyRoom: + case kTSA0BRobotsFromReadyRoomToCommandCenter: + case kTSA0BRobotsFromCommandCenterToFrontDoor: + case kTSA0BRobotsFromFrontDoorToCommandCenter: + case kTSA0BRobotsFromFrontDoorToReadyRoom: + case kTSA0BRobotsFromReadyRoomToFrontDoor: + _sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite); + _sprite2.show(); + break; + + // TBP monitor. + case kTSA0BWestZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + + if (GameState.getTSAState() == kTSAPlayerNotArrived) { + turnTo(kWest); + GameState.setTSACommandCenterLocked(true); + GameState.setTSAState(kTSAPlayerForcedReview); + } + + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + GameState.setT0BMonitorMode(kMonitorNeutral); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + // Keep the current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BTBPTheory: + case kTSA0BTBPBackground: + case kTSA0BTBPProcedure: + initializeTBPMonitor(kMonitorNeutral, 0); + break; + + // Ready room + case kTSA22RedEastZoomInSequence: + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true); + setCurrentActivation(kActivationKeyVaultOpen); + break; + case kTSA23RedWestVaultZoomInSequence: + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true); + setCurrentActivation(kActivationChipVaultOpen); + break; + case kTSA25NorthPutOnSuit: + GameState.setTSABiosuitOn(true); + GameState.setScoringGotBiosuit(true); + // Fall through... + case kTSA25NorthAlreadyHaveSuit: + requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput); + requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA25NorthDescending2: + arriveAt(kTSA26, kNorth); + break; + + // Pegasus. + case kTSA37HorseToAI1: + case kTSA37AI2ToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + GameState.setTSAState(kPlayerOnWayToPrehistoric); + break; + case kTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToPrehistoric: + _vm->jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + GameState.setScoringGoToPrehistoric(true); + GameState.setTSAState(kPlayerWentToPrehistoric); + break; + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerWentToPrehistoric: + arriveFromPrehistoric(); + break; + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + if (GameState.allTimeZonesFinished()) { + requestExtraSequence(kTSA37OpMemReviewToAllClear, 0, kFilterNoInput); + requestExtraSequence(kTSA37AllClearToCongratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37Congratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37CongratulationsToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kTSA37RecallToDownload: + case kTSA37ReviewRequiredToExit: + GameState.setTSAState(kTSAPlayerGotHistoricalLog); + initializePegasusButtons(kPegasusUnresolved, kPegasusCanExit); + break; + case kTSA37ZoomToMainMenu: + case kTSA37HorseToColonel2: + case kTSA37DownloadToMainMenu: + case kTSA37OpMemReviewToMainMenu: + case kTSA37AI4ToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTSA37JumpToNoradMenu: + setCurrentActivation(kActivationJumpToNorad); + break; + case kTSA37JumpToMarsMenu: + setCurrentActivation(kActivationJumpToMars); + break; + case kTSA37JumpToWSCMenu: + setCurrentActivation(kActivationJumpToWSC); + break; + case kTSA37CancelNorad: + case kTSA37CancelMars: + case kTSA37CancelWSC: + showMainJumpMenu(); + break; + case kTSA37CongratulationsToExit: + GameState.setTSAState(kPlayerFinishedWithTSA); + initializePegasusButtons(true, true); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void FullTSA::arriveFromPrehistoric() { + if (_vm->playerHasItemID(kHistoricalLog)) { + GameState.setScoringFinishedPrehistoric(); + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + requestExtraSequence(kTSA37DownloadToColonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1ToReviewRequired, 0, kFilterNoInput); + requestExtraSequence(kTSA37ReviewRequiredToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + // Make sure rip timer is going... + startExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromNorad() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromMars() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromWSC() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::initializePegasusButtons(bool resolved, bool exit) { + if (resolved) { + _sprite1.addPICTResourceFrame(kResolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kResolvedLeft, kResolvedTop); + } else { + _sprite1.addPICTResourceFrame(kUnresolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kUnresolvedLeft, kUnresolvedTop); + } + + _sprite1.setCurrentFrameIndex(0); + _sprite1.show(); + + if (exit) { + _sprite2.addPICTResourceFrame(kExitPICTID, false, 0, 0); + _sprite2.addPICTResourceFrame(kExitHilitedPICTID, false, kExitHilitedLeft - kExitLeft, kExitHilitedTop - kExitTop); + _sprite2.moveElementTo(kExitLeft, kExitTop); + setCurrentActivation(kActivationReadyToExit); + } else { + _sprite2.addPICTResourceFrame(kJumpMenuPICTID, false, 0, 0); + _sprite2.addPICTResourceFrame(kJumpMenuHilitedPICTID, false, kJumpMenuHilitedLeft - kJumpMenuLeft, kJumpMenuHilitedTop - kJumpMenuTop); + _sprite2.moveElementTo(kJumpMenuLeft, kJumpMenuTop); + setCurrentActivation(kActivationReadyForJumpMenu); + } + + _sprite2.setCurrentFrameIndex(0); + _sprite2.show(); +} + +Hotspot *FullTSA::getItemScreenSpot(Item *item, DisplayElement *element) { + switch (item->getObjectID()) { + case kJourneymanKey: + return g_allHotspots.findHotspotByID(kTSA22EastKeySpotID); + break; + case kPegasusBiochip: + return g_allHotspots.findHotspotByID(kTSA23WestChipsSpotID); + break; + } + + return Neighborhood::getItemScreenSpot(item, element); +} + +void FullTSA::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + Neighborhood::dropItemIntoRoom(item, dropSpot); + + switch (item->getObjectID()) { + case kKeyCard: + if (dropSpot->getObjectID() == kTSAGTCardDropSpotID) + startExtraSequence(kTSAGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kHistoricalLog: + if (dropSpot->getObjectID() == kTSA0BNorthHistLogSpotID) { + requestExtraSequence(kTSA0BNorthHistLogCloseWithLog, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BEastZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BComparisonStartup, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringPutLogInReader(true); + } + break; + } +} + +uint FullTSA::getHistoricalLogIndex() { + uint index; + + if (GameState.getTSASeenNoradNormal() && GameState.getTSASeenNoradAltered()) + index = 8; + else + index = 0; + + if (GameState.getTSASeenMarsNormal() && GameState.getTSASeenMarsAltered()) + index += 4; + + if (GameState.getTSASeenCaldoriaNormal() && GameState.getTSASeenCaldoriaAltered()) + index += 2; + + if (GameState.getTSASeenWSCNormal() && GameState.getTSASeenWSCAltered()) + index += 1; + + return index; +} + +void FullTSA::handleInput(const Input &input, const Hotspot *cursorSpot) { + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning() && GameState.getT0BMonitorMode() == kMonitorNeutral) { + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + switch (cursorSpot->getObjectID()) { + case kTSA0BEastCompareNoradSpotID: + _sprite1.setCurrentFrameIndex(0); + _sprite2.setCurrentFrameIndex(0); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareMarsSpotID: + _sprite1.setCurrentFrameIndex(1); + _sprite2.setCurrentFrameIndex(1); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareCaldoriaSpotID: + _sprite1.setCurrentFrameIndex(2); + _sprite2.setCurrentFrameIndex(2); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareWSCSpotID: + _sprite1.setCurrentFrameIndex(3); + _sprite2.setCurrentFrameIndex(3); + _sprite1.show(); + _sprite2.show(); + break; + default: + _sprite1.hide(); + _sprite2.hide(); + break; + } + break; + } + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning()) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + switch (cursorSpot->getObjectID()) { + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDRolloverSprite); + _sprite1.show(); + break; + default: + _sprite1.hide(); + break; + } + break; + } + } + break; + } + + Neighborhood::handleInput(input, cursorSpot); +} + +void FullTSA::releaseSprites() { + _sprite1.hide(); + _sprite2.hide(); + _sprite3.hide(); + _sprite1.discardFrames(); + _sprite2.discardFrames(); + _sprite3.discardFrames(); +} + +bool FullTSA::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kTSA0B, kNorth) && + GameState.getTSA0BZoomedIn() && + (GameState.getTSAState() == kRobotsAtCommandCenter || + GameState.getTSAState() == kRobotsAtFrontDoor || + GameState.getTSAState() == kRobotsAtReadyRoom); +} + +void FullTSA::doSolve() { + // REROUTING ROBOTS + + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } +} + +void FullTSA::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BEastMonitorSpotID: + case kTSA0BNorthMonitorSpotID: + case kTSA0BWestMonitorSpotID: + case kTSA22EastMonitorSpotID: + case kTSA23WestMonitorSpotID: + _vm->_cursor->setCurrentFrameIndex(1); + return; + case kTSA0BEastMonitorOutSpotID: + case kTSA0BNorthMonitorOutSpotID: + case kTSA0BWestMonitorOutSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String FullTSA::getNavMovieName() { + return "Images/TSA/Full TSA.movie"; +} + +Common::String FullTSA::getSoundSpotsName() { + return "Sounds/TSA/TSA Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h new file mode 100755 index 0000000000..447482f737 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.h @@ -0,0 +1,158 @@ +/* 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_TSA_FULLTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +class RipTimer : public IdlerAnimation { +public: + RipTimer(const tDisplayElementID id) : IdlerAnimation(id) {} + virtual ~RipTimer() {} + + void initImage(); + void releaseImage(); + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + tCoordType _middle; + Surface _timerImage; +}; + +// Room IDs. + +const tRoomID kTSA00 = 0; +const tRoomID kTSA37 = 42; + +class FullTSA : public Neighborhood { +friend void uncreatedInTSAFunction(FunctionPtr *, void *tsa); + +public: + FullTSA(InputHandler *, PegasusEngine *); + virtual ~FullTSA() {} + + virtual void init(); + + void start(); + + virtual uint16 getDateResID() const; + + void flushGameState(); + + void checkContinuePoint(const tRoomID, const tDirectionConstant); + + bool canSolve(); + void doSolve(); + + void updateCursor(const Common::Point, const Hotspot *); + +protected: + enum { + kTSAPrivateLogReaderOpenFlag, + kTSAPrivateKeyVaultOpenFlag, + kTSAPrivateChipVaultOpenFlag, + kTSAPrivatePlayingLeftComparisonFlag, + kTSAPrivatePlayingRightComparisonFlag, + kTSAPrivateSeenRobotWarningFlag, + kNumTSAPrivateFlags + }; + + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *spot); + virtual void activateHotspots(); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void dropItemIntoRoom(Item *, Hotspot *); + void downButton(const Input &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + TimeValue getViewTime(const tRoomID, const tDirectionConstant); + void findSpotEntry(const tRoomID, const tDirectionConstant, tSpotFlags, SpotTable::Entry &); + void turnTo(const tDirectionConstant); + tCanMoveForwardReason canMoveForward(ExitTable::Entry &); + tCanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void bumpIntoWall(); + void initializeTBPMonitor(const int, const tExtraID); + void playTBPMonitor(); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void openDoor(); + void turnRight(); + void turnLeft(); + void closeDoorOffScreen(const tRoomID, const tDirectionConstant); + void playExtraMovie(const ExtraTable::Entry &, const tNotificationFlags, const tInputBits interruptionInput); + void handleInput(const Input &, const Hotspot *); + void arriveAtTSA25Red(); + void startUpComparisonMonitor(); + void shutDownComparisonMonitor(); + void initializeComparisonMonitor(const int, const tExtraID); + void playLeftComparison(); + void playRightComparison(); + void startRobotGame(); + void setOffRipAlarm(); + uint getHistoricalLogIndex(); + void startUpRobotMonitor(); + void shutDownRobotMonitor(); + void pickedUpItem(Item *item); + void arriveFromPrehistoric(); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + tInputBits getInputFilter(); + void arriveAt(const tRoomID, const tDirectionConstant); + void initializePegasusButtons(bool, bool); + void releaseSprites(); + void showMainJumpMenu(); + void arriveAtTSA37(); + void receiveNotification(Notification *, const tNotificationFlags); + void checkRobotLocations(const tRoomID, const tDirectionConstant); + void getExtraEntry(const uint32, ExtraTable::Entry &); + + Sprite _sprite1, _sprite2, _sprite3; + FuseFunction _utilityFuse; + RipTimer _ripTimer; + + FlagsArray<byte, kNumTSAPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp new file mode 100755 index 0000000000..fa611bd4d7 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -0,0 +1,454 @@ +/* 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/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +const short kCompassShift = 30; + +const TimeScale kTinyTSAMovieScale = 600; +const TimeScale kTinyTSAFramesPerSecond = 15; +const TimeScale kTinyTSAFrameDuration = 40; + +// Alternate IDs. + +const tAlternateID kAltTinyTSANormal = 0; + +// Hot Spot Activation IDs. + +const tHotSpotActivationID kActivationTinyTSAJumpToNorad = 1; +const tHotSpotActivationID kActivationTinyTSAJumpToMars = 2; +const tHotSpotActivationID kActivationTinyTSAJumpToWSC = 3; +const tHotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4; +const tHotSpotActivationID kActivationTinyTSAMainJumpMenu = 5; + +// Hot Spot IDs. + +const tHotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000; +const tHotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001; +const tHotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002; +const tHotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003; +const tHotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004; +const tHotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005; +const tHotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006; +const tHotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007; +const tHotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008; +const tHotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009; + +// Extra sequence IDs. + +const tExtraID kTinyTSA37PegasusDepart = 0; +const tExtraID kTinyTSA37TimeJumpToPegasus = 1; +const tExtraID kTinyTSA37RecallToDownload = 2; +const tExtraID kTinyTSA37ExitHilited = 3; +const tExtraID kTinyTSA37ExitToHorse = 4; +const tExtraID kTinyTSA37JumpMenu000 = 5; +const tExtraID kTinyTSA37JumpMenu001 = 6; +const tExtraID kTinyTSA37JumpMenu010 = 7; +const tExtraID kTinyTSA37JumpMenu011 = 8; +const tExtraID kTinyTSA37JumpMenu100 = 9; +const tExtraID kTinyTSA37JumpMenu101 = 10; +const tExtraID kTinyTSA37JumpMenu110 = 11; +const tExtraID kTinyTSA37JumpMenu111 = 12; +const tExtraID kTinyTSA37JumpToWSCMenu = 13; +const tExtraID kTinyTSA37CancelWSC = 14; +const tExtraID kTinyTSA37JumpToWSC = 15; +const tExtraID kTinyTSA37WSCToAI5 = 16; +const tExtraID kTinyTSA37PegasusAI5 = 17; +const tExtraID kTinyTSA37AI5ToWSC = 18; +const tExtraID kTinyTSA37WSCToDepart = 19; +const tExtraID kTinyTSA37JumpToMarsMenu = 20; +const tExtraID kTinyTSA37CancelMars = 21; +const tExtraID kTinyTSA37JumpToMars = 22; +const tExtraID kTinyTSA37MarsToAI6 = 23; +const tExtraID kTinyTSA37PegasusAI6 = 24; +const tExtraID kTinyTSA37AI6ToMars = 25; +const tExtraID kTinyTSA37MarsToDepart = 26; +const tExtraID kTinyTSA37JumpToNoradMenu = 27; +const tExtraID kTinyTSA37CancelNorad = 28; +const tExtraID kTinyTSA37JumpToNorad = 29; +const tExtraID kTinyTSA37NoradToAI7 = 30; +const tExtraID kTinyTSA37PegasusAI7 = 31; +const tExtraID kTinyTSA37AI7ToNorad = 32; +const tExtraID kTinyTSA37NoradToDepart = 33; +const tExtraID kTinyTSA37EnvironmentalScan = 34; +const tExtraID kTinyTSA37DownloadToMainMenu = 35; +const tExtraID kTinyTSA37DownloadToOpMemReview = 36; +const tExtraID kTinyTSA37OpMemReviewToMainMenu = 37; + +TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) { +} + +void TinyTSA::start() { + g_energyMonitor->stopEnergyDraining(); + Neighborhood::start(); +} + +Common::String TinyTSA::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + default: + movieName = "Images/AI/TSA/XT04"; + break; + } + } + + return movieName; +} + +Common::String TinyTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTinyTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTinyTSA37JumpToNoradMenu); + break; + case kActivationTinyTSAJumpToMars: + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTinyTSA37JumpToMarsMenu); + break; + case kActivationTinyTSAJumpToWSC: + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTinyTSA37JumpToWSCMenu); + break; + default: + showMainJumpMenu(); + break; + } + + g_AIChip->clearClicked(); + } + + return movieName; +} + +void TinyTSA::loadAmbientLoops() { + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +} + +int16 TinyTSA::getStaticCompassAngle(const tRoomID room, const tDirectionConstant dir) { + return Neighborhood::getStaticCompassAngle(room, dir) - kCompassShift; +} + + +uint16 TinyTSA::getDateResID() const { + return kDate2318ID; +} + +tInputBits TinyTSA::getInputFilter() { + // Can't move forward... + return Neighborhood::getInputFilter() & ~(kFilterUpButton | kFilterUpAuto); +} + +void TinyTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTinyTSA37NorthJumpMenuSpotID: + warning("jump menu spot"); + return; + case kTinyTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTinyTSA37JumpToNorad, 0, kFilterNoInput); + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTinyTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTinyTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTinyTSA37JumpToMars, 0, kFilterNoInput); + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTinyTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTinyTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTinyTSA37JumpToWSC, 0, kFilterNoInput); + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTinyTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTinyTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + } + } + + Neighborhood::clickInHotspot(input, clickedSpot); +} + +void TinyTSA::showMainJumpMenu() { + tExtraID jumpMenuView = kTinyTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationTinyTSAMainJumpMenu); +} + +void TinyTSA::checkContinuePoint(const tRoomID, const tDirectionConstant) { + makeContinuePoint(); +} + +void TinyTSA::arriveAt(const tRoomID room, const tDirectionConstant direction) { + Neighborhood::arriveAt(room, direction); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTinyTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + } +} + +void TinyTSA::receiveNotification(Notification *notification, const tNotificationFlags flags) { + tExtraID lastExtra = _lastExtra; + + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTinyTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTinyTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTinyTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kTinyTSA37DownloadToMainMenu: + case kTinyTSA37OpMemReviewToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTinyTSA37JumpToNoradMenu: + setCurrentActivation(kActivationTinyTSAJumpToNorad); + break; + case kTinyTSA37JumpToMarsMenu: + setCurrentActivation(kActivationTinyTSAJumpToMars); + break; + case kTinyTSA37JumpToWSCMenu: + setCurrentActivation(kActivationTinyTSAJumpToWSC); + break; + case kTinyTSA37CancelNorad: + case kTinyTSA37CancelMars: + case kTinyTSA37CancelWSC: + showMainJumpMenu(); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void TinyTSA::arriveFromNorad() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromMars() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromWSC() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String TinyTSA::getNavMovieName() { + return "Images/TSA/Tiny TSA.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.h b/engines/pegasus/neighborhood/tsa/tinytsa.h new file mode 100755 index 0000000000..705010ece5 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.h @@ -0,0 +1,71 @@ +/* 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_TSA_TINYTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// Room IDs. + +const tRoomID kTinyTSA37 = 0; + +class TinyTSA : public Neighborhood { +public: + TinyTSA(InputHandler *, PegasusEngine *); + virtual ~TinyTSA() {} + + virtual uint16 getDateResID() const; + + void start(); + + void checkContinuePoint(const tRoomID, const tDirectionConstant); + +protected: + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const tRoomID, const tDirectionConstant); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + tInputBits getInputFilter(); + void arriveAt(const tRoomID, const tDirectionConstant); + void showMainJumpMenu(); + void receiveNotification(Notification *, const tNotificationFlags); + + Common::String getNavMovieName(); + Common::String getSoundSpotsName() { return ""; } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/turn.cpp b/engines/pegasus/neighborhood/turn.cpp new file mode 100755 index 0000000000..6b66b32d28 --- /dev/null +++ b/engines/pegasus/neighborhood/turn.cpp @@ -0,0 +1,63 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/turn.h" + +namespace Pegasus { + +void TurnTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].turnDirection = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].endDirection = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Turn[%d]: %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].turnDirection, _entries[i].altCode, _entries[i].endDirection); + } +} + +void TurnTable::clear() { + _entries.clear(); +} + +TurnTable::Entry TurnTable::findEntry(tRoomID room, tDirectionConstant direction, tTurnDirection turnDirection, tAlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].turnDirection == turnDirection && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/turn.h b/engines/pegasus/neighborhood/turn.h new file mode 100755 index 0000000000..48970d745e --- /dev/null +++ b/engines/pegasus/neighborhood/turn.h @@ -0,0 +1,69 @@ +/* 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_TURN_H +#define PEGASUS_NEIGHBORHOOD_TURN_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class TurnTable { +public: + TurnTable() {} + ~TurnTable() {} + + static const uint32 getResTag() { return MKTAG('T', 'u', 'r', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { endDirection = kNoDirection; } + bool isEmpty() { return endDirection == kNoDirection; } + + tRoomID room; + tDirectionConstant direction; + tTurnDirection turnDirection; + tAlternateID altCode; + tDirectionConstant endDirection; + }; + + Entry findEntry(tRoomID room, tDirectionConstant direction, tTurnDirection turnDirection, tAlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/view.cpp b/engines/pegasus/neighborhood/view.cpp new file mode 100755 index 0000000000..e57fc88a13 --- /dev/null +++ b/engines/pegasus/neighborhood/view.cpp @@ -0,0 +1,60 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/view.h" + +namespace Pegasus { + +void ViewTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].time = stream->readUint32BE(); + debug(0, "View[%d]: %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].time); + } +} + +void ViewTable::clear() { + _entries.clear(); +} + +ViewTable::Entry ViewTable::findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace pegasus diff --git a/engines/pegasus/neighborhood/view.h b/engines/pegasus/neighborhood/view.h new file mode 100755 index 0000000000..6a732d507e --- /dev/null +++ b/engines/pegasus/neighborhood/view.h @@ -0,0 +1,68 @@ +/* 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_VIEW_H +#define PEGASUS_NEIGHBORHOOD_VIEW_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ViewTable { +public: + ViewTable() {} + ~ViewTable() {} + + static const uint32 getResTag() { return MKTAG('V', 'i', 'e', 'w'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { time = 0xffffffff; } + bool isEmpty() { return time == 0xffffffff; } + + tRoomID room; + tDirectionConstant direction; + tAlternateID altCode; + TimeValue time; + }; + + Entry findEntry(tRoomID room, tDirectionConstant direction, tAlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp new file mode 100755 index 0000000000..942289038f --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.cpp @@ -0,0 +1,70 @@ +/* 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 "common/textconsole.h" + +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +void ZoomTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + debug(0, "Zoom[%d]: %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].room, _entries[i].direction); + stream->readByte(); // alignment + } +} + +void ZoomTable::clear() { + _entries.clear(); +} + +ZoomTable::Entry::Entry() { + hotspot = kNoHotSpotID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + room = kNoRoomID; + direction = kNoDirection; +} + +ZoomTable::Entry ZoomTable::findEntry(tHotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus
\ No newline at end of file diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h new file mode 100755 index 0000000000..4046a76827 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.h @@ -0,0 +1,69 @@ +/* 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_ZOOM_H +#define PEGASUS_NEIGHBORHOOD_ZOOM_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ZoomTable { +public: + ZoomTable() {} + ~ZoomTable() {} + + static const uint32 getResTag() { return MKTAG('Z', 'o', 'o', 'm'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry(); + bool isEmpty() { return movieStart == 0xffffffff; } + + tHotSpotID hotspot; + TimeValue movieStart; + TimeValue movieEnd; + tRoomID room; + tDirectionConstant direction; + }; + + Entry findEntry(tHotSpotID hotspot); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/notification.cpp b/engines/pegasus/notification.cpp new file mode 100755 index 0000000000..5ded610e28 --- /dev/null +++ b/engines/pegasus/notification.cpp @@ -0,0 +1,149 @@ +/* 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/constants.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +typedef tReceiverList::iterator tReceiverIterator; + +Notification::Notification(const tNotificationID id, NotificationManager *owner) : IDObject(id) { + _owner = owner; + _currentFlags = kNoNotificationFlags; + if (_owner) + _owner->addNotification(this); +} + +Notification::~Notification() { + for (tReceiverIterator it = _receivers.begin(); it != _receivers.end(); it++) + it->receiver->newNotification(NULL); + + if (_owner) + _owner->removeNotification(this); +} + +// Selectively set or clear notificiation bits. +// Wherever mask is 0, leave existing bits untouched. +// Wherever mask is 1, set bit equivalent to flags. +void Notification::notifyMe(NotificationReceiver *receiver, tNotificationFlags flags, tNotificationFlags mask) { + for (tReceiverIterator it = _receivers.begin(); it != _receivers.end(); it++) { + if (it->receiver == receiver) { + it->mask = (it->mask & ~mask) | (flags & mask); + receiver->newNotification(this); + return; + } + } + + tReceiverEntry newEntry; + newEntry.receiver = receiver; + newEntry.mask = flags; + _receivers.push_back(newEntry); + + receiver->newNotification(this); +} + +void Notification::cancelNotification(NotificationReceiver *receiver) { + for (tReceiverIterator it = _receivers.begin(); it != _receivers.end();) { + if (it->receiver == receiver) + it = _receivers.erase(it); + else + it++; + } +} + +void Notification::setNotificationFlags(tNotificationFlags flags, tNotificationFlags mask) { + _currentFlags = (_currentFlags & ~mask) | flags; +} + +void Notification::checkReceivers() { + tNotificationFlags currentFlags = _currentFlags; + _currentFlags = kNoNotificationFlags; + + for (tReceiverIterator it = _receivers.begin(); it != _receivers.end(); it++) + if (it->mask & currentFlags) + it->receiver->receiveNotification(this, currentFlags); +} + +// Receiver entries are equal if their receivers are equal. + +int operator==(const tReceiverEntry &entry1, const tReceiverEntry &entry2) { + return entry1.receiver == entry2.receiver; +} + +int operator!=(const tReceiverEntry &entry1, const tReceiverEntry &entry2) { + return entry1.receiver != entry2.receiver; +} + +NotificationReceiver::NotificationReceiver() { + _notification = NULL; +} + +NotificationReceiver::~NotificationReceiver() { + if (_notification) + _notification->cancelNotification(this); +} + +void NotificationReceiver::receiveNotification(Notification *, const tNotificationFlags) { +} + +void NotificationReceiver::newNotification(Notification *notification) { + _notification = notification; +} + +typedef tNotificationList::iterator tNotificationIterator; + +NotificationManager::NotificationManager() { +} + +NotificationManager::~NotificationManager() { + detachNotifications(); +} + +void NotificationManager::addNotification(Notification *notification) { + _notifications.push_back(notification); +} + +void NotificationManager::removeNotification(Notification *notification) { + for (tNotificationIterator it = _notifications.begin(); it != _notifications.end();) { + if ((*it) == notification) + it = _notifications.erase(it); + else + it++; + } +} + +void NotificationManager::detachNotifications() { + for (tNotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + (*it)->_owner = 0; +} + +void NotificationManager::checkNotifications() { + for (tNotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + if ((*it)->_currentFlags != kNoNotificationFlags) + (*it)->checkReceivers(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/notification.h b/engines/pegasus/notification.h new file mode 100755 index 0000000000..ded66478fc --- /dev/null +++ b/engines/pegasus/notification.h @@ -0,0 +1,122 @@ +/* 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_NOTIFICATION_H +#define PEGASUS_NOTIFICATION_H + +#include "common/list.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class NotificationManager; +class NotificationReceiver; + +struct tReceiverEntry { + NotificationReceiver *receiver; + tNotificationFlags mask; +}; + +int operator==(const tReceiverEntry &entry1, const tReceiverEntry &entry1); +int operator!=(const tReceiverEntry &entry1, const tReceiverEntry &entry1); + +typedef Common::List<tReceiverEntry> tReceiverList; + +/* + A notification can have 32 flags associated with it, which can be user-defined. +*/ + +class Notification : public IDObject { +friend class NotificationManager; + +public: + Notification(const tNotificationID id, NotificationManager *owner); + virtual ~Notification(); + + // NotifyMe will have this receiver notified when any of the specified notification + // flags are set. + // If there is already a notification set for this receiver, NotifyMe does a bitwise + // OR with the receiver's current notification flags. + + // Can selectively set or clear notification bits by using the flags and mask argument. + + void notifyMe(NotificationReceiver*, tNotificationFlags flags, tNotificationFlags mask); + void cancelNotification(NotificationReceiver *receiver); + + void setNotificationFlags(tNotificationFlags flags, tNotificationFlags mask); + tNotificationFlags getNotificationFlags() { return _currentFlags; } + + void clearNotificationFlags() { setNotificationFlags(0, ~(tNotificationFlags)0); } + +protected: + void checkReceivers(); + + NotificationManager *_owner; + tReceiverList _receivers; + tNotificationFlags _currentFlags; +}; + +class NotificationReceiver { +friend class Notification; + +public: + NotificationReceiver(); + virtual ~NotificationReceiver(); + +protected: + // ReceiveNotification is called automatically whenever a notification that this + // receiver depends on has its flags set + + virtual void receiveNotification(Notification *, const tNotificationFlags); + virtual void newNotification(Notification *notification); + +private: + Notification *_notification; +}; + +typedef Common::List<Notification *> tNotificationList; + +class NotificationManager : public NotificationReceiver { +friend class Notification; + +public: + NotificationManager(); + virtual ~NotificationManager(); + + void checkNotifications(); + +protected: + void addNotification(Notification *notification); + void removeNotification(Notification *notification); + void detachNotifications(); + + tNotificationList _notifications; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp new file mode 100644 index 0000000000..e68861e63e --- /dev/null +++ b/engines/pegasus/pegasus.cpp @@ -0,0 +1,1880 @@ +/* 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/config-manager.h" +#include "common/error.h" +#include "common/events.h" +#include "common/fs.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/translation.h" +#include "common/random.h" +#include "base/plugins.h" +#include "base/version.h" +#include "gui/saveload.h" +#include "video/qt_decoder.h" + +#include "pegasus/console.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/menu.h" +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/gascanister.h" +#include "pegasus/items/inventory/inventoryitem.h" +#include "pegasus/items/inventory/keycard.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), InputHandler(0), _gameDescription(gamedesc), + _shellNotification(kJMPDCShellNotificationID, this), _returnHotspot(kInfoReturnSpotID), _itemDragger(this), _bigInfoMovie(kNoDisplayElement), + _smallInfoMovie(kNoDisplayElement) { + _continuePoint = 0; + _saveAllowed = _loadAllowed = true; + _gameMenu = 0; + _deathReason = kDeathStranded; + _neighborhood = 0; + _FXLevel = 0x80; + _ambientLevel = 0x80; + _gameMode = kNoMode; + _switchModesSync = false; + _draggingItem = 0; + _dragType = kDragNoDrag; + _idlerHead = 0; +} + +PegasusEngine::~PegasusEngine() { + delete _resFork; + delete _console; + delete _cursor; + delete _continuePoint; + delete _gameMenu; + delete _neighborhood; + delete _rnd; + + // NOTE: This must be deleted last! + delete _gfx; +} + +Common::Error PegasusEngine::run() { + _console = new PegasusConsole(this); + _gfx = new GraphicsManager(this); + _resFork = new Common::MacResManager(); + _cursor = new Cursor(); + _rnd = new Common::RandomSource("Pegasus"); + + if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork()) + error("Could not load JMP PP Resources"); + + // Initialize items + createItems(); + + // Initialize cursors + _cursor->addCursorFrames(0x80); // Main + _cursor->addCursorFrames(900); // Mars Shuttle + + // Initialize the item dragger bounds + _itemDragger.setHighlightBounds(); + + if (!isDemo() && !detectOpeningClosingDirectory()) { + Common::String message = "Missing intro directory. "; + + // Give Mac OS X a more specific message because we can +#ifdef MACOSX + message += "Make sure \"Opening/Closing\" is present."; +#else + message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\"."; +#endif + + GUIErrorMessage(message); + warning("%s", message.c_str()); + return Common::kNoGameDataFoundError; + } + + // Set up input + InputHandler::setInputHandler(this); + allowInput(true); + + // Set up inventories + _items.setWeightLimit(9); + _items.setOwnerID(kPlayerID); + _biochips.setWeightLimit(8); + _biochips.setOwnerID(kPlayerID); + + // Start up the first notification + _shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags); + _shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag); + + _returnHotspot.setArea(Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); + _returnHotspot.setHotspotFlags(kInfoReturnSpotFlag); + g_allHotspots.push_back(&_returnHotspot); + + _screenDimmer.setBounds(Common::Rect(0, 0, 640, 480)); + _screenDimmer.setDisplayOrder(kScreenDimmerOrder); + + while (!shouldQuit()) { + processShell(); + _system->delayMillis(10); // Ease off the CPU + } + + return Common::kNoError; +} + +bool PegasusEngine::detectOpeningClosingDirectory() { + // We need to detect what our Opening/Closing directory is listed as + // On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash + // Mac OS X will display this as 'Opening:Closing' and we can use that directly + // On other systems, users will need to rename to "Opening_Closing" + + Common::FSNode gameDataDir(ConfMan.get("path")); + gameDataDir = gameDataDir.getChild("Images"); + + if (!gameDataDir.exists()) + return false; + + Common::FSList fsList; + if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true)) + return false; + + for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) { + Common::String name = fsList[i].getName(); + + if (name.equalsIgnoreCase("Opening:Closing")) + _introDirectory = name; + else if (name.equalsIgnoreCase("Opening_Closing")) + _introDirectory = name; + } + + if (_introDirectory.empty()) + return false; + + debug(0, "Detected intro location as '%s'", _introDirectory.c_str()); + _introDirectory = Common::String("Images/") + _introDirectory; + return true; +} + +void PegasusEngine::createItems() { + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80); + + uint16 entryCount = res->readUint16BE(); + + for (uint16 i = 0; i < entryCount; i++) { + tItemID itemID = res->readUint16BE(); + tNeighborhoodID neighborhoodID = res->readUint16BE(); + tRoomID roomID = res->readUint16BE(); + tDirectionConstant direction = res->readByte(); + res->readByte(); // alignment + + createItem(itemID, neighborhoodID, roomID, direction); + } + + delete res; +} + +void PegasusEngine::createItem(tItemID itemID, tNeighborhoodID neighborhoodID, tRoomID roomID, tDirectionConstant direction) { + switch (itemID) { + case kInterfaceBiochip: + // Unused in game, but still in the data and we need to create + // it because it's saved/loaded from save files. + new BiochipItem(itemID, neighborhoodID, roomID, direction); + break; + case kAIBiochip: + new AIChip(itemID, neighborhoodID, roomID, direction); + break; + case kPegasusBiochip: + new PegasusChip(itemID, neighborhoodID, roomID, direction); + break; + case kOpticalBiochip: + new OpticalChip(itemID, neighborhoodID, roomID, direction); + break; + case kMapBiochip: + // TODO: Implement this biochip + new BiochipItem(itemID, neighborhoodID, roomID, direction); + break; + case kRetinalScanBiochip: + new RetScanChip(itemID, neighborhoodID, roomID, direction); + break; + case kShieldBiochip: + new ShieldChip(itemID, neighborhoodID, roomID, direction); + break; + case kAirMask: + new AirMask(itemID, neighborhoodID, roomID, direction); + break; + case kKeyCard: + new KeyCard(itemID, neighborhoodID, roomID, direction); + break; + case kGasCanister: + new GasCanister(itemID, neighborhoodID, roomID, direction); + break; + default: + // Everything else is a normal inventory item + new InventoryItem(itemID, neighborhoodID, roomID, direction); + break; + } +} + +void PegasusEngine::runIntro() { + bool skipped = false; + + Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) { + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); + _system->updateScreen(); + } + + Input input; + InputHandler::getCurrentInputDevice()->getInput(input, kFilterAllInput); + if (input.anyInput()) + skipped = true; + + _system->delayMillis(10); + } + } + + delete video; + + if (shouldQuit() || skipped) + return; + + video = new Video::QuickTimeDecoder(); + + if (!video->loadFile(_introDirectory + "/Big Movie.movie")) + error("Could not load intro movie"); + + video->seekToTime(Audio::Timestamp(0, 10 * 600, 600)); + + playMovieScaled(video, 0, 0); + + delete video; +} + +void PegasusEngine::showLoadDialog() { + GUI::SaveLoadChooser slc(_("Load game:"), _("Load")); + slc.setSaveMode(false); + + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + + if (slot >= 0) { + warning("TODO: Load game"); + } + + slc.close(); +} + +GUI::Debugger *PegasusEngine::getDebugger() { + return _console; +} + +void PegasusEngine::addIdler(Idler *idler) { + idler->_nextIdler = _idlerHead; + if (_idlerHead) + _idlerHead->_prevIdler = idler; + idler->_prevIdler = 0; + _idlerHead = idler; +} + +void PegasusEngine::removeIdler(Idler *idler) { + if (idler->_prevIdler) + idler->_prevIdler->_nextIdler = idler->_nextIdler; + if (idler->_nextIdler) + idler->_nextIdler->_prevIdler = idler->_prevIdler; + if (idler == _idlerHead) + _idlerHead = idler->_nextIdler; + idler->_nextIdler = 0; + idler->_prevIdler = 0; +} + +void PegasusEngine::giveIdleTime() { + for (Idler *idler = _idlerHead; idler != 0; idler = idler->_nextIdler) + idler->useIdleTime(); +} + +void PegasusEngine::addTimeBase(TimeBase *timeBase) { + _timeBases.push_back(timeBase); +} + +void PegasusEngine::removeTimeBase(TimeBase *timeBase) { + _timeBases.remove(timeBase); +} + +bool PegasusEngine::loadFromStream(Common::ReadStream *stream) { + // Dispose currently running stuff + useMenu(0); + useNeighborhood(0); + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + _currentItemID = kNoItemID; + _currentBiochipID = kNoItemID; + + // Signature + uint32 creator = stream->readUint32BE(); + if (creator != kPegasusPrimeCreator) { + warning("Bad save creator '%s'", tag2str(creator)); + return false; + } + + uint32 gameType = stream->readUint32BE(); + int saveType; + + switch (gameType) { + case kPegasusPrimeDisk1GameType: + case kPegasusPrimeDisk2GameType: + case kPegasusPrimeDisk3GameType: + case kPegasusPrimeDisk4GameType: + saveType = kNormalSave; + break; + case kPegasusPrimeContinueType: + saveType = kContinueSave; + break; + default: + // There are five other possible game types on the Pippin + // version, but hopefully we don't see any of those here + warning("Unhandled pegasus game type '%s'", tag2str(gameType)); + return false; + } + + uint32 version = stream->readUint32BE(); + if (version != kPegasusPrimeVersion) { + warning("Where did you get this save? It's a beta (v%04x)!", version & 0x7fff); + return false; + } + + // Game State + GameState.readGameState(stream); + + // Energy + setLastEnergyValue(stream->readUint32BE() >> 16); + + // Death reason + setEnergyDeathReason(stream->readByte()); + + // TODO: This is as far as we can go right now (until I implement the mapping biochip and AI rules loading) + return true; + + // Items + g_allItems.readFromStream(stream); + + // Inventory + uint32 itemCount = stream->readUint32BE(); + + if (itemCount > 0) { + for (uint32 i = 0; i < itemCount; i++) { + InventoryItem *inv = (InventoryItem *)g_allItems.findItemByID((tItemID)stream->readUint16BE()); + addItemToInventory(inv); + } + + g_interface->setCurrentInventoryItemID((tItemID)stream->readUint16BE()); + } + + // Biochips + uint32 biochipCount = stream->readUint32BE(); + + if (biochipCount > 0) { + for (uint32 i = 0; i < biochipCount; i++) { + BiochipItem *biochip = (BiochipItem *)g_allItems.findItemByID((tItemID)stream->readUint16BE()); + addItemToBiochips(biochip); + } + + g_interface->setCurrentBiochipID((tItemID)stream->readUint16BE()); + } + + + // TODO: Disc check + + // Jump to environment + jumpToNewEnvironment(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + performJump(GameState.getCurrentNeighborhood()); + + // AI rules + if (g_AIArea) + g_AIArea->readAIRules(stream); + + startNeighborhood(); + + // Make a new continue point if this isn't already one + if (saveType == kNormalSave) + makeContinuePoint(); + + return true; +} + +bool PegasusEngine::writeToStream(Common::WriteStream *stream, int saveType) { + // Not ready yet! :P + return false; + + // Signature + stream->writeUint32BE(kPegasusPrimeCreator); + + if (saveType == kNormalSave) { + // TODO: Disc check + stream->writeUint32BE(kPegasusPrimeDisk1GameType); + } else { // Continue + stream->writeUint32BE(kPegasusPrimeContinueType); + } + + stream->writeUint32BE(kPegasusPrimeVersion); + + // Game State + GameState.writeGameState(stream); + + // Energy + stream->writeUint32BE(getSavedEnergyValue() << 16); + + // Death reason + stream->writeByte(getEnergyDeathReason()); + + // Items + g_allItems.writeToStream(stream); + + // Inventory + uint32 itemCount = _items.getNumItems(); + stream->writeUint32BE(itemCount); + + if (itemCount > 0) { + for (uint32 i = 0; i < itemCount; i++) + stream->writeUint16BE(_items.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID()); + } + + // Biochips + uint32 biochipCount = _biochips.getNumItems(); + stream->writeUint32BE(biochipCount); + + if (itemCount > 0) { + for (uint32 i = 0; i < biochipCount; i++) + stream->writeUint16BE(_biochips.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentBiochip()->getObjectID()); + } + + // AI rules + if (g_AIArea) + g_AIArea->writeAIRules(stream); + + return true; +} + +void PegasusEngine::makeContinuePoint() { + delete _continuePoint; + + Common::MemoryWriteStreamDynamic newPoint(DisposeAfterUse::NO); + writeToStream(&newPoint, kContinueSave); + _continuePoint = new Common::MemoryReadStream(newPoint.getData(), newPoint.size(), DisposeAfterUse::YES); +} + +void PegasusEngine::loadFromContinuePoint() { + // Failure to load a continue point is fatal + + if (!_continuePoint) + error("Attempting to load from non-existant continue point"); + + if (!loadFromStream(_continuePoint)) + error("Failed loading continue point"); +} + +Common::Error PegasusEngine::loadGameState(int slot) { + Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav"); + Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]); + if (!loadFile) + return Common::kUnknownError; + + bool valid = loadFromStream(loadFile); + delete loadFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) { + Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str()); + Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output); + if (!saveFile) + return Common::kUnknownError; + + bool valid = writeToStream(saveFile, kNormalSave); + delete saveFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +void PegasusEngine::receiveNotification(Notification *notification, const tNotificationFlags flags) { + if (&_shellNotification == notification) { + switch (flags) { + case kGameStartingFlag: { + if (!isDemo()) + runIntro(); + else + showTempScreen("Images/Demo/NGsplashScrn.pict"); + + if (shouldQuit()) + return; + + useMenu(new MainMenu()); + _gfx->invalRect(Common::Rect(0, 0, 640, 480)); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + break; + } + case kPlayerDiedFlag: + doDeath(); + break; + case kNeedNewJumpFlag: + performJump(GameState.getNextNeighborhood()); + startNeighborhood(); + break; + default: + break; + } + } +} + +void PegasusEngine::checkCallBacks() { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->checkCallBacks(); +} + +void PegasusEngine::resetIntroTimer() { + // TODO +} + +void PegasusEngine::delayShell(TimeValue time, TimeScale scale) { + if (time == 0 || scale == 0) + return; + + uint32 startTime = g_system->getMillis(); + uint32 timeInMillis = time * 1000 / scale; + + while (g_system->getMillis() < startTime + timeInMillis) { + checkCallBacks(); + _gfx->updateDisplay(); + } +} + +void PegasusEngine::useMenu(GameMenu *newMenu) { + if (_gameMenu) { + _gameMenu->restorePreviousHandler(); + delete _gameMenu; + } + + _gameMenu = newMenu; + + if (_gameMenu) + _gameMenu->becomeCurrentHandler(); +} + +bool PegasusEngine::checkGameMenu() { + tGameMenuCommand command = kMenuCmdNoCommand; + + if (_gameMenu) { + command = _gameMenu->getLastCommand(); + if (command != kMenuCmdNoCommand) { + _gameMenu->clearLastCommand(); + doGameMenuCommand(command); + } + } + + return command != kMenuCmdNoCommand; +} + +void PegasusEngine::doGameMenuCommand(const tGameMenuCommand command) { + switch (command) { + case kMenuCmdStartAdventure: + GameState.setWalkthroughMode(false); + startNewGame(); + break; + case kMenuCmdCredits: + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + // TODO: Stop intro timer + _gfx->doFadeOutSync(); + useMenu(new CreditsMenu()); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } + break; + case kMenuCmdQuit: + case kMenuCmdDeathQuitDemo: + if (isDemo()) + showTempScreen("Images/Demo/NGquitScrn.pict"); + _system->quit(); + break; + case kMenuCmdOverview: + // TODO: Stop intro timer + doInterfaceOverview(); + resetIntroTimer(); + break; + case kMenuCmdStartWalkthrough: + GameState.setWalkthroughMode(true); + startNewGame(); + break; + case kMenuCmdRestore: + case kMenuCmdDeathRestore: + error("Load game"); + break; + case kMenuCmdCreditsMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdDeathContinue: + if (((DeathMenu *)_gameMenu)->playerWon()) { + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->clearScreen(); + _gfx->updateDisplay(); + + Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile(_introDirectory + "/Closing.movie")) + error("Could not load closing movie"); + + uint16 x = (640 - video->getWidth() * 2) / 2; + uint16 y = (480 - video->getHeight() * 2) / 2; + + playMovieScaled(video, x, y); + + delete video; + + if (shouldQuit()) + return; + + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + } + } else { + loadFromContinuePoint(); + } + break; + case kMenuCmdDeathMainMenuDemo: + case kMenuCmdDeathMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + if (!isDemo()) + resetIntroTimer(); + break; + case kMenuCmdPauseSave: + error("Save game"); + break; + case kMenuCmdPauseContinue: + pauseMenu(false); + break; + case kMenuCmdPauseRestore: + error("Load game"); + break; + case kMenuCmdPauseQuit: + _gfx->doFadeOutSync(); + throwAwayEverything(); + pauseMenu(false); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + if (!isDemo()) + resetIntroTimer(); + break; + case kMenuCmdNoCommand: + break; + default: + error("Unknown menu command %d", command); + } +} + +void PegasusEngine::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (!checkGameMenu()) + shellGameInput(input, cursorSpot); + + // Handle the console here + if (input.isConsoleRequested()) { + _console->attach(); + _console->onFrame(); + } + + // TODO: Save request + // TODO: Load request +} + +void PegasusEngine::doInterfaceOverview() { + static const short kNumOverviewSpots = 11; + static const Common::Rect overviewSpots[kNumOverviewSpots] = { + Common::Rect(354, 318, 354 + 204, 318 + 12), + Common::Rect(211, 34, 211 + 114, 34 + 28), + Common::Rect(502, 344, 502 + 138, 344 + 120), + Common::Rect(132, 40, 132 + 79, 40 + 22), + Common::Rect(325, 40, 332 + 115, 40 + 22), + Common::Rect(70, 318, 70 + 284, 318 + 12), + Common::Rect(76, 334, 76 + 96, 334 + 96), + Common::Rect(64, 64, 64 + 512, 64 + 256), + Common::Rect(364, 334, 364 + 96, 334 + 96), + Common::Rect(172, 334, 172 + 192, 334 + 96), + Common::Rect(542, 36, 542 + 58, 36 + 20) + }; + + _gfx->doFadeOutSync(); + useMenu(0); + + Picture leftBackground(kNoDisplayElement); + leftBackground.initFromPICTFile("Images/Interface/OVLeft.mac"); + leftBackground.setDisplayOrder(0); + leftBackground.moveElementTo(kBackground1Left, kBackground1Top); + leftBackground.startDisplaying(); + leftBackground.show(); + + Picture topBackground(kNoDisplayElement); + topBackground.initFromPICTFile("Images/Interface/OVTop.mac"); + topBackground.setDisplayOrder(0); + topBackground.moveElementTo(kBackground2Left, kBackground2Top); + topBackground.startDisplaying(); + topBackground.show(); + + Picture rightBackground(kNoDisplayElement); + rightBackground.initFromPICTFile("Images/Interface/OVRight.mac"); + rightBackground.setDisplayOrder(0); + rightBackground.moveElementTo(kBackground3Left, kBackground3Top); + rightBackground.startDisplaying(); + rightBackground.show(); + + Picture bottomBackground(kNoDisplayElement); + bottomBackground.initFromPICTFile("Images/Interface/OVBottom.mac"); + bottomBackground.setDisplayOrder(0); + bottomBackground.moveElementTo(kBackground4Left, kBackground4Top); + bottomBackground.startDisplaying(); + bottomBackground.show(); + + Picture controllerHighlight(kNoDisplayElement); + controllerHighlight.initFromPICTFile("Images/Interface/OVcontrollerHilite.mac"); + controllerHighlight.setDisplayOrder(0); + controllerHighlight.moveElementTo(kOverviewControllerLeft, kOverviewControllerTop); + controllerHighlight.startDisplaying(); + + Movie overviewText(kNoDisplayElement); + overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie"); + overviewText.setDisplayOrder(0); + overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop); + overviewText.startDisplaying(); + overviewText.show(); + overviewText.redrawMovieWorld(); + + DropHighlight highlight(kNoDisplayElement); + highlight.setDisplayOrder(1); + highlight.startDisplaying(); + highlight.setHighlightThickness(4); + highlight.setHighlightColor(g_system->getScreenFormat().RGBToColor(239, 239, 0)); + highlight.setHighlightCornerDiameter(8); + + Input input; + InputHandler::getCurrentInputDevice()->getInput(input, kFilterAllInput); + + Common::Point cursorLoc; + input.getInputLocation(cursorLoc); + + uint16 i; + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + TimeValue time; + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + + _cursor->setCurrentFrameIndex(3); + _cursor->show(); + + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + + for (;;) { + InputHandler::getCurrentInputDevice()->getInput(input, kFilterAllInput); + + if (input.anyInput() || shouldQuit()) // TODO: Check for save/load requests too + break; + + input.getInputLocation(cursorLoc); + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + + refreshDisplay(); + } + + if (shouldQuit()) + return; + + highlight.hide(); + _cursor->hide(); + + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + + // TODO: Cancel save/load requests? +} + +void PegasusEngine::showTempScreen(const Common::String &fileName) { + _gfx->doFadeOutSync(); + + Picture picture(0); + picture.initFromPICTFile(fileName); + picture.setDisplayOrder(kMaxAvailableOrder); + picture.startDisplaying(); + picture.show(); + _gfx->updateDisplay(); + + _gfx->doFadeInSync(); + + // Wait for the next event + bool done = false; + while (!shouldQuit() && !done) { + Common::Event event; + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_KEYDOWN: + done = true; + break; + default: + break; + } + } + + _system->delayMillis(10); + } +} + +void PegasusEngine::refreshDisplay() { + giveIdleTime(); + _gfx->updateDisplay(); +} + +void PegasusEngine::resetEnergyDeathReason() { + switch (getCurrentNeighborhoodID()) { + case kMarsID: + _deathReason = kDeathArrestedInMars; + break; + case kNoradAlphaID: + case kNoradDeltaID: + _deathReason = kDeathArrestedInNorad; + break; + case kWSCID: + _deathReason = kDeathArrestedInWSC; + break; + default: + _deathReason = kDeathStranded; + break; + } +} + +bool PegasusEngine::playerHasItem(const Item *item) { + return playerHasItemID(item->getObjectID()); +} + +bool PegasusEngine::playerHasItemID(const tItemID itemID) { + return itemInInventory(itemID) || itemInBiochips(itemID); +} + +InventoryItem *PegasusEngine::getCurrentInventoryItem() { + if (g_interface) + return g_interface->getCurrentInventoryItem(); + + return 0; +} + +bool PegasusEngine::itemInInventory(InventoryItem *item) { + return _items.itemInInventory(item); +} + +bool PegasusEngine::itemInInventory(tItemID id) { + return _items.itemInInventory(id); +} + +BiochipItem *PegasusEngine::getCurrentBiochip() { + if (g_interface) + return g_interface->getCurrentBiochip(); + + return 0; +} + +bool PegasusEngine::itemInBiochips(BiochipItem *item) { + return _biochips.itemInInventory(item); +} + +bool PegasusEngine::itemInBiochips(tItemID id) { + return _biochips.itemInInventory(id); +} + +bool PegasusEngine::playerAlive() { + return (_shellNotification.getNotificationFlags() & kPlayerDiedFlag) == 0; +} + +Common::String PegasusEngine::getBriefingMovie() { + if (_neighborhood) + return _neighborhood->getBriefingMovie(); + + return Common::String(); +} + +Common::String PegasusEngine::getEnvScanMovie() { + if (_neighborhood) + return _neighborhood->getEnvScanMovie(); + + return Common::String(); +} + +uint PegasusEngine::getNumHints() { + if (_neighborhood) + return _neighborhood->getNumHints(); + + return 0; +} + +Common::String PegasusEngine::getHintMovie(uint hintNum) { + if (_neighborhood) + return _neighborhood->getHintMovie(hintNum); + + return Common::String(); +} + +bool PegasusEngine::canSolve() { + if (_neighborhood) + return _neighborhood->canSolve(); + + return false; +} + +void PegasusEngine::prepareForAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->prepareForAIHint(movieName); +} + +void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->cleanUpAfterAIHint(movieName); +} + +void PegasusEngine::jumpToNewEnvironment(const tNeighborhoodID neighborhoodID, const tRoomID roomID, const tDirectionConstant direction) { + GameState.setNextLocation(neighborhoodID, roomID, direction); + _shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag); +} + +void PegasusEngine::checkFlashlight() { + if (_neighborhood) + _neighborhood->checkFlashlight(); +} + +bool PegasusEngine::playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y) { + bool skipped = false; + + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + // Scale up the frame doing some simple scaling + Graphics::Surface scaledFrame; + scaledFrame.create(frame->w * 2, frame->h * 2, frame->format); + const byte *src = (const byte *)frame->pixels; + byte *dst1 = (byte *)scaledFrame.pixels; + byte *dst2 = (byte *)scaledFrame.pixels + scaledFrame.pitch; + + for (int i = 0; i < frame->h; i++) { + for (int j = 0; j < frame->w; j++) { + memcpy(dst1, src, frame->format.bytesPerPixel); + dst1 += frame->format.bytesPerPixel; + memcpy(dst1, src, frame->format.bytesPerPixel); + dst1 += frame->format.bytesPerPixel; + memcpy(dst2, src, frame->format.bytesPerPixel); + dst2 += frame->format.bytesPerPixel; + memcpy(dst2, src, frame->format.bytesPerPixel); + dst2 += frame->format.bytesPerPixel; + src += frame->format.bytesPerPixel; + } + + src += frame->pitch - frame->format.bytesPerPixel * frame->w; + dst1 += scaledFrame.pitch * 2 - scaledFrame.format.bytesPerPixel * scaledFrame.w; + dst2 += scaledFrame.pitch * 2 - scaledFrame.format.bytesPerPixel * scaledFrame.w; + } + + _system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); + _system->updateScreen(); + scaledFrame.free(); + } + + Input input; + InputHandler::getCurrentInputDevice()->getInput(input, kFilterAllInput); + if (input.anyInput()) + skipped = true; + + _system->delayMillis(10); + } + + return skipped; +} + +void PegasusEngine::die(const tDeathReason reason) { + Input dummy; + if (isDragging()) + _itemDragger.stopTracking(dummy); + + _deathReason = reason; + _shellNotification.setNotificationFlags(kPlayerDiedFlag, kPlayerDiedFlag); +} + +void PegasusEngine::doDeath() { + _gfx->doFadeOutSync(); + throwAwayEverything(); + useMenu(new DeathMenu(_deathReason)); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); +} + +void PegasusEngine::throwAwayEverything() { + if (_items.getNumItems() != 0) + _currentItemID = g_interface->getCurrentInventoryItem()->getObjectID(); + else + _currentItemID = kNoItemID; + + if (_biochips.getNumItems() != 0) + _currentItemID = g_interface->getCurrentBiochip()->getObjectID(); + else + _currentItemID = kNoItemID; + + useMenu(0); + useNeighborhood(0); + + delete g_interface; + g_interface = 0; +} + +void PegasusEngine::processShell() { + checkCallBacks(); + checkNotifications(); + InputHandler::pollForInput(); + refreshDisplay(); +} + +void PegasusEngine::createInterface() { + if (!g_interface) + new Interface(); + + g_interface->createInterface(); +} + +void PegasusEngine::setGameMode(const tGameMode newMode) { + if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) { + switchGameMode(newMode, _gameMode); + _gameMode = newMode; + } +} + +void PegasusEngine::switchGameMode(const tGameMode newMode, const tGameMode oldMode) { + // Start raising panels before lowering panels, to give the activating panel time + // to set itself up without cutting into the lowering panel's animation time. + + if (_switchModesSync) { + if (newMode == kModeInventoryPick) + raiseInventoryDrawerSync(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawerSync(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawerSync(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawerSync(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } else { + if (newMode == kModeInventoryPick) + raiseInventoryDrawer(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawer(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawer(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawer(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } +} + +bool PegasusEngine::canSwitchGameMode(const tGameMode newMode, const tGameMode oldMode) { + if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick) + return false; + if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick) + return false; + return true; +} + +bool PegasusEngine::itemInLocation(const tItemID itemID, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant direction) { + tNeighborhoodID itemNeighborhood; + tRoomID itemRoom; + tDirectionConstant itemDirection; + + Item *item = g_allItems.findItemByID(itemID); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction; +} + +tInventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) { + tInventoryResult result; + + do { + if (g_interface) + result = g_interface->addInventoryItem(item); + else + result = _items.addItem(item); + + // TODO + if (result == kTooMuchWeight) + error("Out of inventory space"); + } while (result != kInventoryOK); + + GameState.setTakenItem(item, true); + if (g_neighborhood) + g_neighborhood->pickedUpItem(item); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::useNeighborhood(Neighborhood *neighborhood) { + delete _neighborhood; + _neighborhood = neighborhood; + + if (_neighborhood) { + InputHandler::setInputHandler(_neighborhood); + _neighborhood->init(); + _neighborhood->moveNavTo(kNavAreaLeft, kNavAreaTop); + g_interface->setDate(_neighborhood->getDateResID()); + } else { + InputHandler::setInputHandler(this); + } +} + +void PegasusEngine::performJump(const tNeighborhoodID neighborhoodID) { + if (neighborhoodID == kNoradSubChaseID) + error("TODO: Sub chase"); + + if (_neighborhood) + useNeighborhood(0); + + Neighborhood *neighborhood; + makeNeighborhood(neighborhoodID, neighborhood); + useNeighborhood(neighborhood); +} + +void PegasusEngine::startNeighborhood() { + if (_currentItemID != kNoItemID) + g_interface->setCurrentInventoryItemID(_currentItemID); + + if (_currentBiochipID != kNoItemID) + g_interface->setCurrentBiochipID(_currentBiochipID); + + setGameMode(kModeNavigation); + + if (_neighborhood) + _neighborhood->start(); +} + +void PegasusEngine::startNewGame() { + // WORKAROUND: The original game ignored the menu difficulty + // setting. We're going to pass it through here so that + // the menu actually makes sense now. + bool isWalkthrough = GameState.getWalkthroughMode(); + GameState.resetGameState(); + GameState.setWalkthroughMode(isWalkthrough); + + // TODO: Enable erase + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->updateDisplay(); + + createInterface(); + + if (isDemo()) { + setLastEnergyValue(kFullEnergy); + jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + } else { + jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast); + } + + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + + BiochipItem *biochip = (BiochipItem *)g_allItems.findItemByID(kAIBiochip); + addItemToBiochips(biochip); + + if (isDemo()) { + biochip = (BiochipItem *)g_allItems.findItemByID(kPegasusBiochip); + addItemToBiochips(biochip); + biochip = (BiochipItem *)g_allItems.findItemByID(kMapBiochip); + addItemToBiochips(biochip); + InventoryItem *item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); + addItemToInventory(item); + item = (InventoryItem *)g_allItems.findItemByID(kJourneymanKey); + addItemToInventory(item); + _currentItemID = kJourneymanKey; + } else { + _currentItemID = kNoItemID; + } + + _currentBiochipID = kAIBiochip; + + // Clear jump notification flags and just perform the jump... + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + + performJump(GameState.getNextNeighborhood()); + + startNeighborhood(); +} + +void PegasusEngine::makeNeighborhood(tNeighborhoodID neighborhoodID, Neighborhood *&neighborhood) { + // TODO: CD check + + switch (neighborhoodID) { + case kCaldoriaID: + neighborhood = new Caldoria(g_AIArea, this); + break; + case kPrehistoricID: + neighborhood = new Prehistoric(g_AIArea, this); + break; + case kFullTSAID: + neighborhood = new FullTSA(g_AIArea, this); + break; + case kTinyTSAID: + neighborhood = new TinyTSA(g_AIArea, this); + break; + } +} + +bool PegasusEngine::wantsCursor() { + return _gameMenu == 0; +} + +void PegasusEngine::updateCursor(const Common::Point, const Hotspot *cursorSpot) { + if (_itemDragger.isTracking()) { + _cursor->setCurrentFrameIndex(5); + } else { + if (!cursorSpot) { + _cursor->setCurrentFrameIndex(0); + } else { + uint32 id = cursorSpot->getObjectID(); + + switch (id) { + case kCurrentItemSpotID: + if (countInventoryItems() != 0) + _cursor->setCurrentFrameIndex(4); + else + _cursor->setCurrentFrameIndex(0); + break; + default: + tHotSpotFlags flags = cursorSpot->getHotspotFlags(); + + if (flags & kZoomInSpotFlag) + _cursor->setCurrentFrameIndex(1); + else if (flags & kZoomOutSpotFlag) + _cursor->setCurrentFrameIndex(2); + else if (flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) + _cursor->setCurrentFrameIndex(4); + else if (flags & kJMPClickingSpotFlags) + _cursor->setCurrentFrameIndex(3); + else + _cursor->setCurrentFrameIndex(0); + } + } + } +} + +void PegasusEngine::toggleInventoryDisplay() { + if (_gameMode == kModeInventoryPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeInventoryPick); +} + +void PegasusEngine::toggleBiochipDisplay() { + if (_gameMode == kModeBiochipPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeBiochipPick); +} + +void PegasusEngine::showInfoScreen() { + if (g_neighborhood) { + // Break the input handler chain... + _savedHandler = InputHandler::getCurrentHandler(); + InputHandler::setInputHandler(this); + + Picture *pushPicture = ((Neighborhood *)g_neighborhood)->getTurnPushPicture(); + + _bigInfoMovie.shareSurface(pushPicture); + _smallInfoMovie.shareSurface(pushPicture); + + g_neighborhood->hideNav(); + + _smallInfoMovie.initFromMovieFile("Images/Items/Info Right Movie"); + _smallInfoMovie.setDisplayOrder(kInfoSpinOrder); + _smallInfoMovie.moveElementTo(kNavAreaLeft + 304, kNavAreaTop + 8); + _smallInfoMovie.moveMovieBoxTo(304, 8); + _smallInfoMovie.startDisplaying(); + _smallInfoMovie.show(); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.initFromMovieFile("Images/Items/Info Left Movie"); + _bigInfoMovie.setDisplayOrder(kInfoBackgroundOrder); + _bigInfoMovie.moveElementTo(kNavAreaLeft, kNavAreaTop); + _bigInfoMovie.startDisplaying(); + _bigInfoMovie.show(); + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } +} + +void PegasusEngine::hideInfoScreen() { + if (g_neighborhood) { + InputHandler::setInputHandler(_savedHandler); + + _bigInfoMovie.hide(); + _bigInfoMovie.stopDisplaying(); + _bigInfoMovie.releaseMovie(); + + _smallInfoMovie.hide(); + _smallInfoMovie.stopDisplaying(); + _smallInfoMovie.stop(); + _smallInfoMovie.releaseMovie(); + + g_neighborhood->showNav(); + } +} + +void PegasusEngine::raiseInventoryDrawer() { + if (g_interface) + g_interface->raiseInventoryDrawer(); +} + +void PegasusEngine::raiseBiochipDrawer() { + if (g_interface) + g_interface->raiseBiochipDrawer(); +} + +void PegasusEngine::lowerInventoryDrawer() { + if (g_interface) + g_interface->lowerInventoryDrawer(); +} + +void PegasusEngine::lowerBiochipDrawer() { + if (g_interface) + g_interface->lowerBiochipDrawer(); +} + +void PegasusEngine::raiseInventoryDrawerSync() { + if (g_interface) + g_interface->raiseInventoryDrawerSync(); +} + +void PegasusEngine::raiseBiochipDrawerSync() { + if (g_interface) + g_interface->raiseBiochipDrawerSync(); +} + +void PegasusEngine::lowerInventoryDrawerSync() { + if (g_interface) + g_interface->lowerInventoryDrawerSync(); +} + +void PegasusEngine::lowerBiochipDrawerSync() { + if (g_interface) + g_interface->lowerBiochipDrawerSync(); +} + +void PegasusEngine::toggleInfo() { + if (_gameMode == kModeInfoScreen) + setGameMode(kModeNavigation); + else if (_gameMode == kModeNavigation) + setGameMode(kModeInfoScreen); +} + +void PegasusEngine::dragTerminated(const Input &) { + Hotspot *finalSpot = _itemDragger.getLastHotspot(); + tInventoryResult result; + + if (_dragType == kDragInventoryPickup) { + if (finalSpot && finalSpot->getObjectID() == kInventoryDropSpotID) + result = addItemToInventory((InventoryItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragBiochipPickup) { + if (finalSpot && finalSpot->getObjectID() == kBiochipDropSpotID) + result = addItemToBiochips((BiochipItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragInventoryUse) { + if (finalSpot && (finalSpot->getHotspotFlags() & kDropItemSpotFlag) != 0) { + // *** Need to decide on a case by case basis what to do here. + // the crowbar should break the cover off the Mars reactor if its frozen, the + // global transport card should slide through the slot, the oxygen mask should + // attach to the filling station, and so on... + _neighborhood->dropItemIntoRoom(_draggingItem, finalSpot); + delete _draggingSprite; + } else { + autoDragItemIntoInventory(_draggingItem, _draggingSprite); + } + } + + _dragType = kDragNoDrag; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + + +void PegasusEngine::dragItem(const Input &input, Item *item, tDragType type) { + _draggingItem = item; + _dragType = type; + + // Create the sprite. + _draggingSprite = _draggingItem->getDragSprite(kDraggingSpriteID); + Common::Point where; + input.getInputLocation(where); + Common::Rect r1; + _draggingSprite->getBounds(r1); + r1 = Common::Rect::center(where.x, where.y, r1.width(), r1.height()); + _draggingSprite->setBounds(r1); + + // Set up drag constraints. + DisplayElement *navMovie = _gfx->findDisplayElement(kNavMovieID); + Common::Rect r2; + navMovie->getBounds(r2); + r2.left -= r1.width() / 3; + r2.right += r1.width() / 3; + r2.top -= r1.height() / 3; + r2.bottom += r2.height() / 3; + + r1 = Common::Rect(-30000, -30000, 30000, 30000); + _itemDragger.setDragConstraints(r2, r1); + + // Start dragging. + _draggingSprite->setDisplayOrder(kDragSpriteOrder); + _draggingSprite->startDisplaying(); + _draggingSprite->show(); + _itemDragger.setDragSprite(_draggingSprite); + _itemDragger.setNextHandler(_neighborhood); + _itemDragger.startTracking(input); + + if (g_AIArea) + g_AIArea->lockAIOut(); +} + +void PegasusEngine::shellGameInput(const Input &input, const Hotspot *cursorSpot) { + if (_gameMode == kModeInfoScreen) { + if (JMPPPInput::isToggleAIMiddleInput(input)) { + tLowerClientSignature middleOwner = g_AIArea->getMiddleAreaOwner(); + g_AIArea->toggleMiddleAreaOwner(); + + if (middleOwner != g_AIArea->getMiddleAreaOwner()) { + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + _smallInfoMovie.stop(); + _smallInfoMovie.setFlags(0); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } + } + } else { + if (JMPPPInput::isRaiseInventoryInput(input)) + toggleInventoryDisplay(); + + if (JMPPPInput::isRaiseBiochipsInput(input)) + toggleBiochipDisplay(); + + if (JMPPPInput::isTogglePauseInput(input) && _neighborhood) + pauseMenu(!isPaused()); + } + + if (JMPPPInput::isToggleInfoInput(input)) + toggleInfo(); +} + +void PegasusEngine::activateHotspots() { + if (_gameMode == kModeInfoScreen) { + g_allHotspots.activateOneHotspot(kInfoReturnSpotID); + } else { + // Set up hot spots. + if (_dragType == kDragInventoryPickup) + g_allHotspots.activateOneHotspot(kInventoryDropSpotID); + else if (_dragType == kDragBiochipPickup) + g_allHotspots.activateOneHotspot(kBiochipDropSpotID); + else if (_dragType == kDragNoDrag) + g_allHotspots.activateMaskedHotspots(kShellSpotFlag); + } +} + +bool PegasusEngine::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_cursor->isVisible() && cursorSpot) + return JMPPPInput::isClickInput(input); + else + return false; +} + +tInputBits PegasusEngine::getClickFilter() { + return JMPPPInput::getClickInputFilter(); +} + +void PegasusEngine::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot->getObjectID() == kCurrentItemSpotID) { + InventoryItem *currentItem = getCurrentInventoryItem(); + if (currentItem) { + removeItemFromInventory(currentItem); + dragItem(input, currentItem, kDragInventoryUse); + } + } else if (clickedSpot->getObjectID() == kInfoReturnSpotID) { + toggleInfo(); + } +} + +tInventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) { + tInventoryResult result; + + if (g_interface) + result = g_interface->removeInventoryItem(item); + else + result = _items.removeItem(item); + + // This should never happen + assert(result == kInventoryOK); + + return result; +} + +void PegasusEngine::removeAllItemsFromInventory() { + if (g_interface) + g_interface->removeAllItemsFromInventory(); + else + _items.removeAllItems(); +} + +///////////////////////////////////////////// +// +// Biochip handling. + +// Adding biochips to the biochip drawer is a little funny, because of two things: +// -- We get the map biochip and pegasus biochip at the same time by dragging +// one sprite in TSA +// -- We can drag in more than one copy of the various biochips. +// Because of this we need to make sure that no more than one copy of each biochip +// is ever added. + +tInventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) { + tInventoryResult result; + + if (g_interface) + result = g_interface->addBiochip(biochip); + else + result = _biochips.addItem(biochip); + + // This can never happen + assert(result == kInventoryOK); + + GameState.setTakenItem(biochip, true); + + if (g_neighborhood) + g_neighborhood->pickedUpItem(biochip); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::removeAllItemsFromBiochips() { + if (g_interface) + g_interface->removeAllItemsFromBiochips(); + else + _biochips.removeAllItems(); +} + +void PegasusEngine::setSoundFXLevel(uint16 fxLevel) { + _FXLevel = fxLevel; + if (_neighborhood) + _neighborhood->setSoundFXLevel(fxLevel); + if (g_AIArea) + g_AIArea->setAIVolume(fxLevel); +} + +void PegasusEngine::setAmbienceLevel(uint16 ambientLevel) { + _ambientLevel = ambientLevel; + if (_neighborhood) + _neighborhood->setAmbienceLevel(ambientLevel); +} + +void PegasusEngine::pauseMenu(bool menuUp) { + if (menuUp) { + pauseEngine(true); + _screenDimmer.startDisplaying(); + _screenDimmer.show(); + _gfx->updateDisplay(); + useMenu(new PauseMenu()); + } else { + pauseEngine(false); + _screenDimmer.hide(); + _screenDimmer.stopDisplaying(); + useMenu(0); + g_AIArea->checkMiddleArea(); + } +} + +void PegasusEngine::autoDragItemIntoRoom(Item *item, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start, stop; + draggingSprite->getLocation(start.x, start.y); + + Hotspot *dropSpot = _neighborhood->getItemScreenSpot(item, draggingSprite); + + if (dropSpot) { + dropSpot->getCenter(stop.x, stop.y); + } else { + stop.x = kNavAreaLeft + 256; + stop.y = kNavAreaTop + 128; + } + + Common::Rect bounds; + draggingSprite->getBounds(bounds); + stop.x -= bounds.width() >> 1; + stop.y -= bounds.height() >> 1; + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + _neighborhood->dropItemIntoRoom(_draggingItem, dropSpot); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +void PegasusEngine::autoDragItemIntoInventory(Item *, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start; + draggingSprite->getLocation(start.x, start.y); + + Common::Rect r; + draggingSprite->getBounds(r); + + Common::Point stop((76 + 172 - r.width()) / 2, 334 - (2 * r.height() / 3)); + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + addItemToInventory((InventoryItem *)_draggingItem); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +tNeighborhoodID PegasusEngine::getCurrentNeighborhoodID() const { + if (_neighborhood) + return _neighborhood->getObjectID(); + + return kNoNeighborhoodID; +} + +void PegasusEngine::pauseEngineIntern(bool pause) { + Engine::pauseEngineIntern(pause); + + if (pause) { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->pause(); + } else { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->resume(); + } +} + +uint PegasusEngine::getRandomBit() { + return _rnd->getRandomBit(); +} + +void PegasusEngine::playEndMessage() { + if (g_interface) { + allowInput(false); + g_interface->playEndMessage(); + allowInput(true); + } + + die(kPlayerWonGame); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h new file mode 100644 index 0000000000..a09a9231b2 --- /dev/null +++ b/engines/pegasus/pegasus.h @@ -0,0 +1,303 @@ +/* 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_H +#define PEGASUS_H + +#include "common/list.h" +#include "common/macresman.h" +#include "common/rect.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" + +#include "engines/engine.h" + +#include "pegasus/graphics.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/items/autodragger.h" +#include "pegasus/items/inventory.h" +#include "pegasus/items/itemdragger.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Common { + class RandomSource; +} + +namespace Video { + class SeekableVideoDecoder; +} + +namespace Pegasus { + +class PegasusConsole; +struct PegasusGameDescription; +class SoundManager; +class GraphicsManager; +class Idler; +class Cursor; +class TimeBase; +class GameMenu; +class InventoryItem; +class BiochipItem; +class Neighborhood; + +class PegasusEngine : public ::Engine, public InputHandler, public NotificationManager { +friend class InputHandler; + +public: + PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc); + virtual ~PegasusEngine(); + + // Engine stuff + const PegasusGameDescription *_gameDescription; + bool hasFeature(EngineFeature f) const; + GUI::Debugger *getDebugger(); + bool canLoadGameStateCurrently() { return _loadAllowed && !isDemo(); } + bool canSaveGameStateCurrently() { return _saveAllowed && !isDemo(); } + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const Common::String &desc); + + // Base classes + GraphicsManager *_gfx; + Common::MacResManager *_resFork; + Cursor *_cursor; + + // Menu + void useMenu(GameMenu *menu); + bool checkGameMenu(); + + // Misc. + bool isDemo() const; + void addIdler(Idler *idler); + void removeIdler(Idler *idler); + void addTimeBase(TimeBase *timeBase); + void removeTimeBase(TimeBase *timeBase); + void delayShell(TimeValue time, TimeScale scale); + void resetIntroTimer(); + void refreshDisplay(); + bool playerAlive(); + void processShell(); + void checkCallBacks(); + void createInterface(); + void setGameMode(const tGameMode); + tGameMode getGameMode() const { return _gameMode; } + uint getRandomBit(); + + // Energy + void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; } + int32 getSavedEnergyValue() { return _savedEnergyValue; } + + // Death + void setEnergyDeathReason(const tDeathReason reason) { _deathReason = reason; } + tDeathReason getEnergyDeathReason() { return _deathReason; } + void resetEnergyDeathReason(); + void die(const tDeathReason); + void playEndMessage(); + + // Volume + uint16 getSoundFXLevel() { return _FXLevel; } + void setSoundFXLevel(uint16); + uint16 getAmbienceLevel() { return _ambientLevel; } + void setAmbienceLevel(uint16); + + // Items + bool playerHasItem(const Item *); + bool playerHasItemID(const tItemID); + void checkFlashlight(); + bool itemInLocation(const tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + + // Inventory Items + InventoryItem *getCurrentInventoryItem(); + bool itemInInventory(InventoryItem *); + bool itemInInventory(tItemID); + Inventory *getItemsInventory() { return &_items; } + tInventoryResult addItemToInventory(InventoryItem *); + void removeAllItemsFromInventory(); + tInventoryResult removeItemFromInventory(InventoryItem *); + uint32 countInventoryItems() { return _items.getNumItems(); } + + // Biochips + BiochipItem *getCurrentBiochip(); + bool itemInBiochips(BiochipItem *); + bool itemInBiochips(tItemID); + Inventory *getBiochipsInventory() { return &_biochips; } + void removeAllItemsFromBiochips(); + tInventoryResult addItemToBiochips(BiochipItem *); + + // AI + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + bool canSolve(); + void prepareForAIHint(const Common::String &); + void cleanUpAfterAIHint(const Common::String &); + + // Neighborhood + void jumpToNewEnvironment(const tNeighborhoodID, const tRoomID, const tDirectionConstant); + tNeighborhoodID getCurrentNeighborhoodID() const; + + // Dragging + void dragItem(const Input &, Item *, tDragType); + bool isDragging() const { return _dragType != kDragNoDrag; } + tDragType getDragType() const { return _dragType; } + Item *getDraggingItem() const { return _draggingItem; } + void dragTerminated(const Input &); + void autoDragItemIntoRoom(Item *, Sprite *); + void autoDragItemIntoInventory(Item *, Sprite*); + + // Save/Load + void makeContinuePoint(); + bool swapSaveAllowed(bool allow) { + bool old = _saveAllowed; + _saveAllowed = allow; + return old; + } + bool swapLoadAllowed(bool allow) { + bool old = _loadAllowed; + _loadAllowed = allow; + return old; + } + +protected: + Common::Error run(); + void pauseEngineIntern(bool pause); + + Notification _shellNotification; + virtual void receiveNotification(Notification *notification, const tNotificationFlags flags); + + void handleInput(const Input &input, const Hotspot *cursorSpot); + virtual bool isClickInput(const Input &, const Hotspot *); + virtual tInputBits getClickFilter(); + + void clickInHotspot(const Input &, const Hotspot *); + void activateHotspots(void); + + void updateCursor(const Common::Point, const Hotspot *); + bool wantsCursor(); + +private: + // Console + PegasusConsole *_console; + + // Intro + void runIntro(); + bool detectOpeningClosingDirectory(); + Common::String _introDirectory; + + // Idlers + Idler *_idlerHead; + void giveIdleTime(); + + // Items + void createItems(); + void createItem(tItemID itemID, tNeighborhoodID neighborhoodID, tRoomID roomID, tDirectionConstant direction); + Inventory _items; + Inventory _biochips; + tItemID _currentItemID; + tItemID _currentBiochipID; + + // TimeBases + Common::List<TimeBase *> _timeBases; + + // Save/Load + bool loadFromStream(Common::ReadStream *stream); + bool writeToStream(Common::WriteStream *stream, int saveType); + void loadFromContinuePoint(); + Common::ReadStream *_continuePoint; + bool _saveAllowed, _loadAllowed; // It's so nice that this was in the original code already :P + + // Misc. + Hotspot _returnHotspot; + InputHandler *_savedHandler; + void showLoadDialog(); + void showTempScreen(const Common::String &fileName); + bool playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y); + void throwAwayEverything(); + void shellGameInput(const Input &input, const Hotspot *cursorSpot); + Common::RandomSource *_rnd; + + // Menu + GameMenu *_gameMenu; + void doGameMenuCommand(const tGameMenuCommand); + void doInterfaceOverview(); + ScreenDimmer _screenDimmer; + void pauseMenu(bool menuUp); + + // Energy + int32 _savedEnergyValue; + + // Death + tDeathReason _deathReason; + void doDeath(); + + // Neighborhood + Neighborhood *_neighborhood; + void useNeighborhood(Neighborhood *neighborhood); + void performJump(const tNeighborhoodID start); + void startNewGame(); + void startNeighborhood(); + void makeNeighborhood(tNeighborhoodID, Neighborhood *&); + + // Sound + uint16 _ambientLevel; + uint16 _FXLevel; + + // Game Mode + tGameMode _gameMode; + bool _switchModesSync; + void switchGameMode(const tGameMode, const tGameMode); + bool canSwitchGameMode(const tGameMode, const tGameMode); + + // Dragging + ItemDragger _itemDragger; + Item *_draggingItem; + Sprite *_draggingSprite; + tDragType _dragType; + AutoDragger _autoDragger; + + // Interface + void toggleInventoryDisplay(); + void toggleBiochipDisplay(); + void raiseInventoryDrawer(); + void raiseBiochipDrawer(); + void lowerInventoryDrawer(); + void lowerBiochipDrawer(); + void raiseInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerInventoryDrawerSync(); + void lowerBiochipDrawerSync(); + void showInfoScreen(); + void hideInfoScreen(); + void toggleInfo(); + Movie _bigInfoMovie, _smallInfoMovie; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/scoring.h b/engines/pegasus/scoring.h new file mode 100755 index 0000000000..bd09f2b834 --- /dev/null +++ b/engines/pegasus/scoring.h @@ -0,0 +1,281 @@ +/* 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_SCORING_H +#define PEGASUS_SCORING_H + +#include "pegasus/types.h" + +namespace Pegasus { + +///////////////////////////////////////////// +// +// Scoring. + +const tCoordType kDeathScreenScoreLeft = 151; +const tCoordType kDeathScreenScoreTop = 212; +const tCoordType kDeathScreenScoreWidth = 124; +const tCoordType kDeathScreenScoreHeight = 12; +const tCoordType kDeathScreenScoreSkipVert = -16; + +// Caldoria & TSA + +const tGameScoreType kSawINNScore = 5; +const tGameScoreType kTookShowerScore = 2; +const tGameScoreType kFixedHairScore = 2; +const tGameScoreType kGotKeyCardScore = 5; +const tGameScoreType kReadPaperScore = 2; +const tGameScoreType kLookThroughTelescopeScore = 2; +const tGameScoreType kSawCaldoriaKioskScore = 2; +const tGameScoreType kGoToTSAScore = 3; + +const tGameScoreType kEnterTSAScore = 2; +const tGameScoreType kSawBust1Score = 2; +const tGameScoreType kSawBust2Score = 2; +const tGameScoreType kSawBust3Score = 2; +const tGameScoreType kSawBust4Score = 2; +const tGameScoreType kSawBust5Score = 2; +const tGameScoreType kSawBust6Score = 2; +const tGameScoreType kSawTheoryScore = 4; +const tGameScoreType kSawBackgroundScore = 4; +const tGameScoreType kSawProcedureScore = 4; +const tGameScoreType kGotJourneymanKeyScore = 5; +const tGameScoreType kGotPegasusBiochipScore = 5; +const tGameScoreType kGotBiosuitScore = 5; +const tGameScoreType kGoToPrehistoricScore = 5; + +const tGameScoreType kPutLogInReaderScore = 5; +const tGameScoreType kSawCaldoriaNormalScore = 2; +const tGameScoreType kSawCaldoriaAlteredScore = 2; +const tGameScoreType kSawNoradNormalScore = 2; +const tGameScoreType kSawNoradAlteredScore = 2; +const tGameScoreType kSawMarsNormalScore = 2; +const tGameScoreType kSawMarsAlteredScore = 2; +const tGameScoreType kSawWSCNormalScore = 2; +const tGameScoreType kSawWSCAlteredScore = 2; +const tGameScoreType kWentToReadyRoom2Score = 5; +const tGameScoreType kWentAfterSinclairScore = 5; +const tGameScoreType kUsedCardBombScore = 10; +const tGameScoreType kShieldedCardBombScore = 5; +const tGameScoreType kStunnedSinclairScore = 10; +const tGameScoreType kDisarmedNukeScore = 10; + +const tGameScoreType kMaxCaldoriaTSAScoreBefore = kSawINNScore + + kTookShowerScore + + kFixedHairScore + + kGotKeyCardScore + + kReadPaperScore + + kLookThroughTelescopeScore + + kSawCaldoriaKioskScore + + kGoToTSAScore + + kEnterTSAScore + + kSawBust1Score + + kSawBust2Score + + kSawBust3Score + + kSawBust4Score + + kSawBust5Score + + kSawBust6Score + + kSawTheoryScore + + kSawBackgroundScore + + kSawProcedureScore + + kGotJourneymanKeyScore + + kGotPegasusBiochipScore + + kGotBiosuitScore + + kGoToPrehistoricScore + + kPutLogInReaderScore + + kSawCaldoriaNormalScore + + kSawCaldoriaAlteredScore + + kSawNoradNormalScore + + kSawNoradAlteredScore + + kSawMarsNormalScore + + kSawMarsAlteredScore + + kSawWSCNormalScore + + kSawWSCAlteredScore + + kWentToReadyRoom2Score; + +const tGameScoreType kMaxCaldoriaTSAScoreAfter = kWentAfterSinclairScore + + kUsedCardBombScore + + kShieldedCardBombScore + + kStunnedSinclairScore + + kDisarmedNukeScore; + +const tGameScoreType kMaxCaldoriaTSAScore = kMaxCaldoriaTSAScoreBefore + + kMaxCaldoriaTSAScoreAfter; + +// Prehistoric + +const tGameScoreType kThrewBreakerScore = 10; +const tGameScoreType kExtendedBridgeScore = 10; +const tGameScoreType kGotHistoricalLogScore = 5; +const tGameScoreType kFinishedPrehistoricScore = 10; + +const tGameScoreType kMaxPrehistoricScore = kThrewBreakerScore + + kExtendedBridgeScore + + kGotHistoricalLogScore + + kFinishedPrehistoricScore; + +// Mars + +const tGameScoreType kThrownByRobotScore = 3; +const tGameScoreType kGotMarsCardScore = 5; +const tGameScoreType kSawMarsKioskScore = 2; +const tGameScoreType kSawTransportMapScore = 2; +const tGameScoreType kGotCrowBarScore = 5; +const tGameScoreType kTurnedOnTransportScore = 5; +const tGameScoreType kGotOxygenMaskScore = 5; +const tGameScoreType kAvoidedRobotScore = 5; +const tGameScoreType kActivatedPlatformScore = 2; +const tGameScoreType kUsedLiquidNitrogenScore = 3; +const tGameScoreType kUsedCrowBarScore = 3; +const tGameScoreType kFoundCardBombScore = 4; +const tGameScoreType kDisarmedCardBombScore = 8; +const tGameScoreType kGotCardBombScore = 5; +const tGameScoreType kThreadedMazeScore = 5; +const tGameScoreType kThreadedGearRoomScore = 2; +const tGameScoreType kEnteredShuttleScore = 2; +const tGameScoreType kEnteredLaunchTubeScore = 4; +const tGameScoreType kStoppedRobotsShuttleScore = 10; +const tGameScoreType kGotMarsOpMemChipScore = 10; +const tGameScoreType kFinishedMarsScore = 10; + +const tGameScoreType kMaxMarsScore = kThrownByRobotScore + + kGotMarsCardScore + + kSawMarsKioskScore + + kSawTransportMapScore + + kGotCrowBarScore + + kTurnedOnTransportScore + + kGotOxygenMaskScore + + kAvoidedRobotScore + + kActivatedPlatformScore + + kUsedLiquidNitrogenScore + + kUsedCrowBarScore + + kFoundCardBombScore + + kDisarmedCardBombScore + + kGotCardBombScore + + kThreadedMazeScore + + kThreadedGearRoomScore + + kEnteredShuttleScore + + kEnteredLaunchTubeScore + + kStoppedRobotsShuttleScore + + kGotMarsOpMemChipScore + + kFinishedMarsScore; + +// Norad + +const tGameScoreType kSawSecurityMonitorScore = 5; +const tGameScoreType kFilledOxygenCanisterScore = 5; +const tGameScoreType kFilledArgonCanisterScore = 5; +const tGameScoreType kSawUnconsciousOperatorScore = 5; +const tGameScoreType kWentThroughPressureDoorScore = 5; +const tGameScoreType kPreppedSubScore = 5; +const tGameScoreType kEnteredSubScore = 5; +const tGameScoreType kExitedSubScore = 10; +const tGameScoreType kSawRobotAt54NorthScore = 5; +const tGameScoreType kPlayedWithClawScore = 5; +const tGameScoreType kUsedRetinalChipScore = 5; +const tGameScoreType kFinishedGlobeGameScore = 10; +const tGameScoreType kStoppedNoradRobotScore = 10; +const tGameScoreType kGotNoradOpMemChipScore = 10; +const tGameScoreType kFinishedNoradScore = 10; + +const tGameScoreType kMaxNoradScore = kSawSecurityMonitorScore + + kFilledOxygenCanisterScore + + kFilledArgonCanisterScore + + kSawUnconsciousOperatorScore + + kWentThroughPressureDoorScore + + kPreppedSubScore + + kEnteredSubScore + + kExitedSubScore + + kSawRobotAt54NorthScore + + kPlayedWithClawScore + + kUsedRetinalChipScore + + kFinishedGlobeGameScore + + kStoppedNoradRobotScore + + kGotNoradOpMemChipScore + + kFinishedNoradScore; + +// WSC + +const tGameScoreType kRemovedDartScore = 5; +const tGameScoreType kAnalyzedDartScore = 5; +const tGameScoreType kBuiltAntidoteScore = 5; +const tGameScoreType kGotSinclairKeyScore = 5; +const tGameScoreType kGotArgonCanisterScore = 5; +const tGameScoreType kGotNitrogenCanisterScore = 5; +const tGameScoreType kPlayedWithMessagesScore = 2; +const tGameScoreType kSawMorphExperimentScore = 3; +const tGameScoreType kEnteredSinclairOfficeScore = 2; +const tGameScoreType kSawBrochureScore = 3; +const tGameScoreType kSawSinclairEntry1Score = 3; +const tGameScoreType kSawSinclairEntry2Score = 3; +const tGameScoreType kSawSinclairEntry3Score = 3; +const tGameScoreType kSawWSCDirectoryScore = 3; +const tGameScoreType kUsedCrowBarInWSCScore = 5; +const tGameScoreType kFinishedPlasmaDodgeScore = 10; +const tGameScoreType kOpenedCatwalkScore = 3; +const tGameScoreType kStoppedWSCRobotScore = 10; +const tGameScoreType kGotWSCOpMemChipScore = 10; +const tGameScoreType kFinishedWSCScore = 10; + +const tGameScoreType kMaxWSCScore = kRemovedDartScore + + kAnalyzedDartScore + + kBuiltAntidoteScore + + kGotSinclairKeyScore + + kGotArgonCanisterScore + + kGotNitrogenCanisterScore + + kPlayedWithMessagesScore + + kSawMorphExperimentScore + + kEnteredSinclairOfficeScore + + kSawBrochureScore + + kSawSinclairEntry1Score + + kSawSinclairEntry2Score + + kSawSinclairEntry3Score + + kSawWSCDirectoryScore + + kUsedCrowBarInWSCScore + + kFinishedPlasmaDodgeScore + + kOpenedCatwalkScore + + kStoppedWSCRobotScore + + kGotWSCOpMemChipScore + + kFinishedWSCScore; + +// Gandhi + +const tGameScoreType kMarsGandhiScore = 10; +const tGameScoreType kNoradGandhiScore = 10; +const tGameScoreType kWSCGandhiScore = 10; + +const tGameScoreType kMaxGandhiScore = kMarsGandhiScore + + kNoradGandhiScore + + kWSCGandhiScore; + +const tGameScoreType kMaxTotalScore = kMaxCaldoriaTSAScore + + kMaxPrehistoricScore + + kMaxMarsScore + + kMaxNoradScore + + kMaxWSCScore + + kMaxGandhiScore; +} // End of namespace Pegasus + +#endif
\ No newline at end of file diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp new file mode 100755 index 0000000000..847106aa4d --- /dev/null +++ b/engines/pegasus/sound.cpp @@ -0,0 +1,150 @@ +/* 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 "audio/audiostream.h" +#include "audio/decoders/aiff.h" +#include "audio/decoders/quicktime.h" +#include "common/file.h" +#include "common/system.h" + +#include "pegasus/fader.h" +#include "pegasus/sound.h" + +namespace Pegasus { + +Sound::Sound() { + _stream = 0; + _volume = 0xFF; + _fader = 0; +} + +Sound::~Sound() { + disposeSound(); +} + +void Sound::disposeSound() { + stopSound(); + delete _stream; _stream = 0; +} + +void Sound::initFromAIFFFile(const Common::String &fileName) { + disposeSound(); + + Common::File *file = new Common::File(); + if (!file->open(fileName)) { + delete file; + return; + } + + _stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); +} + +void Sound::initFromQuickTime(const Common::String &fileName) { + disposeSound(); + + _stream = Audio::makeQuickTimeStream(fileName); +} + +void Sound::attachFader(SoundFader *fader) { + if (_fader) + _fader->attachSound(0); + + _fader = fader; + + if (_fader) + _fader->attachSound(this); +} + +void Sound::playSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + if (_fader) + setVolume(_fader->getFaderValue()); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); +} + +void Sound::loopSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + // Create a looping stream + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + + // Assume that if there is a fader, we're going to fade the sound in. + if (_fader) + setVolume(0); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::playSoundSegment(uint32 start, uint32 end) { + if (!isSoundLoaded()) + return; + + stopSound(); + + Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::loopSoundSegment(uint32 start, uint32 end) { + if (!isSoundLoaded()) + return; + + stopSound(); + + Audio::AudioStream *subLoopStream = new Audio::SubLoopingAudioStream(_stream, 0, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subLoopStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::stopSound() { + g_system->getMixer()->stopHandle(_handle); +} + +void Sound::setVolume(const uint16 volume) { + // Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100] + // We store the volume in case SetVolume is called before the sound starts + + _volume = (volume == 0x100) ? 0xFF : volume; + g_system->getMixer()->setChannelVolume(_handle, _volume); +} + +bool Sound::isPlaying() { + return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle); +} + +bool Sound::isSoundLoaded() const { + return _stream != 0; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h new file mode 100755 index 0000000000..f66c3f4250 --- /dev/null +++ b/engines/pegasus/sound.h @@ -0,0 +1,87 @@ +/* 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_SOUND_H +#define PEGASUS_SOUND_H + +#include "audio/mixer.h" +#include "common/str.h" + +namespace Audio { + class SeekableAudioStream; +} + +namespace Pegasus { + +class SoundFader; + +// Things you might want to do with sound: +// Start it +// Stop it +// Loop it +// Pause it +// Set the volume +// Set the pitch (rate) +// Pan the sound +// Change these settings dynamically over time + +class Sound { +public: + Sound(); + ~Sound(); + + // We only have one access point here because we should + // only be opening an AIFF file from a file name. We're + // not using the resource fork string resources. + void initFromAIFFFile(const Common::String &fileName); + + // Unlike the original game, we're going to use a regular + // audio stream for sound spots. The original treated them + // as movies. + void initFromQuickTime(const Common::String &fileName); + + void disposeSound(); + bool isSoundLoaded() const; + void playSound(); + void loopSound(); + void playSoundSegment(uint32 start, uint32 end); + void loopSoundSegment(uint32 start, uint32 end); + void stopSound(); + void setVolume(const uint16 volume); + bool isPlaying(); + + void attachFader(SoundFader *fader); + +protected: + Audio::SeekableAudioStream *_stream; + Audio::SoundHandle _handle; + byte _volume; + + SoundFader *_fader; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp new file mode 100755 index 0000000000..43cf90af28 --- /dev/null +++ b/engines/pegasus/surface.cpp @@ -0,0 +1,282 @@ +/* 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/file.h" +#include "common/macresman.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/pict.h" +#include "graphics/surface.h" +#include "video/video_decoder.h" + +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Surface::Surface() { + _ownsSurface = false; + _surface = 0; +} + +Surface::~Surface() { + deallocateSurface(); +} + +void Surface::deallocateSurface() { + if (_surface) { + if (_ownsSurface) { + _surface->free(); + delete _surface; + } + + _surface = 0; + _bounds = Common::Rect(); + _ownsSurface = false; + } +} + +void Surface::shareSurface(Surface *surface) { + deallocateSurface(); + + if (surface) { + _ownsSurface = false; + _surface = surface->getSurface(); + surface->getSurfaceBounds(_bounds); + } +} + +void Surface::allocateSurface(const Common::Rect &bounds) { + deallocateSurface(); + + if (bounds.isEmpty()) + return; + + _bounds = bounds; + _surface = new Graphics::Surface(); + _surface->create(bounds.width(), bounds.height(), g_system->getScreenFormat()); + _ownsSurface = true; +} + +void Surface::getImageFromPICTFile(const Common::String &fileName) { + Common::File pict; + if (!pict.open(fileName)) + error("Could not open picture '%s'", fileName.c_str()); + + getImageFromPICTStream(&pict); +} + +void Surface::getImageFromPICTResource(Common::MacResManager *resFork, uint16 id) { + Common::SeekableReadStream *res = resFork->getResource(MKTAG('P', 'I', 'C', 'T'), id); + if (!res) + error("Could not open PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); + + getImageFromPICTStream(res); + delete res; +} + +void Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) { + Graphics::PictDecoder pict(g_system->getScreenFormat()); + byte pal[256 * 3]; + + Graphics::Surface *surface = pict.decodeImage(stream, pal); + + // Create the surface if not present + if (!_surface) + _surface = new Graphics::Surface(); + + // Update + if (surface->format.bytesPerPixel == 1) { + // Convert to true color + _surface->create(surface->w, surface->h, g_system->getScreenFormat()); + + for (int y = 0; y < surface->h; y++) { + for (int x = 0; x < surface->w; x++) { + byte index = *((byte *)surface->getBasePtr(x, y)); + uint32 color = _surface->format.RGBToColor(pal[index * 3], pal[index * 3 + 1], pal[index * 3 + 2]); + if (_surface->format.bytesPerPixel == 2) + *((uint16 *)_surface->getBasePtr(x, y)) = color; + else + *((uint32 *)_surface->getBasePtr(x, y)) = color; + } + } + } else { + // Just a copy + _surface->copyFrom(*surface); + } + + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); +} + +void Surface::getImageFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time) { + video->seekToTime(Audio::Timestamp(0, time, 600)); + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) { + if (!_surface) + _surface = new Graphics::Surface(); + + _surface->copyFrom(*frame); + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); + } else { + deallocateSurface(); + } +} + +void Surface::copyToCurrentPort() const { + copyToCurrentPort(_bounds); +} + +void Surface::copyToCurrentPortTransparent() const { + copyToCurrentPortTransparent(_bounds); +} + +void Surface::copyToCurrentPort(const Common::Rect &rect) const { + copyToCurrentPort(rect, rect); +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &rect) const { + copyToCurrentPortTransparent(rect, rect); +} + +void Surface::copyToCurrentPort(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + memcpy(dst, src, lineSize); + src += _surface->pitch; + dst += screen->pitch; + } +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // HACK: Seems we're truncating some color data somewhere... + uint32 transColor1 = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); + uint32 transColor2 = g_system->getScreenFormat().RGBToColor(0xf8, 0xf8, 0xf8); + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(src); + if (color != transColor1 && color != transColor2) + memcpy(dst, src, 2); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(src); + if (color != transColor1 && color != transColor2) + memcpy(dst, src, 4); + } + + src += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +PixelImage::PixelImage() { + _transparent = false; +} + +void PixelImage::drawImage(const Common::Rect &sourceBounds, const Common::Rect &destBounds) { + if (!isSurfaceValid()) + return; + + // Draw from sourceBounds to destBounds based on _transparent + if (_transparent) + copyToCurrentPortTransparent(sourceBounds, destBounds); + else + copyToCurrentPort(sourceBounds, destBounds); +} + +void Frame::initFromPICTFile(const Common::String &fileName, bool transparent) { + getImageFromPICTFile(fileName); + _transparent = transparent; +} + +void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + getImageFromPICTResource(resFork, id); + _transparent = transparent; +} + +void Frame::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { + getImageFromMovieFrame(video, time); + _transparent = transparent; +} + +void Picture::draw(const Common::Rect &r) { + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + Common::Rect r1 = r; + + Common::Rect bounds; + getBounds(bounds); + surfaceBounds.moveTo(bounds.left, bounds.top); + r1 = r1.findIntersectingRect(surfaceBounds); + getSurfaceBounds(surfaceBounds); + + Common::Rect r2 = r1; + r2.translate(surfaceBounds.left - bounds.left, surfaceBounds.top - bounds.top); + drawImage(r2, r1); +} + +void Picture::initFromPICTFile(const Common::String &fileName, bool transparent) { + Frame::initFromPICTFile(fileName, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + Frame::initFromPICTResource(resFork, id, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { + Frame::initFromMovieFrame(video, time, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h new file mode 100755 index 0000000000..c94ccdf0fc --- /dev/null +++ b/engines/pegasus/surface.h @@ -0,0 +1,134 @@ +/* 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_SURFACE_H +#define PEGASUS_SURFACE_H + +#include "common/rect.h" +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/types.h" + +namespace Common { + class MacResManager; +} + +namespace Graphics { + struct Surface; +} + +namespace Video { + class SeekableVideoDecoder; +} + +namespace Pegasus { + +// Surface bounds are always normalized. + +class Surface { +public: + Surface(); + virtual ~Surface(); + + virtual void allocateSurface(const Common::Rect &); + virtual void deallocateSurface(); + virtual void shareSurface(Surface *surface); + bool isSurfaceValid() const { return _surface != 0; } + + Graphics::Surface *getSurface() const { return _surface; } + void getSurfaceBounds(Common::Rect &r) { r = _bounds; } + + // None of the CopyToCurrentPort functions do any sanity checks. + // For speed, they just call CopyBits. + // It's up to clients to make sure that the GWorld is valid. + void copyToCurrentPort() const; + void copyToCurrentPortTransparent() const; + void copyToCurrentPort(const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &) const; + void copyToCurrentPort(const Common::Rect &, const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &, const Common::Rect &) const; + + virtual void getImageFromPICTFile(const Common::String &fileName); + virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id); + virtual void getImageFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue); + +protected: + bool _ownsSurface; + Graphics::Surface *_surface; + Common::Rect _bounds; + +private: + void getImageFromPICTStream(Common::SeekableReadStream *stream); +}; + +class PixelImage : public Surface { +public: + PixelImage(); + virtual ~PixelImage() {} + + void drawImage(const Common::Rect &, const Common::Rect &); + +protected: + virtual void setTransparent(bool transparent) { _transparent = transparent; } + + bool _transparent; +}; + +class Frame : public PixelImage { +public: + Frame() {} + virtual ~Frame() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); +}; + +class SpriteFrame : public Frame { +friend class Sprite; +public: + SpriteFrame() { _referenceCount = 0; } + virtual ~SpriteFrame() {} + +protected: + uint32 _referenceCount; +}; + +class Picture : public DisplayElement, public Frame { +public: + Picture(const tDisplayElementID id) : DisplayElement(id) {} + virtual ~Picture() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); + + virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp new file mode 100755 index 0000000000..8ca7de98bf --- /dev/null +++ b/engines/pegasus/timers.cpp @@ -0,0 +1,399 @@ +/* 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/pegasus.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +Idler::Idler() { + _isIdling = false; + _nextIdler = 0; + _prevIdler = 0; +} + +Idler::~Idler() { + stopIdling(); +} + +void Idler::startIdling() { + if (!isIdling()) { + ((PegasusEngine *)g_engine)->addIdler(this); + _isIdling = true; + } +} + +void Idler::stopIdling() { + if (isIdling()) { + ((PegasusEngine *)g_engine)->removeIdler(this); + _isIdling = false; + } +} + +TimeBase::TimeBase(const TimeScale preferredScale) { + _preferredScale = preferredScale; + _callBackList = 0; + _paused = false; + _flags = 0; + _lastMillis = 0; + _time = 0; + _rate = 0; + _startTime = 0; + _startScale = 1; + _stopTime = 0xffffffff; + _stopScale = 1; + _master = 0; + _pausedRate = 0; + + ((PegasusEngine *)g_engine)->addTimeBase(this); +} + +TimeBase::~TimeBase() { + if (_master) + _master->_slaves.remove(this); + + ((PegasusEngine *)g_engine)->removeTimeBase(this); + disposeAllCallBacks(); + + // TODO: Remove slaves? Make them remove themselves? +} + +void TimeBase::setTime(const TimeValue time, const TimeScale scale) { + _time = Common::Rational(time, (scale == 0) ? _preferredScale : scale); + _lastMillis = 0; +} + +TimeValue TimeBase::getTime(const TimeScale scale) { + return _time.getNumerator() * ((scale == 0) ? _preferredScale : scale) / _time.getDenominator(); +} + +void TimeBase::setRate(const Common::Rational rate) { + _rate = rate; + + if (_rate == 0) + _paused = false; +} + +Common::Rational TimeBase::getEffectiveRate() const { + return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate()); +} + +void TimeBase::start() { + if (_paused) + _pausedRate = 1; + else + setRate(1); +} + +void TimeBase::stop() { + setRate(0); + _paused = false; +} + +void TimeBase::pause() { + if (isRunning() && !_paused) { + _pausedRate = getRate(); + stop(); + _paused = true; + } +} + +void TimeBase::resume() { + if (_paused) { + setRate(_pausedRate); + _paused = false; + } +} + +bool TimeBase::isRunning() { + if (_paused && _pausedRate != 0) + return true; + + Common::Rational rate = getRate(); + + if (rate == 0) + return false; + + if (getFlags() & kLoopTimeBase) + return true; + + if (rate > 0) + return getTime() != getStop(); + + return getTime() != getStart(); +} + +void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) { + _startTime = startTime; + _startScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStart(const TimeScale scale) const { + if (scale) + return _startTime * scale / _startScale; + + return _startTime * _preferredScale / _startScale; +} + +void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) { + _stopTime = stopTime; + _stopScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStop(const TimeScale scale) const { + if (scale) + return _stopTime * scale / _stopScale; + + return _stopTime * _preferredScale / _stopScale; +} + +void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) { + setStart(startTime, scale); + setStop(stopTime, scale); +} + +void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const { + startTime = getStart(scale); + stopTime = getStop(scale); +} + +TimeValue TimeBase::getDuration(const TimeScale scale) const { + TimeValue startTime, stopTime; + getSegment(startTime, stopTime, scale); + return stopTime - startTime; +} + +void TimeBase::setMasterTimeBase(TimeBase *tb) { + // TODO: We're just ignoring the master (except for effective rate) + // for now to simplify things + if (_master) + _master->_slaves.remove(this); + + _master = tb; + + if (_master) + _master->_slaves.push_back(this); +} + +void TimeBase::checkCallBacks() { + // Nothing to do if we're paused or not running + if (_paused || !isRunning()) + return; + + Common::Rational startTime = Common::Rational(_startTime, _startScale); + Common::Rational stopTime = Common::Rational(_stopTime, _stopScale); + + // First step: update the times + if (_lastMillis == 0) { + _lastMillis = g_engine->getTotalPlayTime(); + } else { + uint32 curTime = g_engine->getTotalPlayTime(); + if (_lastMillis == curTime) // No change + return; + + _time += Common::Rational(curTime - _lastMillis, 1000) * getEffectiveRate(); + _lastMillis = curTime; + + // Clip time to the boundaries + if (_time >= stopTime) + _time = stopTime; + else if (_time <= startTime) + _time = startTime; + } + + // TODO: Update the slaves? + + Common::Rational time = Common::Rational(getTime(), getScale()); + + // Check if we've triggered any callbacks + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) { + if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) { + if (getTime() >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) + runner->callBack(); + } else if (runner->_type == kCallBackAtExtremes) { + if (runner->_trigger == kTriggerAtStop) { + if (time == stopTime) + runner->callBack(); + } else if (runner->_trigger == kTriggerAtStart) { + if (time == startTime) + runner->callBack(); + } + } + } + + // Loop if necessary + if (getFlags() & kLoopTimeBase) { + if (getRate() < 0 && time == startTime) + setTime(_stopTime, _stopScale); + else if (getRate() > 0 && time == stopTime) + setTime(_startTime, _startScale); + } +} + +// Protected functions only called by TimeBaseCallBack. + +void TimeBase::addCallBack(TimeBaseCallBack *callBack) { + callBack->_nextCallBack = _callBackList; + _callBackList = callBack; +} + +void TimeBase::removeCallBack(TimeBaseCallBack *callBack) { + if (_callBackList == callBack) { + _callBackList = callBack->_nextCallBack; + } else { + TimeBaseCallBack *runner, *prevRunner; + + for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack) + ; + + prevRunner->_nextCallBack = runner->_nextCallBack; + } + + callBack->_nextCallBack = 0; +} + +void TimeBase::disposeAllCallBacks() { + TimeBaseCallBack *nextRunner; + + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) { + nextRunner = runner->_nextCallBack; + runner->disposeCallBack(); + runner->_nextCallBack = 0; + } + + _callBackList = 0; +} + +TimeBaseCallBack::TimeBaseCallBack() { + _timeBase = 0; + _nextCallBack = 0; + _trigger = kTriggerNone; + _type = kCallBackNone; +} + +TimeBaseCallBack::~TimeBaseCallBack() { + releaseCallBack(); +} + +void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) { + releaseCallBack(); + _timeBase = tb; + _timeBase->addCallBack(this); + _type = type; +} + +void TimeBaseCallBack::releaseCallBack() { + if (_timeBase) + _timeBase->removeCallBack(this); + disposeCallBack(); +} + +void TimeBaseCallBack::disposeCallBack() { + _timeBase = 0; +} + +void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) { + // TODO: Rename param2/param3? + _trigger = trigger; + _param2 = param2; + _param3 = param3; +} + +void TimeBaseCallBack::cancelCallBack() { + _trigger = kTriggerNone; +} + +IdlerTimeBase::IdlerTimeBase() { + _lastTime = 0xffffffff; + startIdling(); +} + +void IdlerTimeBase::useIdleTime() { + uint32 currentTime = getTime(); + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +NotificationCallBack::NotificationCallBack() { + _callBackFlag = 0; + _notifier = 0; +} + +void NotificationCallBack::callBack() { + if (_notifier) + _notifier->setNotificationFlags(_callBackFlag, _callBackFlag); +} + +static const tNotificationFlags kFuseExpiredFlag = 1; + +Fuse::Fuse() : _fuseNotification(0, (NotificationManager *)((PegasusEngine *)g_engine)) { + _fuseNotification.notifyMe(this, kFuseExpiredFlag, kFuseExpiredFlag); + _fuseCallBack.setNotification(&_fuseNotification); + _fuseCallBack.initCallBack(&_fuseTimer, kCallBackAtExtremes); + _fuseCallBack.setCallBackFlag(kFuseExpiredFlag); +} + +void Fuse::primeFuse(const TimeValue frequency, const TimeScale scale) { + stopFuse(); + _fuseTimer.setScale(scale); + _fuseTimer.setSegment(0, frequency); + _fuseTimer.setTime(0); +} + +void Fuse::lightFuse() { + if (!_fuseTimer.isRunning()) { + _fuseCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _fuseTimer.start(); + } +} + +void Fuse::stopFuse() { + _fuseTimer.stop(); + _fuseCallBack.cancelCallBack(); + // Make sure the fuse has not triggered but not been caught yet... + _fuseNotification.setNotificationFlags(0, 0xffffffff); +} + +void Fuse::advanceFuse(const TimeValue time) { + if (_fuseTimer.isRunning()) { + _fuseTimer.stop(); + _fuseTimer.setTime(_fuseTimer.getTime() + time); + _fuseTimer.start(); + } +} + +TimeValue Fuse::getTimeRemaining() { + return _fuseTimer.getStop() - _fuseTimer.getTime(); +} + +void Fuse::receiveNotification(Notification *, const tNotificationFlags) { + stopFuse(); + invokeAction(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h new file mode 100755 index 0000000000..65e7d21372 --- /dev/null +++ b/engines/pegasus/timers.h @@ -0,0 +1,254 @@ +/* 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_TIMERS_H +#define PEGASUS_TIMERS_H + +#include "common/list.h" +#include "common/rational.h" + +#include "pegasus/constants.h" +#include "pegasus/notification.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Idler { +friend class PegasusEngine; + +public: + Idler(); + virtual ~Idler(); + + virtual void startIdling(); + virtual void stopIdling(); + bool isIdling() const { return _isIdling; } + +protected: + virtual void useIdleTime() {} + + bool _isIdling; + Idler *_nextIdler, *_prevIdler; +}; + +enum { + kLoopTimeBase = 1, + kPalindromeLoopTimeBase = 2, + kMaintainTimeBaseZero = 4 +}; + +class TimeBaseCallBack; + +class TimeBase { +friend class TimeBaseCallBack; +public: + TimeBase(const TimeScale = kDefaultTimeScale); + virtual ~TimeBase(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + virtual TimeValue getTime(const TimeScale = 0); + + virtual void setScale(const TimeScale scale) { _preferredScale = scale; } + virtual TimeScale getScale() const { return _preferredScale; } + + virtual void setRate(const Common::Rational); + virtual Common::Rational getRate() const { return _rate; } + + virtual void start(); + virtual void stop(); + virtual bool isRunning(); + + virtual void pause(); + virtual void resume(); + bool isPaused() const { return _paused; } + + virtual void setFlags(const uint32 flags) { _flags = flags; } + virtual uint32 getFlags() const { return _flags; } + + virtual void setStart(const TimeValue, const TimeScale = 0); + virtual TimeValue getStart(const TimeScale = 0) const; + + virtual void setStop(const TimeValue, const TimeScale = 0); + virtual TimeValue getStop(const TimeScale = 0) const; + + virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0); + virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const; + + virtual TimeValue getDuration(const TimeScale = 0) const; + + virtual void setMasterTimeBase(TimeBase *timeBase); + + void disposeAllCallBacks(); + + // ScummVM's API additions (to replace the need for actual timers) + virtual void checkCallBacks(); + +protected: + void addCallBack(TimeBaseCallBack *); + void removeCallBack(TimeBaseCallBack *); + + TimeBase *_master; + TimeScale _preferredScale; + TimeBaseCallBack *_callBackList; + Common::Rational _rate, _pausedRate; + bool _paused; + uint32 _startTime, _startScale; + uint32 _stopTime, _stopScale; + uint32 _flags; + + Common::Rational _time; + uint32 _lastMillis; + +private: + Common::Rational getEffectiveRate() const; + + Common::List<TimeBase *> _slaves; +}; + +// Type passed to initCallBack() +enum CallBackType { + kCallBackNone = 0, + kCallBackAtTime = 1, + kCallBackAtExtremes = 4 +}; + +// Trigger passed to scheduleCallBack() +enum CallBackTrigger { + kTriggerNone = 0, + + // AtTime flags + kTriggerTimeFwd = 1, + + // AtExtremes flags + kTriggerAtStart = 1, + kTriggerAtStop = 2 +}; + +class TimeBaseCallBack { +friend class TimeBase; + +public: + TimeBaseCallBack(); + virtual ~TimeBaseCallBack(); + + void initCallBack(TimeBase *, CallBackType type); + + void releaseCallBack(); + + void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3); + void cancelCallBack(); + +protected: + virtual void callBack() = 0; + + TimeBase *_timeBase; + + // Owned and operated by TimeBase; + TimeBaseCallBack *_nextCallBack; + + // Our storage of the QuickTime timer crap + CallBackType _type; + CallBackTrigger _trigger; + uint32 _param2, _param3; + +private: + void disposeCallBack(); +}; + +class IdlerTimeBase : public Idler, public TimeBase { +public: + IdlerTimeBase(); + virtual ~IdlerTimeBase() { stopIdling(); } + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue) {} + + TimeValue _lastTime; + +}; + +class NotificationCallBack : public TimeBaseCallBack { +public: + NotificationCallBack(); + virtual ~NotificationCallBack() {} + + void setNotification(Notification *notifier) { _notifier = notifier; } + + void setCallBackFlag(const tNotificationFlags flag) { _callBackFlag = flag; } + tNotificationFlags getCallBackFlag() const { return _callBackFlag; } + +protected: + void callBack(); + + Notification *_notifier; + tNotificationFlags _callBackFlag; +}; + +class DynamicElement : public TimeBase { +public: + TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; } +}; + +class Fuse : private NotificationReceiver { +public: + Fuse(); + virtual ~Fuse() {} + + void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P + void lightFuse(); + void stopFuse(); + bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); } + void advanceFuse(const TimeValue); + TimeValue getTimeRemaining(); + TimeScale getFuseScale() { return _fuseTimer.getScale(); } + + void pauseFuse() { _fuseTimer.pause(); } + void resumeFuse() { _fuseTimer.resume(); } + bool isFusePaused() { return _fuseTimer.isPaused(); } + +protected: + virtual void receiveNotification(Notification *, const tNotificationFlags); + virtual void invokeAction() {} + + TimeBase _fuseTimer; + NotificationCallBack _fuseCallBack; + Notification _fuseNotification; +}; + +class FuseFunction : public Fuse, public FunctionPtr { +public: + FuseFunction() {} + virtual ~FuseFunction() {} + +protected: + virtual void invokeAction() { callFunction(); } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/transition.cpp b/engines/pegasus/transition.cpp new file mode 100755 index 0000000000..73bd277233 --- /dev/null +++ b/engines/pegasus/transition.cpp @@ -0,0 +1,150 @@ +/* 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/system.h" + +#include "pegasus/transition.h" + +namespace Pegasus { + +ScreenFader::ScreenFader() { + _fadeTarget = getBlack(); + // Initially, assume screens are on at full brightness. + Fader::setFaderValue(100); +} + +void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) { + _fadeTarget = fadeTarget; + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0); + startFaderSync(spec); +} + +void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) { + _fadeTarget = fadeTarget; + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100); + startFaderSync(spec); +} + +void ScreenFader::setFaderValue(const int32 value) { + if (value != getFaderValue()) { + Fader::setFaderValue(value); + + // TODO: Gamma fading + } +} + +uint32 ScreenFader::getBlack() { + return g_system->getScreenFormat().RGBToColor(0, 0, 0); +} + +Transition::Transition(const tDisplayElementID id) : FaderAnimation(id) { + _outPicture = 0; + _inPicture = 0; +} + +void Transition::setBounds(const Common::Rect &r) { + FaderAnimation::setBounds(r); + _boundsWidth = _bounds.width(); + _boundsHeight = _bounds.height(); +} + +void Transition::setInAndOutElements(DisplayElement *inElement, DisplayElement *outElement) { + _inPicture = inElement; + _outPicture = outElement; + + Common::Rect r; + + if (_outPicture) + _outPicture->getBounds(r); + else if (_inPicture) + _inPicture->getBounds(r); + + setBounds(r); +} + +void Slide::draw(const Common::Rect &r) { + Common::Rect oldBounds, newBounds; + + adjustSlideRects(oldBounds, newBounds); + drawElements(r, oldBounds, newBounds); +} + +void Slide::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + oldBounds = _bounds; + newBounds = _bounds; +} + +void Slide::drawElements(const Common::Rect &drawRect, const Common::Rect &oldBounds, const Common::Rect &newBounds) { + drawSlideElement(drawRect, oldBounds, _outPicture); + drawSlideElement(drawRect, newBounds, _inPicture); +} + +void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) { + if (picture && drawRect.intersects(oldBounds)) { + picture->moveElementTo(oldBounds.left, oldBounds.top); + picture->draw(drawRect.findIntersectingRect(oldBounds)); + } +} + +void Push::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + switch (_direction & kSlideHorizMask) { + case kSlideLeftMask: + newBounds.left = oldBounds.right = _bounds.right - pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + newBounds.right = newBounds.left + _boundsWidth; + oldBounds.left = oldBounds.right - _boundsWidth; + break; + case kSlideRightMask: + oldBounds.left = newBounds.right = _bounds.left + pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + oldBounds.right = oldBounds.left + _boundsWidth; + newBounds.left = newBounds.right - _boundsWidth; + break; + default: + newBounds.left = oldBounds.left = _bounds.left; + newBounds.right = oldBounds.right = _bounds.right; + break; + } + switch (_direction & kSlideVertMask) { + case kSlideDownMask: + oldBounds.top = newBounds.bottom = _bounds.top + pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + oldBounds.bottom = oldBounds.top + _boundsHeight; + newBounds.top = newBounds.bottom - _boundsHeight; + break; + case kSlideUpMask: + newBounds.top = oldBounds.bottom = _bounds.bottom - pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + newBounds.bottom = newBounds.top + _boundsHeight; + oldBounds.top = oldBounds.bottom - _boundsHeight; + break; + default: + newBounds.top = oldBounds.top = _bounds.top; + newBounds.bottom = oldBounds.bottom = _bounds.bottom; + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/transition.h b/engines/pegasus/transition.h new file mode 100755 index 0000000000..718808c5d8 --- /dev/null +++ b/engines/pegasus/transition.h @@ -0,0 +1,105 @@ +/* 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_TRANSITION_H +#define PEGASUS_TRANSITION_H + +#include "pegasus/fader.h" + +namespace Pegasus { + +class ScreenFader : public Fader { +public: + ScreenFader(); + virtual ~ScreenFader() {} + + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack()); + void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack()); + + void setFaderValue(const int32); + +protected: + uint32 _fadeTarget; + +private: + static uint32 getBlack(); +}; + +// Transitions are faders that range over [0,1000], which makes their +// "resolution" one tenth of a percent + +const long kTransitionBottom = 0; +const long kTransitionTop = 1000; + +const long kTransitionRange = kTransitionTop - kTransitionBottom; + +class Transition : public FaderAnimation { +public: + Transition(const tDisplayElementID id); + virtual ~Transition() {} + + virtual void setBounds(const Common::Rect &); + + virtual void setInAndOutElements(DisplayElement *, DisplayElement *); + DisplayElement *getInElement() { return _inPicture; } + DisplayElement *getOutElement() { return _outPicture; } + +protected: + DisplayElement *_outPicture; + DisplayElement *_inPicture; + + tCoordType _boundsWidth, _boundsHeight; +}; + +class Slide : public Transition { +public: + Slide(const tDisplayElementID id) : Transition(id) {} + virtual ~Slide() {} + + virtual void setSlideDirection(tSlideDirection dir) { _direction = dir; } + virtual void draw(const Common::Rect &); + + virtual void setDirection(const tSlideDirection dir) { _direction = dir; } + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); + virtual void drawElements(const Common::Rect &, const Common::Rect &, const Common::Rect &); + virtual void drawSlideElement(const Common::Rect &, const Common::Rect &, DisplayElement *); + + tSlideDirection _direction; +}; + +class Push : public Slide { +public: + Push(const tDisplayElementID id) : Slide(id) {} + virtual ~Push() {} + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif
\ No newline at end of file diff --git a/engines/pegasus/types.h b/engines/pegasus/types.h new file mode 100755 index 0000000000..ed227847e4 --- /dev/null +++ b/engines/pegasus/types.h @@ -0,0 +1,192 @@ +/* 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_TYPES_H +#define PEGASUS_TYPES_H + +#include "common/scummsys.h" + +namespace Pegasus { + +// TODO: All of the "tMM"-prefixed defines should be replaced eventually +// TODO: Probably all of these don't really need to be typedef'd... +typedef int8 tMM8BitS; +typedef uint8 tMM8BitU; + +typedef int16 tMM16BitS; +typedef uint16 tMM16BitU; + +typedef int32 tMM32BitS; +typedef uint32 tMM32BitU; + +typedef tMM8BitS tMM8BitID; +typedef tMM16BitS tMM16BitID; +typedef tMM32BitS tMM32BitID; + +typedef tMM8BitU tMM8BitFlags; +typedef tMM16BitU tMM16BitFlags; +typedef tMM32BitU tMM32BitFlags; + +typedef tMM32BitID tDisplayElementID; +typedef tMM32BitS tDisplayOrder; + +typedef tMM16BitID tHotSpotID; +typedef tMM32BitFlags tHotSpotFlags; + +typedef tMM8BitFlags tButtonState; +typedef tMM32BitFlags tInputBits; + +typedef tMM8BitU tKeyMapType[16]; +typedef tMM8BitU tKeyType; +typedef tMM8BitU tKeyMapIndexType; +typedef tMM8BitU tKeyMapBitType; + +typedef tMM32BitID tNotificationID; +typedef tMM32BitFlags tNotificationFlags; + +// Mac types. +typedef tMM16BitS tResIDType; +typedef tMM16BitS tCoordType; +typedef tMM16BitS tQDCopyMode; +typedef tMM16BitS tResItemCountType; + +enum tCopyMode { + kNoMask, + kUseClipArea, + kUseTransparency +}; + +enum tSlideDirection { + kSlideLeftMask = 1, + kSlideRightMask = kSlideLeftMask << 1, + kSlideUpMask = kSlideRightMask << 1 << 1, + kSlideDownMask = kSlideUpMask << 1, + + kSlideHorizMask = kSlideLeftMask | kSlideRightMask, + kSlideVertMask = kSlideUpMask | kSlideDownMask, + + kSlideUpLeftMask = kSlideLeftMask | kSlideUpMask, + kSlideUpRightMask = kSlideRightMask | kSlideUpMask, + kSlideDownLeftMask = kSlideLeftMask | kSlideDownMask, + kSlideDownRightMask = kSlideRightMask | kSlideDownMask +}; + +// ScummVM QuickTime/QuickDraw replacement types +typedef uint TimeValue; +typedef uint TimeScale; +// TODO: Fixed and RGBColor + +typedef tMM16BitID tGameID; + +typedef tGameID tItemID; +typedef tGameID tActorID; +typedef tGameID tRoomID; +typedef tGameID tNeighborhoodID; +typedef tMM8BitU tAlternateID; +typedef tMM8BitS tHotSpotActivationID; + +typedef tMM16BitS tWeightType; + +typedef tMM8BitU tDirectionConstant; +typedef tMM8BitU tTurnDirection; + +// Meant to be room in low 16 bits and direction in high 16 bits. +typedef tMM32BitU tRoomViewID; + +#define MakeRoomView(room, direction) (((tRoomViewID) (room)) | (((tRoomViewID) (direction)) << 16)) + +typedef tMM32BitU tExtraID; + +typedef tMM16BitS tGameMode; + +typedef tMM16BitS tWeightType; + +typedef tMM16BitS tItemState; + +typedef tMM8BitS tDeathReason; + +typedef tMM32BitS tGameMenuCommand; + +typedef tMM32BitS tGameScoreType; + +typedef long tCanMoveForwardReason; + +typedef long tCanTurnReason; + +typedef long tCanOpenDoorReason; + +enum tInventoryResult { + kInventoryOK, + kTooMuchWeight, + kItemNotInInventory +}; + +typedef tMM32BitID tInteractionID; + +typedef tMM32BitID tAIConditionID; + +enum tEnergyStage { + kStageNoStage, + kStageCasual, // more than 50% energy + kStageWorried, // more than 25% energy + kStageNervous, // more than 5% energy + kStagePanicStricken // less than 5% energy +}; + +enum tNoradSubPrepState { + kSubNotPrepped, + kSubPrepped, + kSubDamaged +}; + +enum tLowerClientSignature { + kNoClientSignature, + kInventorySignature, + kBiochipSignature, + kAISignature +}; + +enum tLowerAreaSignature { + kLeftAreaSignature, + kMiddleAreaSignature, + kRightAreaSignature +}; + +enum tAirQuality { + kAirQualityGood, + kAirQualityDirty, + kAirQualityVacuum +}; + +enum tDragType { + kDragNoDrag, + kDragInventoryPickup, + kDragBiochipPickup, + kDragInventoryUse +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/util.cpp b/engines/pegasus/util.cpp new file mode 100755 index 0000000000..a4c9a351c2 --- /dev/null +++ b/engines/pegasus/util.cpp @@ -0,0 +1,100 @@ +/* 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/random.h" +#include "common/util.h" + +#include "pegasus/util.h" + +namespace Pegasus { + +IDObject::IDObject(const tMM32BitID id) { + _objectID = id; +} + +IDObject::~IDObject() { +} + +tMM32BitID IDObject::getObjectID() const { + return _objectID; +} + +int operator==(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() == arg2.getObjectID(); +} + +int operator!=(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() != arg2.getObjectID(); +} + +FunctionPtr::FunctionPtr() { + _function = 0; + _functionArg = 0; +} + +FunctionPtr::~FunctionPtr() { +} + +void FunctionPtr::setFunctionPtr(tFunctionPtr function, void *functionArg) { + _function = function; + _functionArg = functionArg; +} + +void FunctionPtr::callFunction() { + if (_function != 0) + (*_function)(this, _functionArg); +} + +inline int32 pegasusRound(const int32 a, const int32 b) { + if (b < 0) + if (a < 0) + return -((a - (-b >> 1)) / -b); + else + return -((a + (-b >> 1)) / -b); + else + if (a < 0) + return (a - (b >> 1)) / b; + else + return (a + (b >> 1)) / b; +} + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2) { + if (start2 == stop2) + return start2; + else + return start2 + pegasusRound((current1 - start1) * (stop2 - start2), (stop1 - start1)); +} + +void shuffleArray(int32 *arr, int32 count, Common::RandomSource &random) { + if (count > 1) { + for (int32 i = 1; i < count; ++i) { + int32 j = random.getRandomNumber(i); + if (j != i) + SWAP(arr[i], arr[j]); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h new file mode 100755 index 0000000000..6369c49fdb --- /dev/null +++ b/engines/pegasus/util.h @@ -0,0 +1,135 @@ +/* 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_UTIL_H +#define PEGASUS_UTIL_H + +#include "common/stream.h" + +#include "pegasus/types.h" + +namespace Common { + class RandomSource; +} + +namespace Pegasus { + +class IDObject { +public: + IDObject(const tMM32BitID id); + ~IDObject(); + + tMM32BitID getObjectID() const; + +private: + tMM32BitID _objectID; +}; + +class FunctionPtr; + +typedef void (*tFunctionPtr)(FunctionPtr *theFunction, void *functionArg); + +class FunctionPtr { +public: + FunctionPtr(); + virtual ~FunctionPtr(); + + void setFunctionPtr(tFunctionPtr function, void *functionArg); + +protected: + void callFunction(); + + tFunctionPtr _function; + void *_functionArg; +}; + +#define NUM_FLAGS (sizeof(Unit) * 8) +#define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3) +#define BIT_INDEX_MASK (NUM_FLAGS - 1) + +template <typename Unit, uint32 kNumFlags> +class FlagsArray { +public: + FlagsArray() { clearAllFlags(); } + void clearAllFlags() { memset(_flags, 0, sizeof(_flags)); } + void setAllFlags() { memset(_flags, ~((Unit)0), sizeof(_flags)); } + void setFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] |= 1 << (flag & BIT_INDEX_MASK); } + void clearFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] &= ~(1 << (flag & BIT_INDEX_MASK)); } + void setFlag(uint32 flag, bool val) { if (val) setFlag(flag); else clearFlag(flag); } + bool getFlag(uint32 flag) { return (_flags[flag >> BIT_INDEX_SHIFT] & (1 << (flag & BIT_INDEX_MASK))) != 0; } + bool anyFlagSet() { + for (uint32 i = 0; i < sizeof(_flags); i++) + if (_flags[i] != 0) + return true; + return false; + } + + void readFromStream(Common::ReadStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->read(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + _flags[i] = stream->readUint16BE(); + else /* if (sizeof(Unit) == 4) */ + _flags[i] = stream->readUint32BE(); + } + } + + void writeToStream(Common::WriteStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->write(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + stream->writeUint16BE(_flags[i]); + else /* if (sizeof(Unit) == 4) */ + stream->writeUint32BE(_flags[i]); + } + } + +private: + Unit _flags[(kNumFlags - 1) / NUM_FLAGS + 1]; +}; + +#undef NUM_FLAGS; +#undef BIT_INDEX_SHIFT +#undef BIT_INDEX_MASK + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2); + +void shuffleArray(int32 *arr, int32 count, Common::RandomSource &random); + +int32 pegasusRound(const int32 a, const int32 b); + +} // End of namespace Pegasus + +#endif |