1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "engines/util.h"
24 #include "gui/saveload.h"
25 #include "common/translation.h"
26 #include "sherlock/scalpel/scalpel.h"
27 #include "sherlock/scalpel/scalpel_fixed_text.h"
28 #include "sherlock/scalpel/scalpel_map.h"
29 #include "sherlock/scalpel/scalpel_people.h"
30 #include "sherlock/scalpel/scalpel_scene.h"
31 #include "sherlock/scalpel/scalpel_screen.h"
32 #include "sherlock/scalpel/3do/scalpel_3do_screen.h"
33 #include "sherlock/scalpel/tsage/logo.h"
34 #include "sherlock/sherlock.h"
35 #include "sherlock/music.h"
36 #include "sherlock/animation.h"
37 #include "video/3do_decoder.h"
38
39 namespace Sherlock {
40
41 namespace Scalpel {
42
43 #define PROLOGUE_NAMES_COUNT 6
44
45 // The following are a list of filenames played in the prologue that have
46 // special effects associated with them at specific frames
47 static const char *const PROLOGUE_NAMES[PROLOGUE_NAMES_COUNT] = {
48 "subway1", "subway2", "finale2", "suicid", "coff3", "coff4"
49 };
50
51 static const int PROLOGUE_FRAMES[6][9] = {
52 { 4, 26, 54, 72, 92, 134, FRAMES_END },
53 { 2, 80, 95, 117, 166, FRAMES_END },
54 { 1, FRAMES_END },
55 { 42, FRAMES_END },
56 { FRAMES_END },
57 { FRAMES_END }
58 };
59
60 #define TITLE_NAMES_COUNT 7
61
62 // Title animations file list
63 static const char *const TITLE_NAMES[TITLE_NAMES_COUNT] = {
64 "27pro1", "14note", "coff1", "coff2", "coff3", "coff4", "14kick"
65 };
66
67 static const int TITLE_FRAMES[7][9] = {
68 { 29, 131, FRAMES_END },
69 { 55, 80, 95, 117, 166, FRAMES_END },
70 { 15, FRAMES_END },
71 { 4, 37, 92, FRAMES_END },
72 { 2, 43, FRAMES_END },
73 { 2, FRAMES_END },
74 { 10, 50, FRAMES_END }
75 };
76
77 #define NUM_PLACES 100
78
79 static const int MAP_X[NUM_PLACES] = {
80 0, 368, 0, 219, 0, 282, 0, 43, 0, 0, 396, 408, 0, 0, 0, 568, 37, 325,
81 28, 0, 263, 36, 148, 469, 342, 143, 443, 229, 298, 0, 157, 260, 432,
82 174, 0, 351, 0, 528, 0, 136, 0, 0, 0, 555, 165, 0, 506, 0, 0, 344, 0, 0
83 };
84 static const int MAP_Y[NUM_PLACES] = {
85 0, 147, 0, 166, 0, 109, 0, 61, 0, 0, 264, 70, 0, 0, 0, 266, 341, 30, 275,
86 0, 294, 146, 311, 230, 184, 268, 133, 94, 207, 0, 142, 142, 330, 255, 0,
87 37, 0, 70, 0, 116, 0, 0, 0, 50, 21, 0, 303, 0, 0, 229, 0, 0
88 };
89
90 static const int MAP_TRANSLATE[NUM_PLACES] = {
91 0, 0, 0, 1, 0, 2, 0, 3, 4, 0, 4, 6, 0, 0, 0, 8, 9, 10, 11, 0, 12, 13, 14, 7,
92 15, 16, 17, 18, 19, 0, 20, 21, 22, 23, 0, 24, 0, 25, 0, 26, 0, 0, 0, 27,
93 28, 0, 29, 0, 0, 30, 0
94 };
95
96 static const byte MAP_SEQUENCES[3][MAX_FRAME] = {
97 { 1, 1, 2, 3, 4, 0 }, // Overview Still
98 { 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0 },
99 { 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0 }
100 };
101
102 #define MAX_PEOPLE 66
103
104 struct PeopleData {
105 const char *portrait;
106 int fixedTextId;
107 byte stillSequences[MAX_TALK_SEQUENCES];
108 byte talkSequences[MAX_TALK_SEQUENCES];
109 };
110
111 const PeopleData PEOPLE_DATA[MAX_PEOPLE] = {
112 { "HOLM", kFixedText_People_SherlockHolmes, { 1, 0, 0 }, { 1, 0, 0 } },
113 { "WATS", kFixedText_People_DrWatson, { 6, 0, 0 }, { 5, 5, 6, 7, 8, 7, 8, 6, 0, 0 } },
114 { "LEST", kFixedText_People_InspectorLestrade, { 4, 0, 0 }, { 2, 0, 0 } },
115 { "CON1", kFixedText_People_ConstableOBrien, { 2, 0, 0 }, { 1, 0, 0 } },
116 { "CON2", kFixedText_People_ConstableLewis, { 2, 0, 0 }, { 1, 0, 0 } },
117 { "SHEI", kFixedText_People_SheilaParker, { 2, 0, 0 }, { 2, 3, 0, 0 } },
118 { "HENR", kFixedText_People_HenryCarruthers, { 3, 0, 0 }, { 3, 0, 0 } },
119 { "LESL", kFixedText_People_Lesley, { 9, 0, 0 }, { 1, 2, 3, 2, 1, 2, 3, 0, 0 } },
120 { "USH1", kFixedText_People_AnUsher, { 13, 0, 0 }, { 13, 14, 0, 0 } },
121 { "USH2", kFixedText_People_AnUsher, { 2, 0, 0 }, { 2, 0, 0 } },
122 { "FRED", kFixedText_People_FredrickEpstein, { 4, 0, 0 }, { 1, 2, 3, 4, 3, 4, 3, 2, 0, 0 } },
123 { "WORT", kFixedText_People_MrsWorthington, { 9, 0, 0 }, { 8, 0, 0 } },
124 { "COAC", kFixedText_People_TheCoach, { 2, 0, 0 }, { 1, 2, 3, 4, 5, 4, 3, 2, 0, 0 } },
125 { "PLAY", kFixedText_People_APlayer, { 8, 0, 0 }, { 7, 8, 0, 0 } },
126 { "WBOY", kFixedText_People_Tim, { 13, 0, 0 }, { 12, 13, 0, 0 } },
127 { "JAME", kFixedText_People_JamesSanders, { 6, 0, 0 }, { 3, 4, 0, 0 } },
128 { "BELL", kFixedText_People_Belle, { 1, 0, 0 }, { 4, 5, 0, 0 } },
129 { "GIRL", kFixedText_People_CleaningGirl, { 20, 0, 0 }, { 14, 15, 16, 17, 18, 19, 20, 20, 20, 0, 0 } },
130 { "EPST", kFixedText_People_FredrickEpstein, { 17, 0, 0 }, { 16, 17, 18, 18, 18, 17, 17, 0, 0 } },
131 { "WIGG", kFixedText_People_Wiggins, { 3, 0, 0 }, { 2, 3, 0, 0 } },
132 { "PAUL", kFixedText_People_Paul, { 2, 0, 0 }, { 1, 2, 0, 0 } },
133 { "BART", kFixedText_People_TheBartender, { 1, 0, 0 }, { 1, 0, 0 } },
134 { "DIRT", kFixedText_People_ADirtyDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
135 { "SHOU", kFixedText_People_AShoutingDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
136 { "STAG", kFixedText_People_AStaggeringDrunk, { 1, 0, 0 }, { 1, 0, 0 } },
137 { "BOUN", kFixedText_People_TheBouncer, { 1, 0, 0 }, { 1, 0, 0 } },
138 { "SAND", kFixedText_People_JamesSanders, { 6, 0, 0 }, { 5, 6, 0, 0 } },
139 { "CORO", kFixedText_People_TheCoroner, { 6, 0, 0 }, { 4, 5, 0, 0 } },
140 { "EQUE", kFixedText_People_ReginaldSnipes, { 1, 0, 0 }, { 1, 0, 0 } },
141 { "GEOR", kFixedText_People_GeorgeBlackwood, { 1, 0, 0 }, { 1, 0, 0 } },
142 { "LARS", kFixedText_People_Lars, { 7, 0, 0 }, { 5, 6, 0, 0 } },
143 { "PARK", kFixedText_People_SheilaParker, { 1, 0, 0 }, { 1, 0, 0 } },
144 { "CHEM", kFixedText_People_TheChemist, { 8, 0, 0 }, { 8, 9, 0, 0 } },
145 { "GREG", kFixedText_People_InspectorGregson, { 6, 0, 0 }, { 5, 6, 0, 0 } },
146 { "LAWY", kFixedText_People_JacobFarthington, { 1, 0, 0 }, { 1, 0, 0 } },
147 { "MYCR", kFixedText_People_Mycroft, { 1, 0, 0 }, { 1, 0, 0 } },
148 { "SHER", kFixedText_People_OldSherman, { 7, 0, 0 }, { 7, 8, 0, 0 } },
149 { "CHMB", kFixedText_People_Richard, { 1, 0, 0 }, { 1, 0, 0 } },
150 { "BARM", kFixedText_People_TheBarman, { 1, 0, 0 }, { 1, 0, 0 } },
151 { "DAND", kFixedText_People_ADandyPlayer, { 1, 0, 0 }, { 1, 0, 0 } },
152 { "ROUG", kFixedText_People_ARoughlookingPlayer, { 1, 0, 0 }, { 1, 0, 0 } },
153 { "SPEC", kFixedText_People_ASpectator, { 1, 0, 0 }, { 1, 0, 0 } },
154 { "HUNT", kFixedText_People_RobertHunt, { 1, 0, 0 }, { 1, 0, 0 } },
155 { "VIOL", kFixedText_People_Violet, { 3, 0, 0 }, { 3, 4, 0, 0 } },
156 { "PETT", kFixedText_People_Pettigrew, { 1, 0, 0 }, { 1, 0, 0 } },
157 { "APPL", kFixedText_People_Augie, { 8, 0, 0 }, { 14, 15, 0, 0 } },
158 { "ANNA", kFixedText_People_AnnaCarroway, { 16, 0, 0 }, { 3, 4, 5, 6, 0, 0 } },
159 { "GUAR", kFixedText_People_AGuard, { 1, 0, 0 }, { 4, 5, 6, 0, 0 } },
160 { "ANTO", kFixedText_People_AntonioCaruso, { 8, 0, 0 }, { 7, 8, 0, 0 } },
161 { "TOBY", kFixedText_People_TobyTheDog, { 1, 0, 0 }, { 1, 0, 0 } },
162 { "KING", kFixedText_People_SimonKingsley, { 13, 0, 0 }, { 13, 14, 0, 0 } },
163 { "ALFR", kFixedText_People_Alfred, { 2, 0, 0 }, { 2, 3, 0, 0 } },
164 { "LADY", kFixedText_People_LadyBrumwell, { 1, 0, 0 }, { 3, 4, 0, 0 } },
165 { "ROSA", kFixedText_People_MadameRosa, { 1, 0, 0 }, { 1, 30, 0, 0 } },
166 { "LADB", kFixedText_People_LadyBrumwell, { 1, 0, 0 }, { 3, 4, 0, 0 } },
167 { "MOOR", kFixedText_People_JosephMoorehead, { 1, 0, 0 }, { 1, 0, 0 } },
168 { "BEAL", kFixedText_People_MrsBeale, { 5, 0, 0 }, { 14, 15, 16, 17, 18, 19, 20, 0, 0 } },
169 { "LION", kFixedText_People_Felix, { 1, 0, 0 }, { 1, 0, 0 } },
170 { "HOLL", kFixedText_People_Hollingston, { 1, 0, 0 }, { 1, 0, 0 } },
171 { "CALL", kFixedText_People_ConstableCallaghan, { 1, 0, 0 }, { 1, 0, 0 } },
172 { "JERE", kFixedText_People_SergeantDuncan, { 2, 0, 0 }, { 1, 1, 2, 2, 0, 0 } },
173 { "LORD", kFixedText_People_LordBrumwell, { 1, 0, 0 }, { 9, 10, 0, 0 } },
174 { "NIGE", kFixedText_People_NigelJaimeson, { 1, 0, 0 }, { 1, 2, 0, 138, 3, 4, 0, 138, 0, 0 } },
175 { "JONA", kFixedText_People_Jonas, { 1, 0, 0 }, { 1, 8, 0, 0 } },
176 { "DUGA", kFixedText_People_ConstableDugan, { 1, 0, 0 }, { 1, 0, 0 } },
177 { "INSP", kFixedText_People_InspectorLestrade, { 4, 0, 0 }, { 2, 0, 0 } }
178 };
179
180 uint INFO_BLACK;
181 uint BORDER_COLOR;
182 uint COMMAND_BACKGROUND;
183 uint BUTTON_BACKGROUND;
184 uint TALK_FOREGROUND;
185 uint TALK_NULL;
186 uint BUTTON_TOP;
187 uint BUTTON_MIDDLE;
188 uint BUTTON_BOTTOM;
189 uint COMMAND_FOREGROUND;
190 uint COMMAND_HIGHLIGHTED;
191 uint COMMAND_NULL;
192 uint INFO_FOREGROUND;
193 uint INFO_BACKGROUND;
194 uint INV_FOREGROUND;
195 uint INV_BACKGROUND;
196 uint PEN_COLOR;
197
198 /*----------------------------------------------------------------*/
199
200 #define FROM_RGB(r, g, b) pixelFormatRGB565.RGBToColor(r, g, b)
201
ScalpelEngine(OSystem * syst,const SherlockGameDescription * gameDesc)202 ScalpelEngine::ScalpelEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
203 SherlockEngine(syst, gameDesc) {
204 _darts = nullptr;
205 _mapResult = 0;
206
207 if (getPlatform() == Common::kPlatform3DO) {
208 const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
209 INFO_BLACK = FROM_RGB(0, 0, 0);
210 BORDER_COLOR = FROM_RGB(0x6d, 0x38, 0x10);
211 COMMAND_BACKGROUND = FROM_RGB(0x38, 0x38, 0xce);
212 BUTTON_BACKGROUND = FROM_RGB(0x95, 0x5d, 0x24);
213 TALK_FOREGROUND = FROM_RGB(0xff, 0x55, 0x55);
214 TALK_NULL = FROM_RGB(0xce, 0xc6, 0xc2);
215 BUTTON_TOP = FROM_RGB(0xbe, 0x85, 0x3c);
216 BUTTON_MIDDLE = FROM_RGB(0x9d, 0x40, 0);
217 BUTTON_BOTTOM = FROM_RGB(0x69, 0x24, 0);
218 COMMAND_FOREGROUND = FROM_RGB(0xFF, 0xFF, 0xFF);
219 COMMAND_HIGHLIGHTED = FROM_RGB(0x55, 0xff, 0x55);
220 COMMAND_NULL = FROM_RGB(0x69, 0x24, 0);
221 INFO_FOREGROUND = FROM_RGB(0x55, 0xff, 0xff);
222 INFO_BACKGROUND = FROM_RGB(0, 0, 0x48);
223 INV_FOREGROUND = FROM_RGB(0xff, 0xff, 0x55);
224 INV_BACKGROUND = FROM_RGB(0, 0, 0x48);
225 PEN_COLOR = FROM_RGB(0x50, 0x18, 0);
226 } else {
227 INFO_BLACK = 1;
228 BORDER_COLOR = 237;
229 COMMAND_BACKGROUND = 4;
230 BUTTON_BACKGROUND = 235;
231 TALK_FOREGROUND = 12;
232 TALK_NULL = 16;
233 BUTTON_TOP = 233;
234 BUTTON_MIDDLE = 244;
235 BUTTON_BOTTOM = 248;
236 COMMAND_FOREGROUND = 15;
237 COMMAND_HIGHLIGHTED = 10;
238 COMMAND_NULL = 248;
239 INFO_FOREGROUND = 11;
240 INFO_BACKGROUND = 1;
241 INV_FOREGROUND = 14;
242 INV_BACKGROUND = 1;
243 PEN_COLOR = 250;
244 }
245 }
246
~ScalpelEngine()247 ScalpelEngine::~ScalpelEngine() {
248 delete _darts;
249 }
250
setupGraphics()251 void ScalpelEngine::setupGraphics() {
252 if (getPlatform() != Common::kPlatform3DO) {
253 // 320x200 palettized
254 initGraphics(320, 200);
255 } else {
256 // 3DO actually uses RGB555, but some platforms of ours only support RGB565, so we use that
257 const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
258
259 // First try for a 640x400 mode
260 g_system->beginGFXTransaction();
261 initCommonGFX();
262 g_system->initSize(640, 400, &pixelFormatRGB565);
263 OSystem::TransactionError gfxError = g_system->endGFXTransaction();
264
265 if (gfxError == OSystem::kTransactionSuccess) {
266 _isScreenDoubled = true;
267 } else {
268 // System doesn't support it, so fall back on 320x200 mode
269 initGraphics(320, 200, &pixelFormatRGB565);
270 }
271 }
272 }
273
initialize()274 void ScalpelEngine::initialize() {
275 // Setup graphics mode
276 setupGraphics();
277
278 // Let the base engine intialize
279 SherlockEngine::initialize();
280
281 _darts = new Darts(this);
282
283 _flags.resize(100 * 8);
284 _flags[3] = true; // Turn on Alley
285 _flags[39] = true; // Turn on Baker Street
286
287 if (!isDemo()) {
288 // Load the map co-ordinates for each scene and sequence data
289 ScalpelMap &map = *(ScalpelMap *)_map;
290 map.loadPoints(NUM_PLACES, &MAP_X[0], &MAP_Y[0], &MAP_TRANSLATE[0]);
291 map.loadSequences(3, &MAP_SEQUENCES[0][0]);
292 map._oldCharPoint = BAKER_ST_EXTERIOR;
293 }
294
295 // Load the inventory
296 loadInventory();
297
298 // Set up list of people
299 ScalpelFixedText &fixedText = *(ScalpelFixedText *)_fixedText;
300 const char *peopleNamePtr = nullptr;
301
302 for (int idx = 0; idx < MAX_PEOPLE; ++idx) {
303 peopleNamePtr = fixedText.getText(PEOPLE_DATA[idx].fixedTextId);
304 _people->_characters.push_back(PersonData(peopleNamePtr, PEOPLE_DATA[idx].portrait,
305 PEOPLE_DATA[idx].stillSequences, PEOPLE_DATA[idx].talkSequences));
306 }
307
308 _animation->setPrologueNames(&PROLOGUE_NAMES[0], PROLOGUE_NAMES_COUNT);
309 _animation->setPrologueFrames(&PROLOGUE_FRAMES[0][0], 6, 9);
310
311 _animation->setTitleNames(&TITLE_NAMES[0], TITLE_NAMES_COUNT);
312 _animation->setTitleFrames(&TITLE_FRAMES[0][0], 7, 9);
313
314 // Starting scene
315 if (isDemo() && _interactiveFl)
316 _scene->_goToScene = 3;
317 else
318 _scene->_goToScene = 4;
319 }
320
showOpening()321 void ScalpelEngine::showOpening() {
322 bool finished = true;
323
324 if (isDemo() && _interactiveFl)
325 return;
326
327 _events->setFrameRate(60);
328
329 if (getPlatform() == Common::kPlatform3DO) {
330 show3DOSplash();
331
332 finished = showCityCutscene3DO();
333 if (finished)
334 finished = showAlleyCutscene3DO();
335 if (finished)
336 finished = showStreetCutscene3DO();
337 if (finished)
338 showOfficeCutscene3DO();
339
340 _events->clearEvents();
341 _music->stopMusic();
342 } else {
343 TsAGE::Logo::show(this);
344
345 finished = showCityCutscene();
346 if (finished)
347 finished = showAlleyCutscene();
348 if (finished)
349 finished = showStreetCutscene();
350 if (finished)
351 showOfficeCutscene();
352
353 _events->clearEvents();
354 _music->stopMusic();
355 }
356
357 _events->setFrameRate(GAME_FRAME_RATE);
358 }
359
showCityCutscene()360 bool ScalpelEngine::showCityCutscene() {
361 byte greyPalette[PALETTE_SIZE];
362 byte palette[PALETTE_SIZE];
363
364 // Demo fades from black into grey and then fades from grey into the scene
365 Common::fill(&greyPalette[0], &greyPalette[PALETTE_SIZE], 142);
366 _screen->fadeIn((const byte *)greyPalette, 3);
367
368 _music->loadSong("prolog1");
369 _animation->_gfxLibraryFilename = "title.lib";
370 _animation->_soundLibraryFilename = "title.snd";
371 bool finished = _animation->play("26open1", true, 1, 255, true, 2);
372
373 if (finished) {
374 ImageFile titleImages_LondonNovember("title2.vgs", true);
375 _screen->_backBuffer1.SHblitFrom(*_screen);
376 _screen->_backBuffer2.SHblitFrom(*_screen);
377
378 Common::Point londonPosition;
379
380 if ((titleImages_LondonNovember[0]._width == 302) && (titleImages_LondonNovember[0]._height == 39)) {
381 // Spanish
382 londonPosition = Common::Point(9, 8);
383 } else {
384 // English (German uses the same English graphics), width 272, height 37
385 // In the German version this is placed differently, check against German floppy version TODO
386 londonPosition = Common::Point(30, 50);
387 }
388
389 // London, England
390 _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[0], londonPosition);
391 _screen->randomTransition();
392 finished = _events->delay(1000, true);
393
394 // November, 1888
395 if (finished) {
396 _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100));
397 _screen->randomTransition();
398 finished = _events->delay(5000, true);
399 }
400
401 // Transition out the title
402 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
403 _screen->randomTransition();
404 }
405
406 if (finished)
407 finished = _animation->play("26open2", true, 1, 0, false, 2);
408
409 if (finished) {
410 ImageFile titleImages_SherlockHolmesTitle("title.vgs", true);
411 _screen->_backBuffer1.SHblitFrom(*_screen);
412 _screen->_backBuffer2.SHblitFrom(*_screen);
413
414 Common::Point lostFilesPosition;
415 Common::Point sherlockHolmesPosition;
416 Common::Point copyrightPosition;
417
418 if ((titleImages_SherlockHolmesTitle[0]._width == 306) && (titleImages_SherlockHolmesTitle[0]._height == 39)) {
419 // Spanish
420 lostFilesPosition = Common::Point(5, 5);
421 sherlockHolmesPosition = Common::Point(24, 40);
422 copyrightPosition = Common::Point(3, 190);
423 } else {
424 // English (German uses the same English graphics), width 208, height 39
425 lostFilesPosition = Common::Point(75, 6);
426 sherlockHolmesPosition = Common::Point(34, 21);
427 copyrightPosition = Common::Point(4, 190);
428 }
429
430 // The Lost Files of
431 _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition);
432 // Sherlock Holmes
433 _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition);
434 // copyright
435 _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition);
436
437 _screen->verticalTransition();
438 finished = _events->delay(4000, true);
439
440 if (finished) {
441 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
442 _screen->randomTransition();
443 finished = _events->delay(2000);
444 }
445
446 if (finished) {
447 _screen->getPalette(palette);
448 _screen->fadeToBlack(2);
449 }
450
451 if (finished) {
452 // In the alley...
453 Common::Point alleyPosition;
454
455 if ((titleImages_SherlockHolmesTitle[3]._width == 105) && (titleImages_SherlockHolmesTitle[3]._height == 16)) {
456 // German
457 alleyPosition = Common::Point(72, 50);
458 } else if ((titleImages_SherlockHolmesTitle[3]._width == 166) && (titleImages_SherlockHolmesTitle[3]._height == 36)) {
459 // Spanish
460 alleyPosition = Common::Point(71, 50);
461 } else {
462 // English, width 175, height 38
463 alleyPosition = Common::Point(72, 51);
464 }
465 _screen->SHtransBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition);
466 _screen->fadeIn(palette, 3);
467
468 // Wait until the track got looped and the first few notes were played
469 finished = _music->waitUntilMSec(4300, 21300, 0, 2500); // ticks 0x104 / ticks 0x500
470 }
471 }
472
473 _animation->_gfxLibraryFilename = "";
474 _animation->_soundLibraryFilename = "";
475 return finished;
476 }
477
showAlleyCutscene()478 bool ScalpelEngine::showAlleyCutscene() {
479 byte palette[PALETTE_SIZE];
480 _music->loadSong("prolog2");
481
482 _animation->_gfxLibraryFilename = "TITLE.LIB";
483 _animation->_soundLibraryFilename = "TITLE.SND";
484
485 // Fade "In The Alley..." text to black
486 _screen->fadeToBlack(2);
487
488 bool finished = _animation->play("27PRO1", true, 1, 3, true, 2);
489 if (finished) {
490 _screen->getPalette(palette);
491 _screen->fadeToBlack(2);
492
493 // wait until second lower main note
494 finished = _music->waitUntilMSec(26800, 0xFFFFFFFF, 0, 1000); // ticks 0x64A
495 }
496
497 if (finished) {
498 _screen->setPalette(palette);
499 finished = _animation->play("27PRO2", true, 1, 0, false, 2);
500 }
501
502 if (finished) {
503 showLBV("scream.lbv");
504
505 // wait until first "scream" in music happened
506 finished = _music->waitUntilMSec(45800, 0xFFFFFFFF, 0, 6000); // ticks 0xABE
507 }
508
509 if (finished) {
510 // quick fade out
511 _screen->fadeToBlack(1);
512
513 // wait until after third "scream" in music happened
514 finished = _music->waitUntilMSec(49000, 0xFFFFFFFF, 0, 2000); // ticks 0xB80
515 }
516
517 if (finished)
518 finished = _animation->play("27PRO3", true, 1, 0, true, 2);
519
520 if (finished) {
521 _screen->getPalette(palette);
522 _screen->fadeToBlack(2);
523 }
524
525 if (finished) {
526 ImageFile titleImages_EarlyTheFollowingMorning("title3.vgs", true);
527 // "Early the following morning on Baker Street..."
528 Common::Point earlyTheFollowingMorningPosition;
529
530 if ((titleImages_EarlyTheFollowingMorning[0]._width == 164) && (titleImages_EarlyTheFollowingMorning[0]._height == 19)) {
531 // German
532 earlyTheFollowingMorningPosition = Common::Point(35, 50);
533 } else if ((titleImages_EarlyTheFollowingMorning[0]._width == 171) && (titleImages_EarlyTheFollowingMorning[0]._height == 32)) {
534 // Spanish
535 earlyTheFollowingMorningPosition = Common::Point(35, 50);
536 } else {
537 // English, width 218, height 31
538 earlyTheFollowingMorningPosition = Common::Point(35, 52);
539 }
540
541 _screen->SHtransBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition);
542
543 // fast fade-in
544 _screen->fadeIn(palette, 1);
545
546 // wait for music to end and wait an additional 2.5 seconds
547 finished = _music->waitUntilMSec(0xFFFFFFFF, 0xFFFFFFFF, 2500, 3000);
548 }
549
550 _animation->_gfxLibraryFilename = "";
551 _animation->_soundLibraryFilename = "";
552 return finished;
553 }
554
showStreetCutscene()555 bool ScalpelEngine::showStreetCutscene() {
556 _animation->_gfxLibraryFilename = "TITLE.LIB";
557 _animation->_soundLibraryFilename = "TITLE.SND";
558
559 _music->loadSong("prolog3");
560
561 // wait a bit
562 bool finished = _events->delay(500);
563
564 if (finished) {
565 // fade out "Early the following morning..."
566 _screen->fadeToBlack(2);
567
568 // wait for music a bit
569 finished = _music->waitUntilMSec(3800, 0xFFFFFFFF, 0, 1000); // ticks 0xE4
570 }
571
572 if (finished)
573 finished = _animation->play("14KICK", true, 1, 3, true, 2);
574
575 // Constable animation plays slower than speed 2
576 // If we play it with speed 2, music gets obviously out of sync
577 if (finished)
578 finished = _animation->play("14NOTE", true, 1, 0, false, 3);
579
580 // Fade to black
581 if (finished)
582 _screen->fadeToBlack(1);
583
584 _animation->_gfxLibraryFilename = "";
585 _animation->_soundLibraryFilename = "";
586 return finished;
587 }
588
showOfficeCutscene()589 bool ScalpelEngine::showOfficeCutscene() {
590 _music->loadSong("prolog4");
591 _animation->_gfxLibraryFilename = "TITLE2.LIB";
592 _animation->_soundLibraryFilename = "TITLE.SND";
593
594 bool finished = _animation->play("COFF1", true, 1, 3, true, 3);
595 if (finished)
596 finished = _animation->play("COFF2", true, 1, 0, false, 3);
597 if (finished) {
598 showLBV("note.lbv");
599
600 if (_sound->_voices) {
601 finished = _sound->playSound("NOTE1", WAIT_KBD_OR_FINISH);
602 if (finished)
603 finished = _sound->playSound("NOTE2", WAIT_KBD_OR_FINISH);
604 if (finished)
605 finished = _sound->playSound("NOTE3", WAIT_KBD_OR_FINISH);
606 if (finished)
607 finished = _sound->playSound("NOTE4", WAIT_KBD_OR_FINISH);
608 } else
609 finished = _events->delay(19000);
610
611 if (finished) {
612 _events->clearEvents();
613 finished = _events->delay(500);
614 }
615 }
616
617 if (finished)
618 finished = _animation->play("COFF3", true, 1, 0, true, 3);
619
620 if (finished)
621 finished = _animation->play("COFF4", true, 1, 0, false, 3);
622
623 if (finished)
624 finished = scrollCredits();
625
626 if (finished)
627 _screen->fadeToBlack(3);
628
629 _animation->_gfxLibraryFilename = "";
630 _animation->_soundLibraryFilename = "";
631 return finished;
632 }
633
scrollCredits()634 bool ScalpelEngine::scrollCredits() {
635 // Load the images for displaying credit text
636 Common::SeekableReadStream *stream = _res->load("credits.vgs", "title.lib");
637 ImageFile creditsImages(*stream);
638
639 // Demo fades slowly from the scene into credits palette
640 _screen->fadeIn(creditsImages._palette, 3);
641
642 delete stream;
643
644 // Save a copy of the screen background for use in drawing each credit frame
645 _screen->_backBuffer1.SHblitFrom(*_screen);
646
647 // Loop for showing the credits
648 for(int idx = 0; idx < 600 && !_events->kbHit() && !shouldQuit(); ++idx) {
649 // Copy the entire screen background before writing text
650 _screen->SHblitFrom(_screen->_backBuffer1);
651
652 // Write the text appropriate for the next frame
653 if (idx < 400)
654 _screen->SHtransBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0);
655 if (idx > 200)
656 _screen->SHtransBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0);
657
658 // Don't show credit text on the top and bottom ten rows of the screen
659 _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->width(), 10));
660 _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, _screen->height() - 10),
661 Common::Rect(0, _screen->height() - 10, _screen->width(), _screen->height()));
662
663 _events->delay(100);
664 }
665
666 return true;
667 }
668
669 // 3DO variant
show3DOSplash()670 bool ScalpelEngine::show3DOSplash() {
671 // 3DO EA Splash screen
672 ImageFile3DO titleImage_3DOSplash("3DOSplash.cel", kImageFile3DOType_Cel);
673
674 _screen->SHtransBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20));
675 bool finished = _events->delay(3000, true);
676
677 if (finished) {
678 _screen->clear();
679 finished = _events->delay(500, true);
680 }
681
682 if (finished) {
683 // EA logo movie
684 play3doMovie("EAlogo.stream", Common::Point(20, 0));
685 }
686
687 // Always clear screen
688 _screen->clear();
689 return finished;
690 }
691
showCityCutscene3DO()692 bool ScalpelEngine::showCityCutscene3DO() {
693 Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
694 _animation->_soundLibraryFilename = "TITLE.SND";
695
696 screen.clear();
697 bool finished = _events->delay(2500, true);
698
699 if (finished) {
700 finished = _events->delay(2500, true);
701
702 // Play intro music
703 _music->loadSong("prolog");
704
705 // Loop rain.aiff until the Sherlock logo fades away.
706 // TODO: The volume is just a guess.
707 _sound->playAiff("prologue/sounds/rain.aiff", 15, true);
708
709 // Fade screen to grey
710 screen._backBuffer1.clear(0xCE59); // RGB565: 25, 50, 25 (grey)
711 screen.fadeIntoScreen3DO(2);
712 }
713
714 if (finished) {
715 finished = _music->waitUntilMSec(3400, 0, 0, 3400);
716 }
717
718 if (finished) {
719 screen._backBuffer1.clear(0); // fill backbuffer with black to avoid issues during fade from white
720 finished = _animation->play3DO("26open1", true, 1, true, 2);
721 }
722
723 if (finished) {
724 screen._backBuffer2.SHblitFrom(screen._backBuffer1);
725
726 // "London, England"
727 ImageFile3DO titleImage_London("title2a.cel", kImageFile3DOType_Cel);
728 screen._backBuffer1.SHtransBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
729
730 screen.fadeIntoScreen3DO(1);
731 finished = _events->delay(1500, true);
732
733 if (finished) {
734 // "November, 1888"
735 ImageFile3DO titleImage_November("title2b.cel", kImageFile3DOType_Cel);
736 screen._backBuffer1.SHtransBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
737
738 screen.fadeIntoScreen3DO(1);
739 finished = _music->waitUntilMSec(14700, 0, 0, 5000);
740 }
741
742 if (finished) {
743 // Restore screen
744 _screen->_backBuffer1.SHblitFrom(screen._backBuffer2);
745 _screen->SHblitFrom(screen._backBuffer1);
746 }
747 }
748
749 if (finished)
750 finished = _animation->play3DO("26open2", true, 1, false, 2);
751
752 if (finished) {
753 // "Sherlock Holmes" (title)
754 ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel", kImageFile3DOType_Cel);
755 screen._backBuffer1.SHtransBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
756
757 // Blend in
758 screen.fadeIntoScreen3DO(2);
759 finished = _events->delay(500, true);
760
761 // Title should fade in, Copyright should be displayed a bit after that
762 if (finished) {
763 ImageFile3DO titleImage_Copyright("title1c.cel", kImageFile3DOType_Cel);
764
765 screen.SHtransBlitFrom(titleImage_Copyright[0]._frame, Common::Point(40, 380));
766 finished = _events->delay(3500, true);
767 }
768 }
769
770 if (finished)
771 finished = _music->waitUntilMSec(33600, 0, 0, 2000);
772
773 _sound->stopAiff();
774
775 if (finished) {
776 // Fade to black
777 screen._backBuffer1.clear();
778 screen.fadeIntoScreen3DO(3);
779 }
780
781 if (finished) {
782 // "In the alley behind the Regency Theatre..."
783 ImageFile3DO titleImage_InTheAlley("title1d.cel", kImageFile3DOType_Cel);
784 screen._backBuffer1.SHtransBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
785
786 // Fade in
787 screen.fadeIntoScreen3DO(4);
788 finished = _music->waitUntilMSec(39900, 0, 0, 2500);
789
790 // Fade out
791 screen._backBuffer1.clear();
792 screen.fadeIntoScreen3DO(4);
793 }
794 return finished;
795 }
796
showAlleyCutscene3DO()797 bool ScalpelEngine::showAlleyCutscene3DO() {
798 Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
799 bool finished = _music->waitUntilMSec(43500, 0, 0, 1000);
800
801 if (finished)
802 finished = _animation->play3DO("27PRO1", true, 1, false, 2);
803
804 if (finished) {
805 // Fade out...
806 screen._backBuffer1.clear();
807 screen.fadeIntoScreen3DO(3);
808
809 finished = _music->waitUntilMSec(67100, 0, 0, 1000); // 66700
810 }
811
812 if (finished)
813 finished = _animation->play3DO("27PRO2", true, 1, false, 2);
814
815 if (finished)
816 finished = _music->waitUntilMSec(76000, 0, 0, 1000);
817
818 if (finished) {
819 // Show screaming victim
820 ImageFile3DO titleImage_ScreamingVictim("scream.cel", kImageFile3DOType_Cel);
821
822 screen.clear();
823 screen.SHtransBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
824
825 // Play "scream.aiff"
826 if (_sound->_voices)
827 _sound->playSound("prologue/sounds/scream.aiff", WAIT_RETURN_IMMEDIATELY, 100);
828
829 finished = _music->waitUntilMSec(81600, 0, 0, 6000);
830 }
831
832 if (finished) {
833 // Fade out
834 screen._backBuffer1.clear();
835 screen.fadeIntoScreen3DO(5);
836
837 finished = _music->waitUntilMSec(84400, 0, 0, 2000);
838 }
839
840 if (finished)
841 finished = _animation->play3DO("27PRO3", true, 1, false, 2);
842
843 if (finished) {
844 // Fade out
845 screen._backBuffer1.clear();
846 screen.fadeIntoScreen3DO(5);
847 }
848
849 if (finished) {
850 // "Early the following morning on Baker Street..."
851 ImageFile3DO titleImage_EarlyTheFollowingMorning("title3.cel", kImageFile3DOType_Cel);
852 screen._backBuffer1.SHtransBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
853
854 // Fade in
855 screen.fadeIntoScreen3DO(4);
856 finished = _music->waitUntilMSec(96700, 0, 0, 3000);
857 }
858
859 return finished;
860 }
861
showStreetCutscene3DO()862 bool ScalpelEngine::showStreetCutscene3DO() {
863 Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
864 bool finished = true;
865
866 if (finished) {
867 // fade out "Early the following morning..."
868 screen._backBuffer1.clear();
869 screen.fadeIntoScreen3DO(4);
870
871 // wait for music a bit
872 finished = _music->waitUntilMSec(100300, 0, 0, 1000);
873 }
874
875 if (finished)
876 finished = _animation->play3DO("14KICK", true, 1, false, 2);
877
878 // note: part of the constable is sticking to the door during the following
879 // animation, when he walks away. This is a bug of course, but it actually happened on 3DO!
880 // I'm not sure if it happens because the door is pure black (0, 0, 0) and it's because
881 // of transparency - or if the animation itself is bad. We will definitely have to adjust
882 // the animation data to fix it.
883 if (finished)
884 finished = _animation->play3DO("14NOTE", true, 1, false, 3);
885
886 if (finished) {
887 // Fade out
888 screen._backBuffer1.clear();
889 screen.fadeIntoScreen3DO(4);
890 }
891
892 return finished;
893 }
894
showOfficeCutscene3DO()895 bool ScalpelEngine::showOfficeCutscene3DO() {
896 bool finished = _music->waitUntilMSec(151000, 0, 0, 1000);
897
898 if (finished)
899 finished = _animation->play3DO("COFF1", true, 1, false, 3);
900
901 if (finished)
902 finished = _animation->play3DO("COFF2", true, 1, false, 3);
903
904 if (finished)
905 finished = _music->waitUntilMSec(182400, 0, 0, 1000);
906
907 if (finished) {
908 // Show the note
909 ImageFile3DO titleImage_CoffeeNote("note.cel", kImageFile3DOType_Cel);
910
911 _screen->clear();
912 _screen->SHtransBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0));
913
914 if (_sound->_voices) {
915 finished = _sound->playSound("prologue/sounds/note.aiff", WAIT_KBD_OR_FINISH);
916 } else
917 finished = _events->delay(19000);
918
919 if (finished)
920 finished = _music->waitUntilMSec(218800, 0, 0, 1000);
921
922 // Fade out
923 _screen->clear();
924 }
925
926 if (finished)
927 finished = _music->waitUntilMSec(222200, 0, 0, 1000);
928
929 if (finished)
930 finished = _animation->play3DO("COFF3", true, 1, false, 3);
931
932 if (finished)
933 finished = _animation->play3DO("COFF4", true, 1, false, 3);
934
935 if (finished) {
936 finished = _music->waitUntilMSec(244500, 0, 0, 2000);
937
938 // TODO: Brighten the image, possibly by doing a partial fade
939 // to white.
940
941 _screen->_backBuffer2.SHblitFrom(_screen->_backBuffer1);
942
943 for (int nr = 1; finished && nr <= 4; nr++) {
944 char filename[15];
945 sprintf(filename, "credits%d.cel", nr);
946 ImageFile3DO *creditsImage = new ImageFile3DO(filename, kImageFile3DOType_Cel);
947 ImageFrame *creditsFrame = &(*creditsImage)[0];
948 for (int i = 0; finished && i < 200 + creditsFrame->_height; i++) {
949 _screen->SHblitFrom(_screen->_backBuffer2);
950 _screen->SHtransBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i));
951 if (!_events->delay(70, true))
952 finished = false;
953 }
954 delete creditsImage;
955 }
956 }
957
958 return finished;
959 }
960
loadInventory()961 void ScalpelEngine::loadInventory() {
962 ScalpelFixedText &fixedText = *(ScalpelFixedText *)_fixedText;
963 Inventory &inv = *_inventory;
964
965 Common::String fixedText_Message = fixedText.getText(kFixedText_InitInventory_Message);
966 Common::String fixedText_HolmesCard = fixedText.getText(kFixedText_InitInventory_HolmesCard);
967 Common::String fixedText_Tickets = fixedText.getText(kFixedText_InitInventory_Tickets);
968 Common::String fixedText_CuffLink = fixedText.getText(kFixedText_InitInventory_CuffLink);
969 Common::String fixedText_WireHook = fixedText.getText(kFixedText_InitInventory_WireHook);
970 Common::String fixedText_Note = fixedText.getText(kFixedText_InitInventory_Note);
971 Common::String fixedText_OpenWatch = fixedText.getText(kFixedText_InitInventory_OpenWatch);
972 Common::String fixedText_Paper = fixedText.getText(kFixedText_InitInventory_Paper);
973 Common::String fixedText_Letter = fixedText.getText(kFixedText_InitInventory_Letter);
974 Common::String fixedText_Tarot = fixedText.getText(kFixedText_InitInventory_Tarot);
975 Common::String fixedText_OrnateKey = fixedText.getText(kFixedText_InitInventory_OrnateKey);
976 Common::String fixedText_PawnTicket = fixedText.getText(kFixedText_InitInventory_PawnTicket);
977
978 // Initial inventory
979 inv._holdings = 2;
980 inv.push_back(InventoryItem(0, "Message", fixedText_Message, "_ITEM03A"));
981 inv.push_back(InventoryItem(0, "Holmes Card", fixedText_HolmesCard, "_ITEM07A"));
982
983 // Hidden items
984 inv.push_back(InventoryItem(95, "Tickets", fixedText_Tickets, "_ITEM10A"));
985 inv.push_back(InventoryItem(138, "Cuff Link", fixedText_CuffLink, "_ITEM04A"));
986 inv.push_back(InventoryItem(138, "Wire Hook", fixedText_WireHook, "_ITEM06A"));
987 inv.push_back(InventoryItem(150, "Note", fixedText_Note, "_ITEM13A"));
988 inv.push_back(InventoryItem(481, "Open Watch", fixedText_OpenWatch, "_ITEM62A"));
989 inv.push_back(InventoryItem(481, "Paper", fixedText_Paper, "_ITEM44A"));
990 inv.push_back(InventoryItem(532, "Letter", fixedText_Letter, "_ITEM68A"));
991 inv.push_back(InventoryItem(544, "Tarot", fixedText_Tarot, "_ITEM71A"));
992 inv.push_back(InventoryItem(544, "Ornate Key", fixedText_OrnateKey, "_ITEM70A"));
993 inv.push_back(InventoryItem(586, "Pawn ticket", fixedText_PawnTicket, "_ITEM16A"));
994 }
995
showLBV(const Common::String & filename)996 void ScalpelEngine::showLBV(const Common::String &filename) {
997 Common::SeekableReadStream *stream = _res->load(filename, "title.lib");
998 ImageFile images(*stream);
999 delete stream;
1000
1001 _screen->setPalette(images._palette);
1002 _screen->_backBuffer1.SHblitFrom(images[0]);
1003 _screen->verticalTransition();
1004 }
1005
startScene()1006 void ScalpelEngine::startScene() {
1007 if (_scene->_goToScene == OVERHEAD_MAP || _scene->_goToScene == OVERHEAD_MAP2) {
1008 // Show the map
1009 if (_music->_musicOn && _music->loadSong(100))
1010 _music->startSong();
1011
1012 _scene->_goToScene = _map->show();
1013
1014 _music->freeSong();
1015 _people->_savedPos = Common::Point(-1, -1);
1016 _people->_savedPos._facing = -1;
1017 }
1018
1019 // Some rooms are prologue cutscenes, rather than normal game scenes. These are:
1020 // 2: Blackwood's capture
1021 // 52: Rescuing Anna
1022 // 53: Moorehead's death / subway train
1023 // 55: Fade out and exit
1024 // 70: Brumwell suicide
1025 switch (_scene->_goToScene) {
1026 case BLACKWOOD_CAPTURE:
1027 case RESCUE_ANNA:
1028 case MOOREHEAD_DEATH:
1029 case BRUMWELL_SUICIDE:
1030 if (_music->_musicOn && _music->loadSong(_scene->_goToScene))
1031 _music->startSong();
1032
1033 switch (_scene->_goToScene) {
1034 case BLACKWOOD_CAPTURE:
1035 // Blackwood's capture
1036 _res->addToCache("final2.vda", "epilogue.lib");
1037 _res->addToCache("final2.vdx", "epilogue.lib");
1038 _animation->play("final1", false, 1, 3, true, 2);
1039 _animation->play("final2", false, 1, 0, false, 2);
1040 break;
1041
1042 case RESCUE_ANNA:
1043 // Rescuing Anna
1044 _res->addToCache("finalr2.vda", "epilogue.lib");
1045 _res->addToCache("finalr2.vdx", "epilogue.lib");
1046 _res->addToCache("finale1.vda", "epilogue.lib");
1047 _res->addToCache("finale1.vdx", "epilogue.lib");
1048 _res->addToCache("finale2.vda", "epilogue.lib");
1049 _res->addToCache("finale2.vdx", "epilogue.lib");
1050 _res->addToCache("finale3.vda", "epilogue.lib");
1051 _res->addToCache("finale3.vdx", "epilogue.lib");
1052 _res->addToCache("finale4.vda", "EPILOG2.lib");
1053 _res->addToCache("finale4.vdx", "EPILOG2.lib");
1054
1055 _animation->play("finalr1", false, 1, 3, true, 2);
1056 _animation->play("finalr2", false, 1, 0, false, 2);
1057
1058 if (!_res->isInCache("finale2.vda")) {
1059 // Finale file isn't cached
1060 _res->addToCache("finale2.vda", "epilogue.lib");
1061 _res->addToCache("finale2.vdx", "epilogue.lib");
1062 _res->addToCache("finale3.vda", "epilogue.lib");
1063 _res->addToCache("finale3.vdx", "epilogue.lib");
1064 _res->addToCache("finale4.vda", "EPILOG2.lib");
1065 _res->addToCache("finale4.vdx", "EPILOG2.lib");
1066 }
1067
1068 _animation->play("finale1", false, 1, 0, false, 2);
1069 _animation->play("finale2", false, 1, 0, false, 2);
1070 _animation->play("finale3", false, 1, 0, false, 2);
1071
1072 _useEpilogue2 = true;
1073 _animation->play("finale4", false, 1, 0, false, 2);
1074 _useEpilogue2 = false;
1075 break;
1076
1077 case MOOREHEAD_DEATH:
1078 // Moorehead's death / subway train
1079 _res->addToCache("SUBWAY2.vda", "epilogue.lib");
1080 _res->addToCache("SUBWAY2.vdx", "epilogue.lib");
1081 _res->addToCache("SUBWAY3.vda", "epilogue.lib");
1082 _res->addToCache("SUBWAY3.vdx", "epilogue.lib");
1083
1084 _animation->play("SUBWAY1", false, 1, 3, true, 2);
1085 _animation->play("SUBWAY2", false, 1, 0, false, 2);
1086 _animation->play("SUBWAY3", false, 1, 0, false, 2);
1087
1088 // Set fading to direct fade temporary so the transition goes quickly.
1089 _scene->_tempFadeStyle = _screen->_fadeStyle ? 257 : 256;
1090 _screen->_fadeStyle = false;
1091 break;
1092
1093 case BRUMWELL_SUICIDE:
1094 // Brumwell suicide
1095 _animation->play("suicid", false, 1, 3, true, 2);
1096 break;
1097 default:
1098 break;
1099 }
1100
1101 // Except for the Moorehead Murder scene, fade to black first
1102 if (_scene->_goToScene != MOOREHEAD_DEATH) {
1103 _events->wait(40);
1104 _screen->fadeToBlack(3);
1105 }
1106
1107 switch (_scene->_goToScene) {
1108 case 52:
1109 _scene->_goToScene = LAWYER_OFFICE; // Go to the Lawyer's Office
1110 _map->_bigPos = Common::Point(0, 0); // Overland scroll position
1111 _map->_overPos = Common::Point(22900 - 600, 9400 + 900); // Overland position
1112 _map->_oldCharPoint = LAWYER_OFFICE;
1113 break;
1114
1115 case 53:
1116 _scene->_goToScene = STATION; // Go to St. Pancras Station
1117 _map->_bigPos = Common::Point(0, 0); // Overland scroll position
1118 _map->_overPos = Common::Point(32500 - 600, 3000 + 900); // Overland position
1119 _map->_oldCharPoint = STATION;
1120 break;
1121
1122 default:
1123 _scene->_goToScene = BAKER_STREET; // Back to Baker st.
1124 _map->_bigPos = Common::Point(0, 0); // Overland scroll position
1125 _map->_overPos = Common::Point(14500 - 600, 8400 + 900); // Overland position
1126 _map->_oldCharPoint = BAKER_STREET;
1127 break;
1128 }
1129
1130 // Free any song from the previous scene
1131 _music->freeSong();
1132 break;
1133
1134 case EXIT_GAME:
1135 // Exit game
1136 _screen->fadeToBlack(3);
1137 quitGame();
1138 return;
1139
1140 default:
1141 break;
1142 }
1143
1144 _events->setCursor(ARROW);
1145
1146 if (_scene->_goToScene == 99) {
1147 // Darts Board minigame
1148 _darts->playDarts();
1149 _mapResult = _scene->_goToScene = PUB_INTERIOR;
1150 }
1151
1152 _mapResult = _scene->_goToScene;
1153 }
1154
eraseBrumwellMirror()1155 void ScalpelEngine::eraseBrumwellMirror() {
1156 Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
1157
1158 // If player is in range of the mirror, then restore background from the secondary back buffer
1159 if (Common::Rect(70, 100, 200, 200).contains(pt)) {
1160 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 18),
1161 Common::Rect(137, 18, 184, 74));
1162 }
1163 }
1164
doBrumwellMirror()1165 void ScalpelEngine::doBrumwellMirror() {
1166 People &people = *_people;
1167 Person &player = people[HOLMES];
1168
1169 Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
1170 int frameNum = player._walkSequences[player._sequenceNumber][player._frameNumber] +
1171 player._walkSequences[player._sequenceNumber][0] - 2;
1172
1173 switch ((*_people)[HOLMES]._sequenceNumber) {
1174 case WALK_DOWN:
1175 frameNum -= 7;
1176 break;
1177 case WALK_UP:
1178 frameNum += 7;
1179 break;
1180 case WALK_DOWNRIGHT:
1181 frameNum += 7;
1182 break;
1183 case WALK_UPRIGHT:
1184 frameNum -= 7;
1185 break;
1186 case WALK_DOWNLEFT:
1187 frameNum += 7;
1188 break;
1189 case WALK_UPLEFT:
1190 frameNum -= 7;
1191 break;
1192 case STOP_DOWN:
1193 frameNum -= 10;
1194 break;
1195 case STOP_UP:
1196 frameNum += 11;
1197 break;
1198 case STOP_DOWNRIGHT:
1199 frameNum -= 15;
1200 break;
1201 case STOP_DOWNLEFT:
1202 frameNum -= 15;
1203 break;
1204 case STOP_UPRIGHT:
1205 case STOP_UPLEFT:
1206 frameNum += 15;
1207 if (frameNum == 55)
1208 frameNum = 54;
1209 break;
1210 default:
1211 break;
1212 }
1213
1214 if (Common::Rect(80, 100, 145, 138).contains(pt)) {
1215 // Get the frame of Sherlock to draw
1216 ImageFrame &imageFrame = (*people[HOLMES]._images)[frameNum];
1217
1218 // Draw the mirror image of Holmes
1219 bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT
1220 || people[HOLMES]._sequenceNumber == WALK_UPRIGHT || people[HOLMES]._sequenceNumber == STOP_UPRIGHT
1221 || people[HOLMES]._sequenceNumber == WALK_DOWNLEFT || people[HOLMES]._sequenceNumber == STOP_DOWNLEFT;
1222 _screen->_backBuffer1.SHtransBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped);
1223
1224 // Redraw the mirror borders to prevent the drawn image of Holmes from appearing outside of the mirror
1225 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(114, 18),
1226 Common::Rect(114, 18, 137, 114));
1227 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 70),
1228 Common::Rect(137, 70, 142, 114));
1229 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(142, 71),
1230 Common::Rect(142, 71, 159, 114));
1231 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(159, 72),
1232 Common::Rect(159, 72, 170, 116));
1233 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(170, 73),
1234 Common::Rect(170, 73, 184, 114));
1235 _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(184, 18),
1236 Common::Rect(184, 18, 212, 114));
1237 }
1238 }
1239
flushBrumwellMirror()1240 void ScalpelEngine::flushBrumwellMirror() {
1241 Common::Point pt((*_people)[HOLMES]._position.x / FIXED_INT_MULTIPLIER, (*_people)[HOLMES]._position.y / FIXED_INT_MULTIPLIER);
1242
1243 // If player is in range of the mirror, then draw the entire mirror area to the screen
1244 if (Common::Rect(70, 100, 200, 200).contains(pt))
1245 _screen->slamArea(137, 18, 47, 56);
1246 }
1247
1248
showScummVMSaveDialog()1249 void ScalpelEngine::showScummVMSaveDialog() {
1250 GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
1251
1252 int slot = dialog->runModalWithCurrentTarget();
1253 if (slot >= 0) {
1254 Common::String desc = dialog->getResultString();
1255
1256 saveGameState(slot, desc);
1257 }
1258
1259 delete dialog;
1260 }
1261
showScummVMRestoreDialog()1262 void ScalpelEngine::showScummVMRestoreDialog() {
1263 GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
1264
1265 int slot = dialog->runModalWithCurrentTarget();
1266 if (slot >= 0) {
1267 loadGameState(slot);
1268 }
1269
1270 delete dialog;
1271 }
1272
play3doMovie(const Common::String & filename,const Common::Point & pos,bool isPortrait)1273 bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::Point &pos, bool isPortrait) {
1274 Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
1275 Video::ThreeDOMovieDecoder *videoDecoder = new Video::ThreeDOMovieDecoder();
1276 Graphics::ManagedSurface tempSurface;
1277
1278 Common::Point framePos(pos.x, pos.y);
1279 ImageFile3DO *frameImageFile = nullptr;
1280 ImageFrame *frameImage = nullptr;
1281 bool frameShown = false;
1282
1283 if (!videoDecoder->loadFile(filename)) {
1284 warning("Scalpel3DOMoviePlay: could not open '%s'", filename.c_str());
1285 return false;
1286 }
1287
1288 bool halfSize = isPortrait && !_isScreenDoubled;
1289 if (isPortrait) {
1290 // only for portrait videos, not for EA intro logo and such
1291 if ((framePos.x >= 8) && (framePos.y >= 8)) { // safety check
1292 framePos.x -= 8;
1293 framePos.y -= 8; // frame is 8 pixels on left + top, and 7 pixels on right + bottom
1294 }
1295
1296 frameImageFile = new ImageFile3DO("vidframe.cel", kImageFile3DOType_Cel);
1297 frameImage = &(*frameImageFile)[0];
1298 }
1299
1300 bool skipVideo = false;
1301 //byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel;
1302 uint16 width = videoDecoder->getWidth();
1303 uint16 height = videoDecoder->getHeight();
1304 //uint16 pitch = videoDecoder->getWidth() * bytesPerPixel;
1305
1306 _events->clearEvents();
1307 videoDecoder->start();
1308
1309 // If we're to show the movie at half-size, we'll need a temporary intermediate surface
1310 if (halfSize)
1311 tempSurface.create(width / 2, height / 2);
1312
1313 while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
1314 if (videoDecoder->needsUpdate()) {
1315 const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
1316
1317 if (frame) {
1318 if (halfSize) {
1319 // movies are 152 x 200
1320
1321 // Downscale, but calculate average color out of 4 pixels and put that average into the target pixel
1322 // TODO: 3DO actually did pixel weighting, exact details about this are unknown
1323 // It's also unknown what 3DO exactly did for interpolation
1324 // and it's also unknown atm if the CinePak videos contained pixel weighting information
1325
1326 if ((height & 1) || (width & 1)) {
1327 error("Scalpel3DOMoviePlay: critical error, half-size requested on video with uneven height/width");
1328 }
1329
1330 for (int downscaleY = 0; downscaleY < height / 2; downscaleY++) {
1331 const uint16 *downscaleSource1Ptr = (const uint16 *)frame->getBasePtr(0, downscaleY * 2);
1332 const uint16 *downscaleSource2Ptr = (const uint16 *)frame->getBasePtr(0, (downscaleY * 2) + 1);
1333 uint16 *downscaleTargetPtr = (uint16 *)tempSurface.getBasePtr(0, downscaleY);
1334
1335 for (int downscaleX = 0; downscaleX < width / 2; downscaleX++) {
1336 // get 4 pixel colors
1337 uint16 downscaleColor = *downscaleSource1Ptr;
1338 uint32 downscaleRed = downscaleColor >> 11; // 5 bits
1339 uint32 downscaleGreen = (downscaleColor >> 5) & 0x3f; // 6 bits
1340 uint32 downscaleBlue = downscaleColor & 0x1f;
1341
1342 downscaleSource1Ptr++;
1343 downscaleColor = *downscaleSource1Ptr;
1344 downscaleRed += downscaleColor >> 11;
1345 downscaleGreen += (downscaleColor >> 5) & 0x3f;
1346 downscaleBlue += downscaleColor & 0x1f;
1347
1348 downscaleColor = *downscaleSource2Ptr;
1349 downscaleRed += downscaleColor >> 11;
1350 downscaleGreen += (downscaleColor >> 5) & 0x3f;
1351 downscaleBlue += downscaleColor & 0x1f;
1352
1353 downscaleSource2Ptr++;
1354 downscaleColor = *downscaleSource2Ptr;
1355 downscaleRed += downscaleColor >> 11;
1356 downscaleGreen += (downscaleColor >> 5) & 0x3f;
1357 downscaleBlue += downscaleColor & 0x1f;
1358
1359 // Divide colors by 4, so that we get the average
1360 downscaleRed = downscaleRed >> 2;
1361 downscaleGreen = downscaleGreen >> 2;
1362 downscaleBlue = downscaleBlue >> 2;
1363
1364 // write new color to target pixel
1365 downscaleColor = (downscaleRed << 11) | (downscaleGreen << 5) | downscaleBlue;
1366 *downscaleTargetPtr = downscaleColor;
1367
1368 downscaleSource1Ptr++;
1369 downscaleSource2Ptr++;
1370 downscaleTargetPtr++;
1371 }
1372 }
1373
1374 // Point the drawing frame to the temporary surface
1375 frame = &tempSurface.rawSurface();
1376 }
1377
1378 if (isPortrait && !frameShown) {
1379 // Draw the frame (not the frame of the video, but a frame around the video) itself
1380 _screen->SHtransBlitFrom(frameImage->_frame, framePos);
1381 frameShown = true;
1382 }
1383
1384 if (isPortrait && !halfSize) {
1385 screen.rawBlitFrom(*frame, Common::Point(pos.x * 2, pos.y * 2));
1386 } else {
1387 _screen->SHblitFrom(*frame, pos);
1388 }
1389
1390 _screen->update();
1391 }
1392 }
1393
1394 _events->pollEventsAndWait();
1395 _events->setButtonState();
1396
1397 if (_events->kbHit()) {
1398 Common::KeyState keyState = _events->getKey();
1399 if (keyState.keycode == Common::KEYCODE_ESCAPE)
1400 skipVideo = true;
1401 } else if (_events->_pressed) {
1402 skipVideo = true;
1403 }
1404 }
1405
1406 if (halfSize)
1407 tempSurface.free();
1408
1409 videoDecoder->close();
1410 delete videoDecoder;
1411
1412 if (isPortrait) {
1413 delete frameImageFile;
1414 }
1415
1416 // Restore scene
1417 screen._backBuffer1.SHblitFrom(screen._backBuffer2);
1418 _scene->updateBackground();
1419 screen.slamArea(0, 0, screen.width(), CONTROLS_Y);
1420
1421 return !skipVideo;
1422 }
1423
1424 } // End of namespace Scalpel
1425
1426 } // End of namespace Sherlock
1427