From 13e7c9d4d9c6ff0ae682c4885d902c2dda516d22 Mon Sep 17 00:00:00 2001 From: AndywinXp Date: Mon, 3 Oct 2022 22:52:00 +0200 Subject: [PATCH] SCUMM: Implement basic support for v0-3 GUI --- engines/scumm/cursor.cpp | 44 +++++++++++ engines/scumm/dialogs.cpp | 19 ++--- engines/scumm/dialogs.h | 2 +- engines/scumm/gfx_gui.cpp | 142 ++++++++++++++++++++++++++++++++---- engines/scumm/input.cpp | 92 +++++++++++++++-------- engines/scumm/script_v2.cpp | 94 +----------------------- engines/scumm/script_v4.cpp | 2 + engines/scumm/scumm.cpp | 5 +- engines/scumm/scumm.h | 7 ++ engines/scumm/scumm_v2.h | 2 + engines/scumm/scumm_v5.h | 2 +- engines/scumm/string.cpp | 97 ++++++++++++++++++++++++ 12 files changed, 356 insertions(+), 152 deletions(-) diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index 857cdd42271..9652ef273ca 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -91,6 +91,32 @@ static const byte default_v6_cursor[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; +static const byte c64_dos_snail_cursor[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x0F,0x0F,0xFF,0xFF,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x0F,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x0F,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0F,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0F,0x0F,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF, + 0xFF,0xFF,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF, + 0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF, + 0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0xFF,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,0x0F,0x0F, + + // Blank pixels... + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + #ifdef ENABLE_SCUMM_7_8 static const byte default_v7_cursor[] = { 0x01,0x01,0x01,0x01, 0x00,0x0F,0x00, 0x01,0x01,0x01,0x01, @@ -809,6 +835,24 @@ void ScummEngine_v2::setBuiltinCursor(int idx) { updateCursor(); } +void ScummEngine_v2::setSnailCursor() { + memcpy(_grabbedCursor, c64_dos_snail_cursor, sizeof(c64_dos_snail_cursor)); + + if (_game.version == 0) { + for (int i = 0; i < sizeof(c64_dos_snail_cursor); i++) { + if (_grabbedCursor[i] == 0x0F) + _grabbedCursor[i] = 0x01; + } + } + + _cursor.width = 24; + _cursor.height = 21; + _cursor.hotspotX = 11; + _cursor.hotspotY = 10; + + updateCursor(); +} + void ScummEngine_v5::resetCursors() { static const uint16 default_cursor_images[4][16] = { /* cross-hair */ diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 0e0d4d03a97..86bb9bdef02 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -429,8 +429,8 @@ void InfoDialog::reflowLayout() { _text->setSize(_w, _h); } -const char *InfoDialog::getPlainEngineString(int stringno) { - const char *result; +const char *InfoDialog::getPlainEngineString(int stringno, bool forceHardcodedString) { + const char *result = nullptr; if (stringno == 0) return nullptr; @@ -450,7 +450,8 @@ const char *InfoDialog::getPlainEngineString(int stringno) { result = string_map_table_v6[stringno - 1].string; } } else if (_vm->_game.version >= 3) { - result = (const char *)_vm->getStringAddress(getStaticResString(_vm->_language, stringno - 1).num); + if (!forceHardcodedString) + result = (const char *)_vm->getStringAddress(getStaticResString(_vm->_language, stringno - 1).num); if (!result) { result = getStaticResString(_vm->_language, stringno - 1).string; @@ -587,12 +588,12 @@ const ResString &InfoDialog::getStaticResString(Common::Language lang, int strin {6, "Wollen Sie wirklich beenden? (j/n)j"} // (matching the previous sentence) }, { // Italian - {1, "Inserisci il disco %c e premi un pulsante per continuare."}, - {2, "Impossibile trovare %s, (%c%d) Premere un pulsante."}, - {3, "Errore nella lettura del disco %c, (%c%d) Premere un pulsante."}, - {4, "Gioco in pausa. Premi SPAZIO per continuare."}, - {5, "Sei sicuro di voler ricominciare? (S/N)S"}, - {6, "Sei sicuro di voler uscire? (S/N)S"} + {1, "Inserisci il Disk n. Premi ENTER."}, // Original DOS Italian v2 + {2, "Non trovato il file nn.lfl. Premi ENTER."}, // Original DOS Italian v2 + {3, "ERROR READING %d type %d"}, // As found on the Italian v2 executable... + {4, "PAUSA - Premere SPAZIO per continuare."}, // Original DOS Italian v2 + {5, "Sei sicuro di voler ricominciare? (s/n)s"}, // Original DOS Italian v2 + {6, "Sei sicuro di voler uscire? (s/n)s"} // Original DOS Italian v2 }, { // Spanish {1, "Introduce el disco %c y pulsa un bot""\xa2""n para continuar."}, diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index df3a60e95e4..a561f9a5353 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -96,7 +96,7 @@ public: } void reflowLayout() override; - const char *getPlainEngineString(int stringno); + const char *getPlainEngineString(int stringno, bool forceHardcodedString = false); protected: // Query a string from the resources diff --git a/engines/scumm/gfx_gui.cpp b/engines/scumm/gfx_gui.cpp index 04a82984a4c..fc29df89e3b 100644 --- a/engines/scumm/gfx_gui.cpp +++ b/engines/scumm/gfx_gui.cpp @@ -20,6 +20,7 @@ */ #include "scumm/scumm.h" +#include "scumm/scumm_v2.h" #include "scumm/scumm_v4.h" #include "scumm/scumm_v6.h" #include "scumm/scumm_v8.h" @@ -261,6 +262,91 @@ Common::KeyState ScummEngine::showBannerAndPause(int bannerId, int32 waitTime, c return ks; } +Common::KeyState ScummEngine::printMessageAndPause(const char *msg, int32 waitTime, bool drawOnSentenceLine) { + Common::Rect sentenceline; + + // Pause the engine + PauseToken pt = pauseEngine(); + + if (drawOnSentenceLine) { + setSnailCursor(); + + _string[2].charset = 1; + _string[2].ypos = _virtscr[kVerbVirtScreen].topline; + _string[2].xpos = 0; + _string[2].right = _virtscr[kVerbVirtScreen].w - 1; + if (_game.platform == Common::kPlatformNES) { + _string[2].xpos = 16; + _string[2].color = 0; + } else if (_game.platform == Common::kPlatformC64) { + _string[2].color = 16; + } else { + _string[2].color = 13; + } + + byte string[80]; + const char *ptr = msg; + int i = 0, len = 0; + + // Maximum length of printable characters + int maxChars = (_game.platform == Common::kPlatformNES) ? 60 : 40; + while (*ptr) { + if (*ptr != '@') + len++; + if (len > maxChars) { + break; + } + + string[i++] = *ptr++; + + if (_game.platform == Common::kPlatformNES && len == 30) { + string[i++] = 0xFF; + string[i++] = 8; + } + } + string[i] = 0; + + if (_game.platform == Common::kPlatformNES) { + sentenceline.top = _virtscr[kVerbVirtScreen].topline; + sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 16; + sentenceline.left = 16; + sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; + } else { + sentenceline.top = _virtscr[kVerbVirtScreen].topline; + sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8; + sentenceline.left = 0; + sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; + } + restoreBackground(sentenceline); + drawString(2, (byte *)string); + drawDirtyScreenParts(); + } else { + + } + + // Wait until the engine receives a new Keyboard or Mouse input, + // unless we have specified a positive waitTime: in that case, the banner + // will stay on screen until an input has been received or until the time-out. + Common::KeyState ks = Common::KEYCODE_INVALID; + bool leftBtnPressed = false, rightBtnPressed = false; + if (waitTime) { + waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed); + } + setBuiltinCursor(0); + restoreBackground(sentenceline); + + // Restore the sentence which was being displayed before + // (MANIAC v1 doesn't do this) + if (!(_game.id == GID_MANIAC && _game.version <= 1)) + drawSentence(); + + // Finally, resume the engine, clear the input state, and restore the charset. + pt.clear(); + clearClickedStatus();; + + return ks; +} + Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int color, int32 waitTime) { // LOOM VGA uses the new style GUI if (_game.version == 4 && _game.id == GID_LOOM) { @@ -291,10 +377,9 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co // Pause the engine PauseToken pt = pauseEngine(); - // Backup the current charsetId, since we're going to switch - // to charsetId == 1... + // Backup the current charsetId... int oldId = _charset->getCurID(); - _charset->setCurID(1); + _charset->setCurID(_game.version > 3 ? 1 : 0); // Take all the necessary measurements for the box which // will contain the string... @@ -304,14 +389,35 @@ Common::KeyState ScummEngine::showOldStyleBannerAndPause(const char *msg, int co bannerMsgWidth = 100; startingPointY = 80; - bannerSaveYStart = startingPointY - 2; - + bannerSaveYStart = startingPointY - (_game.version == 4 ? 2 : _virtscr[kMainVirtScreen].topline); // Save the pixels which will be overwritten by the banner, // so that we can restore them later... if (!_bannerMem) { - int rowSize = _screenWidth + 8; - _bannerMemSize = (bannerMsgHeight + 2) * (_screenWidth + 8); + int rowSize = _screenWidth + (_game.version == 4 ? 8 : 0); + + // FM-Towns games draw the banner on the text surface, so let's save that +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && !_textSurfBannerMem) { + rowSize *= _textSurfaceMultiplier; + bannerSaveYStart *= _textSurfaceMultiplier; + _textSurfBannerMemSize = bannerMsgHeight * rowSize * _textSurfaceMultiplier; + _textSurfBannerMem = (byte *)malloc(_textSurfBannerMemSize * sizeof(byte)); + if (_textSurfBannerMem) { + memcpy( + _textSurfBannerMem, + &((byte *)_textSurface.getBasePtr(0, _screenTop * _textSurfaceMultiplier))[rowSize * bannerSaveYStart], + _textSurfBannerMemSize); + } + + // We're going to use these same values for saving the + // virtual screen surface, so let's un-multiply them... + rowSize /= _textSurfaceMultiplier; + bannerSaveYStart /= _textSurfaceMultiplier; + } +#endif + + _bannerMemSize = (bannerMsgHeight + 2) * (rowSize); _bannerMem = (byte *)malloc(_bannerMemSize * sizeof(byte)); if (_bannerMem) { memcpy( @@ -362,7 +468,7 @@ void ScummEngine::clearBanner() { // Restore the GFX content which was under the banner, // and then mark that part of the screen as dirty. if (_bannerMem) { - int rowSize = _screenWidth + 8; + int rowSize = _screenWidth + (_game.version >= 4 ? 8 : 0); // Don't manually clear the banner if a SMUSH movie is playing, // as that will cause some rare small glitches. The SMUSH player // will take care of that for us automatically when updating the @@ -371,12 +477,14 @@ void ScummEngine::clearBanner() { int startingPointY; if (_game.version == 8) { startingPointY = _screenHeight / 2 - 10; + } else if (_game.version < 4) { + startingPointY = 80; } else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformFMTowns) { startingPointY = 78; } else { startingPointY = ((_game.version < 7) ? 80 - 2 : _screenHeight / 2 - 10); } - + startingPointY -= (_game.version >= 4 ? 0 : _virtscr[kMainVirtScreen].topline); // FM-Towns games draw the banners on the text surface, so restore both surfaces... #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (_game.platform == Common::kPlatformFMTowns && _textSurfBannerMem) { @@ -393,6 +501,7 @@ void ScummEngine::clearBanner() { startingPointY /= _textSurfaceMultiplier; } #endif + memcpy( &_virtscr[kMainVirtScreen].getPixels(0, _screenTop)[rowSize * startingPointY], _bannerMem, @@ -1138,7 +1247,7 @@ void ScummEngine::saveSurfacesPreGUI() { // so the last line is being drawn on the verb surface; to address this, we // save and restore that too. - if (_game.version < 4 || _game.version > 6) + if (_game.version < 3 || _game.version > 6) return; _tempTextSurface = (byte *)malloc(_textSurface.pitch * _textSurface.h * sizeof(byte)); @@ -1181,7 +1290,7 @@ void ScummEngine::saveSurfacesPreGUI() { void ScummEngine::restoreSurfacesPostGUI() { - if (_game.version < 4 || _game.version > 6) + if (_game.version < 3 || _game.version > 6) return; if (_tempTextSurface) { @@ -1282,10 +1391,13 @@ void ScummEngine::queryQuit(bool returnToLauncher) { // "Are you sure you want to quit? (Y/N)" Common::KeyState ks; - if (_game.version > 4) + if (_game.version > 4) { ks = showBannerAndPause(0, -1, msgLabelPtr); - else + } else if (_game.version == 4) { ks = showOldStyleBannerAndPause(msgLabelPtr, 12, -1); + } else { + ks = printMessageAndPause(msgLabelPtr, -1, true); + } _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); @@ -2999,7 +3111,7 @@ void ScummEngine::drawGUIText(const char *buttonString, Common::Rect *clipRect, _string[5].right = clipRect ? clipRect->right : _screenWidth - 1; _string[5].center = centerFlag; _string[5].color = textColor; - _string[5].charset = 1; + _string[5].charset = _game.version > 3 ? 1 : 0; drawString(5, (const byte *)buttonString); _string[5].right = tmpRight; @@ -3222,7 +3334,7 @@ const char *ScummEngine::getGUIString(int stringId) { } if (resStringId > 0) - return d.getPlainEngineString(resStringId); + return d.getPlainEngineString(resStringId, (_game.id == GID_INDY3) && stringId == gsQuitPrompt); else return _emptyMsg; } diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 716e56a0ea3..b6791c0bf1e 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -121,7 +121,7 @@ void ScummEngine::parseEvent(Common::Event event) { _fastMode ^= 1; } else if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_g) { _fastMode ^= 2; - } else if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_s) { + } else if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.hasFlags(Common::KBD_ALT) && event.kbd.keycode == Common::KEYCODE_s) { _res->resourceStats(); } else if (event.kbd.hasFlags(Common::KBD_ALT) && event.kbd.keycode == Common::KEYCODE_x) { if (_game.version < 8) { @@ -195,6 +195,7 @@ void ScummEngine::parseEvent(Common::Event event) { _leftBtnPressed |= msClicked|msDown; else if (event.type == Common::EVENT_RBUTTONDOWN) _rightBtnPressed |= msClicked|msDown; + _mouse.x = event.mouse.x; _mouse.y = event.mouse.y; @@ -582,7 +583,7 @@ void ScummEngine::waitForBannerInput(int32 waitTime, Common::KeyState &ks, bool while (!validKey && !leftBtnClicked && !rightBtnClicked && !(handeleMouseWheel && _mouseWheelFlag)) { waitForTimer(1); // Allow the engine to update the screen and fetch new inputs... - if (_game.version < 7 && (_guiCursorAnimCounter++ & 16)) { + if (_game.version > 2 && _game.version < 7 && (_guiCursorAnimCounter++ & 16)) { _guiCursorAnimCounter = 0; animateCursor(); } @@ -794,6 +795,18 @@ void ScummEngine_v6::processKeyboard(Common::KeyState lastKeyHit) { } void ScummEngine_v2::processKeyboard(Common::KeyState lastKeyHit) { + if (isUsingOriginalGUI()) { + if (lastKeyHit.keycode == Common::KEYCODE_F5) { + prepareSavegame(); + if (_game.id == GID_MANIAC && _game.version == 0) { + runScript(2, 0, 0, nullptr); + } + if (_game.id == GID_MANIAC && _game.platform == Common::kPlatformNES) { + runScript(163, 0, 0, nullptr); + } + } + } + // RETURN is used to skip cutscenes in the Commodore 64 version of Zak McKracken if (_game.id == GID_ZAK &&_game.platform == Common::kPlatformC64 && lastKeyHit.keycode == Common::KEYCODE_RETURN && lastKeyHit.hasFlags(0)) { lastKeyHit = Common::KeyState(Common::KEYCODE_ESCAPE); @@ -823,13 +836,15 @@ void ScummEngine_v2::processKeyboard(Common::KeyState lastKeyHit) { ScummEngine::processKeyboard(lastKeyHit); // On Alt-F5 prepare savegame for the original save/load dialog. - if (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(Common::KBD_ALT)) { - prepareSavegame(); - if (_game.id == GID_MANIAC && _game.version == 0) { - runScript(2, 0, 0, nullptr); - } - if (_game.id == GID_MANIAC &&_game.platform == Common::kPlatformNES) { - runScript(163, 0, 0, nullptr); + if (!isUsingOriginalGUI()) { + if (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(Common::KBD_ALT)) { + prepareSavegame(); + if (_game.id == GID_MANIAC && _game.version == 0) { + runScript(2, 0, 0, nullptr); + } + if (_game.id == GID_MANIAC && _game.platform == Common::kPlatformNES) { + runScript(163, 0, 0, nullptr); + } } } @@ -844,25 +859,33 @@ void ScummEngine_v2::processKeyboard(Common::KeyState lastKeyHit) { } void ScummEngine_v3::processKeyboard(Common::KeyState lastKeyHit) { + if (isUsingOriginalGUI()) { + if (lastKeyHit.keycode == Common::KEYCODE_F5) { + prepareSavegame(); + } + } + // Fall back to default behavior ScummEngine::processKeyboard(lastKeyHit); - // On Alt-F5 prepare savegame for the original save/load dialog. - if (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(Common::KBD_ALT)) { - prepareSavegame(); - } + if (!isUsingOriginalGUI()) { + // On Alt-F5 prepare savegame for the original save/load dialog. + if (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(Common::KBD_ALT)) { + prepareSavegame(); + } - // 'i' brings up an IQ dialog in Indy3 (disabled in save/load dialog for input) - if (lastKeyHit.ascii == 'i' && _game.id == GID_INDY3 && _currentRoom != 14) { - // SCUMM var 244 is the episode score - // and var 245 is the series score - char text[50]; + // 'i' brings up an IQ dialog in Indy3 (disabled in save/load dialog for input) + if (lastKeyHit.ascii == 'i' && _game.id == GID_INDY3 && _currentRoom != 14) { + // SCUMM var 244 is the episode score + // and var 245 is the series score + char text[50]; - updateIQPoints(); + updateIQPoints(); - Common::sprintf_s(text, "IQ Points: Episode = %d, Series = %d", _scummVars[244], _scummVars[245]); - Indy3IQPointsDialog indy3IQPointsDialog(this, text); - runDialog(indy3IQPointsDialog); + Common::sprintf_s(text, "IQ Points: Episode = %d, Series = %d", _scummVars[244], _scummVars[245]); + Indy3IQPointsDialog indy3IQPointsDialog(this, text); + runDialog(indy3IQPointsDialog); + } } } @@ -898,6 +921,9 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { _cursor.state = oldCursorState; return; + } else if (_game.version <= 2 && lastKeyHit.keycode == Common::KEYCODE_SPACE) { + printMessageAndPause(getGUIString(gsPause), -1, true); + return; } if ((VAR_RESTART_KEY != 0xFF && (lastKeyHit.ascii == VAR(VAR_RESTART_KEY))) || @@ -1013,18 +1039,22 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { } if (VAR_MAINMENU_KEY != 0xFF && (lastKeyHit.ascii == VAR(VAR_MAINMENU_KEY) && lastKeyHit.hasFlags(0)) - && _game.platform != Common::kPlatformFMTowns) { + && _game.platform != Common::kPlatformFMTowns && _game.version > 3) { showMainMenu(); return; } - if (_game.version == 4) { - if (lastKeyHit.keycode == Common::KEYCODE_r && lastKeyHit.hasFlags(Common::KBD_CTRL)) { + if (snapScrollKeyEnabled) { + if ((_game.version <= 3 && lastKeyHit.keycode == Common::KEYCODE_i && lastKeyHit.hasFlags(Common::KBD_ALT)) || + (_game.version == 4 && lastKeyHit.keycode == Common::KEYCODE_r && lastKeyHit.hasFlags(Common::KBD_CTRL))) { + const char *msgSnap = _game.version == 4 ? "Horizontal Screen Snap" : "Screen reposition instantly"; + const char *msgScroll = _game.version == 4 ? "Horizontal Screen Scroll" : "Screen reposition by Scrolling"; + _snapScroll ^= 1; if (_snapScroll) { - showOldStyleBannerAndPause("Horizontal Screen Snap", 9, 90); + showOldStyleBannerAndPause(msgSnap, 9, 90); } else { - showOldStyleBannerAndPause("Horizontal Screen Scroll", 9, 90); + showOldStyleBannerAndPause(msgScroll, 9, 90); } if (VAR_CAMERA_FAST_X != 0xFF) @@ -1066,7 +1096,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { if (_game.id == GID_CMI) mainmenuKeyEnabled = true; - if (mainmenuKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(0))) { + if (mainmenuKeyEnabled && !isUsingOriginalGUI() && (lastKeyHit.keycode == Common::KEYCODE_F5 && lastKeyHit.hasFlags(0))) { if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, nullptr); @@ -1078,10 +1108,10 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { if (VAR_SAVELOAD_SCRIPT2 != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, nullptr); - } else if (restartKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_F8 && lastKeyHit.hasFlags(0))) { + } else if (restartKeyEnabled && !isUsingOriginalGUI() && (lastKeyHit.keycode == Common::KEYCODE_F8 && lastKeyHit.hasFlags(0))) { confirmRestartDialog(); - } else if (pauseKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_SPACE && lastKeyHit.hasFlags(0))) { + } else if (pauseKeyEnabled && !isUsingOriginalGUI() && (lastKeyHit.keycode == Common::KEYCODE_SPACE && lastKeyHit.hasFlags(0))) { pauseGame(); } else if (talkstopKeyEnabled && lastKeyHit.ascii == '.') { @@ -1095,7 +1125,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { // VAR_CUTSCENEEXIT_KEY doesn't exist in SCUMM0 if (VAR_CUTSCENEEXIT_KEY != 0xFF) _mouseAndKeyboardStat = VAR(VAR_CUTSCENEEXIT_KEY); - } else if (snapScrollKeyEnabled && lastKeyHit.keycode == Common::KEYCODE_r && + } else if (snapScrollKeyEnabled && !isUsingOriginalGUI() && lastKeyHit.keycode == Common::KEYCODE_r && lastKeyHit.hasFlags(Common::KBD_CTRL)) { _snapScroll ^= 1; if (_snapScroll) { diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index 99db95ce3d1..7981e67b65f 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -1051,99 +1051,7 @@ void ScummEngine_v2::drawPreposition(int index) { } void ScummEngine_v2::o2_drawSentence() { - Common::Rect sentenceline; - const byte *temp; - int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0); - - if (!((_userState & USERSTATE_IFACE_SENTENCE) || - (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL)))) - return; - - if (getResourceAddress(rtVerb, slot)) - _sentenceBuf = (char *)getResourceAddress(rtVerb, slot); - else - return; - - if (VAR(VAR_SENTENCE_OBJECT1) > 0) { - temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT1)); - if (temp) { - _sentenceBuf += " "; - _sentenceBuf += (const char *)temp; - } - - // For V1 games, the engine must compute the preposition. - // In all other Scumm versions, this is done by the sentence script. - if ((_game.id == GID_MANIAC && _game.version == 1 && !(_game.platform == Common::kPlatformNES)) && (VAR(VAR_SENTENCE_PREPOSITION) == 0)) { - if (_verbs[slot].prep == 0xFF) { - byte *ptr = getOBCDFromObject(VAR(VAR_SENTENCE_OBJECT1)); - assert(ptr); - VAR(VAR_SENTENCE_PREPOSITION) = (*(ptr + 12) >> 5); - } else - VAR(VAR_SENTENCE_PREPOSITION) = _verbs[slot].prep; - } - } - - if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) { - drawPreposition(VAR(VAR_SENTENCE_PREPOSITION)); - } - - if (VAR(VAR_SENTENCE_OBJECT2) > 0) { - temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT2)); - if (temp) { - _sentenceBuf += " "; - _sentenceBuf += (const char *)temp; - } - } - - _string[2].charset = 1; - _string[2].ypos = _virtscr[kVerbVirtScreen].topline; - _string[2].xpos = 0; - _string[2].right = _virtscr[kVerbVirtScreen].w - 1; - if (_game.platform == Common::kPlatformNES) { - _string[2].xpos = 16; - _string[2].color = 0; - } else if (_game.platform == Common::kPlatformC64) { - _string[2].color = 16; - } else { - _string[2].color = 13; - } - - byte string[80]; - const char *ptr = _sentenceBuf.c_str(); - int i = 0, len = 0; - - // Maximum length of printable characters - int maxChars = (_game.platform == Common::kPlatformNES) ? 60 : 40; - while (*ptr) { - if (*ptr != '@') - len++; - if (len > maxChars) { - break; - } - - string[i++] = *ptr++; - - if (_game.platform == Common::kPlatformNES && len == 30) { - string[i++] = 0xFF; - string[i++] = 8; - } - } - string[i] = 0; - - if (_game.platform == Common::kPlatformNES) { - sentenceline.top = _virtscr[kVerbVirtScreen].topline; - sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 16; - sentenceline.left = 16; - sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; - } else { - sentenceline.top = _virtscr[kVerbVirtScreen].topline; - sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8; - sentenceline.left = 0; - sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; - } - restoreBackground(sentenceline); - - drawString(2, (byte *)string); + drawSentence(); } void ScummEngine_v2::o2_ifClassOfIs() { diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp index 62052370855..07e74906425 100644 --- a/engines/scumm/script_v4.cpp +++ b/engines/scumm/script_v4.cpp @@ -408,12 +408,14 @@ void ScummEngine_v4::o4_saveLoadGame() { } break; case 0x40: // load + _lastLoadedRoom = -1; if (loadState(slot, false)) result = 3; // sucess else result = 5; // failed to load break; case 0x80: // save + _lastLoadedRoom = -1; if (_game.version <= 3) { char name[32]; if (_game.version <= 2) { diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 961d8b976d4..678a265b3fe 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -2596,7 +2596,8 @@ load_game: _res->increaseExpireCounter(); - animateCursor(); + if (!isUsingOriginalGUI() || ((_game.version >= 3) || !isPaused())) + animateCursor(); /* show or hide mouse */ CursorMan.showMouse(_cursor.state > 0); @@ -3146,7 +3147,7 @@ bool ScummEngine::isUsingOriginalGUI() { if (_game.heversion != 0) return false; - if (_game.version > 3) + if (_game.version >= 0) return _useOriginalGUI; return false; diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 401c9b9004c..b846ae8a26c 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -653,9 +653,13 @@ protected: int _curCursorHotspotX = 0; int _curCursorHotspotY = 0; + virtual void setSnailCursor() {} + void initBanners(); Common::KeyState showBannerAndPause(int bannerId, int32 waitTime, const char *msg, ...); Common::KeyState showOldStyleBannerAndPause(const char *msg, int color, int32 waitTime); + Common::KeyState printMessageAndPause(const char *msg, int32 waitTime, bool drawOnSentenceLine); + void clearBanner(); void setBannerColors(int bannerId, byte r, byte g, byte b); virtual int getBannerColor(int bannerId); @@ -1201,6 +1205,8 @@ protected: bool _doEffect = false; bool _snapScroll = false; + + virtual void setBuiltinCursor(int index) {} public: bool isLightOn() const; @@ -1478,6 +1484,7 @@ protected: virtual void printString(int m, const byte *msg); virtual bool handleNextCharsetCode(Actor *a, int *c); + virtual void drawSentence() {} virtual void CHARSET_1(); bool newLine(); void drawString(int a, const byte *msg); diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index 37e3048739e..264b2bc25e9 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -92,6 +92,7 @@ protected: void clearStateCommon(byte type); void stopScriptCommon(int script); + void drawSentence() override; void resetSentence() override; void setUserState(byte state); @@ -104,6 +105,7 @@ protected: void initNESMouseOver(); void setBuiltinCursor(int index) override; + void setSnailCursor() override; void drawPreposition(int index); diff --git a/engines/scumm/scumm_v5.h b/engines/scumm/scumm_v5.h index 39b41a2072b..557381b2132 100644 --- a/engines/scumm/scumm_v5.h +++ b/engines/scumm/scumm_v5.h @@ -82,7 +82,7 @@ protected: void animateCursor() override; - virtual void setBuiltinCursor(int index); + void setBuiltinCursor(int index) override; void redefineBuiltinCursorFromChar(int index, int chr); void redefineBuiltinCursorHotspot(int index, int x, int y); diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index c0139fef4fc..6d04c9cacae 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -36,6 +36,7 @@ #endif #include "scumm/resource.h" #include "scumm/scumm.h" +#include "scumm/scumm_v2.h" #include "scumm/scumm_v6.h" #include "scumm/scumm_v7.h" #include "scumm/verbs.h" @@ -706,6 +707,102 @@ void ScummEngine::fakeBidiString(byte *ltext, bool ignoreVerb, int ltextSize) co free(stack); } +void ScummEngine_v2::drawSentence() { + Common::Rect sentenceline; + const byte *temp; + int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0); + + if (!((_userState & USERSTATE_IFACE_SENTENCE) || + (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL)))) + return; + + if (getResourceAddress(rtVerb, slot)) + _sentenceBuf = (char *)getResourceAddress(rtVerb, slot); + else + return; + + if (VAR(VAR_SENTENCE_OBJECT1) > 0) { + temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT1)); + if (temp) { + _sentenceBuf += " "; + _sentenceBuf += (const char *)temp; + } + + // For V1 games, the engine must compute the preposition. + // In all other Scumm versions, this is done by the sentence script. + if ((_game.id == GID_MANIAC && _game.version == 1 && !(_game.platform == Common::kPlatformNES)) && (VAR(VAR_SENTENCE_PREPOSITION) == 0)) { + if (_verbs[slot].prep == 0xFF) { + byte *ptr = getOBCDFromObject(VAR(VAR_SENTENCE_OBJECT1)); + assert(ptr); + VAR(VAR_SENTENCE_PREPOSITION) = (*(ptr + 12) >> 5); + } else + VAR(VAR_SENTENCE_PREPOSITION) = _verbs[slot].prep; + } + } + + if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) { + drawPreposition(VAR(VAR_SENTENCE_PREPOSITION)); + } + + if (VAR(VAR_SENTENCE_OBJECT2) > 0) { + temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT2)); + if (temp) { + _sentenceBuf += " "; + _sentenceBuf += (const char *)temp; + } + } + + _string[2].charset = 1; + _string[2].ypos = _virtscr[kVerbVirtScreen].topline; + _string[2].xpos = 0; + _string[2].right = _virtscr[kVerbVirtScreen].w - 1; + if (_game.platform == Common::kPlatformNES) { + _string[2].xpos = 16; + _string[2].color = 0; + } else if (_game.platform == Common::kPlatformC64) { + _string[2].color = 16; + } else { + _string[2].color = 13; + } + + byte string[80]; + const char *ptr = _sentenceBuf.c_str(); + int i = 0, len = 0; + + // Maximum length of printable characters + int maxChars = (_game.platform == Common::kPlatformNES) ? 60 : 40; + while (*ptr) { + if (*ptr != '@') + len++; + if (len > maxChars) { + break; + } + + string[i++] = *ptr++; + + if (_game.platform == Common::kPlatformNES && len == 30) { + string[i++] = 0xFF; + string[i++] = 8; + } + } + string[i] = 0; + + if (_game.platform == Common::kPlatformNES) { + sentenceline.top = _virtscr[kVerbVirtScreen].topline; + sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 16; + sentenceline.left = 16; + sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; + } else { + sentenceline.top = _virtscr[kVerbVirtScreen].topline; + sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8; + sentenceline.left = 0; + sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; + } + restoreBackground(sentenceline); + + drawString(2, (byte *)string); +} + void ScummEngine::CHARSET_1() { Actor *a; if (_game.heversion >= 70 && _haveMsg == 3) {