/* 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/debug-channels.h" #include "common/scummsys.h" #include "common/error.h" #include "common/fs.h" #include "common/rect.h" #include "engines/dialogs.h" #include "engines/engine.h" #include "engines/util.h" #include "gui/message.h" #include "engines/advancedDetector.h" #include "lab/lab.h" #include "lab/labfun.h" #include "lab/resource.h" #include "lab/anim.h" #include "lab/graphics.h" namespace Lab { LabEngine *g_lab; const uint16 INIT_TILE[4][4] = { { 1, 5, 9, 13 }, { 2, 6, 10, 14 }, { 3, 7, 11, 15 }, { 4, 8, 12, 0 } }; const uint16 SOLUTION[4][4] = { { 7, 1, 8, 3 }, { 2, 11, 15, 4 }, { 9, 5, 14, 6 }, { 10, 13, 12, 0 } }; const int COMBINATION_X[6] = { 45, 83, 129, 166, 211, 248 }; LabEngine::LabEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), _extraGameFeatures(0) { g_lab = this; _screenWidth = 320; _screenHeight = 200; _currentDisplayBuffer = 0; _displayBuffer = 0; _lastWaitTOFTicks = 0; _isHiRes = false; _roomNum = -1; for (int i = 0; i < MAX_CRUMBS; i++) { _breadCrumbs[i]._roomNum = 0; _breadCrumbs[i]._direction = NORTH; } _numCrumbs = 0; _droppingCrumbs = false; _followingCrumbs = false; _followCrumbsFast = false; _isCrumbTurning = false; _isCrumbWaiting = false; _crumbSecs = 0; _crumbMicros = 0; _event = nullptr; _resource = nullptr; _music = nullptr; _anim = nullptr; _graphics = nullptr; _lastTooLong = false; _interfaceOff = false; _alternate = false; for (int i = 0; i < 20; i++) _moveImages[i] = nullptr; for (int i = 0; i < 10; i++) { _invImages[i] = nullptr; Images[i] = nullptr; } _moveGadgetList = nullptr; _invGadgetList = nullptr; _curFileName = nullptr; _nextFileName = nullptr; _newFileName = nullptr; _curFileName = " "; _msgFont = 0; _inventory = 0; for (int i = 0; i < 16; i++) Tiles[i] = nullptr; for (int i= 0; i < 4; i++) { for (int j = 0; j < 4; j++) CurTile[i][j] = INIT_TILE[i][j]; } for (int i = 0; i < 6; i++) combination[i] = 0; //const Common::FSNode gameDataDir(ConfMan.get("path")); //SearchMan.addSubDirectoryMatching(gameDataDir, "game"); //SearchMan.addSubDirectoryMatching(gameDataDir, "game/pict"); //SearchMan.addSubDirectoryMatching(gameDataDir, "game/spict"); //SearchMan.addSubDirectoryMatching(gameDataDir, "music"); } LabEngine::~LabEngine() { // Remove all of our debug levels here DebugMan.clearAllDebugChannels(); delete _event; delete _resource; delete _music; delete _anim; delete _graphics; for (int i = 0; i < 16; i++) delete Tiles[i]; } Common::Error LabEngine::run() { if (getFeatures() & GF_LOWRES) initGraphics(320, 200, false); else initGraphics(640, 480, true); _event = new EventManager(this); _resource = new Resource(this); _music = new Music(this); _anim = new Anim(this); _graphics = new DisplayMan(this); if (getPlatform() == Common::kPlatformWindows) { // Check if this is the Wyrmkeep trial Common::File roomFile; bool knownVersion = true; bool roomFileOpened = roomFile.open("game/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) { warning("Unknown Windows version found, please report this version to the ScummVM team"); return Common::kNoGameDataFoundError; } } } go(); return Common::kNoError; } Common::String LabEngine::generateSaveFileName(uint slot) { return Common::String::format("%s.%03u", _targetName.c_str(), slot); } /*****************************************************************************/ /* Converts SVGA cords to VGA if necessary, otherwise returns VGA cords. */ /*****************************************************************************/ int LabEngine::VGAUnScaleX(int x) { if (_isHiRes) return (x / 2); else return x; } /*****************************************************************************/ /* Converts SVGA cords to VGA if necessary, otherwise returns VGA cords. */ /*****************************************************************************/ int LabEngine::VGAUnScaleY(int y) { if (_isHiRes) return ((y * 5) / 12); else return y; } /*****************************************************************************/ /* Processes mouse clicks and changes the combination. */ /*****************************************************************************/ void LabEngine::mouseTile(Common::Point pos) { int x = VGAUnScaleX(pos.x); int y = VGAUnScaleY(pos.y); if ((x < 101) || (y < 26)) return; x = (x - 101) / 30; y = (y - 26) / 25; if ((x < 4) && (y < 4)) changeTile(x, y); } /*****************************************************************************/ /* Changes the combination number of one of the slots */ /*****************************************************************************/ void LabEngine::changeTile(uint16 col, uint16 row) { int16 scrolltype = -1; if (row > 0) { if (CurTile[col] [row - 1] == 0) { CurTile[col] [row - 1] = CurTile[col] [row]; CurTile[col] [row] = 0; scrolltype = DOWNSCROLL; } } if (col > 0) { if (CurTile[col - 1] [row] == 0) { CurTile[col - 1] [row] = CurTile[col] [row]; CurTile[col] [row] = 0; scrolltype = RIGHTSCROLL; } } if (row < 3) { if (CurTile[col] [row + 1] == 0) { CurTile[col] [row + 1] = CurTile[col] [row]; CurTile[col] [row] = 0; scrolltype = UPSCROLL; } } if (col < 3) { if (CurTile[col + 1] [row] == 0) { CurTile[col + 1] [row] = CurTile[col] [row]; CurTile[col] [row] = 0; scrolltype = LEFTSCROLL; } } if (scrolltype != -1) { doTileScroll(col, row, scrolltype); if (getFeatures() & GF_WINDOWS_TRIAL) { GUI::MessageDialog trialMessage("This puzzle is not available in the trial version of the game"); trialMessage.runModal(); return; } bool check = true; row = 0; col = 0; while (row < 4) { while (col < 4) { check = check && (CurTile[row] [col] == SOLUTION[row] [col]); col++; } row++; col = 0; } if (check) { _conditions->inclElement(BRICKOPEN); /* unlocked combination */ _anim->_doBlack = true; check = _graphics->readPict("p:Up/BDOpen", true); } } } /*****************************************************************************/ /* Processes mouse clicks and changes the combination. */ /*****************************************************************************/ void LabEngine::mouseCombination(Common::Point pos) { uint16 number; int x = VGAUnScaleX(pos.x); int y = VGAUnScaleY(pos.y); if ((y >= 63) && (y <= 99)) { if ((x >= 44) && (x < 83)) number = 0; else if (x < 127) number = 1; else if (x < 165) number = 2; else if (x < 210) number = 3; else if (x < 245) number = 4; else if (x < 286) number = 5; else return; changeCombination(number); } } /*****************************************************************************/ /* Draws the images of the combination lock to the display bitmap. */ /*****************************************************************************/ void LabEngine::doTile(bool showsolution) { uint16 row = 0, col = 0, rowm, colm, num; int16 rows, cols; if (showsolution) { rowm = _graphics->VGAScaleY(23); colm = _graphics->VGAScaleX(27); rows = _graphics->VGAScaleY(31); cols = _graphics->VGAScaleX(105); } else { _graphics->setAPen(0); _graphics->rectFill(_graphics->VGAScaleX(97), _graphics->VGAScaleY(22), _graphics->VGAScaleX(220), _graphics->VGAScaleY(126)); rowm = _graphics->VGAScaleY(25); colm = _graphics->VGAScaleX(30); rows = _graphics->VGAScaleY(25); cols = _graphics->VGAScaleX(100); } while (row < 4) { while (col < 4) { if (showsolution) num = SOLUTION[col] [row]; else num = CurTile[col] [row]; if (showsolution || num) Tiles[num]->drawImage(cols + (col * colm), rows + (row * rowm)); col++; } row++; col = 0; } } /*****************************************************************************/ /* Reads in a backdrop picture. */ /*****************************************************************************/ void LabEngine::showTile(const char *filename, bool showsolution) { uint16 start = showsolution ? 0 : 1; _anim->_doBlack = true; _anim->_noPalChange = true; _graphics->readPict(filename, true); _anim->_noPalChange = false; _graphics->blackScreen(); Common::File *tileFile = tileFile = _resource->openDataFile(showsolution ? "P:TileSolution" : "P:Tile"); for (uint16 curBit = start; curBit < 16; curBit++) Tiles[curBit] = new Image(tileFile); delete tileFile; doTile(showsolution); setPalette(_anim->_diffPalette, 256); } /*****************************************************************************/ /* Does the scrolling for the tiles on the tile puzzle. */ /*****************************************************************************/ void LabEngine::doTileScroll(uint16 col, uint16 row, uint16 scrolltype) { int16 dX = 0, dY = 0, dx = 0, dy = 0, sx = 0, sy = 0; uint16 last = 0, x1, y1; if (scrolltype == LEFTSCROLL) { dX = _graphics->VGAScaleX(5); sx = _graphics->VGAScaleX(5); last = 6; } else if (scrolltype == RIGHTSCROLL) { dX = _graphics->VGAScaleX(-5); dx = _graphics->VGAScaleX(-5); sx = _graphics->VGAScaleX(5); last = 6; } else if (scrolltype == UPSCROLL) { dY = _graphics->VGAScaleY(5); sy = _graphics->VGAScaleY(5); last = 5; } else if (scrolltype == DOWNSCROLL) { dY = _graphics->VGAScaleY(-5); dy = _graphics->VGAScaleY(-5); sy = _graphics->VGAScaleY(5); last = 5; } sx += _graphics->SVGACord(2); x1 = _graphics->VGAScaleX(100) + (col * _graphics->VGAScaleX(30)) + dx; y1 = _graphics->VGAScaleY(25) + (row * _graphics->VGAScaleY(25)) + dy; for (uint16 i = 0; i < last; i++) { waitTOF(); scrollRaster(dX, dY, x1, y1, x1 + _graphics->VGAScaleX(28) + sx, y1 + _graphics->VGAScaleY(23) + sy); x1 += dX; y1 += dY; } } /*****************************************************************************/ /* Changes the combination number of one of the slots */ /*****************************************************************************/ void LabEngine::changeCombination(uint16 number) { static const int solution[6] = { 0, 4, 0, 8, 7, 2 }; Image display; uint16 combnum; bool unlocked = true; if (combination[number] < 9) (combination[number])++; else combination[number] = 0; combnum = combination[number]; display._imageData = getCurrentDrawingBuffer(); display._width = _screenWidth; display._height = _screenHeight; for (uint16 i = 1; i <= (Images[combnum]->_height / 2); i++) { if (_isHiRes) { if (i & 1) waitTOF(); } else waitTOF(); display._imageData = getCurrentDrawingBuffer(); scrollDisplayY(2, _graphics->VGAScaleX(COMBINATION_X[number]), _graphics->VGAScaleY(65), _graphics->VGAScaleX(COMBINATION_X[number]) + (Images[combnum])->_width - 1, _graphics->VGAScaleY(65) + (Images[combnum])->_height); Images[combnum]->bltBitMap(0, (Images[combnum])->_height - (2 * i), &(display), _graphics->VGAScaleX(COMBINATION_X[number]), _graphics->VGAScaleY(65), (Images[combnum])->_width, 2); } for (uint16 i = 0; i < 6; i++) unlocked = (combination[i] == solution[i]) && unlocked; if (unlocked) _conditions->inclElement(COMBINATIONUNLOCKED); else _conditions->exclElement(COMBINATIONUNLOCKED); } void LabEngine::scrollRaster(int16 dx, int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2) { if (dx) scrollDisplayX(dx, x1, y1, x2, y2); if (dy) scrollDisplayY(dy, x1, y1, x2, y2); } /*****************************************************************************/ /* Draws the images of the combination lock to the display bitmap. */ /*****************************************************************************/ void LabEngine::doCombination() { for (uint16 i = 0; i <= 5; i++) Images[combination[i]]->drawImage(_graphics->VGAScaleX(COMBINATION_X[i]), _graphics->VGAScaleY(65)); } /*****************************************************************************/ /* Reads in a backdrop picture. */ /*****************************************************************************/ void LabEngine::showCombination(const char *filename) { _anim->_doBlack = true; _anim->_noPalChange = true; _graphics->readPict(filename, true); _anim->_noPalChange = false; _graphics->blackScreen(); Common::File *numFile = _resource->openDataFile("P:Numbers"); for (uint16 CurBit = 0; CurBit < 10; CurBit++) Images[CurBit] = new Image(numFile); delete numFile; doCombination(); setPalette(_anim->_diffPalette, 256); } } // End of namespace Lab