/* 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(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