diff options
Diffstat (limited to 'engines/sherlock/scalpel')
-rw-r--r-- | engines/sherlock/scalpel/darts.cpp | 557 | ||||
-rw-r--r-- | engines/sherlock/scalpel/darts.h | 136 | ||||
-rw-r--r-- | engines/sherlock/scalpel/drivers/adlib.cpp | 532 | ||||
-rw-r--r-- | engines/sherlock/scalpel/drivers/mididriver.h | 91 | ||||
-rw-r--r-- | engines/sherlock/scalpel/scalpel.cpp | 912 | ||||
-rw-r--r-- | engines/sherlock/scalpel/scalpel.h | 117 |
6 files changed, 2345 insertions, 0 deletions
diff --git a/engines/sherlock/scalpel/darts.cpp b/engines/sherlock/scalpel/darts.cpp new file mode 100644 index 0000000000..8d78335a55 --- /dev/null +++ b/engines/sherlock/scalpel/darts.cpp @@ -0,0 +1,557 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sherlock/scalpel/darts.h" +#include "sherlock/scalpel/scalpel.h" + +namespace Sherlock { + +namespace Scalpel { + +enum { + STATUS_INFO_X = 218, + STATUS_INFO_Y = 53, + DART_INFO_X = 218, + DART_INFO_Y = 103, + DARTBARHX = 35, + DARTHORIZY = 190, + DARTBARVX = 1, + DARTHEIGHTY = 25, + DARTBARSIZE = 150, + DART_BAR_FORE = 8 +}; + +enum { + DART_COL_FORE = 5, + PLAYER_COLOR = 11 +}; +#define OPPONENTS_COUNT 4 + +const char *const OPPONENT_NAMES[OPPONENTS_COUNT] = { + "Skipper", "Willy", "Micky", "Tom" +}; + +/*----------------------------------------------------------------*/ + +Darts::Darts(ScalpelEngine *vm) : _vm(vm) { + _dartImages = nullptr; + _level = 0; + _computerPlayer = 1; + _playerDartMode = false; + _dartScore1 = _dartScore2 = 0; + _roundNumber = 0; + _playerDartMode = false; + _roundScore = 0; + _oldDartButtons = false; +} + +void Darts::playDarts() { + Events &events = *_vm->_events; + Screen &screen = *_vm->_screen; + int playerNumber = 0; + int lastDart; + + // Change the font + int oldFont = screen.fontNumber(); + screen.setFont(2); + + loadDarts(); + initDarts(); + + bool done = false; + do { + int score, roundStartScore; + roundStartScore = score = playerNumber == 0 ? _dartScore1 : _dartScore2; + + // Show player details + showNames(playerNumber); + showStatus(playerNumber); + _roundScore = 0; + + if (_vm->shouldQuit()) + return; + + for (int idx = 0; idx < 3; ++idx) { + // Throw a single dart + if (_computerPlayer == 1) + lastDart = throwDart(idx + 1, playerNumber * 2); + else if (_computerPlayer == 2) + lastDart = throwDart(idx + 1, playerNumber + 1); + else + lastDart = throwDart(idx + 1, 0); + + score -= lastDart; + _roundScore += lastDart; + + screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", idx + 1); + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Scored %d points", lastDart); + + if (score != 0 && playerNumber == 0) + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), DART_COL_FORE, "Press a key"); + + if (score == 0) { + // Some-one has won + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 20), PLAYER_COLOR, "GAME OVER!"); + + if (playerNumber == 0) { + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), PLAYER_COLOR, "Holmes Wins!"); + if (_level < OPPONENTS_COUNT) + setFlagsForDarts(318 + _level); + } else { + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), PLAYER_COLOR, "%s Wins!", _opponent.c_str()); + } + + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 4), DART_COL_FORE, "Press a key"); + + idx = 10; + done = true; + } else if (score < 0) { + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 20), PLAYER_COLOR, "BUSTED!"); + + idx = 10; + score = roundStartScore; + } + + if (playerNumber == 0) + _dartScore1 = score; + else + _dartScore2 = score; + + showStatus(playerNumber); + events.clearKeyboard(); + + if ((playerNumber == 0 && _computerPlayer == 1) || _computerPlayer == 0 || done) { + int dartKey; + while (!(dartKey = dartHit()) && !_vm->shouldQuit()) + events.delay(10); + + if (dartKey == Common::KEYCODE_ESCAPE) { + idx = 10; + done = true; + } + } else { + events.wait(20); + } + + screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); + } + + playerNumber ^= 1; + if (!playerNumber) + ++_roundNumber; + + done |= _vm->shouldQuit(); + + if (!done) { + screen._backBuffer2.blitFrom((*_dartImages)[0], Common::Point(0, 0)); + screen._backBuffer1.blitFrom(screen._backBuffer2); + screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); + } + } while (!done); + + closeDarts(); + screen.fadeToBlack(); + + // Restore font + screen.setFont(oldFont); +} + +void Darts::loadDarts() { + Screen &screen = *_vm->_screen; + + _dartImages = new ImageFile("darts.vgs"); + screen.setPalette(_dartImages->_palette); + + screen._backBuffer1.blitFrom((*_dartImages)[0], Common::Point(0, 0)); + screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); +} + +void Darts::initDarts() { + _dartScore1 = _dartScore2 = 301; + _roundNumber = 1; + + if (_level == 9) { + // No computer players + _computerPlayer = 0; + _level = 0; + } else if (_level == 8) { + _level = _vm->getRandomNumber(3); + _computerPlayer = 2; + } else { + // Check flags for opponents + for (int idx = 0; idx < OPPONENTS_COUNT; ++idx) { + if (_vm->readFlags(314 + idx)) + _level = idx; + } + } + + _opponent = OPPONENT_NAMES[_level]; +} + +void Darts::closeDarts() { + delete _dartImages; + _dartImages = nullptr; +} + +void Darts::showNames(int playerNum) { + Screen &screen = *_vm->_screen; + byte color = playerNum == 0 ? PLAYER_COLOR : DART_COL_FORE; + + // Print Holmes first + if (playerNum == 0) + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), PLAYER_COLOR + 3, "Holmes"); + else + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), color, "Holmes"); + + screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, + STATUS_INFO_X + 31, STATUS_INFO_Y + 12), color); + screen.slamArea(STATUS_INFO_X, STATUS_INFO_Y + 10, 31, 12); + + // Second player + color = playerNum == 1 ? PLAYER_COLOR : DART_COL_FORE; + + if (playerNum != 0) + screen.print(Common::Point(STATUS_INFO_X + 50, STATUS_INFO_Y), PLAYER_COLOR + 3, + "%s", _opponent.c_str()); + else + screen.print(Common::Point(STATUS_INFO_X + 50, STATUS_INFO_Y), color, + "%s", _opponent.c_str()); + + screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X + 50, STATUS_INFO_Y + 10, + STATUS_INFO_X + 81, STATUS_INFO_Y + 12), color); + screen.slamArea(STATUS_INFO_X + 50, STATUS_INFO_Y + 10, 81, 12); + + // Make a copy of the back buffer to the secondary one + screen._backBuffer2.blitFrom(screen._backBuffer1); +} + +void Darts::showStatus(int playerNum) { + Screen &screen = *_vm->_screen; + byte color; + + // Copy scoring screen from secondary back buffer. This will erase any previously displayed status/score info + screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), + Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48)); + + color = (playerNum == 0) ? PLAYER_COLOR : DART_COL_FORE; + screen.print(Common::Point(STATUS_INFO_X + 6, STATUS_INFO_Y + 13), color, "%d", _dartScore1); + + color = (playerNum == 1) ? PLAYER_COLOR : DART_COL_FORE; + screen.print(Common::Point(STATUS_INFO_X + 56, STATUS_INFO_Y + 13), color, "%d", _dartScore2); + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 25), PLAYER_COLOR, "Round: %d", _roundNumber); + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 35), PLAYER_COLOR, "Turn Total: %d", _roundScore); + screen.slamRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48)); +} + +int Darts::throwDart(int dartNum, int computer) { + Events &events = *_vm->_events; + Screen &screen = *_vm->_screen; + Common::Point targetNum; + int width, height; + + events.clearKeyboard(); + + erasePowerBars(); + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", dartNum); + + if (!computer) { + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Hit a key"); + screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 18), DART_COL_FORE, "to start"); + } + + if (!computer) { + while (!_vm->shouldQuit() && !dartHit()) + ; + } else { + events.delay(10); + } + + if (_vm->shouldQuit()) + return 0; + + screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + screen.slamRect(Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + + // If it's a computer player, choose a dart destination + if (computer) + targetNum = getComputerDartDest(computer - 1); + + width = doPowerBar(Common::Point(DARTBARHX, DARTHORIZY), DART_BAR_FORE, targetNum.x, false); + height = 101 - doPowerBar(Common::Point(DARTBARVX, DARTHEIGHTY), DART_BAR_FORE, targetNum.y, true); + + // For human players, slight y adjustment + if (computer == 0) + height += 2; + + // Copy the bars to the secondary back buffer so that they remain fixed at their selected values + // whilst the dart is being animated at being thrown at the board + screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1), + Common::Rect(DARTBARHX - 1, DARTHORIZY - 1, DARTBARHX + DARTBARSIZE + 3, DARTHORIZY + 10)); + screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1), + Common::Rect(DARTBARVX - 1, DARTHEIGHTY - 1, DARTBARVX + 11, DARTHEIGHTY + DARTBARSIZE + 3)); + + // Convert height and width to relative range of -50 to 50, where 0,0 is the exact centre of the board + height -= 50; + width -= 50; + + Common::Point dartPos(111 + width * 2, 99 + height * 2); + drawDartThrow(dartPos); + + return dartScore(dartPos); +} + +void Darts::drawDartThrow(const Common::Point &pt) { + Events &events = *_vm->_events; + Screen &screen = *_vm->_screen; + Common::Point pos(pt.x, pt.y + 2); + Common::Rect oldDrawBounds; + int delta = 9; + + for (int idx = 4; idx < 23; ++idx) { + ImageFrame &frame = (*_dartImages)[idx]; + + // Adjust draw position for animating dart + if (idx < 13) + pos.y -= delta--; + else if (idx == 13) + delta = 1; + else + pos.y += delta++; + + // Draw the dart + Common::Point drawPos(pos.x - frame._width / 2, pos.y - frame._height); + screen._backBuffer1.transBlitFrom(frame, drawPos); + screen.slamArea(drawPos.x, drawPos.y, frame._width, frame._height); + + // Handle erasing old dart frame area + if (!oldDrawBounds.isEmpty()) + screen.slamRect(oldDrawBounds); + + oldDrawBounds = Common::Rect(drawPos.x, drawPos.y, drawPos.x + frame._width, drawPos.y + frame._height); + screen._backBuffer1.blitFrom(screen._backBuffer2, drawPos, oldDrawBounds); + + events.wait(2); + } + + // Draw dart in final "stuck to board" form + screen._backBuffer1.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); + screen._backBuffer2.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); + screen.slamRect(oldDrawBounds); +} + +void Darts::erasePowerBars() { + Screen &screen = *_vm->_screen; + + screen._backBuffer1.fillRect(Common::Rect(DARTBARHX, DARTHORIZY, DARTBARHX + DARTBARSIZE, DARTHORIZY + 10), 0); + screen._backBuffer1.fillRect(Common::Rect(DARTBARVX, DARTHEIGHTY, DARTBARVX + 10, DARTHEIGHTY + DARTBARSIZE), 0); + screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1)); + screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1)); + screen.slamArea(DARTBARHX - 1, DARTHORIZY - 1, DARTBARSIZE + 3, 11); + screen.slamArea(DARTBARVX - 1, DARTHEIGHTY - 1, 11, DARTBARSIZE + 3); +} + +int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, bool isVertical) { + Events &events = *_vm->_events; + Screen &screen = *_vm->_screen; + Music &music = *_vm->_music; + bool done; + int idx = 0; + + events.clearEvents(); + if (music._musicOn) + music.waitTimerRoland(10); + else + events.delay(100); + + // Display loop + do { + done = _vm->shouldQuit() || idx >= DARTBARSIZE; + + if (idx == (goToPower - 1)) + // Reached target power for a computer player + done = true; + else if (goToPower == 0) { + // Check for pres + if (dartHit()) + done = true; + } + + if (isVertical) { + screen._backBuffer1.hLine(pt.x, pt.y + DARTBARSIZE - 1 - idx, pt.x + 8, color); + screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1)); + screen.slamArea(pt.x, pt.y + DARTBARSIZE - 1 - idx, 8, 2); + } else { + screen._backBuffer1.vLine(pt.x + idx, pt.y, pt.y + 8, color); + screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1)); + screen.slamArea(pt.x + idx, pt.y, 1, 8); + } + + if (music._musicOn) { + if (!(idx % 3)) + music.waitTimerRoland(1); + } else if (!(idx % 8)) + events.wait(1); + + ++idx; + } while (!done); + + return MIN(idx * 100 / DARTBARSIZE, 100); +} + +bool Darts::dartHit() { + Events &events = *_vm->_events; + + // Process pending events + events.pollEventsAndWait(); + + if (events.kbHit()) { + // Key was pressed, so discard it and return true + events.clearKeyboard(); + return true; + } + + _oldDartButtons = events._pressed; + events.setButtonState(); + + // Only return true if the mouse button is newly pressed + return (events._pressed && !_oldDartButtons) ? 1 : 0; +} + +int Darts::dartScore(const Common::Point &pt) { + Common::Point pos(pt.x - 37, pt.y - 33); + Graphics::Surface &scoreImg = (*_dartImages)[1]._frame; + + if (pos.x < 0 || pos.y < 0 || pos.x >= scoreImg.w || pos.y >= scoreImg.h) + // Not on the board + return 0; + + // On board, so get the score from the pixel at that position + int score = *(const byte *)scoreImg.getBasePtr(pos.x, pos.y); + return score; +} + +Common::Point Darts::getComputerDartDest(int playerNum) { + Common::Point target; + int score = playerNum == 0 ? _dartScore1 : _dartScore2; + + if (score > 50) { + // Aim for the bullseye + target.x = target.y = 76; + + if (_level <= 1 && _vm->getRandomNumber(1) == 1) { + // Introduce margin of error + target.x += _vm->getRandomNumber(21) - 10; + target.y += _vm->getRandomNumber(21) - 10; + } + } else { + int aim = score; + + bool done; + Common::Point pt; + do { + done = findNumberOnBoard(aim, pt); + --aim; + } while (!done); + + target.x = 75 + ((target.x - 75) * 20 / 27); + target.y = 75 + ((target.y - 75) * 2 / 3); + } + + // Pick a level of accuracy. The higher the level, the more accurate their throw will be + int accuracy = _vm->getRandomNumber(10) + _level * 2; + + if (accuracy <= 2) { + target.x += _vm->getRandomNumber(71) - 35; + target.y += _vm->getRandomNumber(71) - 35; + } else if (accuracy <= 4) { + target.x += _vm->getRandomNumber(51) - 25; + target.y += _vm->getRandomNumber(51) - 25; + } else if (accuracy <= 6) { + target.x += _vm->getRandomNumber(31) - 15; + target.y += _vm->getRandomNumber(31) - 15; + } else if (accuracy <= 8) { + target.x += _vm->getRandomNumber(21) - 10; + target.y += _vm->getRandomNumber(21) - 10; + } else if (accuracy <= 10) { + target.x += _vm->getRandomNumber(11) - 5; + target.y += _vm->getRandomNumber(11) - 5; + } + + if (target.x < 1) + target.x = 1; + if (target.y < 1) + target.y = 1; + + return target; +} + +bool Darts::findNumberOnBoard(int aim, Common::Point &pt) { + ImageFrame &board = (*_dartImages)[1]; + + // Scan board image for the special "center" pixels + bool done = false; + for (int yp = 0; yp < 132 && !done; ++yp) { + const byte *srcP = (const byte *)board._frame.getBasePtr(0, yp); + for (int xp = 0; xp < 147 && !done; ++xp, ++srcP) { + int score = *srcP; + + // Check for match + if (score == aim) { + done = true; + + // Aim at non-double/triple numbers where possible + if (aim < 21) { + pt.x = xp + 5; + pt.y = yp + 5; + + score = *(const byte *)board._frame.getBasePtr(xp + 10, yp + 10); + if (score != aim) + // Not aiming at non-double/triple number yet + done = false; + } else { + // Aiming at a double or triple + pt.x = xp + 3; + pt.y = yp + 3; + } + } + } + } + + if (aim == 3) + pt.x += 15; + pt.y = 132 - pt.y; + + return done; +} + +void Darts::setFlagsForDarts(int flagNum) { + _vm->_flags[ABS(flagNum)] = flagNum >= 0; +} + +} // End of namespace Scalpel + +} // End of namespace Scalpel diff --git a/engines/sherlock/scalpel/darts.h b/engines/sherlock/scalpel/darts.h new file mode 100644 index 0000000000..42990f8056 --- /dev/null +++ b/engines/sherlock/scalpel/darts.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SHERLOCK_DARTS_H +#define SHERLOCK_DARTS_H + +#include "sherlock/resources.h" + +namespace Sherlock { + +namespace Scalpel { + +class ScalpelEngine; + +class Darts { +private: + ScalpelEngine *_vm; + ImageFile *_dartImages; + int _dartScore1, _dartScore2; + int _roundNumber; + int _level; + int _computerPlayer; + Common::String _opponent; + bool _playerDartMode; + int _roundScore; + bool _oldDartButtons; + + /** + * Load the graphics needed for the dart game + */ + void loadDarts(); + + /** + * Initializes the variables needed for the dart game + */ + void initDarts(); + + /** + * Frees the images used by the dart game + */ + void closeDarts(); + + /** + * Show the names of the people playing, Holmes and his opponent + */ + void showNames(int playerNum); + + /** + * Show the player score and game status + */ + void showStatus(int playerNum); + + /** + * Throws a single dart. + * @param dartNum Dart number + * @param computer 0 = Player, 1 = 1st player computer, 2 = 2nd player computer + * @returns Score for what dart hit + */ + int throwDart(int dartNum, int computer); + + /** + * Draw a dart moving towards the board + */ + void drawDartThrow(const Common::Point &pt); + + /** + * Erases the power bars + */ + void erasePowerBars(); + + /** + * Show a gradually incrementing incrementing power that bar. If goToPower is provided, it will + * increment to that power level ignoring all keyboard input (ie. for computer throws). + * Otherwise, it will increment until either a key/mouse button is pressed, or it reaches the end + */ + int doPowerBar(const Common::Point &pt, byte color, int goToPower, bool isVertical); + + /** + * Returns true if a mouse button or key is pressed. + */ + bool dartHit(); + + /** + * Return the score of the given location on the dart-board + */ + int dartScore(const Common::Point &pt); + + /** + * Calculates where a computer player is trying to throw their dart, and choose the actual + * point that was hit with some margin of error + */ + Common::Point getComputerDartDest(int playerNum); + + /** + * Returns the center position for the area of the dartboard with a given number + */ + bool findNumberOnBoard(int aim, Common::Point &pt); + + /** + * Set a global flag to 0 or 1 depending on whether the passed flag is negative or positive. + * @remarks We don't use the global setFlags method because we don't want to check scene flags + */ + void setFlagsForDarts(int flagNum); +public: + Darts(ScalpelEngine *vm); + + /** + * Main method for playing darts game + */ + void playDarts(); +}; + +} // End of namespace Scalpel + +} // End of namespace Sherlock + +#endif diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp new file mode 100644 index 0000000000..7ee5256138 --- /dev/null +++ b/engines/sherlock/scalpel/drivers/adlib.cpp @@ -0,0 +1,532 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sherlock/sherlock.h" +#include "sherlock/scalpel/drivers/mididriver.h" + +#include "common/file.h" +#include "common/system.h" +#include "common/textconsole.h" + +#include "audio/fmopl.h" +#include "audio/softsynth/emumidi.h" + +namespace Sherlock { + +#define USE_SCI_MIDI_PLAYER 1 + +#define SHERLOCK_ADLIB_VOICES_COUNT 9 +#define SHERLOCK_ADLIB_NOTES_COUNT 96 + +byte adlib_Operator1Register[SHERLOCK_ADLIB_VOICES_COUNT] = { + 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12 +}; + +byte adlib_Operator2Register[SHERLOCK_ADLIB_VOICES_COUNT] = { + 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15 +}; + +struct adlib_InstrumentEntry { + byte reg20op1; + byte reg40op1; + byte reg60op1; + byte reg80op1; + byte regE0op1; + byte reg20op2; + byte reg40op2; + byte reg60op2; + byte reg80op2; + byte regE0op2; + byte regC0; + byte frequencyAdjust; +}; + +// hardcoded, dumped from ADHOM.DRV +const adlib_InstrumentEntry adlib_instrumentTable[] = { + { 0x71, 0x89, 0x51, 0x11, 0x00, 0x61, 0x23, 0x42, 0x15, 0x01, 0x02, 0xF4 }, + { 0x22, 0x20, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 }, + { 0x70, 0x1A, 0x64, 0x13, 0x00, 0x20, 0x1F, 0x53, 0x46, 0x00, 0x0E, 0xF4 }, + { 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xE8 }, + { 0x71, 0x8B, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x35, 0x01, 0x02, 0xF4 }, + { 0x71, 0x8A, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x25, 0x01, 0x02, 0xF4 }, + { 0x23, 0x0F, 0xF4, 0x04, 0x02, 0x2F, 0x25, 0xF0, 0x43, 0x00, 0x06, 0xE8 }, + { 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x8A, 0x6E, 0x17, 0x00, 0x25, 0x27, 0x6B, 0x0E, 0x00, 0x02, 0xF4 }, + { 0x71, 0x1D, 0x81, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x17, 0x00, 0x0E, 0xF4 }, + { 0x01, 0x4B, 0xF1, 0x50, 0x00, 0x01, 0x23, 0xD2, 0x76, 0x00, 0x06, 0xF4 }, + { 0x2F, 0xCA, 0xF8, 0xE5, 0x00, 0x21, 0x1F, 0xC0, 0xFF, 0x00, 0x00, 0xF4 }, + { 0x29, 0xCD, 0xF0, 0x91, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 }, + { 0x24, 0xD0, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 }, + { 0x23, 0xC8, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 }, + { 0x64, 0xC9, 0xB0, 0x01, 0x00, 0x61, 0x1F, 0xF0, 0x86, 0x00, 0x02, 0xF4 }, + { 0x33, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x72, 0x23, 0x00, 0x08, 0xF4 }, + { 0x31, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 }, + { 0x31, 0x81, 0xA1, 0x30, 0x00, 0x16, 0x9F, 0xC2, 0x74, 0x00, 0x08, 0xF4 }, + { 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x02, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 }, + { 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 }, + { 0x23, 0x8A, 0xF2, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 }, + { 0x32, 0x80, 0x01, 0x10, 0x00, 0x12, 0x9F, 0x72, 0x33, 0x00, 0x08, 0xF4 }, + { 0x32, 0x80, 0x01, 0x10, 0x00, 0x14, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 }, + { 0x31, 0x16, 0x73, 0x8E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0xF4 }, + { 0x30, 0x16, 0x73, 0x7E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0x00 }, + { 0x31, 0x94, 0x33, 0x73, 0x00, 0x21, 0x1F, 0xA0, 0x97, 0x00, 0x0E, 0xF4 }, + { 0x31, 0x94, 0xD3, 0x73, 0x00, 0x21, 0x20, 0xA0, 0x97, 0x00, 0x0E, 0xF4 }, + { 0x31, 0x45, 0xF1, 0x53, 0x00, 0x32, 0x1F, 0xF2, 0x27, 0x00, 0x06, 0xF4 }, + { 0x13, 0x0C, 0xF2, 0x01, 0x00, 0x15, 0x2F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 }, + { 0x11, 0x0C, 0xF2, 0x01, 0x00, 0x11, 0x1F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 }, + { 0x11, 0x0A, 0xFE, 0x04, 0x00, 0x11, 0x1F, 0xF2, 0xBD, 0x00, 0x08, 0xF4 }, + { 0x16, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 }, + { 0x16, 0x40, 0xBA, 0x11, 0x00, 0xF1, 0x20, 0x24, 0x31, 0x00, 0x08, 0xF4 }, + { 0x61, 0xA7, 0x72, 0x8E, 0x00, 0xE1, 0x9F, 0x50, 0x1A, 0x00, 0x02, 0xF4 }, + { 0x18, 0x4D, 0x32, 0x13, 0x00, 0xE1, 0x20, 0x51, 0xE3, 0x00, 0x08, 0xF4 }, + { 0x17, 0xC0, 0x12, 0x41, 0x00, 0x31, 0x9F, 0x13, 0x31, 0x00, 0x06, 0xF4 }, + { 0x03, 0x8F, 0xF5, 0x55, 0x00, 0x21, 0x9F, 0xF3, 0x33, 0x00, 0x00, 0xF4 }, + { 0x13, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 }, + { 0x11, 0x43, 0x20, 0x15, 0x00, 0xF1, 0x20, 0x31, 0xF8, 0x00, 0x08, 0xF4 }, + { 0x11, 0x03, 0x82, 0x97, 0x00, 0xE4, 0x60, 0xF0, 0xF2, 0x00, 0x08, 0xF4 }, + { 0x05, 0x40, 0xD1, 0x53, 0x00, 0x14, 0x1F, 0x51, 0x71, 0x00, 0x06, 0xF4 }, + { 0xF1, 0x01, 0x77, 0x17, 0x00, 0x21, 0x1F, 0x81, 0x18, 0x00, 0x02, 0xF4 }, + { 0xF1, 0x18, 0x32, 0x11, 0x00, 0xE1, 0x1F, 0xF1, 0x13, 0x00, 0x00, 0xF4 }, + { 0x73, 0x48, 0xF1, 0x53, 0x00, 0x71, 0x1F, 0xF1, 0x06, 0x00, 0x08, 0xF4 }, + { 0x71, 0x8D, 0x71, 0x11, 0x00, 0x61, 0x5F, 0x72, 0x15, 0x00, 0x06, 0xF4 }, + { 0xD7, 0x4F, 0xF2, 0x61, 0x00, 0xD2, 0x1F, 0xF1, 0xB2, 0x00, 0x08, 0xF4 }, + { 0x01, 0x11, 0xF0, 0xFF, 0x00, 0x01, 0x1F, 0xF0, 0xF8, 0x00, 0x0A, 0xF4 }, + { 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x22, 0x13, 0x00, 0x06, 0xF4 }, + { 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x07, 0x00, 0x0E, 0xF4 }, + { 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x32, 0x15, 0x00, 0x02, 0xF4 }, + { 0x71, 0x1C, 0xFD, 0x13, 0x00, 0x21, 0x1F, 0xE7, 0xD6, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x67, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x1C, 0x54, 0x15, 0x00, 0x21, 0x1F, 0x53, 0x49, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x56, 0x51, 0x03, 0x00, 0x61, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 }, + { 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 }, + { 0x02, 0x29, 0xF5, 0x75, 0x00, 0x01, 0x9F, 0xF2, 0xF3, 0x00, 0x00, 0xF4 }, + { 0x02, 0x29, 0xF0, 0x75, 0x00, 0x01, 0x9F, 0xF4, 0x33, 0x00, 0x00, 0xF4 }, + { 0x01, 0x49, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 }, + { 0x01, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 }, + { 0x02, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 }, + { 0x02, 0x80, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 }, + { 0x01, 0x40, 0xF1, 0x53, 0x00, 0x08, 0x5F, 0xF1, 0x53, 0x00, 0x00, 0xF4 }, + { 0x21, 0x15, 0xD3, 0x2C, 0x00, 0x21, 0x9F, 0xC3, 0x2C, 0x00, 0x0A, 0xF4 }, + { 0x01, 0x18, 0xD4, 0xF2, 0x00, 0x21, 0x9F, 0xC4, 0x8A, 0x00, 0x0A, 0xF4 }, + { 0x01, 0x4E, 0xF0, 0x7B, 0x00, 0x11, 0x1F, 0xF4, 0xC8, 0x00, 0x04, 0xF4 }, + { 0x01, 0x44, 0xF0, 0xAB, 0x00, 0x11, 0x1F, 0xF3, 0xAB, 0x00, 0x04, 0xF4 }, + { 0x53, 0x0E, 0xF4, 0xC8, 0x00, 0x11, 0x1F, 0xF1, 0xBB, 0x00, 0x04, 0xF4 }, + { 0x53, 0x0B, 0xF2, 0xC8, 0x00, 0x11, 0x1F, 0xF2, 0xC5, 0x00, 0x04, 0xF4 }, + { 0x21, 0x15, 0xB4, 0x4C, 0x00, 0x21, 0x1F, 0x94, 0xAC, 0x00, 0x0A, 0xF4 }, + { 0x21, 0x15, 0x94, 0x1C, 0x00, 0x21, 0x1F, 0x64, 0xAC, 0x00, 0x0A, 0xF4 }, + { 0x22, 0x1B, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 }, + { 0x21, 0x19, 0x77, 0xBF, 0x00, 0xA1, 0x9F, 0x60, 0x2A, 0x00, 0x06, 0xF4 }, + { 0xA1, 0x13, 0xD6, 0xAF, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 }, + { 0xA2, 0x1D, 0x95, 0x24, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 }, + { 0x32, 0x9A, 0x51, 0x19, 0x00, 0x61, 0x9F, 0x60, 0x39, 0x00, 0x0C, 0xF4 }, + { 0xA4, 0x12, 0xF4, 0x30, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 }, + { 0x21, 0x16, 0x63, 0x0E, 0x00, 0x21, 0x1F, 0x63, 0x0E, 0x00, 0x0C, 0xF4 }, + { 0x31, 0x16, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 }, + { 0x21, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 }, + { 0x20, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 }, + { 0x32, 0x1C, 0x82, 0x18, 0x00, 0x61, 0x9F, 0x60, 0x07, 0x00, 0x0C, 0xF4 }, + { 0x32, 0x18, 0x61, 0x14, 0x00, 0xE1, 0x9F, 0x72, 0x16, 0x00, 0x0C, 0xF4 }, + { 0x31, 0xC0, 0x77, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x09, 0x00, 0x02, 0xF4 }, + { 0x71, 0xC3, 0x8E, 0x17, 0x00, 0x22, 0x24, 0x8B, 0x0E, 0x00, 0x02, 0xF4 }, + { 0x70, 0x8D, 0x6E, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x0E, 0x00, 0x02, 0xF4 }, + { 0x24, 0x4F, 0xF2, 0x06, 0x00, 0x31, 0x1F, 0x52, 0x06, 0x00, 0x0E, 0xF4 }, + { 0x31, 0x1B, 0x64, 0x07, 0x00, 0x61, 0x1F, 0xD0, 0x67, 0x00, 0x0E, 0xF4 }, + { 0x31, 0x1B, 0x61, 0x06, 0x00, 0x61, 0x1F, 0xD2, 0x36, 0x00, 0x0C, 0xF4 }, + { 0x31, 0x1F, 0x31, 0x06, 0x00, 0x61, 0x1F, 0x50, 0x36, 0x00, 0x0C, 0xF4 }, + { 0x31, 0x1F, 0x41, 0x06, 0x00, 0x61, 0x1F, 0xA0, 0x36, 0x00, 0x0C, 0xF4 }, + { 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 }, + { 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 }, + { 0x61, 0x19, 0x53, 0x58, 0x00, 0x21, 0x1F, 0xA0, 0x18, 0x00, 0x0C, 0xF4 }, + { 0x61, 0x19, 0x73, 0x57, 0x00, 0x21, 0x1F, 0xA0, 0x17, 0x00, 0x0C, 0xF4 }, + { 0x21, 0x1B, 0x71, 0xA6, 0x00, 0x21, 0x1F, 0xA1, 0x96, 0x00, 0x0E, 0xF4 }, + { 0x85, 0x91, 0xF5, 0x44, 0x00, 0xA1, 0x1F, 0xF0, 0x45, 0x00, 0x06, 0xF4 }, + { 0x07, 0x51, 0xF5, 0x33, 0x00, 0x61, 0x1F, 0xF0, 0x25, 0x00, 0x06, 0xF4 }, + { 0x13, 0x8C, 0xFF, 0x21, 0x00, 0x11, 0x9F, 0xFF, 0x03, 0x00, 0x0E, 0xF4 }, + { 0x38, 0x8C, 0xF3, 0x0D, 0x00, 0xB1, 0x5F, 0xF5, 0x33, 0x00, 0x0E, 0xF4 }, + { 0x87, 0x91, 0xF5, 0x55, 0x00, 0x22, 0x1F, 0xF0, 0x54, 0x00, 0x06, 0xF4 }, + { 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xF4 }, + { 0x04, 0x00, 0xFE, 0xF0, 0x00, 0xC2, 0x1F, 0xF6, 0xB5, 0x00, 0x0E, 0xF4 }, + { 0x05, 0x4E, 0xDA, 0x15, 0x00, 0x01, 0x9F, 0xF0, 0x13, 0x00, 0x0A, 0xF4 }, + { 0x31, 0x44, 0xF2, 0x9A, 0x00, 0x32, 0x1F, 0xF0, 0x27, 0x00, 0x06, 0xF4 }, + { 0xB0, 0xC4, 0xA4, 0x02, 0x00, 0xD7, 0x9F, 0x40, 0x42, 0x00, 0x00, 0xF4 }, + { 0xCA, 0x84, 0xF0, 0xF0, 0x00, 0xCF, 0x1F, 0x59, 0x62, 0x00, 0x0C, 0xF4 }, + { 0x30, 0x35, 0xF5, 0xF0, 0x00, 0x35, 0x1F, 0xF0, 0x9B, 0x00, 0x02, 0xF4 }, + { 0x63, 0x0F, 0xF4, 0x04, 0x02, 0x6F, 0x1F, 0xF0, 0x43, 0x00, 0x06, 0xF4 }, + { 0x07, 0x40, 0x09, 0x53, 0x00, 0x05, 0x1F, 0xF6, 0x94, 0x00, 0x0E, 0xF4 }, + { 0x09, 0x4E, 0xDA, 0x25, 0x00, 0x01, 0x1F, 0xF1, 0x15, 0x00, 0x0A, 0xF4 }, + { 0x04, 0x00, 0xF3, 0xA0, 0x02, 0x04, 0x1F, 0xF8, 0x46, 0x00, 0x0E, 0xF4 }, + { 0x07, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x1F, 0x5C, 0xDC, 0x00, 0x0E, 0xF4 }, + { 0x1F, 0x1E, 0xE5, 0x5B, 0x00, 0x0F, 0x1F, 0x5D, 0xFA, 0x00, 0x0E, 0xF4 }, + { 0x11, 0x8A, 0xF1, 0x11, 0x00, 0x01, 0x5F, 0xF1, 0xB3, 0x00, 0x06, 0xF4 }, + { 0x00, 0x40, 0xD1, 0x53, 0x00, 0x00, 0x1F, 0xF2, 0x56, 0x00, 0x0E, 0xF4 }, + { 0x32, 0x44, 0xF8, 0xFF, 0x00, 0x11, 0x1F, 0xF5, 0x7F, 0x00, 0x0E, 0xF4 }, + { 0x00, 0x40, 0x09, 0x53, 0x00, 0x02, 0x1F, 0xF7, 0x94, 0x00, 0x0E, 0xF4 }, + { 0x11, 0x86, 0xF2, 0xA8, 0x00, 0x01, 0x9F, 0xA0, 0xA8, 0x00, 0x08, 0xF4 }, + { 0x00, 0x50, 0xF2, 0x70, 0x00, 0x13, 0x1F, 0xF2, 0x72, 0x00, 0x0E, 0xF4 }, + { 0xF0, 0x00, 0x11, 0x11, 0x00, 0xE0, 0xDF, 0x11, 0x11, 0x00, 0x0E, 0xF4 } +}; + +// hardcoded, dumped from ADHOM.DRV +uint16 adlib_FrequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = { + 0x0158, 0x016C, 0x0182, 0x0199, 0x01B1, 0x01CB, 0x01E6, 0x0203, 0x0222, 0x0242, + 0x0265, 0x0289, 0x0558, 0x056C, 0x0582, 0x0599, 0x05B1, 0x05CB, 0x05E6, 0x0603, + 0x0622, 0x0642, 0x0665, 0x0689, 0x0958, 0x096C, 0x0982, 0x0999, 0x09B1, 0x09CB, + 0x09E6, 0x0A03, 0x0A22, 0x0A42, 0x0A65, 0x0A89, 0x0D58, 0x0D6C, 0x0D82, 0x0D99, + 0x0DB1, 0x0DCB, 0x0DE6, 0x0E03, 0x0E22, 0x0E42, 0x0E65, 0x0E89, 0x1158, 0x116C, + 0x1182, 0x1199, 0x11B1, 0x11CB, 0x11E6, 0x1203, 0x1222, 0x1242, 0x1265, 0x1289, + 0x1558, 0x156C, 0x1582, 0x1599, 0x15B1, 0x15CB, 0x15E6, 0x1603, 0x1622, 0x1642, + 0x1665, 0x1689, 0x1958, 0x196C, 0x1982, 0x1999, 0x19B1, 0x19CB, 0x19E6, 0x1A03, + 0x1A22, 0x1A42, 0x1A65, 0x1A89, 0x1D58, 0x1D6C, 0x1D82, 0x1D99, 0x1DB1, 0x1DCB, + 0x1DE6, 0x1E03, 0x1E22, 0x1E42, 0x1E65, 0x1E89 +}; + +class MidiDriver_AdLib : public MidiDriver_Emulated { +public: + MidiDriver_AdLib(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { + memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping)); + } + virtual ~MidiDriver_AdLib() { } + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel() { return NULL; } + MidiChannel *getPercussionChannel() { return NULL; } + + // AudioStream + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; } + bool hasRhythmChannel() const { return false; } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + + void setVolume(byte volume); + virtual uint32 property(int prop, uint32 param); + + bool useRhythmChannel() const { return _rhythmKeyMap != NULL; } + + void newMusicData(byte *musicData, int32 musicDataSize); + +private: + struct adlib_ChannelEntry { + bool inUse; + const adlib_InstrumentEntry *currentInstrumentPtr; + byte currentNote; + byte currentA0hReg; + byte currentB0hReg; + + adlib_ChannelEntry() : inUse(false), currentInstrumentPtr(NULL), currentNote(0), + currentA0hReg(0), currentB0hReg(0) { } + }; + + OPL::OPL *_opl; + int _masterVolume; + byte *_rhythmKeyMap; + + // points to a MIDI channel for each of the new voice channels + byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT]; + + // stores information about all FM voice channels + adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT]; + + void programChange(byte channel, byte parameter); + void setRegister(int reg, int value); + void noteOn(byte channel, byte note, byte velocity); + void noteOff(byte channel, byte note); + void voiceOnOff(byte FMVoiceChannel, bool KeyOn, byte note, byte velocity); +}; + +#if USE_SCI_MIDI_PLAYER +class MidiPlayer_AdLib : public MidiPlayer { +public: + MidiPlayer_AdLib() : MidiPlayer() { _driver = new MidiDriver_AdLib(g_system->getMixer()); } + ~MidiPlayer_AdLib() { + delete _driver; + _driver = 0; + } + + int open(); + void close(); + + byte getPlayId() const; + int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; } + bool hasRhythmChannel() const { return false; } + void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); } + + //int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); } + + void newMusicData(byte *musicData, int32 musicDataSize) { static_cast<MidiDriver_AdLib *>(_driver)->newMusicData(musicData, musicDataSize); } +}; +#endif + +int MidiDriver_AdLib::open() { + int rate = _mixer->getOutputRate(); + + debug(3, "ADLIB: Starting driver"); + + _opl = OPL::Config::create(OPL::Config::kOpl2); + + if (!_opl) + return -1; + + _opl->init(rate); + + setRegister(0xBD, 0); + setRegister(0x08, 0); + setRegister(0x01, 0x20); + + MidiDriver_Emulated::open(); + + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + + return 0; +} + +void MidiDriver_AdLib::close() { + _mixer->stopHandle(_mixerSoundHandle); + + delete _opl; + delete[] _rhythmKeyMap; +} + +void MidiDriver_AdLib::setVolume(byte volume) { + _masterVolume = volume; + //renewNotes(-1, true); +} + +// Called when a music track got loaded into memory +void MidiDriver_AdLib::newMusicData(byte *musicData, int32 musicDataSize) { + assert(musicDataSize >= 0x7F); + // MIDI Channel <-> FM Voice Channel mapping at offset 0x22 of music data + memcpy(&_voiceChannelMapping, musicData + 0x22, 9); + + // reset OPL here? + // reset current channel data + memset(&_channels, 0, sizeof(_channels)); +} + +// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php +void MidiDriver_AdLib::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: + noteOff(channel, op1); + break; + case 0x90: + noteOn(channel, op1, op2); + break; + case 0xb0: // Control change + // Doesn't seem to be implemented in the Sherlock Holmes adlib driver + break; + case 0xc0: // Program Change + programChange(channel, op1); + break; + case 0xa0: // Polyphonic key pressure (aftertouch) + case 0xd0: // Channel pressure (aftertouch) + // Aftertouch doesn't seem to be implemented in the Sherlock Holmes adlib driver + break; + case 0xe0: + // TODO: Implement this, occurs right in the intro, second scene + warning("pitch bend change"); + break; + case 0xf0: // SysEx + warning("SysEx: %lx", b); + break; + default: + warning("ADLIB: Unknown event %02x", command); + } +} + +void MidiDriver_AdLib::generateSamples(int16 *data, int len) { + _opl->readBuffer(data, len); +} + +void MidiDriver_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) { + if (velocity == 0) + return noteOff(MIDIchannel, note); + + if (MIDIchannel != 9) { + // Not Percussion + for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { + if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) { + if (!_channels[FMvoiceChannel].inUse) { + _channels[FMvoiceChannel].inUse = true; + _channels[FMvoiceChannel].currentNote = note; + + voiceOnOff(FMvoiceChannel, true, note, velocity); + return; + } + } + } + } + warning("MIDI channel not mapped/all FM voice channels busy %d", MIDIchannel); +} + +void MidiDriver_AdLib::noteOff(byte MIDIchannel, byte note) { + for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { + if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) { + if (_channels[FMvoiceChannel].currentNote == note) { + _channels[FMvoiceChannel].inUse = false; + + voiceOnOff(FMvoiceChannel, false, note, 0); + return; + } + } + } +} + +void MidiDriver_AdLib::voiceOnOff(byte FMvoiceChannel, bool keyOn, byte note, byte velocity) { + byte frequencyOffset = 0; + uint16 frequency = 0; + byte op2RegAdjust = 0; + byte regValue40h = 0; + byte regValueA0h = 0; + byte regValueB0h = 0; + + // Look up frequency + if (_channels[FMvoiceChannel].currentInstrumentPtr) { + frequencyOffset = note + _channels[FMvoiceChannel].currentInstrumentPtr->frequencyAdjust; + } else { + frequencyOffset = note; + } + if (frequencyOffset >= SHERLOCK_ADLIB_NOTES_COUNT) { + error("bad note!"); + } + frequency = adlib_FrequencyLookUpTable[frequencyOffset]; + + if (keyOn) { + // adjust register 40h + if (_channels[FMvoiceChannel].currentInstrumentPtr) { + regValue40h = _channels[FMvoiceChannel].currentInstrumentPtr->reg40op2; + } + regValue40h = regValue40h - (velocity >> 3); + op2RegAdjust = adlib_Operator2Register[FMvoiceChannel]; + setRegister(0x40 + op2RegAdjust, regValue40h); + } + + regValueA0h = frequency & 0xFF; + regValueB0h = frequency >> 8; + if (keyOn) { + regValueB0h |= 0x20; // set Key-On flag + } + + setRegister(0xA0 + FMvoiceChannel, regValueA0h); + setRegister(0xB0 + FMvoiceChannel, regValueB0h); + _channels[FMvoiceChannel].currentA0hReg = regValueA0h; + _channels[FMvoiceChannel].currentB0hReg = regValueB0h; +} + +void MidiDriver_AdLib::programChange(byte MIDIchannel, byte op1) { + const adlib_InstrumentEntry *instrumentPtr; + byte op1Reg = 0; + byte op2Reg = 0; + + // setup instrument + instrumentPtr = &adlib_instrumentTable[op1]; + //warning("program change for MIDI channel %d, instrument id %d", MIDIchannel, op1); + + for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { + if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) { + + op1Reg = adlib_Operator1Register[FMvoiceChannel]; + op2Reg = adlib_Operator2Register[FMvoiceChannel]; + + setRegister(0x20 + op1Reg, instrumentPtr->reg20op1); + setRegister(0x40 + op1Reg, instrumentPtr->reg40op1); + setRegister(0x60 + op1Reg, instrumentPtr->reg60op1); + setRegister(0x80 + op1Reg, instrumentPtr->reg80op1); + setRegister(0xE0 + op1Reg, instrumentPtr->regE0op1); + + setRegister(0x20 + op2Reg, instrumentPtr->reg20op2); + setRegister(0x40 + op2Reg, instrumentPtr->reg40op2); + setRegister(0x60 + op2Reg, instrumentPtr->reg60op2); + setRegister(0x80 + op2Reg, instrumentPtr->reg80op2); + setRegister(0xE0 + op2Reg, instrumentPtr->regE0op2); + + setRegister(0xC0 + FMvoiceChannel, instrumentPtr->regC0); + + // Remember instrument + _channels[FMvoiceChannel].currentInstrumentPtr = instrumentPtr; + } + } +} +void MidiDriver_AdLib::setRegister(int reg, int value) { + _opl->write(0x220, reg); + _opl->write(0x221, value); +} + +uint32 MidiDriver_AdLib::property(int prop, uint32 param) { +#if 0 + switch(prop) { + case MIDI_PROP_MASTER_VOLUME: + if (param != 0xffff) + _masterVolume = param; + return _masterVolume; + default: + break; + } +#endif + return 0; +} + +#if USE_SCI_MIDI_PLAYER +int MidiPlayer_AdLib::open() { + return static_cast<MidiDriver_AdLib *>(_driver)->open(); +} + +void MidiPlayer_AdLib::close() { + if (_driver) { + _driver->close(); + } +} + +byte MidiPlayer_AdLib::getPlayId() const { + return 0x00; +} + +MidiPlayer *MidiPlayer_AdLib_create() { + return new MidiPlayer_AdLib(); +} + +void MidiPlayer_AdLib_newMusicData(MidiPlayer *driver, byte *musicData, int32 musicDataSize) { + static_cast<MidiPlayer_AdLib *>(driver)->newMusicData(musicData, musicDataSize); +} +#endif + +MidiDriver *MidiDriver_AdLib_create() { + return new MidiDriver_AdLib(g_system->getMixer()); +} + +void MidiDriver_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize) { + static_cast<MidiDriver_AdLib *>(driver)->newMusicData(musicData, musicDataSize); +} + +} // End of namespace Sci diff --git a/engines/sherlock/scalpel/drivers/mididriver.h b/engines/sherlock/scalpel/drivers/mididriver.h new file mode 100644 index 0000000000..f1366f8ebc --- /dev/null +++ b/engines/sherlock/scalpel/drivers/mididriver.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SHERLOCK_SOFTSEQ_MIDIDRIVER_H +#define SHERLOCK_SOFTSEQ_MIDIDRIVER_H + +#include "sherlock/sherlock.h" +//#include "audio/mididrv.h" +#include "common/error.h" + +namespace Sherlock { + +#define USE_SCI_MIDIPLAYER 1 + +#if USE_SCI_MIDIPLAYER +enum { + MIDI_CHANNELS = 16, + MIDI_PROP_MASTER_VOLUME = 0 +}; + +#define MIDI_RHYTHM_CHANNEL 9 + +class MidiPlayer : public MidiDriver_BASE { +protected: + MidiDriver *_driver; + int8 _reverb; + +public: + MidiPlayer() : _driver(0), _reverb(-1) { } + + virtual int open() { return _driver->open(); } + virtual void close() { _driver->close(); } + virtual void send(uint32 b) { _driver->send(b); } + virtual uint32 getBaseTempo() { return _driver->getBaseTempo(); } + virtual bool hasRhythmChannel() const = 0; + virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); } + + virtual byte getPlayId() const = 0; + virtual int getPolyphony() const = 0; + virtual int getFirstChannel() const { return 0; } + //virtual int getLastChannel() const { return 15; } + + virtual void setVolume(byte volume) { + if(_driver) + _driver->property(MIDI_PROP_MASTER_VOLUME, volume); + } + + virtual int getVolume() { + return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0; + } + + // Returns the current reverb, or -1 when no reverb is active + int8 getReverb() const { return _reverb; } + // Sets the current reverb, used mainly in MT-32 + virtual void setReverb(int8 reverb) { _reverb = reverb; } + + // Special stuff for Sherlock Holmes +// virtual void newMusicData(byte *musicData, int32 musicDataSize); + +//protected: +}; + +extern MidiPlayer *MidiPlayer_AdLib_create(); +extern void MidiPlayer_AdLib_newMusicData(MidiPlayer *driver, byte *musicData, int32 musicDataSize); +#endif + +extern MidiDriver *MidiDriver_AdLib_create(); +extern void MidiDriver_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize); + +} // End of namespace Sci + +#endif // SHERLOCK_SOFTSEQ_MIDIDRIVER_H diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp new file mode 100644 index 0000000000..bee44bfb7d --- /dev/null +++ b/engines/sherlock/scalpel/scalpel.cpp @@ -0,0 +1,912 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sherlock/scalpel/scalpel.h" +#include "sherlock/sherlock.h" +#include "sherlock/music.h" +#include "sherlock/animation.h" + +namespace Sherlock { + +namespace Scalpel { + +#define PROLOGUE_NAMES_COUNT 6 + +// The following are a list of filenames played in the prologue that have +// special effects associated with them at specific frames +static const char *const PROLOGUE_NAMES[PROLOGUE_NAMES_COUNT] = { + "subway1", "subway2", "finale2", "suicid", "coff3", "coff4" +}; + +static const int PROLOGUE_FRAMES[6][9] = { + { 4, 26, 54, 72, 92, 134, FRAMES_END }, + { 2, 80, 95, 117, 166, FRAMES_END }, + { 1, FRAMES_END }, + { 42, FRAMES_END }, + { FRAMES_END }, + { FRAMES_END } +}; + +#define TITLE_NAMES_COUNT 7 + +// Title animations file list +static const char *const TITLE_NAMES[TITLE_NAMES_COUNT] = { + "27pro1", "14note", "coff1", "coff2", "coff3", "coff4", "14kick" +}; + +static const int TITLE_FRAMES[7][9] = { + { 29, 131, FRAMES_END }, + { 55, 80, 95, 117, 166, FRAMES_END }, + { 15, FRAMES_END }, + { 4, 37, 92, FRAMES_END }, + { 2, 43, FRAMES_END }, + { 2, FRAMES_END }, + { 10, 50, FRAMES_END } +}; + +#define NUM_PLACES 100 + +static const int MAP_X[NUM_PLACES] = { + 0, 368, 0, 219, 0, 282, 0, 43, 0, 0, 396, 408, 0, 0, 0, 568, 37, 325, + 28, 0, 263, 36, 148, 469, 342, 143, 443, 229, 298, 0, 157, 260, 432, + 174, 0, 351, 0, 528, 0, 136, 0, 0, 0, 555, 165, 0, 506, 0, 0, 344, 0, 0 +}; +static const int MAP_Y[NUM_PLACES] = { + 0, 147, 0, 166, 0, 109, 0, 61, 0, 0, 264, 70, 0, 0, 0, 266, 341, 30, 275, + 0, 294, 146, 311, 230, 184, 268, 133, 94, 207, 0, 142, 142, 330, 255, 0, + 37, 0, 70, 0, 116, 0, 0, 0, 50, 21, 0, 303, 0, 0, 229, 0, 0 +}; + +static const int MAP_TRANSLATE[NUM_PLACES] = { + 0, 0, 0, 1, 0, 2, 0, 3, 4, 0, 4, 6, 0, 0, 0, 8, 9, 10, 11, 0, 12, 13, 14, 7, + 15, 16, 17, 18, 19, 0, 20, 21, 22, 23, 0, 24, 0, 25, 0, 26, 0, 0, 0, 27, + 28, 0, 29, 0, 0, 30, 0 +}; + +static const byte MAP_SEQUENCES[3][MAX_FRAME] = { + { 1, 1, 2, 3, 4, 0 }, // Overview Still + { 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0 }, + { 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0 } +}; + +#define MAX_PEOPLE 66 + +const char PEOPLE_PORTRAITS[MAX_PEOPLE][5] = { + { "HOLM" }, // Sherlock Holmes + { "WATS" }, // Dr. Watson + { "LEST" }, // Inspector Lestrade + { "CON1" }, // Constable O'Brien + { "CON2" }, // Constable Lewis + { "SHEI" }, // Sheila Parker + { "HENR" }, // Henry Carruthers + { "LESL" }, // Lesley (flower girl) + { "USH1" }, // Usher #1 + { "USH2" }, // Usher #2 + { "FRED" }, // Fredrick Epstein + { "WORT" }, // Mrs. Worthington + { "COAC" }, // Coach + { "PLAY" }, // Player + { "WBOY" }, // Tim (Waterboy) + { "JAME" }, // James Sanders + { "BELL" }, // Belle (perfumerie) + { "GIRL" }, // Cleaning Girl (perfumerie) + { "EPST" }, // Epstien in the Opera Balcony + { "WIGG" }, // Wiggins + { "PAUL" }, // Paul (Brumwell / Carroway) + { "BART" }, // Bartender + { "DIRT" }, // Dirty Drunk + { "SHOU" }, // Shouting Drunk + { "STAG" }, // Staggering Drunk + { "BOUN" }, // Bouncer + { "SAND" }, // James Sanders - At Home + { "CORO" }, // The Coroner + { "EQUE" }, // The Equestrian Shop Keeper + { "GEOR" }, // George Blackwood + { "LARS" }, // Lars + { "PARK" }, // Sheila Parker (happy) + { "CHEM" }, // Chemist + { "GREG" }, // Inspector Gregson + { "LAWY" }, // Jacob Farthington Lawyer + { "MYCR" }, // Mycroft + { "SHER" }, // Old Sherman + { "CHMB" }, // Richard Chemist Stock boy + { "BARM" }, // Barman + { "DAND" }, // Dandy Player + { "ROUG" }, // Rough-looking Player + { "SPEC" }, // Spectator + { "HUNT" }, // Robert Hunt + { "VIOL" }, // Violet Secretary + { "PETT" }, // Pettigrew + { "APPL" }, // Augie (apple seller) + { "ANNA" }, // Anna Carroway + { "GUAR" }, // Guard + { "ANTO" }, // Antonio Caruso + { "TOBY" }, // Toby the Dog + { "KING" }, // Simon Kingsley + { "ALFR" }, // Alfred Tobacco Clerk + { "LADY" }, // Lady Brumwell + { "ROSA" }, // Madame Rosa + { "LADB" }, // Lady Brumwell + { "MOOR" }, // Joseph Moorehead + { "BEAL" }, // Mrs. Beale + { "LION" }, // Felix the Lion + { "HOLL" }, // Hollingston + { "CALL" }, // Constable Callaghan + { "JERE" }, // Sergeant Jeremy Duncan + { "LORD" }, // Lord Brumwell + { "NIGE" }, // Nigel Jameson + { "JONA" }, // Jonas (newspaper seller) + { "DUGA" }, // Constable Dugan + { "INSP" } // Inspector Lestrade (Scotland Yard) +}; + +const char *const PEOPLE_NAMES[MAX_PEOPLE] = { + "Sherlock Holmes", + "Dr. Watson", + "Inspector Lestrade", + "Constable O'Brien", + "Constable Lewis", + "Sheila Parker", + "Henry Carruthers", + "Lesley", + "An Usher", + "An Usher", + "Fredrick Epstein", + "Mrs. Worthington", + "The Coach", + "A Player", + "Tim", + "James Sanders", + "Belle", + "Cleaning Girl", + "Fredrick Epstein", + "Wiggins", + "Paul", + "The Bartender", + "A Dirty Drunk", + "A Shouting Drunk", + "A Staggering Drunk", + "The Bouncer", + "James Sanders", + "The Coroner", + "Reginald Snipes", + "George Blackwood", + "Lars", + "Sheila Parker", + "The Chemist", + "Inspector Gregson", + "Jacob Farthington", + "Mycroft", + "Old Sherman", + "Richard", + "The Barman", + "A Dandy Player", + "A Rough-looking Player", + "A Spectator", + "Robert Hunt", + "Violet", + "Pettigrew", + "Augie", + "Anna Carroway", + "A Guard", + "Antonio Caruso", + "Toby the Dog", + "Simon Kingsley", + "Alfred", + "Lady Brumwell", + "Madame Rosa", + "Lady Brumwell", + "Joseph Moorehead", + "Mrs. Beale", + "Felix", + "Hollingston", + "Constable Callaghan", + "Sergeant Duncan", + "Lord Brumwell", + "Nigel Jaimeson", + "Jonas", + "Constable Dugan", + "Inspector Lestrade" +}; + +static const byte PEOPLE_STILL_SEQUENCES[MAX_PEOPLE][MAX_TALK_SEQUENCES] = { + { 1, 0, 0 }, // Sherlock Holmes + { 6, 0, 0 }, // Dr. Watson + { 4, 0, 0 }, // Inspector Lestrade + { 2, 0, 0 }, // Constable #1 + { 2, 0, 0 }, // Constable #2 + { 2, 0, 0 }, // Sheila Parker + { 3, 0, 0 }, // Henry Carruthers + { 9, 0, 0 }, // Lesly (flower girl) + { 13, 0, 0 }, // Usher #1 + { 2, 0, 0 }, // Usher #2 + { 4, 0, 0 }, // Fredrick Epstein + { 9, 0, 0 }, // Mrs.Worthington + { 2, 0, 0 }, // Coach + { 8, 0, 0 }, // Player + { 13, 0, 0 }, // Waterboy + { 6, 0, 0 }, // James Sanders + { 1, 0, 0 }, // Belle (perfumerie) + { 20, 0, 0 }, // Cleaning Girl (perfumerie) + { 17, 0, 0 }, // Epstien in the Opera Balcony + { 3, 0, 0 }, // Wiggins + { 2, 0, 0 }, // Paul (Brumwell/Carroway) + { 1, 0, 0 }, // Bartender + { 1, 0, 0 }, // Dirty Drunk + { 1, 0, 0 }, // Shouting Drunk + { 1, 0, 0 }, // Staggering Drunk + { 1, 0, 0 }, // Bouncer + { 6, 0, 0 }, // James Sanders - At Home + { 6, 0, 0 }, // The Coroner + { 1, 0, 0 }, // The Equestrian Shop Keeper + { 1, 0, 0 }, // George Blackwood + { 7, 0, 0 }, // Lars + { 1, 0, 0 }, // Sheila Parker + { 8, 0, 0 }, // Chemist + { 6, 0, 0 }, // Inspector Gregson + { 1, 0, 0 }, // Lawyer + { 1, 0, 0 }, // Mycroft + { 7, 0, 0 }, // Old Sherman + { 1, 0, 0 }, // Stock Boy in Chemist Shop + { 1, 0, 0 }, // Barman + { 1, 0, 0 }, // Dandy Player + { 1, 0, 0 }, // Rough-looking Player + { 1, 0, 0 }, // Spectator + { 1, 0, 0 }, // Robert Hunt + { 3, 0, 0 }, // Violet Secretary + { 1, 0, 0 }, // Pettigrew + { 8, 0, 0 }, // Augie (apple seller) + { 16, 0, 0 }, // Anna Carroway + { 1, 0, 0 }, // Guard + { 8, 0, 0 }, // Antonio Caruso + { 1, 0, 0 }, // Toby the Dog + { 13, 0, 0 }, // Simon Kingsley + { 2, 0, 0 }, // Alfred Tobacco Clerk + { 1, 0, 0 }, // Lady Brumwell + { 1, 0, 0 }, // Madame Rosa + { 1, 0, 0 }, // Lady Brumwell + { 1, 0, 0 }, // Joseph Moorehead + { 5, 0, 0 }, // Mrs. Beale + { 1, 0, 0 }, // Felix the Lion + { 1, 0, 0 }, // Hollingston + { 1, 0, 0 }, // Constable Callaghan + { 2, 0, 0 }, // Sergeant Jeremy Duncan + { 1, 0, 0 }, // Lord Brumwell + { 1, 0, 0 }, // Nigel Jameson + { 1, 0, 0 }, // Jonas (newspaper seller) + { 1, 0, 0 }, // Constable Dugan + { 4, 0, 0 } // Inspector Lestrade (Yard) +}; + +static const byte PEOPLE_TALK_SEQUENCES[MAX_PEOPLE][MAX_TALK_SEQUENCES] = { + { 1, 0, 0 }, // Sherlock Holmes + { 5, 5, 6, 7, 8, 7, 8, 6, 0, 0 }, // Dr. Watson + { 2, 0, 0 }, // Inspector Lestrade + { 1, 0, 0 }, // Constable #1 + { 1, 0, 0 }, // Constable #2 + { 2, 3, 0, 0 }, // Sheila Parker + { 3, 0, 0 }, // Henry Carruthers + { 1, 2, 3, 2, 1, 2, 3, 0, 0 }, // Lesly (flower girl) + { 13, 14, 0, 0 }, // Usher #1 + { 2, 0, 0 }, // Usher #2 + { 1, 2, 3, 4, 3, 4, 3, 2, 0, 0 }, // Fredrick Epstein + { 8, 0, 0 }, // Mrs.Worthington + { 1, 2, 3, 4, 5, 4, 3, 2, 0, 0 }, // Coach + { 7, 8, 0, 0 }, // Player + { 12, 13, 0, 0 }, // Waterboy + { 3, 4, 0, 0 }, // James Sanders + { 4, 5, 0, 0 }, // Belle (perfumerie) + { 14, 15, 16, 17, 18, 19, 20, 20, 20, 0, 0 }, // Cleaning Girl (perfumerie) + { 16, 17, 18, 18, 18, 17, 17, 0, 0 }, // Epstien in the Opera Balcony + { 2, 3, 0, 0 }, // Wiggins + { 1, 2, 0, 0 }, // Paul (Brumwell/Carroway) + { 1, 0, 0 }, // Bartender + { 1, 0, 0 }, // Dirty Drunk + { 1, 0, 0 }, // Shouting Drunk + { 1, 0, 0 }, // Staggering Drunk + { 1, 0, 0 }, // Bouncer + { 5, 6, 0, 0 }, // James Sanders - At Home + { 4, 5, 0, 0 }, // The Coroner + { 1, 0, 0 }, // The Equestrian Shop Keeper + { 1, 0, 0 }, // George Blackwood + { 5, 6, 0, 0 }, // Lars + { 1, 0, 0 }, // Sheila Parker + { 8, 9, 0, 0 }, // Chemist + { 5, 6, 0, 0 }, // Inspector Gregson + { 1, 0, 0 }, // Lawyer + { 1, 0, 0 }, // Mycroft + { 7, 8, 0, 0 }, // Old Sherman + { 1, 0, 0 }, // Stock Boy in Chemist Shop + { 1, 0, 0 }, // Barman + { 1, 0, 0 }, // Dandy Player + { 1, 0, 0 }, // Rough-looking Player + { 1, 0, 0 }, // Spectator + { 1, 0, 0 }, // Robert Hunt + { 3, 4, 0, 0 }, // Violet Secretary + { 1, 0, 0 }, // Pettigrew + { 14, 15, 0, 0 }, // Augie (apple seller) + { 3, 4, 5, 6, 0, 0 }, // Anna Carroway + { 4, 5, 6, 0, 0 }, // Guard + { 7, 8, 0, 0 }, // Antonio Caruso + { 1, 0, 0 }, // Toby the Dog + { 13, 14, 0, 0 }, // Simon Kingsley + { 2, 3, 0, 0 }, // Alfred Tobacco Clerk + { 3, 4, 0, 0 }, // Lady Brumwell + { 1, 30, 0, 0 }, // Madame Rosa + { 3, 4, 0, 0 }, // Lady Brumwell + { 1, 0, 0 }, // Joseph Moorehead + { 14, 15, 16, 17, 18, 19, 20, 0, 0 }, // Mrs. Beale + { 1, 0, 0 }, // Felix the Lion + { 1, 0, 0 }, // Hollingston + { 1, 0, 0 }, // Constable Callaghan + { 1, 1, 2, 2, 0, 0 }, // Sergeant Jeremy Duncan + { 9, 10, 0, 0 }, // Lord Brumwell + { 1, 2, 0, 138, 3, 4, 0, 138, 0, 0 }, // Nigel Jameson + { 1, 8, 0, 0 }, // Jonas (newspaper seller) + { 1, 0, 0 }, // Constable Dugan + { 2, 0, 0 } // Inspector Lestrade (Yard) +}; + +/*----------------------------------------------------------------*/ + +ScalpelEngine::ScalpelEngine(OSystem *syst, const SherlockGameDescription *gameDesc) : + SherlockEngine(syst, gameDesc) { + _darts = nullptr; + _mapResult = 0; +} + +ScalpelEngine::~ScalpelEngine() { + delete _darts; +} + +void ScalpelEngine::initialize() { + SherlockEngine::initialize(); + + _darts = new Darts(this); + + _flags.resize(100 * 8); + _flags[3] = true; // Turn on Alley + _flags[39] = true; // Turn on Baker Street + + if (!isDemo()) { + // Load the map co-ordinates for each scene and sequence data + _map->loadPoints(NUM_PLACES, &MAP_X[0], &MAP_Y[0], &MAP_TRANSLATE[0]); + _map->loadSequences(3, &MAP_SEQUENCES[0][0]); + _map->_oldCharPoint = BAKER_ST_EXTERIOR; + } + + // Load the inventory + loadInventory(); + + // Set up list of people + for (int idx = 0; idx < MAX_PEOPLE; ++idx) + _people->_characters.push_back(PersonData(PEOPLE_NAMES[idx], PEOPLE_PORTRAITS[idx], + PEOPLE_STILL_SEQUENCES[idx], PEOPLE_TALK_SEQUENCES[idx])); + + _animation->setPrologueNames(&PROLOGUE_NAMES[0], PROLOGUE_NAMES_COUNT); + _animation->setPrologueFrames(&PROLOGUE_FRAMES[0][0], 6, 9); + + _animation->setTitleNames(&TITLE_NAMES[0], TITLE_NAMES_COUNT); + _animation->setTitleFrames(&TITLE_FRAMES[0][0], 7, 9); + + // Starting scene + if (isDemo() && _interactiveFl) + _scene->_goToScene = 3; + else + _scene->_goToScene = 4; +} + +void ScalpelEngine::showOpening() { + if (isDemo() && _interactiveFl) + return; + + if (!showCityCutscene()) + return; + if (!showAlleyCutscene()) + return; + if (!showStreetCutscene()) + return; + if (!showOfficeCutscene()) + return; + + _events->clearEvents(); + _music->stopMusic(); +} + +bool ScalpelEngine::showCityCutscene() { + byte palette[PALETTE_SIZE]; + + _music->playMusic("prolog1.mus"); + _animation->_gfxLibraryFilename = "title.lib"; + _animation->_soundLibraryFilename = "title.snd"; + bool finished = _animation->play("26open1", 1, 255, true, 2); + + if (finished) { + ImageFile titleImages("title2.vgs", true); + _screen->_backBuffer1.blitFrom(*_screen); + _screen->_backBuffer2.blitFrom(*_screen); + + // London, England + _screen->_backBuffer1.transBlitFrom(titleImages[0], Common::Point(10, 11)); + _screen->randomTransition(); + finished = _events->delay(1000, true); + + // November, 1888 + if (finished) { + _screen->_backBuffer1.transBlitFrom(titleImages[1], Common::Point(101, 102)); + _screen->randomTransition(); + finished = _events->delay(5000, true); + } + + // Transition out the title + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2); + _screen->randomTransition(); + } + + if (finished) + finished = _animation->play("26open2", 1, 0, false, 2); + + if (finished) { + ImageFile titleImages("title.vgs", true); + _screen->_backBuffer1.blitFrom(*_screen); + _screen->_backBuffer2.blitFrom(*_screen); + + // The Lost Files of + _screen->_backBuffer1.transBlitFrom(titleImages[0], Common::Point(75, 6)); + // Sherlock Holmes + _screen->_backBuffer1.transBlitFrom(titleImages[1], Common::Point(34, 21)); + // copyright + _screen->_backBuffer1.transBlitFrom(titleImages[2], Common::Point(4, 190)); + + _screen->verticalTransition(); + finished = _events->delay(4000, true); + + if (finished) { + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2); + _screen->randomTransition(); + finished = _events->delay(2000); + } + + if (finished) { + _screen->getPalette(palette); + _screen->fadeToBlack(2); + } + + if (finished) { + // In the alley... + _screen->transBlitFrom(titleImages[3], Common::Point(72, 51)); + _screen->fadeIn(palette, 3); + finished = _events->delay(3000, true); + } + } + + _animation->_gfxLibraryFilename = ""; + _animation->_soundLibraryFilename = ""; + return finished; +} + +bool ScalpelEngine::showAlleyCutscene() { + byte palette[PALETTE_SIZE]; + _music->playMusic("prolog2.mus"); + + _animation->_gfxLibraryFilename = "TITLE.LIB"; + _animation->_soundLibraryFilename = "TITLE.SND"; + + bool finished = _animation->play("27PRO1", 1, 3, true, 2); + if (finished) + finished = _animation->play("27PRO2", 1, 0, false, 2); + + if (finished) { + showLBV("scream.lbv"); + finished = _events->delay(6000); + } + + if (finished) + finished = _animation->play("27PRO3", 1, 0, true, 2); + + if (finished) { + _screen->getPalette(palette); + _screen->fadeToBlack(2); + } + + if (finished) { + ImageFile titleImages("title3.vgs", true); + // "Early the following morning on Baker Street..." + _screen->_backBuffer1.transBlitFrom(titleImages[0], Common::Point(35, 51), false, 0); + _screen->fadeIn(palette, 3); + finished = _events->delay(1000); + } + + _animation->_gfxLibraryFilename = ""; + _animation->_soundLibraryFilename = ""; + return finished; +} + +bool ScalpelEngine::showStreetCutscene() { + _animation->_gfxLibraryFilename = "TITLE.LIB"; + _animation->_soundLibraryFilename = "TITLE.SND"; + + _music->playMusic("PROLOG3.MUS"); + + bool finished = _animation->play("14KICK", 1, 3, true, 2); + + if (finished) + finished = _animation->play("14NOTE", 1, 0, false, 2); + + _animation->_gfxLibraryFilename = ""; + _animation->_soundLibraryFilename = ""; + return finished; +} + + +bool ScalpelEngine::scrollCredits() { + // Load the images for displaying credit text + Common::SeekableReadStream *stream = _res->load("credits.vgs", "title.lib"); + ImageFile creditsImages(*stream); + _screen->setPalette(creditsImages._palette); + delete stream; + + // Save a copy of the screen background for use in drawing each credit frame + _screen->_backBuffer1.blitFrom(*_screen); + + // Loop for showing the credits + for(int idx = 0; idx < 600 && !_events->kbHit() && !shouldQuit(); ++idx) { + // Copy the entire screen background before writing text + _screen->blitFrom(_screen->_backBuffer1); + + // Write the text appropriate for the next frame + if (idx < 400) + _screen->transBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0); + if (idx > 200) + _screen->transBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0); + + // Don't show credit text on the top and bottom ten rows of the screen + _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 10)); + _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 10), + Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 10, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); + + _events->delay(100); + } + + return true; +} + +bool ScalpelEngine::showOfficeCutscene() { + _music->playMusic("PROLOG4.MUS"); + _animation->_gfxLibraryFilename = "TITLE2.LIB"; + _animation->_soundLibraryFilename = "TITLE.SND"; + + bool finished = _animation->play("COFF1", 1, 3, true, 3); + if (finished) + finished = _animation->play("COFF2", 1, 0, false, 3); + if (finished) { + showLBV("note.lbv"); + + if (_sound->_voices) { + finished = _sound->playSound("NOTE1", WAIT_KBD_OR_FINISH); + if (finished) + finished = _sound->playSound("NOTE2", WAIT_KBD_OR_FINISH); + if (finished) + finished = _sound->playSound("NOTE3", WAIT_KBD_OR_FINISH); + if (finished) + finished = _sound->playSound("NOTE4", WAIT_KBD_OR_FINISH); + } else + finished = _events->delay(19000); + + if (finished) { + _events->clearEvents(); + finished = _events->delay(500); + } + } + + if (finished) + finished = _animation->play("COFF3", 1, 0, true, 3); + + if (finished) + finished = _animation->play("COFF4", 1, 0, false, 3); + + if (finished) + finished = scrollCredits(); + + if (finished) + _screen->fadeToBlack(3); + + _animation->_gfxLibraryFilename = ""; + _animation->_soundLibraryFilename = ""; + return finished; +} + +void ScalpelEngine::loadInventory() { + Inventory &inv = *_inventory; + + // Initial inventory + inv._holdings = 2; + inv.push_back(InventoryItem(0, "Message", "A message requesting help", "_ITEM03A")); + inv.push_back(InventoryItem(0, "Holmes Card", "A number of business cards", "_ITEM07A")); + + // Hidden items + inv.push_back(InventoryItem(95, "Tickets", "Opera Tickets", "_ITEM10A")); + inv.push_back(InventoryItem(138, "Cuff Link", "Cuff Link", "_ITEM04A")); + inv.push_back(InventoryItem(138, "Wire Hook", "Wire Hook", "_ITEM06A")); + inv.push_back(InventoryItem(150, "Note", "Note", "_ITEM13A")); + inv.push_back(InventoryItem(481, "Open Watch", "An open pocket watch", "_ITEM62A")); + inv.push_back(InventoryItem(481, "Paper", "A piece of paper with numbers on it", "_ITEM44A")); + inv.push_back(InventoryItem(532, "Letter", "A letter folded many times", "_ITEM68A")); + inv.push_back(InventoryItem(544, "Tarot", "Tarot Cards", "_ITEM71A")); + inv.push_back(InventoryItem(544, "Ornate Key", "An ornate key", "_ITEM70A")); + inv.push_back(InventoryItem(586, "Pawn ticket", "A pawn ticket", "_ITEM16A")); +} + +void ScalpelEngine::showLBV(const Common::String &filename) { + Common::SeekableReadStream *stream = _res->load(filename, "title.lib"); + ImageFile images(*stream); + delete stream; + + _screen->setPalette(images._palette); + _screen->_backBuffer1.blitFrom(images[0]); + _screen->verticalTransition(); +} + +void ScalpelEngine::startScene() { + if (_scene->_goToScene == OVERHEAD_MAP || _scene->_goToScene == OVERHEAD_MAP2) { + // Show the map + if (_music->_musicOn && _music->loadSong(100)) + _music->startSong(); + + _scene->_goToScene = _map->show(); + + _music->freeSong(); + _people->_hSavedPos = Common::Point(-1, -1); + _people->_hSavedFacing = -1; + } + + // Some rooms are prologue cutscenes, rather than normal game scenes. These are: + // 2: Blackwood's capture + // 52: Rescuing Anna + // 53: Moorehead's death / subway train + // 55: Fade out and exit + // 70: Brumwell suicide + switch (_scene->_goToScene) { + case BLACKWOOD_CAPTURE: + case RESCUE_ANNA: + case MOOREHEAD_DEATH: + case BRUMWELL_SUICIDE: + if (_music->_musicOn && _music->loadSong(_scene->_goToScene)) + _music->startSong(); + + switch (_scene->_goToScene) { + case BLACKWOOD_CAPTURE: + // Blackwood's capture + _res->addToCache("final2.vda", "epilogue.lib"); + _res->addToCache("final2.vdx", "epilogue.lib"); + _animation->play("final1", 1, 3, true, 4); + _animation->play("final2", 1, 0, false, 4); + break; + + case RESCUE_ANNA: + // Rescuing Anna + _res->addToCache("finalr2.vda", "epilogue.lib"); + _res->addToCache("finalr2.vdx", "epilogue.lib"); + _res->addToCache("finale1.vda", "epilogue.lib"); + _res->addToCache("finale1.vdx", "epilogue.lib"); + _res->addToCache("finale2.vda", "epilogue.lib"); + _res->addToCache("finale2.vdx", "epilogue.lib"); + _res->addToCache("finale3.vda", "epilogue.lib"); + _res->addToCache("finale3.vdx", "epilogue.lib"); + _res->addToCache("finale4.vda", "EPILOG2.lib"); + _res->addToCache("finale4.vdx", "EPILOG2.lib"); + + _animation->play("finalr1", 1, 3, true, 4); + _animation->play("finalr2", 1, 0, false, 4); + + if (!_res->isInCache("finale2.vda")) { + // Finale file isn't cached + _res->addToCache("finale2.vda", "epilogue.lib"); + _res->addToCache("finale2.vdx", "epilogue.lib"); + _res->addToCache("finale3.vda", "epilogue.lib"); + _res->addToCache("finale3.vdx", "epilogue.lib"); + _res->addToCache("finale4.vda", "EPILOG2.lib"); + _res->addToCache("finale4.vdx", "EPILOG2.lib"); + } + + _animation->play("finale1", 1, 0, false, 4); + _animation->play("finale2", 1, 0, false, 4); + _animation->play("finale3", 1, 0, false, 4); + + _useEpilogue2 = true; + _animation->play("finale4", 1, 0, false, 4); + _useEpilogue2 = false; + break; + + case MOOREHEAD_DEATH: + // Moorehead's death / subway train + _res->addToCache("SUBWAY2.vda", "epilogue.lib"); + _res->addToCache("SUBWAY2.vdx", "epilogue.lib"); + _res->addToCache("SUBWAY3.vda", "epilogue.lib"); + _res->addToCache("SUBWAY3.vdx", "epilogue.lib"); + + _animation->play("SUBWAY1", 1, 3, true, 4); + _animation->play("SUBWAY2", 1, 0, false, 4); + _animation->play("SUBWAY3", 1, 0, false, 4); + + // Set fading to direct fade temporary so the transition goes quickly. + _scene->_tempFadeStyle = _screen->_fadeStyle ? 257 : 256; + _screen->_fadeStyle = false; + break; + + case BRUMWELL_SUICIDE: + // Brumwell suicide + _animation->play("suicid", 1, 3, true, 4); + break; + default: + break; + } + + // Except for the Moorehead Murder scene, fade to black first + if (_scene->_goToScene != MOOREHEAD_DEATH) { + _events->wait(40); + _screen->fadeToBlack(3); + } + + switch (_scene->_goToScene) { + case 52: + _scene->_goToScene = LAWYER_OFFICE; // Go to the Lawyer's Office + _map->_bigPos = Common::Point(0, 0); // Overland scroll position + _map->_overPos = Common::Point(22900 - 600, 9400 + 900); // Overland position + _map->_oldCharPoint = LAWYER_OFFICE; + break; + + case 53: + _scene->_goToScene = STATION; // Go to St. Pancras Station + _map->_bigPos = Common::Point(0, 0); // Overland scroll position + _map->_overPos = Common::Point(32500 - 600, 3000 + 900); // Overland position + _map->_oldCharPoint = STATION; + break; + + default: + _scene->_goToScene = BAKER_STREET; // Back to Baker st. + _map->_bigPos = Common::Point(0, 0); // Overland scroll position + _map->_overPos = Common::Point(14500 - 600, 8400 + 900); // Overland position + _map->_oldCharPoint = BAKER_STREET; + break; + } + + // Free any song from the previous scene + _music->freeSong(); + break; + + case EXIT_GAME: + // Exit game + _screen->fadeToBlack(3); + quitGame(); + return; + + default: + break; + } + + _events->loadCursors("rmouse.vgs"); + _events->setCursor(ARROW); + + if (_scene->_goToScene == 99) { + // Darts Board minigame + _darts->playDarts(); + _mapResult = _scene->_goToScene = PUB_INTERIOR; + } + + _mapResult = _scene->_goToScene; +} + +void ScalpelEngine::eraseMirror12() { + Common::Point pt((*_people)[AL]._position.x / 100, (*_people)[AL]._position.y / 100); + + // If player is in range of the mirror, then restore background from the secondary back buffer + if (Common::Rect(70, 100, 200, 200).contains(pt)) { + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 18), + Common::Rect(137, 18, 184, 74)); + } +} + +void ScalpelEngine::doMirror12() { + People &people = *_people; + Common::Point pt((*_people)[AL]._position.x / 100, (*_people)[AL]._position.y / 100); + int frameNum = (*people[AL]._sequences)[people[AL]._sequenceNumber][people[AL]._frameNumber] + + (*people[AL]._sequences)[people[AL]._sequenceNumber][0] - 2; + + switch ((*_people)[AL]._sequenceNumber) { + case WALK_DOWN: + frameNum -= 7; + break; + case WALK_UP: + frameNum += 7; + break; + case WALK_DOWNRIGHT: + frameNum += 7; + break; + case WALK_UPRIGHT: + frameNum -= 7; + break; + case WALK_DOWNLEFT: + frameNum += 7; + break; + case WALK_UPLEFT: + frameNum -= 7; + break; + case STOP_DOWN: + frameNum -= 10; + break; + case STOP_UP: + frameNum += 11; + break; + case STOP_DOWNRIGHT: + frameNum -= 15; + break; + case STOP_DOWNLEFT: + frameNum -= 15; + break; + case STOP_UPRIGHT: + case STOP_UPLEFT: + frameNum += 15; + if (frameNum == 55) + frameNum = 54; + break; + default: + break; + } + + if (Common::Rect(80, 100, 145, 138).contains(pt)) { + // Get the frame of Sherlock to draw + ImageFrame &imageFrame = (*people[AL]._images)[frameNum]; + + // Draw the mirror image of Holmes + bool flipped = people[AL]._sequenceNumber == WALK_LEFT || people[AL]._sequenceNumber == STOP_LEFT + || people[AL]._sequenceNumber == WALK_UPRIGHT || people[AL]._sequenceNumber == STOP_UPRIGHT + || people[AL]._sequenceNumber == WALK_DOWNLEFT || people[AL]._sequenceNumber == STOP_DOWNLEFT; + _screen->_backBuffer1.transBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped); + + // Redraw the mirror borders to prevent the drawn image of Holmes from appearing outside of the mirror + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(114, 18), + Common::Rect(114, 18, 137, 114)); + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 70), + Common::Rect(137, 70, 142, 114)); + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(142, 71), + Common::Rect(142, 71, 159, 114)); + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(159, 72), + Common::Rect(159, 72, 170, 116)); + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(170, 73), + Common::Rect(170, 73, 184, 114)); + _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(184, 18), + Common::Rect(184, 18, 212, 114)); + } +} + +void ScalpelEngine::flushMirror12() { + Common::Point pt((*_people)[AL]._position.x / 100, (*_people)[AL]._position.y / 100); + + // If player is in range of the mirror, then draw the entire mirror area to the screen + if (Common::Rect(70, 100, 200, 200).contains(pt)) + _screen->slamArea(137, 18, 47, 56); +} + +} // End of namespace Scalpel + +} // End of namespace Scalpel diff --git a/engines/sherlock/scalpel/scalpel.h b/engines/sherlock/scalpel/scalpel.h new file mode 100644 index 0000000000..8743bfb7a9 --- /dev/null +++ b/engines/sherlock/scalpel/scalpel.h @@ -0,0 +1,117 @@ +/* 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. + * + */ + +#ifndef SHERLOCK_SCALPEL_H +#define SHERLOCK_SCALPEL_H + +#include "sherlock/sherlock.h" +#include "sherlock/scalpel/darts.h" + +namespace Sherlock { + +namespace Scalpel { + +enum { BLACKWOOD_CAPTURE = 2, BAKER_STREET = 4, DRAWING_ROOM = 12, STATION = 17, PUB_INTERIOR = 19, + LAWYER_OFFICE = 27, BAKER_ST_EXTERIOR = 39, RESCUE_ANNA = 52, MOOREHEAD_DEATH = 53, EXIT_GAME = 55, + BRUMWELL_SUICIDE = 70, OVERHEAD_MAP2 = 98, DARTS_GAME = 99, OVERHEAD_MAP = 100 }; + +class ScalpelEngine : public SherlockEngine { +private: + Darts *_darts; + int _mapResult; + + /** + * Show the starting city cutscene which shows the game title + */ + bool showCityCutscene(); + + /** + * Show the back alley where the initial murder takes place + */ + bool showAlleyCutscene(); + + /** + * Show the Baker Street outside cutscene + */ + bool showStreetCutscene(); + + /** + * Show Holmes and Watson at the breakfast table, lestrade's note, and then the scrolling credits + */ + bool showOfficeCutscene(); + + /** + * Show the game credits + */ + bool scrollCredits(); + + /** + * Load the default inventory for the game, which includes both the initial active inventory, + * as well as special pending inventory items which can appear automatically in the player's + * inventory once given required flags are set + */ + void loadInventory(); + + /** + * Transition to show an image + */ + void showLBV(const Common::String &filename); +protected: + /** + * Game initialization + */ + virtual void initialize(); + + /** + * Show the opening sequence + */ + virtual void showOpening(); + + /** + * Starting a scene within the game + */ + virtual void startScene(); +public: + ScalpelEngine(OSystem *syst, const SherlockGameDescription *gameDesc); + virtual ~ScalpelEngine(); + + /** + * Takes care of clearing the mirror in scene 12 (mansion drawing room), in case anything drew over it + */ + void eraseMirror12(); + + /** + * Takes care of drawing Holme's reflection onto the mirror in scene 12 (mansion drawing room) + */ + void doMirror12(); + + /** + * This clears the mirror in scene 12 (mansion drawing room) in case anything messed draw over it + */ + void flushMirror12(); +}; + +} // End of namespace Scalpel + +} // End of namespace Sherlock + +#endif |