aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/scalpel/scalpel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sherlock/scalpel/scalpel.cpp')
-rw-r--r--engines/sherlock/scalpel/scalpel.cpp370
1 files changed, 314 insertions, 56 deletions
diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp
index fc8b1e609a..a76b73e109 100644
--- a/engines/sherlock/scalpel/scalpel.cpp
+++ b/engines/sherlock/scalpel/scalpel.cpp
@@ -21,16 +21,18 @@
*/
#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/tsage/logo.h"
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
#include "sherlock/animation.h"
-// for 3DO
#include "sherlock/scalpel/3do/movie_decoder.h"
namespace Sherlock {
@@ -174,29 +176,103 @@ const PeopleData PEOPLE_DATA[MAX_PEOPLE] = {
{ "INSP", "Inspector Lestrade", { 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::initialize() {
- // 3DO actually uses RGB555, but some platforms of ours only support RGB565, so we use that
-
- if (getPlatform() == Common::kPlatform3DO) {
- const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
- // 320x200 16-bit RGB565 for 3DO support
- initGraphics(320, 200, false, &pixelFormatRGB565);
- } else {
+void ScalpelEngine::setupGraphics() {
+ if (getPlatform() != Common::kPlatform3DO) {
// 320x200 palettized
initGraphics(320, 200, false);
+ } 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(true);
+ 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, false, &pixelFormatRGB565);
+ }
}
+}
+
+void ScalpelEngine::initialize() {
+ // Setup graphics mode
+ setupGraphics();
// Let the base engine intialize
SherlockEngine::initialize();
@@ -242,6 +318,8 @@ void ScalpelEngine::showOpening() {
if (isDemo() && _interactiveFl)
return;
+ _events->setFrameRate(60);
+
if (getPlatform() == Common::kPlatform3DO) {
show3DOSplash();
@@ -255,20 +333,22 @@ void ScalpelEngine::showOpening() {
_events->clearEvents();
_music->stopMusic();
- return;
- }
+ } else {
+ TsAGE::Logo::show(this);
- TsAGE::Logo::show(this);
- finished = showCityCutscene();
- if (finished)
- finished = showAlleyCutscene();
- if (finished)
- finished = showStreetCutscene();
- if (finished)
- showOfficeCutscene();
+ finished = showCityCutscene();
+ if (finished)
+ finished = showAlleyCutscene();
+ if (finished)
+ finished = showStreetCutscene();
+ if (finished)
+ showOfficeCutscene();
- _events->clearEvents();
- _music->stopMusic();
+ _events->clearEvents();
+ _music->stopMusic();
+ }
+
+ _events->setFrameRate(GAME_FRAME_RATE);
}
bool ScalpelEngine::showCityCutscene() {
@@ -595,7 +675,7 @@ bool ScalpelEngine::show3DOSplash() {
if (finished) {
// EA logo movie
- Scalpel3DOMoviePlay("EAlogo.stream", Common::Point(20, 0));
+ play3doMovie("EAlogo.stream", Common::Point(20, 0));
}
// Always clear screen
@@ -604,9 +684,10 @@ bool ScalpelEngine::show3DOSplash() {
}
bool ScalpelEngine::showCityCutscene3DO() {
+ Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
_animation->_soundLibraryFilename = "TITLE.SND";
- _screen->clear();
+ screen.clear();
bool finished = _events->delay(2500, true);
// rain.aiff seems to be playing in an endless loop until
@@ -619,8 +700,8 @@ bool ScalpelEngine::showCityCutscene3DO() {
_music->loadSong("prolog");
// Fade screen to grey
- _screen->_backBuffer1.fill(0xCE59); // RGB565: 25, 50, 25 (grey)
- _screen->fadeIntoScreen3DO(2);
+ screen._backBuffer1.fill(0xCE59); // RGB565: 25, 50, 25 (grey)
+ screen.fadeIntoScreen3DO(2);
}
if (finished) {
@@ -628,27 +709,27 @@ bool ScalpelEngine::showCityCutscene3DO() {
}
if (finished) {
- _screen->_backBuffer1.fill(0); // fill backbuffer with black to avoid issues during fade from white
+ screen._backBuffer1.fill(0); // fill backbuffer with black to avoid issues during fade from white
finished = _animation->play3DO("26open1", true, 1, true, 2);
}
if (finished) {
- _screen->_backBuffer1.blitFrom(*_screen); // save into backbuffer 1, used for fade
- _screen->_backBuffer2.blitFrom(*_screen); // save into backbuffer 2, for restoring later
+ screen._backBuffer1.blitFrom(*_screen); // save into backbuffer 1, used for fade
+ screen._backBuffer2.blitFrom(*_screen); // save into backbuffer 2, for restoring later
// "London, England"
ImageFile3DO titleImage_London("title2a.cel", kImageFile3DOType_Cel);
- _screen->_backBuffer1.transBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
+ screen._backBuffer1.transBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
- _screen->fadeIntoScreen3DO(1);
+ screen.fadeIntoScreen3DO(1);
finished = _events->delay(1500, true);
if (finished) {
// "November, 1888"
ImageFile3DO titleImage_November("title2b.cel", kImageFile3DOType_Cel);
- _screen->_backBuffer1.transBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
+ screen._backBuffer1.transBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
- _screen->fadeIntoScreen3DO(1);
+ screen.fadeIntoScreen3DO(1);
finished = _music->waitUntilMSec(14700, 0, 0, 5000);
}
@@ -662,21 +743,21 @@ bool ScalpelEngine::showCityCutscene3DO() {
finished = _animation->play3DO("26open2", true, 1, false, 2);
if (finished) {
- _screen->_backBuffer1.blitFrom(*_screen); // save into backbuffer 1, used for fade
+ screen._backBuffer1.blitFrom(screen); // save into backbuffer 1, used for fade
// "Sherlock Holmes" (title)
ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel", kImageFile3DOType_Cel);
- _screen->_backBuffer1.transBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
+ screen._backBuffer1.transBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
// Blend in
- _screen->fadeIntoScreen3DO(2);
+ 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->transBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190));
+ screen.transBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190));
finished = _events->delay(3500, true);
}
}
@@ -686,27 +767,28 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
// Fade to black
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(3);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(3);
}
if (finished) {
// "In the alley behind the Regency Theatre..."
ImageFile3DO titleImage_InTheAlley("title1d.cel", kImageFile3DOType_Cel);
- _screen->_backBuffer1.transBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
+ screen._backBuffer1.transBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
// Fade in
- _screen->fadeIntoScreen3DO(4);
+ screen.fadeIntoScreen3DO(4);
finished = _music->waitUntilMSec(39900, 0, 0, 2500);
// Fade out
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(4);
+ 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)
@@ -714,8 +796,8 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
if (finished) {
// Fade out...
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(3);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(3);
finished = _music->waitUntilMSec(67100, 0, 0, 1000); // 66700
}
@@ -730,8 +812,8 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
// Show screaming victim
ImageFile3DO titleImage_ScreamingVictim("scream.cel", kImageFile3DOType_Cel);
- _screen->clear();
- _screen->transBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
+ screen.clear();
+ screen.transBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
// Play "scream.aiff"
if (_sound->_voices)
@@ -742,8 +824,8 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
if (finished) {
// Fade out
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(5);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(5);
finished = _music->waitUntilMSec(84400, 0, 0, 2000);
}
@@ -753,17 +835,17 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
if (finished) {
// Fade out
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(5);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(5);
}
if (finished) {
// "Early the following morning on Baker Street..."
ImageFile3DO titleImage_EarlyTheFollowingMorning("title3.cel", kImageFile3DOType_Cel);
- _screen->_backBuffer1.transBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
+ screen._backBuffer1.transBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
// Fade in
- _screen->fadeIntoScreen3DO(4);
+ screen.fadeIntoScreen3DO(4);
finished = _music->waitUntilMSec(96700, 0, 0, 3000);
}
@@ -771,12 +853,13 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
}
bool ScalpelEngine::showStreetCutscene3DO() {
+ Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
bool finished = true;
if (finished) {
// fade out "Early the following morning..."
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(4);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(4);
// wait for music a bit
finished = _music->waitUntilMSec(100300, 0, 0, 1000);
@@ -795,8 +878,8 @@ bool ScalpelEngine::showStreetCutscene3DO() {
if (finished) {
// Fade out
- _screen->_backBuffer1.clear();
- _screen->fadeIntoScreen3DO(4);
+ screen._backBuffer1.clear();
+ screen.fadeIntoScreen3DO(4);
}
return finished;
@@ -1155,6 +1238,181 @@ void ScalpelEngine::flushBrumwellMirror() {
_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::Surface tempSurface;
+
+ Common::Point framePos(pos.x, pos.y);
+ 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
+ }
+
+ ImageFile3DO *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, _screen->getPixelFormat());
+
+ 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;
+ }
+
+ if (isPortrait && !frameShown) {
+ // Draw the frame (not the frame of the video, but a frame around the video) itself
+ _screen->transBlitFrom(frameImage->_frame, framePos);
+ frameShown = true;
+ }
+
+ if (isPortrait && !halfSize) {
+ screen.rawBlitFrom(*frame, Common::Point(pos.x * 2, pos.y * 2));
+ } else {
+ _screen->blitFrom(*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 (halfSize) {
+ delete frameImage;
+ }
+
+ // Restore scene
+ screen._backBuffer1.blitFrom(screen._backBuffer2);
+ _scene->updateBackground();
+ screen.slamArea(0, 0, screen.w(), CONTROLS_Y);
+
+ return !skipVideo;
+}
+
} // End of namespace Scalpel
} // End of namespace Sherlock