Colin Snover 432fd522d2 ENGINES: Remove default1x scaler flag
This flag is removed for a few reasons:

* Engines universally set this flag to true for widths > 320,
  which made it redundant everywhere;
* This flag functioned primarily as a "force 1x scaler" flag,
  since its behaviour was almost completely undocumented and users
  would need to figure out that they'd need an explicit non-default
  scaler set to get a scaler to operate at widths > 320;
* (Most importantly) engines should not be in the business of
  deciding how the backend may choose to render its virtual screen.
  The choice of rendering behaviour belongs to the user, and the
  backend, in that order.

A nearby future commit restores the default1x scaler behaviour in
the SDL backend code for the moment, but in the future it is my
hope that there will be a better configuration UI to allow users
to specify how they want scaling to work for high resolutions.
2017-10-07 12:30:29 -05:00

1427 lines
46 KiB
C++

/* 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 "engines/util.h"
#include "gui/saveload.h"
#include "common/translation.h"
#include "sherlock/scalpel/scalpel.h"
#include "sherlock/scalpel/scalpel_fixed_text.h"
#include "sherlock/scalpel/scalpel_map.h"
#include "sherlock/scalpel/scalpel_people.h"
#include "sherlock/scalpel/scalpel_scene.h"
#include "sherlock/scalpel/scalpel_screen.h"
#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
#include "sherlock/scalpel/tsage/logo.h"
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
#include "sherlock/animation.h"
#include "sherlock/scalpel/3do/movie_decoder.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
struct PeopleData {
const char *portrait;
int fixedTextId;
byte stillSequences[MAX_TALK_SEQUENCES];
byte talkSequences[MAX_TALK_SEQUENCES];
};
const PeopleData PEOPLE_DATA[MAX_PEOPLE] = {
{ "HOLM", kFixedText_People_SherlockHolmes, { 1, 0, 0 }, { 1, 0, 0 } },
{ "WATS", kFixedText_People_DrWatson, { 6, 0, 0 }, { 5, 5, 6, 7, 8, 7, 8, 6, 0, 0 } },
{ "LEST", kFixedText_People_InspectorLestrade, { 4, 0, 0 }, { 2, 0, 0 } },
{ "CON1", kFixedText_People_ConstableOBrien, { 2, 0, 0 }, { 1, 0, 0 } },
{ "CON2", kFixedText_People_ConstableLewis, { 2, 0, 0 }, { 1, 0, 0 } },
{ "SHEI", kFixedText_People_SheilaParker, { 2, 0, 0 }, { 2, 3, 0, 0 } },
{ "HENR", kFixedText_People_HenryCarruthers, { 3, 0, 0 }, { 3, 0, 0 } },
{ "LESL", kFixedText_People_Lesley, { 9, 0, 0 }, { 1, 2, 3, 2, 1, 2, 3, 0, 0 } },
{ "USH1", kFixedText_People_AnUsher, { 13, 0, 0 }, { 13, 14, 0, 0 } },
{ "USH2", kFixedText_People_AnUsher, { 2, 0, 0 }, { 2, 0, 0 } },
{ "FRED", kFixedText_People_FredrickEpstein, { 4, 0, 0 }, { 1, 2, 3, 4, 3, 4, 3, 2, 0, 0 } },
{ "WORT", kFixedText_People_MrsWorthington, { 9, 0, 0 }, { 8, 0, 0 } },
{ "COAC", kFixedText_People_TheCoach, { 2, 0, 0 }, { 1, 2, 3, 4, 5, 4, 3, 2, 0, 0 } },
{ "PLAY", kFixedText_People_APlayer, { 8, 0, 0 }, { 7, 8, 0, 0 } },
{ "WBOY", kFixedText_People_Tim, { 13, 0, 0 }, { 12, 13, 0, 0 } },
{ "JAME", kFixedText_People_JamesSanders, { 6, 0, 0 }, { 3, 4, 0, 0 } },
{ "BELL", kFixedText_People_Belle, { 1, 0, 0 }, { 4, 5, 0, 0 } },
{ "GIRL", kFixedText_People_CleaningGirl, { 20, 0, 0 }, { 14, 15, 16, 17, 18, 19, 20, 20, 20, 0, 0 } },
{ "EPST", kFixedText_People_FredrickEpstein, { 17, 0, 0 }, { 16, 17, 18, 18, 18, 17, 17, 0, 0 } },
{ "WIGG", kFixedText_People_Wiggins, { 3, 0, 0 }, { 2, 3, 0, 0 } },
{ "PAUL", kFixedText_People_Paul, { 2, 0, 0 }, { 1, 2, 0, 0 } },
{ "BART", kFixedText_People_TheBartender, { 1, 0, 0 }, { 1, 0, 0 } },
{ "DIRT", kFixedText_People_ADirtyDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
{ "SHOU", kFixedText_People_AShoutingDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
{ "STAG", kFixedText_People_AStaggeringDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
{ "BOUN", kFixedText_People_TheBouncer, { 1, 0, 0 }, { 1, 0, 0 } },
{ "SAND", kFixedText_People_JamesSanders, { 6, 0, 0 }, { 5, 6, 0, 0 } },
{ "CORO", kFixedText_People_TheCoroner, { 6, 0, 0 }, { 4, 5, 0, 0 } },
{ "EQUE", kFixedText_People_ReginaldSnipes, { 1, 0, 0 }, { 1, 0, 0 } },
{ "GEOR", kFixedText_People_GeorgeBlackwood, { 1, 0, 0 }, { 1, 0, 0 } },
{ "LARS", kFixedText_People_Lars, { 7, 0, 0 }, { 5, 6, 0, 0 } },
{ "PARK", kFixedText_People_SheilaParker, { 1, 0, 0 }, { 1, 0, 0 } },
{ "CHEM", kFixedText_People_TheChemist, { 8, 0, 0 }, { 8, 9, 0, 0 } },
{ "GREG", kFixedText_People_InspectorGregson, { 6, 0, 0 }, { 5, 6, 0, 0 } },
{ "LAWY", kFixedText_People_JacobFarthington, { 1, 0, 0 }, { 1, 0, 0 } },
{ "MYCR", kFixedText_People_Mycroft, { 1, 0, 0 }, { 1, 0, 0 } },
{ "SHER", kFixedText_People_OldSherman, { 7, 0, 0 }, { 7, 8, 0, 0 } },
{ "CHMB", kFixedText_People_Richard, { 1, 0, 0 }, { 1, 0, 0 } },
{ "BARM", kFixedText_People_TheBarman, { 1, 0, 0 }, { 1, 0, 0 } },
{ "DAND", kFixedText_People_ADandyPlayer, { 1, 0, 0 }, { 1, 0, 0 } },
{ "ROUG", kFixedText_People_ARoughlookingPlayer, { 1, 0, 0 }, { 1, 0, 0 } },
{ "SPEC", kFixedText_People_ASpectator, { 1, 0, 0 }, { 1, 0, 0 } },
{ "HUNT", kFixedText_People_RobertHunt, { 1, 0, 0 }, { 1, 0, 0 } },
{ "VIOL", kFixedText_People_Violet, { 3, 0, 0 }, { 3, 4, 0, 0 } },
{ "PETT", kFixedText_People_Pettigrew, { 1, 0, 0 }, { 1, 0, 0 } },
{ "APPL", kFixedText_People_Augie, { 8, 0, 0 }, { 14, 15, 0, 0 } },
{ "ANNA", kFixedText_People_AnnaCarroway, { 16, 0, 0 }, { 3, 4, 5, 6, 0, 0 } },
{ "GUAR", kFixedText_People_AGuard, { 1, 0, 0 }, { 4, 5, 6, 0, 0 } },
{ "ANTO", kFixedText_People_AntonioCaruso, { 8, 0, 0 }, { 7, 8, 0, 0 } },
{ "TOBY", kFixedText_People_TobyTheDog, { 1, 0, 0 }, { 1, 0, 0 } },
{ "KING", kFixedText_People_SimonKingsley, { 13, 0, 0 }, { 13, 14, 0, 0 } },
{ "ALFR", kFixedText_People_Alfred, { 2, 0, 0 }, { 2, 3, 0, 0 } },
{ "LADY", kFixedText_People_LadyBrumwell, { 1, 0, 0 }, { 3, 4, 0, 0 } },
{ "ROSA", kFixedText_People_MadameRosa, { 1, 0, 0 }, { 1, 30, 0, 0 } },
{ "LADB", kFixedText_People_LadyBrumwell, { 1, 0, 0 }, { 3, 4, 0, 0 } },
{ "MOOR", kFixedText_People_JosephMoorehead, { 1, 0, 0 }, { 1, 0, 0 } },
{ "BEAL", kFixedText_People_MrsBeale, { 5, 0, 0 }, { 14, 15, 16, 17, 18, 19, 20, 0, 0 } },
{ "LION", kFixedText_People_Felix, { 1, 0, 0 }, { 1, 0, 0 } },
{ "HOLL", kFixedText_People_Hollingston, { 1, 0, 0 }, { 1, 0, 0 } },
{ "CALL", kFixedText_People_ConstableCallaghan, { 1, 0, 0 }, { 1, 0, 0 } },
{ "JERE", kFixedText_People_SergeantDuncan, { 2, 0, 0 }, { 1, 1, 2, 2, 0, 0 } },
{ "LORD", kFixedText_People_LordBrumwell, { 1, 0, 0 }, { 9, 10, 0, 0 } },
{ "NIGE", kFixedText_People_NigelJaimeson, { 1, 0, 0 }, { 1, 2, 0, 138, 3, 4, 0, 138, 0, 0 } },
{ "JONA", kFixedText_People_Jonas, { 1, 0, 0 }, { 1, 8, 0, 0 } },
{ "DUGA", kFixedText_People_ConstableDugan, { 1, 0, 0 }, { 1, 0, 0 } },
{ "INSP", kFixedText_People_InspectorLestrade, { 4, 0, 0 }, { 2, 0, 0 } }
};
uint INFO_BLACK;
uint BORDER_COLOR;
uint COMMAND_BACKGROUND;
uint BUTTON_BACKGROUND;
uint TALK_FOREGROUND;
uint TALK_NULL;
uint BUTTON_TOP;
uint BUTTON_MIDDLE;
uint BUTTON_BOTTOM;
uint COMMAND_FOREGROUND;
uint COMMAND_HIGHLIGHTED;
uint COMMAND_NULL;
uint INFO_FOREGROUND;
uint INFO_BACKGROUND;
uint INV_FOREGROUND;
uint INV_BACKGROUND;
uint PEN_COLOR;
/*----------------------------------------------------------------*/
#define FROM_RGB(r, g, b) pixelFormatRGB565.RGBToColor(r, g, b)
ScalpelEngine::ScalpelEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
SherlockEngine(syst, gameDesc) {
_darts = nullptr;
_mapResult = 0;
if (getPlatform() == Common::kPlatform3DO) {
const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
INFO_BLACK = FROM_RGB(0, 0, 0);
BORDER_COLOR = FROM_RGB(0x6d, 0x38, 0x10);
COMMAND_BACKGROUND = FROM_RGB(0x38, 0x38, 0xce);
BUTTON_BACKGROUND = FROM_RGB(0x95, 0x5d, 0x24);
TALK_FOREGROUND = FROM_RGB(0xff, 0x55, 0x55);
TALK_NULL = FROM_RGB(0xce, 0xc6, 0xc2);
BUTTON_TOP = FROM_RGB(0xbe, 0x85, 0x3c);
BUTTON_MIDDLE = FROM_RGB(0x9d, 0x40, 0);
BUTTON_BOTTOM = FROM_RGB(0x69, 0x24, 0);
COMMAND_FOREGROUND = FROM_RGB(0xFF, 0xFF, 0xFF);
COMMAND_HIGHLIGHTED = FROM_RGB(0x55, 0xff, 0x55);
COMMAND_NULL = FROM_RGB(0x69, 0x24, 0);
INFO_FOREGROUND = FROM_RGB(0x55, 0xff, 0xff);
INFO_BACKGROUND = FROM_RGB(0, 0, 0x48);
INV_FOREGROUND = FROM_RGB(0xff, 0xff, 0x55);
INV_BACKGROUND = FROM_RGB(0, 0, 0x48);
PEN_COLOR = FROM_RGB(0x50, 0x18, 0);
} else {
INFO_BLACK = 1;
BORDER_COLOR = 237;
COMMAND_BACKGROUND = 4;
BUTTON_BACKGROUND = 235;
TALK_FOREGROUND = 12;
TALK_NULL = 16;
BUTTON_TOP = 233;
BUTTON_MIDDLE = 244;
BUTTON_BOTTOM = 248;
COMMAND_FOREGROUND = 15;
COMMAND_HIGHLIGHTED = 10;
COMMAND_NULL = 248;
INFO_FOREGROUND = 11;
INFO_BACKGROUND = 1;
INV_FOREGROUND = 14;
INV_BACKGROUND = 1;
PEN_COLOR = 250;
}
}
ScalpelEngine::~ScalpelEngine() {
delete _darts;
}
void ScalpelEngine::setupGraphics() {
if (getPlatform() != Common::kPlatform3DO) {
// 320x200 palettized
initGraphics(320, 200);
} else {
// 3DO actually uses RGB555, but some platforms of ours only support RGB565, so we use that
const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
// First try for a 640x400 mode
g_system->beginGFXTransaction();
initCommonGFX();
g_system->initSize(640, 400, &pixelFormatRGB565);
OSystem::TransactionError gfxError = g_system->endGFXTransaction();
if (gfxError == OSystem::kTransactionSuccess) {
_isScreenDoubled = true;
} else {
// System doesn't support it, so fall back on 320x200 mode
initGraphics(320, 200, &pixelFormatRGB565);
}
}
}
void ScalpelEngine::initialize() {
// Setup graphics mode
setupGraphics();
// Let the base engine intialize
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
ScalpelMap &map = *(ScalpelMap *)_map;
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
ScalpelFixedText &fixedText = *(ScalpelFixedText *)_fixedText;
const char *peopleNamePtr = nullptr;
for (int idx = 0; idx < MAX_PEOPLE; ++idx) {
peopleNamePtr = fixedText.getText(PEOPLE_DATA[idx].fixedTextId);
_people->_characters.push_back(PersonData(peopleNamePtr, PEOPLE_DATA[idx].portrait,
PEOPLE_DATA[idx].stillSequences, PEOPLE_DATA[idx].talkSequences));
}
_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() {
bool finished = true;
if (isDemo() && _interactiveFl)
return;
_events->setFrameRate(60);
if (getPlatform() == Common::kPlatform3DO) {
show3DOSplash();
finished = showCityCutscene3DO();
if (finished)
finished = showAlleyCutscene3DO();
if (finished)
finished = showStreetCutscene3DO();
if (finished)
showOfficeCutscene3DO();
_events->clearEvents();
_music->stopMusic();
} else {
TsAGE::Logo::show(this);
finished = showCityCutscene();
if (finished)
finished = showAlleyCutscene();
if (finished)
finished = showStreetCutscene();
if (finished)
showOfficeCutscene();
_events->clearEvents();
_music->stopMusic();
}
_events->setFrameRate(GAME_FRAME_RATE);
}
bool ScalpelEngine::showCityCutscene() {
byte greyPalette[PALETTE_SIZE];
byte palette[PALETTE_SIZE];
// Demo fades from black into grey and then fades from grey into the scene
Common::fill(&greyPalette[0], &greyPalette[PALETTE_SIZE], 142);
_screen->fadeIn((const byte *)greyPalette, 3);
_music->loadSong("prolog1");
_animation->_gfxLibraryFilename = "title.lib";
_animation->_soundLibraryFilename = "title.snd";
bool finished = _animation->play("26open1", true, 1, 255, true, 2);
if (finished) {
ImageFile titleImages_LondonNovember("title2.vgs", true);
_screen->_backBuffer1.SHblitFrom(*_screen);
_screen->_backBuffer2.SHblitFrom(*_screen);
Common::Point londonPosition;
if ((titleImages_LondonNovember[0]._width == 302) && (titleImages_LondonNovember[0]._height == 39)) {
// Spanish
londonPosition = Common::Point(9, 8);
} else {
// English (German uses the same English graphics), width 272, height 37
// In the German version this is placed differently, check against German floppy version TODO
londonPosition = Common::Point(30, 50);
}
// London, England
_screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[0], londonPosition);
_screen->randomTransition();
finished = _events->delay(1000, true);
// November, 1888
if (finished) {
_screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100));
_screen->randomTransition();
finished = _events->delay(5000, true);
}
// Transition out the title
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
_screen->randomTransition();
}
if (finished)
finished = _animation->play("26open2", true, 1, 0, false, 2);
if (finished) {
ImageFile titleImages_SherlockHolmesTitle("title.vgs", true);
_screen->_backBuffer1.SHblitFrom(*_screen);
_screen->_backBuffer2.SHblitFrom(*_screen);
Common::Point lostFilesPosition;
Common::Point sherlockHolmesPosition;
Common::Point copyrightPosition;
if ((titleImages_SherlockHolmesTitle[0]._width == 306) && (titleImages_SherlockHolmesTitle[0]._height == 39)) {
// Spanish
lostFilesPosition = Common::Point(5, 5);
sherlockHolmesPosition = Common::Point(24, 40);
copyrightPosition = Common::Point(3, 190);
} else {
// English (German uses the same English graphics), width 208, height 39
lostFilesPosition = Common::Point(75, 6);
sherlockHolmesPosition = Common::Point(34, 21);
copyrightPosition = Common::Point(4, 190);
}
// The Lost Files of
_screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition);
// Sherlock Holmes
_screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition);
// copyright
_screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition);
_screen->verticalTransition();
finished = _events->delay(4000, true);
if (finished) {
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
_screen->randomTransition();
finished = _events->delay(2000);
}
if (finished) {
_screen->getPalette(palette);
_screen->fadeToBlack(2);
}
if (finished) {
// In the alley...
Common::Point alleyPosition;
if ((titleImages_SherlockHolmesTitle[3]._width == 105) && (titleImages_SherlockHolmesTitle[3]._height == 16)) {
// German
alleyPosition = Common::Point(72, 50);
} else if ((titleImages_SherlockHolmesTitle[3]._width == 166) && (titleImages_SherlockHolmesTitle[3]._height == 36)) {
// Spanish
alleyPosition = Common::Point(71, 50);
} else {
// English, width 175, height 38
alleyPosition = Common::Point(72, 51);
}
_screen->SHtransBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition);
_screen->fadeIn(palette, 3);
// Wait until the track got looped and the first few notes were played
finished = _music->waitUntilMSec(4300, 21300, 0, 2500); // ticks 0x104 / ticks 0x500
}
}
_animation->_gfxLibraryFilename = "";
_animation->_soundLibraryFilename = "";
return finished;
}
bool ScalpelEngine::showAlleyCutscene() {
byte palette[PALETTE_SIZE];
_music->loadSong("prolog2");
_animation->_gfxLibraryFilename = "TITLE.LIB";
_animation->_soundLibraryFilename = "TITLE.SND";
// Fade "In The Alley..." text to black
_screen->fadeToBlack(2);
bool finished = _animation->play("27PRO1", true, 1, 3, true, 2);
if (finished) {
_screen->getPalette(palette);
_screen->fadeToBlack(2);
// wait until second lower main note
finished = _music->waitUntilMSec(26800, 0xFFFFFFFF, 0, 1000); // ticks 0x64A
}
if (finished) {
_screen->setPalette(palette);
finished = _animation->play("27PRO2", true, 1, 0, false, 2);
}
if (finished) {
showLBV("scream.lbv");
// wait until first "scream" in music happened
finished = _music->waitUntilMSec(45800, 0xFFFFFFFF, 0, 6000); // ticks 0xABE
}
if (finished) {
// quick fade out
_screen->fadeToBlack(1);
// wait until after third "scream" in music happened
finished = _music->waitUntilMSec(49000, 0xFFFFFFFF, 0, 2000); // ticks 0xB80
}
if (finished)
finished = _animation->play("27PRO3", true, 1, 0, true, 2);
if (finished) {
_screen->getPalette(palette);
_screen->fadeToBlack(2);
}
if (finished) {
ImageFile titleImages_EarlyTheFollowingMorning("title3.vgs", true);
// "Early the following morning on Baker Street..."
Common::Point earlyTheFollowingMorningPosition;
if ((titleImages_EarlyTheFollowingMorning[0]._width == 164) && (titleImages_EarlyTheFollowingMorning[0]._height == 19)) {
// German
earlyTheFollowingMorningPosition = Common::Point(35, 50);
} else if ((titleImages_EarlyTheFollowingMorning[0]._width == 171) && (titleImages_EarlyTheFollowingMorning[0]._height == 32)) {
// Spanish
earlyTheFollowingMorningPosition = Common::Point(35, 50);
} else {
// English, width 218, height 31
earlyTheFollowingMorningPosition = Common::Point(35, 52);
}
_screen->SHtransBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition);
// fast fade-in
_screen->fadeIn(palette, 1);
// wait for music to end and wait an additional 2.5 seconds
finished = _music->waitUntilMSec(0xFFFFFFFF, 0xFFFFFFFF, 2500, 3000);
}
_animation->_gfxLibraryFilename = "";
_animation->_soundLibraryFilename = "";
return finished;
}
bool ScalpelEngine::showStreetCutscene() {
_animation->_gfxLibraryFilename = "TITLE.LIB";
_animation->_soundLibraryFilename = "TITLE.SND";
_music->loadSong("prolog3");
// wait a bit
bool finished = _events->delay(500);
if (finished) {
// fade out "Early the following morning..."
_screen->fadeToBlack(2);
// wait for music a bit
finished = _music->waitUntilMSec(3800, 0xFFFFFFFF, 0, 1000); // ticks 0xE4
}
if (finished)
finished = _animation->play("14KICK", true, 1, 3, true, 2);
// Constable animation plays slower than speed 2
// If we play it with speed 2, music gets obviously out of sync
if (finished)
finished = _animation->play("14NOTE", true, 1, 0, false, 3);
// Fade to black
if (finished)
_screen->fadeToBlack(1);
_animation->_gfxLibraryFilename = "";
_animation->_soundLibraryFilename = "";
return finished;
}
bool ScalpelEngine::showOfficeCutscene() {
_music->loadSong("prolog4");
_animation->_gfxLibraryFilename = "TITLE2.LIB";
_animation->_soundLibraryFilename = "TITLE.SND";
bool finished = _animation->play("COFF1", true, 1, 3, true, 3);
if (finished)
finished = _animation->play("COFF2", true, 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", true, 1, 0, true, 3);
if (finished)
finished = _animation->play("COFF4", true, 1, 0, false, 3);
if (finished)
finished = scrollCredits();
if (finished)
_screen->fadeToBlack(3);
_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);
// Demo fades slowly from the scene into credits palette
_screen->fadeIn(creditsImages._palette, 3);
delete stream;
// Save a copy of the screen background for use in drawing each credit frame
_screen->_backBuffer1.SHblitFrom(*_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->SHblitFrom(_screen->_backBuffer1);
// Write the text appropriate for the next frame
if (idx < 400)
_screen->SHtransBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0);
if (idx > 200)
_screen->SHtransBlitFrom(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->SHblitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->width(), 10));
_screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, _screen->height() - 10),
Common::Rect(0, _screen->height() - 10, _screen->width(), _screen->height()));
_events->delay(100);
}
return true;
}
// 3DO variant
bool ScalpelEngine::show3DOSplash() {
// 3DO EA Splash screen
ImageFile3DO titleImage_3DOSplash("3DOSplash.cel", kImageFile3DOType_Cel);
_screen->SHtransBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20));
bool finished = _events->delay(3000, true);
if (finished) {
_screen->clear();
finished = _events->delay(500, true);
}
if (finished) {
// EA logo movie
play3doMovie("EAlogo.stream", Common::Point(20, 0));
}
// Always clear screen
_screen->clear();
return finished;
}
bool ScalpelEngine::showCityCutscene3DO() {
Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
_animation->_soundLibraryFilename = "TITLE.SND";
screen.clear();
bool finished = _events->delay(2500, true);
if (finished) {
finished = _events->delay(2500, true);
// Play intro music
_music->loadSong("prolog");
// Loop rain.aiff until the Sherlock logo fades away.
// TODO: The volume is just a guess.
_sound->playAiff("prologue/sounds/rain.aiff", 15, true);
// Fade screen to grey
screen._backBuffer1.clear(0xCE59); // RGB565: 25, 50, 25 (grey)
screen.fadeIntoScreen3DO(2);
}
if (finished) {
finished = _music->waitUntilMSec(3400, 0, 0, 3400);
}
if (finished) {
screen._backBuffer1.clear(0); // fill backbuffer with black to avoid issues during fade from white
finished = _animation->play3DO("26open1", true, 1, true, 2);
}
if (finished) {
screen._backBuffer2.SHblitFrom(screen._backBuffer1);
// "London, England"
ImageFile3DO titleImage_London("title2a.cel", kImageFile3DOType_Cel);
screen._backBuffer1.SHtransBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
screen.fadeIntoScreen3DO(1);
finished = _events->delay(1500, true);
if (finished) {
// "November, 1888"
ImageFile3DO titleImage_November("title2b.cel", kImageFile3DOType_Cel);
screen._backBuffer1.SHtransBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
screen.fadeIntoScreen3DO(1);
finished = _music->waitUntilMSec(14700, 0, 0, 5000);
}
if (finished) {
// Restore screen
_screen->_backBuffer1.SHblitFrom(screen._backBuffer2);
_screen->SHblitFrom(screen._backBuffer1);
}
}
if (finished)
finished = _animation->play3DO("26open2", true, 1, false, 2);
if (finished) {
// "Sherlock Holmes" (title)
ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel", kImageFile3DOType_Cel);
screen._backBuffer1.SHtransBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
// Blend in
screen.fadeIntoScreen3DO(2);
finished = _events->delay(500, true);
// Title should fade in, Copyright should be displayed a bit after that
if (finished) {
ImageFile3DO titleImage_Copyright("title1c.cel", kImageFile3DOType_Cel);
screen.SHtransBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190));
finished = _events->delay(3500, true);
}
}
if (finished)
finished = _music->waitUntilMSec(33600, 0, 0, 2000);
_sound->stopAiff();
if (finished) {
// Fade to black
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(3);
}
if (finished) {
// "In the alley behind the Regency Theatre..."
ImageFile3DO titleImage_InTheAlley("title1d.cel", kImageFile3DOType_Cel);
screen._backBuffer1.SHtransBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
// Fade in
screen.fadeIntoScreen3DO(4);
finished = _music->waitUntilMSec(39900, 0, 0, 2500);
// Fade out
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(4);
}
return finished;
}
bool ScalpelEngine::showAlleyCutscene3DO() {
Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
bool finished = _music->waitUntilMSec(43500, 0, 0, 1000);
if (finished)
finished = _animation->play3DO("27PRO1", true, 1, false, 2);
if (finished) {
// Fade out...
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(3);
finished = _music->waitUntilMSec(67100, 0, 0, 1000); // 66700
}
if (finished)
finished = _animation->play3DO("27PRO2", true, 1, false, 2);
if (finished)
finished = _music->waitUntilMSec(76000, 0, 0, 1000);
if (finished) {
// Show screaming victim
ImageFile3DO titleImage_ScreamingVictim("scream.cel", kImageFile3DOType_Cel);
screen.clear();
screen.SHtransBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
// Play "scream.aiff"
if (_sound->_voices)
_sound->playSound("prologue/sounds/scream.aiff", WAIT_RETURN_IMMEDIATELY, 100);
finished = _music->waitUntilMSec(81600, 0, 0, 6000);
}
if (finished) {
// Fade out
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(5);
finished = _music->waitUntilMSec(84400, 0, 0, 2000);
}
if (finished)
finished = _animation->play3DO("27PRO3", true, 1, false, 2);
if (finished) {
// Fade out
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(5);
}
if (finished) {
// "Early the following morning on Baker Street..."
ImageFile3DO titleImage_EarlyTheFollowingMorning("title3.cel", kImageFile3DOType_Cel);
screen._backBuffer1.SHtransBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
// Fade in
screen.fadeIntoScreen3DO(4);
finished = _music->waitUntilMSec(96700, 0, 0, 3000);
}
return finished;
}
bool ScalpelEngine::showStreetCutscene3DO() {
Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
bool finished = true;
if (finished) {
// fade out "Early the following morning..."
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(4);
// wait for music a bit
finished = _music->waitUntilMSec(100300, 0, 0, 1000);
}
if (finished)
finished = _animation->play3DO("14KICK", true, 1, false, 2);
// note: part of the constable is sticking to the door during the following
// animation, when he walks away. This is a bug of course, but it actually happened on 3DO!
// I'm not sure if it happens because the door is pure black (0, 0, 0) and it's because
// of transparency - or if the animation itself is bad. We will definitely have to adjust
// the animation data to fix it.
if (finished)
finished = _animation->play3DO("14NOTE", true, 1, false, 3);
if (finished) {
// Fade out
screen._backBuffer1.clear();
screen.fadeIntoScreen3DO(4);
}
return finished;
}
bool ScalpelEngine::showOfficeCutscene3DO() {
bool finished = _music->waitUntilMSec(151000, 0, 0, 1000);
if (finished)
finished = _animation->play3DO("COFF1", true, 1, false, 3);
if (finished)
finished = _animation->play3DO("COFF2", true, 1, false, 3);
if (finished)
finished = _music->waitUntilMSec(182400, 0, 0, 1000);
if (finished) {
// Show the note
ImageFile3DO titleImage_CoffeeNote("note.cel", kImageFile3DOType_Cel);
_screen->clear();
_screen->SHtransBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0));
if (_sound->_voices) {
finished = _sound->playSound("prologue/sounds/note.aiff", WAIT_KBD_OR_FINISH);
} else
finished = _events->delay(19000);
if (finished)
finished = _music->waitUntilMSec(218800, 0, 0, 1000);
// Fade out
_screen->clear();
}
if (finished)
finished = _music->waitUntilMSec(222200, 0, 0, 1000);
if (finished)
finished = _animation->play3DO("COFF3", true, 1, false, 3);
if (finished)
finished = _animation->play3DO("COFF4", true, 1, false, 3);
if (finished) {
finished = _music->waitUntilMSec(244500, 0, 0, 2000);
// TODO: Brighten the image, possibly by doing a partial fade
// to white.
_screen->_backBuffer2.SHblitFrom(_screen->_backBuffer1);
for (int nr = 1; finished && nr <= 4; nr++) {
char filename[15];
sprintf(filename, "credits%d.cel", nr);
ImageFile3DO *creditsImage = new ImageFile3DO(filename, kImageFile3DOType_Cel);
ImageFrame *creditsFrame = &(*creditsImage)[0];
for (int i = 0; finished && i < 200 + creditsFrame->_height; i++) {
_screen->SHblitFrom(_screen->_backBuffer2);
_screen->SHtransBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i));
if (!_events->delay(70, true))
finished = false;
}
delete creditsImage;
}
}
return finished;
}
void ScalpelEngine::loadInventory() {
ScalpelFixedText &fixedText = *(ScalpelFixedText *)_fixedText;
Inventory &inv = *_inventory;
Common::String fixedText_Message = fixedText.getText(kFixedText_InitInventory_Message);
Common::String fixedText_HolmesCard = fixedText.getText(kFixedText_InitInventory_HolmesCard);
Common::String fixedText_Tickets = fixedText.getText(kFixedText_InitInventory_Tickets);
Common::String fixedText_CuffLink = fixedText.getText(kFixedText_InitInventory_CuffLink);
Common::String fixedText_WireHook = fixedText.getText(kFixedText_InitInventory_WireHook);
Common::String fixedText_Note = fixedText.getText(kFixedText_InitInventory_Note);
Common::String fixedText_OpenWatch = fixedText.getText(kFixedText_InitInventory_OpenWatch);
Common::String fixedText_Paper = fixedText.getText(kFixedText_InitInventory_Paper);
Common::String fixedText_Letter = fixedText.getText(kFixedText_InitInventory_Letter);
Common::String fixedText_Tarot = fixedText.getText(kFixedText_InitInventory_Tarot);
Common::String fixedText_OrnateKey = fixedText.getText(kFixedText_InitInventory_OrnateKey);
Common::String fixedText_PawnTicket = fixedText.getText(kFixedText_InitInventory_PawnTicket);
// Initial inventory
inv._holdings = 2;
inv.push_back(InventoryItem(0, "Message", fixedText_Message, "_ITEM03A"));
inv.push_back(InventoryItem(0, "Holmes Card", fixedText_HolmesCard, "_ITEM07A"));
// Hidden items
inv.push_back(InventoryItem(95, "Tickets", fixedText_Tickets, "_ITEM10A"));
inv.push_back(InventoryItem(138, "Cuff Link", fixedText_CuffLink, "_ITEM04A"));
inv.push_back(InventoryItem(138, "Wire Hook", fixedText_WireHook, "_ITEM06A"));
inv.push_back(InventoryItem(150, "Note", fixedText_Note, "_ITEM13A"));
inv.push_back(InventoryItem(481, "Open Watch", fixedText_OpenWatch, "_ITEM62A"));
inv.push_back(InventoryItem(481, "Paper", fixedText_Paper, "_ITEM44A"));
inv.push_back(InventoryItem(532, "Letter", fixedText_Letter, "_ITEM68A"));
inv.push_back(InventoryItem(544, "Tarot", fixedText_Tarot, "_ITEM71A"));
inv.push_back(InventoryItem(544, "Ornate Key", fixedText_OrnateKey, "_ITEM70A"));
inv.push_back(InventoryItem(586, "Pawn ticket", fixedText_PawnTicket, "_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.SHblitFrom(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->_savedPos = Common::Point(-1, -1);
_people->_savedPos._facing = -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", false, 1, 3, true, 4);
_animation->play("final2", false, 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", false, 1, 3, true, 4);
_animation->play("finalr2", false, 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", false, 1, 0, false, 4);
_animation->play("finale2", false, 1, 0, false, 4);
_animation->play("finale3", false, 1, 0, false, 4);
_useEpilogue2 = true;
_animation->play("finale4", false, 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", false, 1, 3, true, 4);
_animation->play("SUBWAY2", false, 1, 0, false, 4);
_animation->play("SUBWAY3", false, 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", false, 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->setCursor(ARROW);
if (_scene->_goToScene == 99) {
// Darts Board minigame
_darts->playDarts();
_mapResult = _scene->_goToScene = PUB_INTERIOR;
}
_mapResult = _scene->_goToScene;
}
void ScalpelEngine::eraseBrumwellMirror() {
Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
// 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.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 18),
Common::Rect(137, 18, 184, 74));
}
}
void ScalpelEngine::doBrumwellMirror() {
People &people = *_people;
Person &player = people[HOLMES];
Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
int frameNum = player._walkSequences[player._sequenceNumber][player._frameNumber] +
player._walkSequences[player._sequenceNumber][0] - 2;
switch ((*_people)[HOLMES]._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[HOLMES]._images)[frameNum];
// Draw the mirror image of Holmes
bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT
|| people[HOLMES]._sequenceNumber == WALK_UPRIGHT || people[HOLMES]._sequenceNumber == STOP_UPRIGHT
|| people[HOLMES]._sequenceNumber == WALK_DOWNLEFT || people[HOLMES]._sequenceNumber == STOP_DOWNLEFT;
_screen->_backBuffer1.SHtransBlitFrom(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.SHblitFrom(_screen->_backBuffer2, Common::Point(114, 18),
Common::Rect(114, 18, 137, 114));
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 70),
Common::Rect(137, 70, 142, 114));
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(142, 71),
Common::Rect(142, 71, 159, 114));
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(159, 72),
Common::Rect(159, 72, 170, 116));
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(170, 73),
Common::Rect(170, 73, 184, 114));
_screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(184, 18),
Common::Rect(184, 18, 212, 114));
}
}
void ScalpelEngine::flushBrumwellMirror() {
Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
// 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);
}
void ScalpelEngine::showScummVMSaveDialog() {
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int slot = dialog->runModalWithCurrentTarget();
if (slot >= 0) {
Common::String desc = dialog->getResultString();
saveGameState(slot, desc);
}
delete dialog;
}
void ScalpelEngine::showScummVMRestoreDialog() {
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int slot = dialog->runModalWithCurrentTarget();
if (slot >= 0) {
loadGameState(slot);
}
delete dialog;
}
bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::Point &pos, bool isPortrait) {
Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
Scalpel3DOMovieDecoder *videoDecoder = new Scalpel3DOMovieDecoder();
Graphics::ManagedSurface tempSurface;
Common::Point framePos(pos.x, pos.y);
ImageFile3DO *frameImageFile = nullptr;
ImageFrame *frameImage = nullptr;
bool frameShown = false;
if (!videoDecoder->loadFile(filename)) {
warning("Scalpel3DOMoviePlay: could not open '%s'", filename.c_str());
return false;
}
bool halfSize = isPortrait && !_isScreenDoubled;
if (isPortrait) {
// only for portrait videos, not for EA intro logo and such
if ((framePos.x >= 8) && (framePos.y >= 8)) { // safety check
framePos.x -= 8;
framePos.y -= 8; // frame is 8 pixels on left + top, and 7 pixels on right + bottom
}
frameImageFile = new ImageFile3DO("vidframe.cel", kImageFile3DOType_Cel);
frameImage = &(*frameImageFile)[0];
}
bool skipVideo = false;
//byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel;
uint16 width = videoDecoder->getWidth();
uint16 height = videoDecoder->getHeight();
//uint16 pitch = videoDecoder->getWidth() * bytesPerPixel;
_events->clearEvents();
videoDecoder->start();
// If we're to show the movie at half-size, we'll need a temporary intermediate surface
if (halfSize)
tempSurface.create(width / 2, height / 2);
while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
if (halfSize) {
// movies are 152 x 200
// Downscale, but calculate average color out of 4 pixels and put that average into the target pixel
// TODO: 3DO actually did pixel weighting, exact details about this are unknown
// It's also unknown what 3DO exactly did for interpolation
// and it's also unknown atm if the CinePak videos contained pixel weighting information
if ((height & 1) || (width & 1)) {
error("Scalpel3DOMoviePlay: critical error, half-size requested on video with uneven height/width");
}
for (int downscaleY = 0; downscaleY < height / 2; downscaleY++) {
const uint16 *downscaleSource1Ptr = (const uint16 *)frame->getBasePtr(0, downscaleY * 2);
const uint16 *downscaleSource2Ptr = (const uint16 *)frame->getBasePtr(0, (downscaleY * 2) + 1);
uint16 *downscaleTargetPtr = (uint16 *)tempSurface.getBasePtr(0, downscaleY);
for (int downscaleX = 0; downscaleX < width / 2; downscaleX++) {
// get 4 pixel colors
uint16 downscaleColor = *downscaleSource1Ptr;
uint32 downscaleRed = downscaleColor >> 11; // 5 bits
uint32 downscaleGreen = (downscaleColor >> 5) & 0x3f; // 6 bits
uint32 downscaleBlue = downscaleColor & 0x1f;
downscaleSource1Ptr++;
downscaleColor = *downscaleSource1Ptr;
downscaleRed += downscaleColor >> 11;
downscaleGreen += (downscaleColor >> 5) & 0x3f;
downscaleBlue += downscaleColor & 0x1f;
downscaleColor = *downscaleSource2Ptr;
downscaleRed += downscaleColor >> 11;
downscaleGreen += (downscaleColor >> 5) & 0x3f;
downscaleBlue += downscaleColor & 0x1f;
downscaleSource2Ptr++;
downscaleColor = *downscaleSource2Ptr;
downscaleRed += downscaleColor >> 11;
downscaleGreen += (downscaleColor >> 5) & 0x3f;
downscaleBlue += downscaleColor & 0x1f;
// Divide colors by 4, so that we get the average
downscaleRed = downscaleRed >> 2;
downscaleGreen = downscaleGreen >> 2;
downscaleBlue = downscaleBlue >> 2;
// write new color to target pixel
downscaleColor = (downscaleRed << 11) | (downscaleGreen << 5) | downscaleBlue;
*downscaleTargetPtr = downscaleColor;
downscaleSource1Ptr++;
downscaleSource2Ptr++;
downscaleTargetPtr++;
}
}
// Point the drawing frame to the temporary surface
frame = &tempSurface.rawSurface();
}
if (isPortrait && !frameShown) {
// Draw the frame (not the frame of the video, but a frame around the video) itself
_screen->SHtransBlitFrom(frameImage->_frame, framePos);
frameShown = true;
}
if (isPortrait && !halfSize) {
screen.rawBlitFrom(*frame, Common::Point(pos.x * 2, pos.y * 2));
} else {
_screen->SHblitFrom(*frame, pos);
}
_screen->update();
}
}
_events->pollEventsAndWait();
_events->setButtonState();
if (_events->kbHit()) {
Common::KeyState keyState = _events->getKey();
if (keyState.keycode == Common::KEYCODE_ESCAPE)
skipVideo = true;
} else if (_events->_pressed) {
skipVideo = true;
}
}
if (halfSize)
tempSurface.free();
videoDecoder->close();
delete videoDecoder;
if (isPortrait) {
delete frameImageFile;
}
// Restore scene
screen._backBuffer1.SHblitFrom(screen._backBuffer2);
_scene->updateBackground();
screen.slamArea(0, 0, screen.width(), CONTROLS_Y);
return !skipVideo;
}
} // End of namespace Scalpel
} // End of namespace Sherlock