diff options
Diffstat (limited to 'engines/lab/engine.cpp')
-rw-r--r-- | engines/lab/engine.cpp | 1124 |
1 files changed, 1124 insertions, 0 deletions
diff --git a/engines/lab/engine.cpp b/engines/lab/engine.cpp new file mode 100644 index 0000000000..4f0a0da777 --- /dev/null +++ b/engines/lab/engine.cpp @@ -0,0 +1,1124 @@ +/* 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. + * + */ + +/* + * This code is based on Labyrinth of Time code with assistance of + * + * Copyright (c) 1993 Terra Nova Development + * Copyright (c) 2004 The Wyrmkeep Entertainment Co. + * + */ + +#include "common/config-manager.h" +#include "common/file.h" + +#include "gui/message.h" + +#include "lab/lab.h" +#include "lab/anim.h" +#include "lab/dispman.h" +#include "lab/eventman.h" +#include "lab/image.h" +#include "lab/interface.h" +#include "lab/intro.h" +#include "lab/labsets.h" +#include "lab/music.h" +#include "lab/processroom.h" +#include "lab/resource.h" +#include "lab/speciallocks.h" +#include "lab/utils.h" + +namespace Lab { + +enum SpecialLock { + kLockCombination = 100, + kLockTiles = 101, + kLockTileSolution = 102 +}; + +enum Items { + kItemHelmet = 1, + kItemBelt = 3, + kItemPithHelmet = 7, + kItemJournal = 9, + kItemNotes = 12, + kItemWestPaper = 18, + kItemWhiskey = 25, + kItemLamp = 27, + kItemMap = 28, + kItemQuarter = 30 +}; + +enum Monitors { + kMonitorMuseum = 71, + kMonitorGramophone = 72, + kMonitorUnicycle = 73, + kMonitorStatue = 74, + kMonitorTalisman = 75, + kMonitorLute = 76, + kMonitorClock = 77, + kMonitorWindow = 78, + //kMonitorBelt = 79, + kMonitorLibrary = 80, + kMonitorTerminal = 81 + //kMonitorLevers = 82 +}; + +enum AltButtons { + kButtonMainDisplay, + kButtonSaveLoad, + kButtonUseItem, + kButtonLookAtItem, + kButtonPrevItem, + kButtonNextItem, + kButtonBreadCrumbs, + kButtonFollowCrumbs +}; + +static char initColors[] = { '\x00', '\x00', '\x00', '\x30', + '\x30', '\x30', '\x10', '\x10', + '\x10', '\x14', '\x14', '\x14', + '\x20', '\x20', '\x20', '\x24', + '\x24', '\x24', '\x2c', '\x2c', + '\x2c', '\x08', '\x08', '\x08' }; + +void LabEngine::handleTrialWarning() { + // Check if this is the Wyrmkeep trial + Common::File roomFile; + bool knownVersion = true; + bool roomFileOpened = roomFile.open("rooms/48"); + + if (!roomFileOpened) + knownVersion = false; + else if (roomFile.size() != 892) + knownVersion = false; + else { + roomFile.seek(352); + byte checkByte = roomFile.readByte(); + if (checkByte == 0x00) { + // Full Windows version + } + else if (checkByte == 0x80) { + // Wyrmkeep trial version + _extraGameFeatures = GF_WINDOWS_TRIAL; + + GUI::MessageDialog trialMessage("This is a trial Windows version of the game. To play the full version, you will need to use the original interpreter and purchase a key from Wyrmkeep"); + trialMessage.runModal(); + } + else { + knownVersion = false; + } + + roomFile.close(); + } + + if (!knownVersion) + error("Unknown Windows version found, please report this version to the ScummVM team"); +} + +uint16 LabEngine::getQuarters() { + return _inventory[kItemQuarter]._quantity; +} + +void LabEngine::setQuarters(uint16 quarters) { + _inventory[kItemQuarter]._quantity = quarters; +} + +void LabEngine::drawRoomMessage(uint16 curInv, const CloseData *closePtr) { + if (_lastTooLong) { + _lastTooLong = false; + return; + } + + if (_alternate) { + if ((curInv <= _numInv) && _conditions->in(curInv) && !_inventory[curInv]._bitmapName.empty()) { + if ((curInv == kItemLamp) && _conditions->in(kCondLampOn)) + // LAB: Labyrinth specific + drawStaticMessage(kTextkLampOn); + else if (_inventory[curInv]._quantity > 1) { + Common::String roomMessage = _inventory[curInv]._name + " (" + Common::String::format("%d", _inventory[curInv]._quantity) + ")"; + _graphics->drawMessage(roomMessage.c_str(), false); + } else + _graphics->drawMessage(_inventory[curInv]._name.c_str(), false); + } + } else + drawDirection(closePtr); + + _lastTooLong = _graphics->_lastMessageLong; +} + +void LabEngine::freeScreens() { + for (int i = 0; i < 20; i++) { + delete _moveImages[i]; + _moveImages[i] = nullptr; + } + + for (int imgIdx = 0; imgIdx < 10; imgIdx++) { + delete _invImages[imgIdx]; + _invImages[imgIdx] = nullptr; + } + + // We can't use freeButtonList() here, because some buttons are shared + // between the two lists. + for (ButtonList::iterator buttonIter = _moveButtonList.begin(); buttonIter != _moveButtonList.end(); ++buttonIter) { + delete *buttonIter; + } + _moveButtonList.clear(); + + for (ButtonList::iterator buttonIter = _invButtonList.begin(); buttonIter != _invButtonList.end(); ++buttonIter) { + delete *buttonIter; + } + _invButtonList.clear(); +} + +void LabEngine::perFlipButton(uint16 buttonId) { + for (ButtonList::iterator button = _moveButtonList.begin(); button != _moveButtonList.end(); ++button) { + Button *topButton = *button; + if (topButton->_buttonId == buttonId) { + SWAP<Image *>(topButton->_image, topButton->_altImage); + + if (!_alternate) + topButton->_image->drawImage(topButton->_x, topButton->_y); + + break; + } + } +} + +void LabEngine::eatMessages() { + IntuiMessage *msg; + + do { + msg = _event->getMsg(); + } while (msg && !shouldQuit()); +} + +void LabEngine::handleMonitorCloseup() { + if (!_closeDataPtr) + return; + + Common::Rect textRect(2, 2, 317, 165); + bool isInteractive = false; + + switch (_closeDataPtr->_closeUpType) { + case kMonitorMuseum: + case kMonitorLibrary: + case kMonitorWindow: + break; + case kMonitorGramophone: + textRect.right = 171; + break; + case kMonitorUnicycle: + textRect.left = 100; + break; + case kMonitorStatue: + textRect.left = 117; + break; + case kMonitorTalisman: + textRect.right = 184; + break; + case kMonitorLute: + textRect.right = 128; + break; + case kMonitorClock: + textRect.right = 206; + break; + case kMonitorTerminal: + isInteractive = true; + break; + default: + return; + } + + doMonitor(_closeDataPtr->_graphicName, _closeDataPtr->_message, isInteractive, textRect); + + _curFileName = " "; + _graphics->drawPanel(); + + _closeDataPtr = nullptr; + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); +} + +Common::String LabEngine::getInvName(uint16 curInv) { + if (_mainDisplay) + return _inventory[curInv]._bitmapName; + + if ((curInv == kItemLamp) && _conditions->in(kCondLampOn)) + return "P:Mines/120"; + + if ((curInv == kItemBelt) && _conditions->in(kCondBeltGlowing)) + return "P:Future/BeltGlow"; + + if (curInv == kItemWestPaper) { + _curFileName = _inventory[curInv]._bitmapName; + _anim->_noPalChange = true; + _graphics->readPict(_curFileName, false); + _anim->_noPalChange = false; + doWestPaper(); + } else if (curInv == kItemNotes) { + _curFileName = _inventory[curInv]._bitmapName; + _anim->_noPalChange = true; + _graphics->readPict(_curFileName, false); + _anim->_noPalChange = false; + doNotes(); + } + + return _inventory[curInv]._bitmapName; +} + +void LabEngine::interfaceOff() { + _interface->attachButtonList(nullptr); + _event->mouseHide(); +} + +void LabEngine::interfaceOn() { + if (_graphics->_longWinInFront) + _interface->attachButtonList(nullptr); + else if (_alternate) + _interface->attachButtonList(&_invButtonList); + else + _interface->attachButtonList(&_moveButtonList); + + _event->mouseShow(); +} + +bool LabEngine::doUse(uint16 curInv) { + switch (curInv) { + case kItemMap: + drawStaticMessage(kTextUseMap); + interfaceOff(); + _anim->stopDiff(); + _curFileName = " "; + _closeDataPtr = nullptr; + doMap(); + _graphics->setPalette(initColors, 8); + _graphics->drawMessage("", false); + _graphics->drawPanel(); + return true; + case kItemJournal: + drawStaticMessage(kTextUseJournal); + interfaceOff(); + _anim->stopDiff(); + _curFileName = " "; + _closeDataPtr = nullptr; + doJournal(); + _graphics->drawPanel(); + _graphics->drawMessage("", false); + return true; + case kItemLamp: + interfaceOff(); + + if (_conditions->in(kCondLampOn)) { + drawStaticMessage(kTextTurnLampOff); + _conditions->exclElement(kCondLampOn); + } else { + drawStaticMessage(kTextTurnkLampOn); + _conditions->inclElement(kCondLampOn); + } + + _anim->_doBlack = false; + _anim->_waitForEffect = true; + _graphics->readPict("Music:Click"); + _anim->_waitForEffect = false; + + _anim->_doBlack = false; + _nextFileName = getInvName(curInv); + return true; + case kItemBelt: + if (!_conditions->in(kCondBeltGlowing)) + _conditions->inclElement(kCondBeltGlowing); + + _anim->_doBlack = false; + _nextFileName = getInvName(curInv); + return true; + case kItemWhiskey: + _conditions->inclElement(kCondUsedHelmet); + drawStaticMessage(kTextUseWhiskey); + return true; + case kItemPithHelmet: + _conditions->inclElement(kCondUsedHelmet); + drawStaticMessage(kTextUsePith); + return true; + case kItemHelmet: + _conditions->inclElement(kCondUsedHelmet); + drawStaticMessage(kTextUseHelmet); + return true; + default: + return false; + } +} + +void LabEngine::decIncInv(uint16 *curInv, bool decreaseFl) { + int8 step = (decreaseFl) ? -1 : 1; + uint newInv = *curInv + step; + + // Handle wrapping + if (newInv < 1) + newInv = _numInv; + if (newInv > _numInv) + newInv = 1; + + interfaceOff(); + + while (newInv && (newInv <= _numInv)) { + if (_conditions->in(newInv) && !_inventory[newInv]._bitmapName.empty()) { + _nextFileName = getInvName(newInv); + *curInv = newInv; + break; + } + + newInv += step; + + // Handle wrapping + if (newInv < 1) + newInv = _numInv; + if (newInv > _numInv) + newInv = 1; + } +} + +void LabEngine::mainGameLoop() { + _graphics->setPalette(initColors, 8); + + _closeDataPtr = nullptr; + _roomNum = 1; + _direction = kDirectionNorth; + + _resource->readRoomData("LAB:Doors"); + if (!(_inventory = _resource->readInventory("LAB:Inventor"))) + return; + + if (!(_conditions = new LargeSet(_highestCondition + 1, this))) + return; + + if (!(_roomsFound = new LargeSet(_manyRooms + 1, this))) + return; + + _conditions->readInitialConditions("LAB:Conditio"); + + _graphics->_longWinInFront = false; + _graphics->drawPanel(); + + uint16 actionMode = 4; + perFlipButton(actionMode); + + // Load saved slot from the launcher, if requested + if (ConfMan.hasKey("save_slot")) { + loadGame(ConfMan.getInt("save_slot")); + + // Since the intro hasn't been shown, init the background music here + _music->resetMusic(false); + } + + uint16 curInv = kItemMap; + bool forceDraw = false; + bool gotMessage = true; + // Set up initial picture. + while (1) { + _event->processInput(); + _system->delayMillis(10); + + if (gotMessage) { + if (_quitLab || shouldQuit()) { + _anim->stopDiff(); + break; + } + + handleMonitorCloseup(); + + // Sets the current picture properly on the screen + if (_mainDisplay) + _nextFileName = getPictName(true); + + if (_noUpdateDiff) { + // Potentially entered another room + _roomsFound->inclElement(_roomNum); + forceDraw |= (_nextFileName != _curFileName); + + _noUpdateDiff = false; + _curFileName = _nextFileName; + } else if (_nextFileName != _curFileName) { + interfaceOff(); + // Potentially entered another room + _roomsFound->inclElement(_roomNum); + _curFileName = _nextFileName; + + if (_closeDataPtr && _mainDisplay) { + switch (_closeDataPtr->_closeUpType) { + case kLockCombination: + _specialLocks->showCombinationLock(_curFileName); + break; + case kLockTiles: + case kLockTileSolution: + _specialLocks->showTileLock(_curFileName, (_closeDataPtr->_closeUpType == kLockTileSolution)); + break; + default: + _graphics->readPict(_curFileName, false); + break; + } + } else + _graphics->readPict(_curFileName, false); + + drawRoomMessage(curInv, _closeDataPtr); + forceDraw = false; + + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + + if (!_followingCrumbs) + eatMessages(); + } + + if (forceDraw) { + drawRoomMessage(curInv, _closeDataPtr); + forceDraw = false; + _graphics->screenUpdate(); + } + } + + // Make sure we check the music at least after every message + updateEvents(); + interfaceOn(); + IntuiMessage *curMsg = _event->getMsg(); + if (shouldQuit()) { + _quitLab = true; + return; + } + + if (!curMsg) { + // Does music load and next animation frame when you've run out of messages + gotMessage = false; + updateEvents(); + _anim->diffNextFrame(); + + if (_followingCrumbs) { + MainButton code = followCrumbs(); + + if (code == kButtonForward || code == kButtonLeft || code == kButtonRight) { + gotMessage = true; + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + if (!processEvent(kMessageButtonUp, code, 0, _event->updateAndGetMousePos(), curInv, curMsg, forceDraw, code, actionMode)) + break; + } + } + + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + } else { + gotMessage = true; + _followingCrumbs = false; + if (!processEvent(curMsg->_msgClass, curMsg->_code, curMsg->_qualifier, curMsg->_mouse, curInv, curMsg, forceDraw, curMsg->_code, actionMode)) + break; + } + } +} + +void LabEngine::showLab2Teaser() { + _graphics->blackAllScreen(); + _graphics->readPict("P:End/L2In.1"); + + for (int i = 0; i < 120; i++) { + updateEvents(); + waitTOF(); + } + + _graphics->readPict("P:End/L2In.9"); + _graphics->readPict("P:End/Lost"); + + while (!_event->getMsg() && !shouldQuit()) { + updateEvents(); + _anim->diffNextFrame(); + waitTOF(); + } +} + +bool LabEngine::processEvent(MessageClass tmpClass, uint16 code, uint16 qualifier, Common::Point tmpPos, + uint16 &curInv, IntuiMessage *curMsg, bool &forceDraw, uint16 buttonId, uint16 &actionMode) { + + if (shouldQuit()) + return false; + + MessageClass msgClass = tmpClass; + Common::Point curPos = tmpPos; + uint16 oldDirection = 0; + uint16 lastInv = kItemMap; + + if (code == Common::KEYCODE_RETURN) + msgClass = kMessageLeftClick; + + bool leftButtonClick = (msgClass == kMessageLeftClick); + bool rightButtonClick = (msgClass == kMessageRightClick); + + _anim->_doBlack = false; + + if (_graphics->_longWinInFront) { + if (msgClass == kMessageRawKey || leftButtonClick || rightButtonClick) { + _graphics->_longWinInFront = false; + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + _graphics->screenUpdate(); + } + } else if (msgClass == kMessageRawKey) { + return processKey(curMsg, msgClass, qualifier, curPos, curInv, forceDraw, code); + } else if (msgClass == kMessageButtonUp) { + if (!_alternate) + processMainButton(curInv, lastInv, oldDirection, forceDraw, buttonId, actionMode); + else + processAltButton(curInv, lastInv, buttonId, actionMode); + } else if (leftButtonClick && _mainDisplay) { + interfaceOff(); + _mainDisplay = true; + + if (_closeDataPtr && _closeDataPtr->_closeUpType == kLockCombination) + _specialLocks->combinationClick(curPos); + else if (_closeDataPtr && _closeDataPtr->_closeUpType == kLockTiles) + _specialLocks->tileClick(curPos); + else + performAction(actionMode, curPos, curInv); + + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + } else if (rightButtonClick) { + eatMessages(); + _alternate = !_alternate; + _anim->_doBlack = true; + _mainDisplay = true; + // Sets the correct button list + interfaceOn(); + + if (_alternate) { + if (lastInv && _conditions->in(lastInv)) + curInv = lastInv; + else + decIncInv(&curInv, false); + } + + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + } + + return true; +} + +bool LabEngine::processKey(IntuiMessage *curMsg, uint32 msgClass, uint16 &qualifier, Common::Point &curPos, uint16 &curInv, bool &forceDraw, uint16 code) { + if ((getPlatform() == Common::kPlatformWindows) && (code == Common::KEYCODE_b)) { + // Start bread crumbs + _breadCrumbs[0]._crumbRoomNum = 0; + _numCrumbs = 0; + _droppingCrumbs = true; + _interface->mayShowCrumbIndicator(); + _graphics->screenUpdate(); + } else if (getPlatform() == Common::kPlatformWindows && (code == Common::KEYCODE_f || code == Common::KEYCODE_r)) { + // Follow bread crumbs + if (_droppingCrumbs) { + if (_numCrumbs > 0) { + _followingCrumbs = true; + _followCrumbsFast = (code == Common::KEYCODE_r); + _isCrumbTurning = false; + _isCrumbWaiting = false; + _crumbTimestamp = _system->getMillis(); + + if (_alternate) { + eatMessages(); + _alternate = false; + _anim->_doBlack = true; + + _mainDisplay = true; + // Sets the correct button list + interfaceOn(); + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + _graphics->screenUpdate(); + } + } else { + _breadCrumbs[0]._crumbRoomNum = 0; + _droppingCrumbs = false; + + _interface->mayShowCrumbIndicatorOff(); + _graphics->screenUpdate(); + } + } + } else if ((code == Common::KEYCODE_x) || (code == Common::KEYCODE_q)) { + // Quit? + _graphics->drawMessage("Do you want to quit? (Y/N)", false); + eatMessages(); + interfaceOff(); + + while (1) { + // Make sure we check the music at least after every message + updateEvents(); + curMsg = _event->getMsg(); + + if (shouldQuit()) + return false; + + if (!curMsg) { + // Does music load and next animation frame when you've run out of messages + updateEvents(); + _anim->diffNextFrame(); + } else if (curMsg->_msgClass == kMessageRawKey) { + if ((curMsg->_code == Common::KEYCODE_y) || (curMsg->_code == Common::KEYCODE_q)) { + _anim->stopDiff(); + return false; + } else if (curMsg->_code < 128) + break; + } else if ((curMsg->_msgClass == kMessageLeftClick) || (curMsg->_msgClass == kMessageRightClick)) + break; + } + + forceDraw = true; + interfaceOn(); + } else if (code == Common::KEYCODE_ESCAPE) { + _closeDataPtr = nullptr; + } else if (code == Common::KEYCODE_TAB) { + const CloseData *tmpClosePtr = _closeDataPtr; + + // get next close-up in list after the one pointed to by curPos + setCurrentClose(curPos, &tmpClosePtr, true, true); + + if (tmpClosePtr != _closeDataPtr) + _event->setMousePos(Common::Point(_utils->scaleX((tmpClosePtr->_x1 + tmpClosePtr->_x2) / 2), _utils->scaleY((tmpClosePtr->_y1 + tmpClosePtr->_y2) / 2))); + } + + eatMessages(); + + return true; +} + +void LabEngine::processMainButton(uint16 &curInv, uint16 &lastInv, uint16 &oldDirection, bool &forceDraw, uint16 buttonId, uint16 &actionMode) { + switch (buttonId) { + case kButtonPickup: + case kButtonUse: + case kButtonOpen: + case kButtonClose: + case kButtonLook: + if ((actionMode == 4) && (buttonId == kButtonLook) && _closeDataPtr) { + doMainView(); + + _anim->_doBlack = true; + _closeDataPtr = nullptr; + _interface->mayShowCrumbIndicator(); + } else { + uint16 oldActionMode = actionMode; + actionMode = buttonId; + + if (oldActionMode < 5) + perFlipButton(oldActionMode); + + perFlipButton(actionMode); + drawStaticMessage(kTextTakeWhat + buttonId); + } + break; + + case kButtonInventory: + eatMessages(); + + _alternate = true; + _anim->_doBlack = true; + // Sets the correct button list + interfaceOn(); + _mainDisplay = false; + + if (lastInv && _conditions->in(lastInv)) { + curInv = lastInv; + _nextFileName = getInvName(curInv); + } else + decIncInv(&curInv, false); + + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + + _interface->mayShowCrumbIndicator(); + break; + + case kButtonLeft: + case kButtonRight: { + _closeDataPtr = nullptr; + if (buttonId == kButtonLeft) + drawStaticMessage(kTextTurnLeft); + else + drawStaticMessage(kTextTurnRight); + + _curFileName = " "; + oldDirection = _direction; + + uint16 newDir = processArrow(_direction, buttonId - 6); + doTurn(_direction, newDir); + _anim->_doBlack = true; + _direction = newDir; + forceDraw = true; + _interface->mayShowCrumbIndicator(); + } + break; + + case kButtonForward: { + _closeDataPtr = nullptr; + int oldRoomNum = _roomNum; + + if (doGoForward()) { + if (oldRoomNum == _roomNum) + _anim->_doBlack = true; + } else { + _anim->_doBlack = true; + _direction = processArrow(_direction, buttonId - 6); + + if (oldRoomNum != _roomNum) { + drawStaticMessage(kTextGoForward); + // Potentially entered a new room + _roomsFound->inclElement(_roomNum); + _curFileName = " "; + forceDraw = true; + } else { + _anim->_doBlack = true; + drawStaticMessage(kTextNoPath); + } + } + + if (_followingCrumbs) { + if (_isCrumbTurning) { + if (_direction == oldDirection) + _followingCrumbs = false; + } else if (_roomNum == oldRoomNum) { // didn't get there? + _followingCrumbs = false; + } + } else if (_droppingCrumbs && (oldRoomNum != _roomNum)) { + // If in surreal maze, turn off DroppingCrumbs. + if ((_roomNum >= 245) && (_roomNum <= 280)) { + _followingCrumbs = false; + _droppingCrumbs = false; + _numCrumbs = 0; + _breadCrumbs[0]._crumbRoomNum = 0; + } else { + bool intersect = false; + for (int idx = 0; idx < _numCrumbs; idx++) { + if (_breadCrumbs[idx]._crumbRoomNum == _roomNum) { + _numCrumbs = idx + 1; + _breadCrumbs[_numCrumbs]._crumbRoomNum = 0; + intersect = true; + } + } + + if (!intersect) { + if (_numCrumbs == MAX_CRUMBS) { + _numCrumbs = MAX_CRUMBS - 1; + memcpy(&_breadCrumbs[0], &_breadCrumbs[1], _numCrumbs * sizeof _breadCrumbs[0]); + } + + _breadCrumbs[_numCrumbs]._crumbRoomNum = _roomNum; + _breadCrumbs[_numCrumbs++]._crumbDirection = _direction; + } + } + } + + _interface->mayShowCrumbIndicator(); + } + break; + + case kButtonMap: + doUse(kItemMap); + + _interface->mayShowCrumbIndicator(); + break; + } + + _graphics->screenUpdate(); +} + +void LabEngine::processAltButton(uint16 &curInv, uint16 &lastInv, uint16 buttonId, uint16 &actionMode) { + _anim->_doBlack = true; + + switch (buttonId) { + case kButtonMainDisplay: + eatMessages(); + _alternate = false; + _anim->_doBlack = true; + + _mainDisplay = true; + // Sets the correct button list + interfaceOn(); + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + break; + + case kButtonSaveLoad: { + interfaceOff(); + _anim->stopDiff(); + _curFileName = " "; + + bool saveRestoreSuccessful = saveRestoreGame(); + _closeDataPtr = nullptr; + _mainDisplay = true; + + curInv = lastInv = kItemMap; + _nextFileName = getInvName(curInv); + + _graphics->drawPanel(); + + if (!saveRestoreSuccessful) { + _graphics->drawMessage("Save/restore aborted", false); + _graphics->setPalette(initColors, 8); + _system->delayMillis(1000); + } + } + break; + + case kButtonUseItem: + if (!doUse(curInv)) { + uint16 oldActionMode = actionMode; + // Use button + actionMode = 5; + + if (oldActionMode < 5) + perFlipButton(oldActionMode); + + drawStaticMessage(kTextUseOnWhat); + _mainDisplay = true; + } + break; + + case kButtonLookAtItem: + _mainDisplay = !_mainDisplay; + + if ((curInv == 0) || (curInv > _numInv)) { + curInv = 1; + + while ((curInv <= _numInv) && !_conditions->in(curInv)) + curInv++; + } + + if ((curInv <= _numInv) && _conditions->in(curInv) && !_inventory[curInv]._bitmapName.empty()) + _nextFileName = getInvName(curInv); + + break; + + case kButtonPrevItem: + decIncInv(&curInv, true); + lastInv = curInv; + drawRoomMessage(curInv, _closeDataPtr); + break; + + case kButtonNextItem: + decIncInv(&curInv, false); + lastInv = curInv; + drawRoomMessage(curInv, _closeDataPtr); + break; + + case kButtonBreadCrumbs: + _breadCrumbs[0]._crumbRoomNum = 0; + _numCrumbs = 0; + _droppingCrumbs = true; + _interface->mayShowCrumbIndicator(); + break; + + case kButtonFollowCrumbs: + if (_droppingCrumbs) { + if (_numCrumbs > 0) { + _followingCrumbs = true; + _followCrumbsFast = false; + _isCrumbTurning = false; + _isCrumbWaiting = false; + _crumbTimestamp = _system->getMillis(); + + eatMessages(); + _alternate = false; + _anim->_doBlack = true; + + _mainDisplay = true; + // Sets the correct button list + interfaceOn(); + _graphics->drawPanel(); + drawRoomMessage(curInv, _closeDataPtr); + } else { + _breadCrumbs[0]._crumbRoomNum = 0; + _droppingCrumbs = false; + + _interface->mayShowCrumbIndicatorOff(); + } + } + break; + } + + _graphics->screenUpdate(); +} + +void LabEngine::performAction(uint16 actionMode, Common::Point curPos, uint16 &curInv) { + eatMessages(); + + switch (actionMode) { + case 0: + // Take something. + if (doActionRule(curPos, actionMode, _roomNum)) + _curFileName = _newFileName; + else if (takeItem(curPos)) + drawStaticMessage(kTextTakeItem); + else if (doActionRule(curPos, kRuleActionTakeDef, _roomNum)) + _curFileName = _newFileName; + else if (doActionRule(curPos, kRuleActionTake, 0)) + _curFileName = _newFileName; + else if (curPos.y < (_utils->vgaScaleY(149) + _utils->svgaCord(2))) + drawStaticMessage(kTextNothing); + + break; + + case 1: + case 2: + case 3: + // Manipulate an object, Open up a "door" or Close a "door" + if (doActionRule(curPos, actionMode, _roomNum)) + _curFileName = _newFileName; + else if (!doActionRule(curPos, actionMode, 0)) { + if (curPos.y < (_utils->vgaScaleY(149) + _utils->svgaCord(2))) + drawStaticMessage(kTextNothing); + } + break; + + case 4: { + // Look at closeups + const CloseData *tmpClosePtr = _closeDataPtr; + setCurrentClose(curPos, &tmpClosePtr, true); + + if (_closeDataPtr == tmpClosePtr) { + if (curPos.y < (_utils->vgaScaleY(149) + _utils->svgaCord(2))) + drawStaticMessage(kTextNothing); + } else if (!tmpClosePtr->_graphicName.empty()) { + _anim->_doBlack = true; + _closeDataPtr = tmpClosePtr; + } else if (curPos.y < (_utils->vgaScaleY(149) + _utils->svgaCord(2))) + drawStaticMessage(kTextNothing); + } + break; + + case 5: + if (_conditions->in(curInv)) { + // Use an item on something else + if (doOperateRule(curPos, curInv)) { + _curFileName = _newFileName; + + if (!_conditions->in(curInv)) + decIncInv(&curInv, false); + } + else if (curPos.y < (_utils->vgaScaleY(149) + _utils->svgaCord(2))) + drawStaticMessage(kTextNothing); + } + } +} + +void LabEngine::go() { + if (getPlatform() == Common::kPlatformWindows) + handleTrialWarning(); + + _isHiRes = ((getFeatures() & GF_LOWRES) == 0); + _graphics->setUpScreens(); + + _event->initMouse(); + if (_msgFont) + _graphics->freeFont(&_msgFont); + + if (getPlatform() != Common::kPlatformAmiga) + _msgFont = _resource->getFont("F:AvanteG.12"); + else + _msgFont = _resource->getFont("F:Map.fon"); + + // If the user has requested to load a game from the launcher, skip the intro + if (!ConfMan.hasKey("save_slot")) { + _event->mouseHide(); + _introPlaying = true; + Intro *intro = new Intro(this); + intro->play(); + delete intro; + _introPlaying = false; + _event->mouseShow(); + } + + mainGameLoop(); + + _graphics->freeFont(&_msgFont); + _graphics->freePict(); + + freeScreens(); + + _music->freeMusic(); +} + +MainButton LabEngine::followCrumbs() { + // kDirectionNorth, kDirectionSouth, kDirectionEast, kDirectionWest + MainButton movement[4][4] = { + { kButtonForward, kButtonRight, kButtonRight, kButtonLeft }, + { kButtonRight, kButtonForward, kButtonLeft, kButtonRight }, + { kButtonLeft, kButtonRight, kButtonForward, kButtonRight }, + { kButtonRight, kButtonLeft, kButtonRight, kButtonForward } + }; + + if (_isCrumbWaiting) { + if (_system->getMillis() <= _crumbTimestamp) + return kButtonNone; + + _isCrumbWaiting = false; + } + + if (!_isCrumbTurning) + _breadCrumbs[_numCrumbs--]._crumbRoomNum = 0; + + // Is the current crumb this room? If not, logic error. + if (_roomNum != _breadCrumbs[_numCrumbs]._crumbRoomNum) { + _numCrumbs = 0; + _breadCrumbs[0]._crumbRoomNum = 0; + _droppingCrumbs = false; + _followingCrumbs = false; + return kButtonNone; + } + + Direction exitDir; + // which direction is last crumb + if (_breadCrumbs[_numCrumbs]._crumbDirection == kDirectionEast) + exitDir = kDirectionWest; + else if (_breadCrumbs[_numCrumbs]._crumbDirection == kDirectionWest) + exitDir = kDirectionEast; + else if (_breadCrumbs[_numCrumbs]._crumbDirection == kDirectionNorth) + exitDir = kDirectionSouth; + else + exitDir = kDirectionNorth; + + MainButton moveDir = movement[_direction][exitDir]; + + if (_numCrumbs == 0) { + _isCrumbTurning = false; + _breadCrumbs[0]._crumbRoomNum = 0; + _droppingCrumbs = false; + _followingCrumbs = false; + } else { + _isCrumbTurning = (moveDir != kButtonForward); + _isCrumbWaiting = true; + + int theDelay = (_followCrumbsFast ? 1000 / 4 : 1000); + _crumbTimestamp = theDelay + _system->getMillis(); + } + + return moveDir; +} + +} // End of namespace Lab |