aboutsummaryrefslogtreecommitdiff
path: root/engines/pegasus/pegasus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/pegasus/pegasus.cpp')
-rw-r--r--engines/pegasus/pegasus.cpp2292
1 files changed, 2292 insertions, 0 deletions
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
new file mode 100644
index 0000000000..43ffc4c460
--- /dev/null
+++ b/engines/pegasus/pegasus.cpp
@@ -0,0 +1,2292 @@
+/* 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/file.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/mapchip.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/mars/mars.h"
+#include "pegasus/neighborhood/norad/constants.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+#include "pegasus/neighborhood/norad/delta/noraddelta.h"
+#include "pegasus/neighborhood/prehistoric/prehistoric.h"
+#include "pegasus/neighborhood/tsa/fulltsa.h"
+#include "pegasus/neighborhood/tsa/tinytsa.h"
+#include "pegasus/neighborhood/wsc/wsc.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;
+ _saveRequested = _loadRequested = false;
+ _gameMenu = 0;
+ _deathReason = kDeathStranded;
+ _neighborhood = 0;
+ _FXLevel = 0x80;
+ _ambientLevel = 0x80;
+ _gameMode = kNoMode;
+ _switchModesSync = false;
+ _draggingItem = 0;
+ _dragType = kDragNoDrag;
+ _idlerHead = 0;
+ _currentCD = 1;
+ _introTimer = 0;
+ _aiSaveStream = 0;
+}
+
+PegasusEngine::~PegasusEngine() {
+ delete _resFork;
+ delete _console;
+ delete _cursor;
+ delete _continuePoint;
+ delete _gameMenu;
+ delete _neighborhood;
+ delete _rnd;
+ delete _introTimer;
+ delete _aiSaveStream;
+ InputDeviceManager::destroy();
+
+ // NOTE: This must be deleted last!
+ delete _gfx;
+}
+
+void introTimerExpiredFunction(FunctionPtr *, void *) {
+ ((PegasusEngine *)g_engine)->introTimerExpired();
+}
+
+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);
+
+ _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);
+
+ // Load from the launcher/cli if requested (and don't show the intro in those cases)
+ bool doIntro = true;
+ if (ConfMan.hasKey("save_slot")) {
+ uint32 gameToLoad = ConfMan.getInt("save_slot");
+ doIntro = (loadGameState(gameToLoad).getCode() != Common::kNoError);
+ }
+
+ _shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags);
+
+ if (doIntro)
+ // Start up the first notification
+ _shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag);
+
+ if (!isDemo()) {
+ _introTimer = new FuseFunction();
+ _introTimer->setFunctionPtr(&introTimerExpiredFunction, 0);
+ }
+
+ 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++) {
+ ItemID itemID = res->readUint16BE();
+ NeighborhoodID neighborhoodID = res->readUint16BE();
+ RoomID roomID = res->readUint16BE();
+ DirectionConstant direction = res->readByte();
+ res->readByte(); // alignment
+
+ createItem(itemID, neighborhoodID, roomID, direction);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant 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:
+ new MapChip(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() {
+ stopIntroTimer();
+
+ 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();
+
+ if (frame) {
+ _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h);
+ _system->updateScreen();
+ }
+ }
+
+ Input input;
+ InputDevice.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;
+}
+
+Common::Error PegasusEngine::showLoadDialog() {
+ GUI::SaveLoadChooser slc(_("Load game:"), _("Load"), false);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+ Common::Error result;
+
+ if (slot >= 0) {
+ if (loadGameState(slot).getCode() == Common::kNoError)
+ result = Common::kNoError;
+ else
+ result = Common::kUnknownError;
+ } else {
+ result = Common::kUserCanceled;
+ }
+
+ slc.close();
+
+ return result;
+}
+
+Common::Error PegasusEngine::showSaveDialog() {
+ GUI::SaveLoadChooser slc(_("Save game:"), _("Save"), true);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+ Common::Error result;
+
+ if (slot >= 0) {
+ if (saveGameState(slot, slc.getResultString()).getCode() == Common::kNoError)
+ result = Common::kNoError;
+ else
+ result = Common::kUnknownError;
+ } else {
+ result = Common::kUserCanceled;
+ }
+
+ slc.close();
+
+ return result;
+}
+
+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;
+
+ if (!g_interface)
+ createInterface();
+
+ // 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:
+ _currentCD = gameType - kPegasusPrimeDisk1GameType + 1;
+ 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());
+
+ // Death reason
+ setEnergyDeathReason(stream->readByte());
+
+ // Items
+ g_allItems.readFromStream(stream);
+
+ // Inventory
+ byte itemCount = stream->readByte();
+
+ if (itemCount > 0) {
+ for (byte i = 0; i < itemCount; i++) {
+ InventoryItem *inv = (InventoryItem *)g_allItems.findItemByID((ItemID)stream->readUint16BE());
+ addItemToInventory(inv);
+ }
+
+ g_interface->setCurrentInventoryItemID((ItemID)stream->readUint16BE());
+ }
+
+ // Biochips
+ byte biochipCount = stream->readByte();
+
+ if (biochipCount > 0) {
+ for (byte i = 0; i < biochipCount; i++) {
+ BiochipItem *biochip = (BiochipItem *)g_allItems.findItemByID((ItemID)stream->readUint16BE());
+ addItemToBiochips(biochip);
+ }
+
+ g_interface->setCurrentBiochipID((ItemID)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) {
+ if (g_neighborhood)
+ g_neighborhood->flushGameState();
+
+ // Signature
+ stream->writeUint32BE(kPegasusPrimeCreator);
+
+ if (saveType == kNormalSave)
+ stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1);
+ else // Continue
+ stream->writeUint32BE(kPegasusPrimeContinueType);
+
+ stream->writeUint32BE(kPegasusPrimeVersion);
+
+ // Game State
+ GameState.writeGameState(stream);
+
+ // Energy
+ stream->writeUint32BE(getSavedEnergyValue());
+
+ // Death reason
+ stream->writeByte(getEnergyDeathReason());
+
+ // Items
+ g_allItems.writeToStream(stream);
+
+ // Inventory
+ byte itemCount = _items.getNumItems();
+ stream->writeByte(itemCount);
+
+ if (itemCount > 0) {
+ for (uint32 i = 0; i < itemCount; i++)
+ stream->writeUint16BE(_items.getItemIDAt(i));
+
+ stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID());
+ }
+
+ // Biochips
+ byte biochipCount = _biochips.getNumItems();
+ stream->writeByte(biochipCount);
+
+ if (biochipCount > 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() {
+ // WORKAROUND: Do not attempt to make a continue point if the interface is not set
+ // up. The original did *not* do this and attempting to restore the game using the pause
+ // menu during the canyon/space chase sequence would segfault the game and crash the
+ // system. Nice!
+ if (!g_interface)
+ return;
+
+ 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 NotificationFlags flags) {
+ if (&_shellNotification == notification) {
+ switch (flags) {
+ case kGameStartingFlag: {
+ useMenu(new MainMenu());
+
+ if (!isDemo()) {
+ runIntro();
+ resetIntroTimer();
+ } else {
+ showTempScreen("Images/Demo/NGsplashScrn.pict");
+ }
+
+ if (shouldQuit())
+ return;
+
+ _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() {
+ if (!isDemo() && _gameMenu && _gameMenu->getObjectID() == kMainMenuID) {
+ _introTimer->stopFuse();
+ _introTimer->primeFuse(kIntroTimeOut);
+ _introTimer->lightFuse();
+ }
+}
+
+void PegasusEngine::introTimerExpired() {
+ if (_gameMenu && _gameMenu->getObjectID() == kMainMenuID) {
+ ((MainMenu *)_gameMenu)->stopMainMenuLoop();
+
+ bool skipped = false;
+
+ Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile(_introDirectory + "/LilMovie.movie"))
+ error("Failed to load little movie");
+
+ bool saveAllowed = swapSaveAllowed(false);
+ bool openAllowed = swapLoadAllowed(false);
+
+ skipped = playMovieScaled(video, 0, 0);
+
+ delete video;
+
+ if (shouldQuit())
+ return;
+
+ if (!skipped) {
+ runIntro();
+
+ if (shouldQuit())
+ return;
+ }
+
+ resetIntroTimer();
+ _gfx->invalRect(Common::Rect(0, 0, 640, 480));
+
+ swapSaveAllowed(saveAllowed);
+ swapLoadAllowed(openAllowed);
+
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ }
+}
+
+void PegasusEngine::stopIntroTimer() {
+ if (_introTimer)
+ _introTimer->stopFuse();
+}
+
+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() {
+ GameMenuCommand command = kMenuCmdNoCommand;
+
+ if (_gameMenu) {
+ command = _gameMenu->getLastCommand();
+ if (command != kMenuCmdNoCommand) {
+ _gameMenu->clearLastCommand();
+ doGameMenuCommand(command);
+ }
+ }
+
+ return command != kMenuCmdNoCommand;
+}
+
+void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) {
+ Common::Error result;
+
+ switch (command) {
+ case kMenuCmdStartAdventure:
+ stopIntroTimer();
+ GameState.setWalkthroughMode(false);
+ startNewGame();
+ break;
+ case kMenuCmdCredits:
+ if (isDemo()) {
+ showTempScreen("Images/Demo/DemoCredits.pict");
+ _gfx->doFadeOutSync();
+ _gfx->updateDisplay();
+ _gfx->doFadeInSync();
+ } else {
+ stopIntroTimer();
+ _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:
+ stopIntroTimer();
+ doInterfaceOverview();
+ resetIntroTimer();
+ break;
+ case kMenuCmdStartWalkthrough:
+ stopIntroTimer();
+ GameState.setWalkthroughMode(true);
+ startNewGame();
+ break;
+ case kMenuCmdRestore:
+ stopIntroTimer();
+ // fall through
+ case kMenuCmdDeathRestore:
+ result = showLoadDialog();
+ if (command == kMenuCmdRestore && result.getCode() != Common::kNoError)
+ resetIntroTimer();
+ 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();
+ resetIntroTimer();
+ break;
+ case kMenuCmdPauseSave:
+ if (showSaveDialog().getCode() != Common::kUserCanceled)
+ pauseMenu(false);
+ break;
+ case kMenuCmdPauseContinue:
+ pauseMenu(false);
+ break;
+ case kMenuCmdPauseRestore:
+ makeContinuePoint();
+ result = showLoadDialog();
+
+ if (result.getCode() == Common::kNoError) {
+ // Successfully loaded, unpause the game
+ pauseMenu(false);
+ } else if (result.getCode() != Common::kUserCanceled) {
+ // Try to get us back to a sane state
+ loadFromContinuePoint();
+ }
+ break;
+ case kMenuCmdPauseQuit:
+ _gfx->doFadeOutSync();
+ throwAwayEverything();
+ pauseMenu(false);
+ useMenu(new MainMenu());
+ _gfx->updateDisplay();
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ _gfx->doFadeInSync();
+ 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();
+ }
+
+ // Handle save requests here
+ if (_saveRequested && _saveAllowed) {
+ _saveRequested = false;
+
+ // Can only save during a game and not in the demo
+ if (g_neighborhood && !isDemo()) {
+ pauseEngine(true);
+ showSaveDialog();
+ pauseEngine(false);
+ }
+ }
+
+ // Handle load requests here
+ if (_loadRequested && _loadAllowed) {
+ _loadRequested = false;
+
+ // WORKAROUND: Do not entertain load requests when the pause menu is up
+ // The original did and the game entered a bad state after loading.
+ // It's theoretically possible to make it so it does work while the
+ // pause menu is up, but the pause state of the engine is just too weird.
+ // Just use the pause menu's restore button since it's there for that
+ // for you to load anyway.
+ if (!isDemo() && !(_gameMenu && _gameMenu->getObjectID() == kPauseMenuID)) {
+ pauseEngine(true);
+
+ if (g_neighborhood) {
+ makeContinuePoint();
+
+ Common::Error result = showLoadDialog();
+ if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled)
+ loadFromContinuePoint();
+ } else {
+ if (_introTimer)
+ _introTimer->stopFuse();
+
+ Common::Error result = showLoadDialog();
+ if (result.getCode() != Common::kNoError) {
+ if (!_gameMenu) {
+ useMenu(new MainMenu());
+ ((MainMenu *)_gameMenu)->startMainMenuLoop();
+ }
+
+ resetIntroTimer();
+ }
+ }
+
+ pauseEngine(false);
+ }
+ }
+}
+
+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;
+ InputDevice.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 (;;) {
+ InputDevice.getInput(input, kFilterAllInput);
+
+ if (input.anyInput() || shouldQuit() || _loadRequested || _saveRequested)
+ 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();
+
+ _saveRequested = false;
+ _loadRequested = false;
+}
+
+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 ItemID 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(ItemID 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(ItemID 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 NeighborhoodID neighborhoodID, const RoomID roomID, const DirectionConstant 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();
+
+ if (frame)
+ drawScaledFrame(frame, x, y);
+ }
+
+ Input input;
+ InputDevice.getInput(input, kFilterAllInput);
+ if (input.anyInput() || _saveRequested || _loadRequested)
+ skipped = true;
+
+ _system->delayMillis(10);
+ }
+
+ return skipped;
+}
+
+void PegasusEngine::die(const DeathReason 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 && g_interface)
+ _currentItemID = g_interface->getCurrentInventoryItem()->getObjectID();
+ else
+ _currentItemID = kNoItemID;
+
+ if (_biochips.getNumItems() != 0 && g_interface)
+ _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 GameMode newMode) {
+ if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) {
+ switchGameMode(newMode, _gameMode);
+ _gameMode = newMode;
+ }
+}
+
+void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode 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 GameMode newMode, const GameMode oldMode) {
+ if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick)
+ return false;
+ if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick)
+ return false;
+ return true;
+}
+
+bool PegasusEngine::itemInLocation(const ItemID itemID, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) {
+ NeighborhoodID itemNeighborhood;
+ RoomID itemRoom;
+ DirectionConstant itemDirection;
+
+ Item *item = g_allItems.findItemByID(itemID);
+ item->getItemRoom(itemNeighborhood, itemRoom, itemDirection);
+
+ return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction;
+}
+
+InventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) {
+ InventoryResult result;
+
+ do {
+ if (g_interface)
+ result = g_interface->addInventoryItem(item);
+ else
+ result = _items.addItem(item);
+
+ if (result == kTooMuchWeight)
+ destroyInventoryItem(pickItemToDestroy());
+ } 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(NeighborhoodID neighborhoodID) {
+ if (_neighborhood)
+ useNeighborhood(0);
+
+ // Sub chase is special
+ if (neighborhoodID == kNoradSubChaseID) {
+ throwAwayEverything();
+ doSubChase();
+
+ if (shouldQuit())
+ return;
+
+ neighborhoodID = kNoradDeltaID;
+ GameState.setNextRoom(kNorad41);
+ GameState.setNextDirection(kEast);
+ }
+
+ Neighborhood *neighborhood;
+ makeNeighborhood(neighborhoodID, neighborhood);
+ useNeighborhood(neighborhood);
+
+ // Update the CD variable (used just for saves currently)
+ _currentCD = getNeighborhoodCD(neighborhoodID);
+}
+
+void PegasusEngine::startNeighborhood() {
+ if (g_interface && _currentItemID != kNoItemID)
+ g_interface->setCurrentInventoryItemID(_currentItemID);
+
+ if (g_interface && _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(NeighborhoodID neighborhoodID, Neighborhood *&neighborhood) {
+ // TODO: CD check
+
+ switch (neighborhoodID) {
+ case kCaldoriaID:
+ neighborhood = new Caldoria(g_AIArea, this);
+ break;
+ case kMarsID:
+ neighborhood = new Mars(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;
+ case kWSCID:
+ neighborhood = new WSC(g_AIArea, this);
+ break;
+ case kNoradAlphaID:
+ neighborhood = new NoradAlpha(g_AIArea, this);
+ break;
+ case kNoradDeltaID:
+ createInterface();
+ neighborhood = new NoradDelta(g_AIArea, this);
+ break;
+ default:
+ error("Unknown neighborhood %d", neighborhoodID);
+ }
+}
+
+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:
+ HotSpotFlags 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.setTime(startTime);
+ _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();
+ InventoryResult 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, DragType 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)) {
+ LowerClientSignature 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.setTime(startTime);
+ _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;
+}
+
+InputBits 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();
+ }
+}
+
+InventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) {
+ InventoryResult 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.
+
+InventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) {
+ InventoryResult 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();
+}
+
+NeighborhoodID 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();
+}
+
+uint PegasusEngine::getRandomNumber(uint max) {
+ return _rnd->getRandomNumber(max);
+}
+
+void PegasusEngine::shuffleArray(int32 *arr, int32 count) {
+ if (count > 1) {
+ for (int32 i = 1; i < count; ++i) {
+ int32 j = _rnd->getRandomNumber(i);
+ if (j != i)
+ SWAP(arr[i], arr[j]);
+ }
+ }
+}
+
+void PegasusEngine::playEndMessage() {
+ if (g_interface) {
+ allowInput(false);
+ g_interface->playEndMessage();
+ allowInput(true);
+ }
+
+ die(kPlayerWonGame);
+}
+
+void PegasusEngine::doSubChase() {
+ static const uint32 endTime = 133200 * 1000 / 600;
+
+ Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder();
+ if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie"))
+ error("Failed to load sub chase");
+
+ while (!shouldQuit() && !video->endOfVideo() && video->getTime() < endTime) {
+ if (video->needsUpdate()) {
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (frame)
+ drawScaledFrame(frame, 0, 0);
+ }
+
+ Common::Event event;
+ while (_eventMan->pollEvent(event))
+ ;
+
+ _system->delayMillis(10);
+ }
+
+ delete video;
+}
+
+void PegasusEngine::drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y) {
+ // 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();
+}
+
+void PegasusEngine::destroyInventoryItem(const ItemID itemID) {
+ InventoryItem *item = (InventoryItem *)g_allItems.findItemByID(itemID);
+
+ ItemExtraEntry entry;
+
+ switch (itemID) {
+ case kAirMask:
+ item->findItemExtra(kRemoveAirMask, entry);
+ item->setItemRoom(kMarsID, kMars49, kSouth);
+ break;
+ case kArgonCanister:
+ item->findItemExtra(kRemoveArgon, entry);
+ item->setItemRoom(kWSCID, kWSC02Morph, kSouth);
+ break;
+ case kCrowbar:
+ item->findItemExtra(kRemoveCrowbar, entry);
+ item->setItemRoom(kMarsID, kMars34, kSouth);
+ break;
+ case kJourneymanKey:
+ item->findItemExtra(kRemoveJourneymanKey, entry);
+ item->setItemRoom(kFullTSAID, kTSA22Red, kEast);
+ break;
+ case kMarsCard:
+ item->findItemExtra(kRemoveMarsCard, entry);
+ item->setItemRoom(kMarsID, kMars31South, kSouth);
+ break;
+ case kNitrogenCanister:
+ item->findItemExtra(kRemoveNitrogen, entry);
+ item->setItemRoom(kWSCID, kWSC02Messages, kSouth);
+ break;
+ case kOrangeJuiceGlassEmpty:
+ item->findItemExtra(kRemoveGlass, entry);
+ item->setItemRoom(kCaldoriaID, kCaldoriaReplicator, kNorth);
+ break;
+ case kPoisonDart:
+ item->findItemExtra(kRemoveDart, entry);
+ item->setItemRoom(kWSCID, kWSC01, kWest);
+ break;
+ case kSinclairKey:
+ item->findItemExtra(kRemoveSinclairKey, entry);
+ item->setItemRoom(kWSCID, kWSC02Morph, kSouth);
+ break;
+ default:
+ return;
+ }
+
+ g_interface->setCurrentInventoryItemID(itemID);
+ g_AIArea->playAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop);
+ removeItemFromInventory(item);
+}
+
+ItemID PegasusEngine::pickItemToDestroy() {
+/*
+ Must pick an item to destroy
+
+ Part I: Polite -- try to find an item that's been used
+ Part II: Desperate -- return the first available item.
+*/
+
+ // Polite:
+ if (playerHasItemID(kOrangeJuiceGlassEmpty))
+ return kOrangeJuiceGlassEmpty;
+ if (playerHasItemID(kPoisonDart)) {
+ if (GameState.getCurrentNeighborhood() != kWSCID ||
+ GameState.getWSCAnalyzedDart())
+ return kPoisonDart;
+ }
+ if (playerHasItemID(kJourneymanKey)) {
+ if (GameState.getTSAState() >= kTSAPlayerGotHistoricalLog &&
+ GameState.getTSAState() != kPlayerOnWayToPrehistoric &&
+ GameState.getTSAState() != kPlayerWentToPrehistoric)
+ return kJourneymanKey;
+ }
+ if (playerHasItemID(kMarsCard)) {
+ if (GameState.getCurrentNeighborhood() != kMarsID || GameState.getMarsArrivedBelow())
+ return kMarsCard;
+ }
+
+ // Don't want to deal with deleting the sinclair key and argon canister, since it's
+ // impossible to pick them up one at a time.
+
+ if (playerHasItemID(kNitrogenCanister)) {
+ if (GameState.getScoringGotCardBomb() && GameState.getCurrentNeighborhood() != kMarsID)
+ return kNitrogenCanister;
+ }
+ if (playerHasItemID(kCrowbar)) {
+ if (GameState.getCurrentNeighborhood() == kWSCID) {
+ if (GameState.getCurrentRoom() >= kWSC62)
+ return kCrowbar;
+ } else if (GameState.getCurrentNeighborhood() == kMarsID) {
+ if (GameState.getScoringGotCardBomb())
+ return kCrowbar;
+ } else
+ return kCrowbar;
+ }
+ if (playerHasItemID(kAirMask)) {
+ if (GameState.getCurrentNeighborhood() == kMarsID) {
+ if (g_neighborhood->getAirQuality(GameState.getCurrentRoom()) == kAirQualityGood)
+ return kAirMask;
+ } else if (GameState.getCurrentNeighborhood() != kNoradAlphaID &&
+ GameState.getCurrentNeighborhood() != kNoradDeltaID) {
+ return kAirMask;
+ }
+ }
+
+ // Desperate:
+ if (playerHasItemID(kPoisonDart))
+ return kPoisonDart;
+ if (playerHasItemID(kJourneymanKey))
+ return kJourneymanKey;
+ if (playerHasItemID(kMarsCard))
+ return kMarsCard;
+ if (playerHasItemID(kNitrogenCanister))
+ return kNitrogenCanister;
+ if (playerHasItemID(kCrowbar))
+ return kCrowbar;
+ if (playerHasItemID(kAirMask))
+ return kAirMask;
+
+ // Should never get this far...
+ error("Could not find item to delete");
+
+ return kNoItemID;
+}
+
+uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const {
+ switch (neighborhood) {
+ case kCaldoriaID:
+ case kNoradAlphaID:
+ case kNoradSubChaseID:
+ return 1;
+ case kFullTSAID:
+ case kPrehistoricID:
+ return 2;
+ case kMarsID:
+ return 3;
+ case kWSCID:
+ case kNoradDeltaID:
+ return 4;
+ case kTinyTSAID:
+ // Tiny TSA exists on three of the CD's, so just continue
+ // with the CD we're on
+ return _currentCD;
+ }
+
+ // Can't really happen, but it's a good fallback anyway :P
+ return 1;
+}
+
+} // End of namespace Pegasus