diff options
-rw-r--r-- | engines/agi/agi.cpp | 107 | ||||
-rw-r--r-- | engines/agi/agi.h | 29 | ||||
-rw-r--r-- | engines/agi/cycle.cpp | 8 | ||||
-rw-r--r-- | engines/agi/op_cmd.cpp | 13 | ||||
-rw-r--r-- | engines/agi/saveload.cpp | 2 |
5 files changed, 130 insertions, 29 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index a4b6c40e39..4f18e8f6de 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -563,6 +563,12 @@ void AgiEngine::syncSoundSettings() { setVolumeViaSystemSetting(); } +// WORKAROUND: +// Sometimes Sierra printed some text on the screen and did a room change immediately afterwards expecting the +// interpreter to load the data for a bit of time. This of course doesn't happen in our AGI, so we try to +// detect such situations via heuristic and then delay the game for a bit. +// In those cases a wait mouse cursor will be shown. +// // Scenes that need this: // // Manhunter 1: @@ -590,7 +596,14 @@ void AgiEngine::nonBlockingText_Forget() { _game.nonBlockingTextShown = false; _game.nonBlockingTextCyclesLeft = 0; } -void AgiEngine::nonBlockingText_CycleDone() { + +void AgiEngine::artificialDelay_Reset() { + nonBlockingText_Forget(); + _artificialDelayCurrentRoom = -1; + _artificialDelayCurrentPicture = -1; +} + +void AgiEngine::artificialDelay_CycleDone() { if (_game.nonBlockingTextCyclesLeft) { _game.nonBlockingTextCyclesLeft--; @@ -601,30 +614,94 @@ void AgiEngine::nonBlockingText_CycleDone() { } } -void AgiEngine::loadingTrigger_NewRoom(int16 newRoomNr) { - if (_game.nonBlockingTextShown) { - _game.nonBlockingTextShown = false; +// WORKAROUND: +// On Apple IIgs, there are situations like for example the Police Quest 1 intro, where music is playing +// and then the scripts switch to a new room, expecting it to load for a bit of time. In ScummVM this results +// in music getting cut off, because our loading is basically done in an instant. This also happens in the +// original interpreter, when you use a faster CPU in emulation. +// +// That's why there is an additional table, where one can add such situations to it. +// These issues are basically impossible to detect, because sometimes music is also supposed to play throughout +// multiple rooms. +// +// Normally all text-based issues should get detected by the current heuristic. Do not add those in here. - int16 curRoomNr = getVar(VM_VAR_CURRENT_ROOM); +// script, description, signature patch +static const AgiArtificialDelayEntry artificialDelayTable[] = { + { GID_PQ1, Common::kPlatformApple2GS, ARTIFICIALDELAYTYPE_NEWPICTURE, 1, 2, 2200 }, // Intro: music track is supposed to finish before credits screen. Developers must have assumed that room loading would take that long. + { GID_AGIDEMO, Common::kPlatformUnknown, ARTIFICIALDELAYTYPE_END, -1, -1, 0 } +}; - if (newRoomNr != curRoomNr) { - if (!_game.automaticRestoreGame) { - // wait a bit, we detected non-blocking text - wait(2000, true); // 2 seconds, set busy +uint16 AgiEngine::artificialDelay_SearchTable(AgiArtificialDelayTriggerType triggerType, int16 orgNr, int16 newNr) { + if (getPlatform() != Common::kPlatformApple2GS) { + return 0; + } + + const AgiArtificialDelayEntry *delayEntry = artificialDelayTable; + + while (delayEntry->triggerType != ARTIFICIALDELAYTYPE_END) { + if (triggerType == delayEntry->triggerType) { + if ((orgNr == delayEntry->orgNr) && (newNr == delayEntry->newNr)) { + if ((getGameID() == delayEntry->gameId) && (getPlatform() == delayEntry->platform)) { + warning("artificial delay forced"); + return delayEntry->millisecondsDelay; + } + } + } + + delayEntry++; + } + return 0; +} + +void AgiEngine::artificialDelayTrigger_NewRoom(int16 newRoomNr) { + uint16 millisecondsDelay = 0; + + //warning("artificial delay trigger: room %d -> new room %d", _artificialDelayCurrentRoom, newRoomNr); + + if (!_game.automaticRestoreGame) { + millisecondsDelay = artificialDelay_SearchTable(ARTIFICIALDELAYTYPE_NEWROOM, _artificialDelayCurrentRoom, newRoomNr); + + if (_game.nonBlockingTextShown) { + _game.nonBlockingTextShown = false; + + if (newRoomNr != _artificialDelayCurrentRoom) { + if (millisecondsDelay < 2000) { + // wait a bit, we detected non-blocking text + millisecondsDelay = 2000; // 2 seconds + } } } + + if (millisecondsDelay) { + wait(millisecondsDelay, true); // set busy mouse cursor + } } + + _artificialDelayCurrentRoom = newRoomNr; } -void AgiEngine::loadingTrigger_DrawPicture() { - if (_game.nonBlockingTextShown) { - _game.nonBlockingTextShown = false; +void AgiEngine::artificialDelayTrigger_DrawPicture(int16 newPictureNr) { + uint16 millisecondsDelay = 0; + + //warning("artificial delay trigger: picture %d -> new picture %d", _artificialDelayCurrentPicture, newPictureNr); + + if (!_game.automaticRestoreGame) { + millisecondsDelay = artificialDelay_SearchTable(ARTIFICIALDELAYTYPE_NEWPICTURE, _artificialDelayCurrentPicture, newPictureNr); + + if (_game.nonBlockingTextShown) { + _game.nonBlockingTextShown = false; + if (millisecondsDelay < 2000) { + // wait a bit, we detected non-blocking text + millisecondsDelay = 2000; // 2 seconds, set busy + } + } - if (!_game.automaticRestoreGame) { - // wait a bit, we detected non-blocking text - wait(2000, true); // 2 seconds, set busy + if (millisecondsDelay) { + wait(millisecondsDelay, true); // set busy mouse cursor } } + _artificialDelayCurrentPicture = newPictureNr; } } // End of namespace Agi diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 93017af099..bcd47c9f08 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -704,6 +704,21 @@ public: } }; +enum AgiArtificialDelayTriggerType { + ARTIFICIALDELAYTYPE_NEWROOM = 0, + ARTIFICIALDELAYTYPE_NEWPICTURE = 1, + ARTIFICIALDELAYTYPE_END = -1 +}; + +struct AgiArtificialDelayEntry { + uint32 gameId; + Common::Platform platform; + AgiArtificialDelayTriggerType triggerType; + int16 orgNr; + int16 newNr; + uint16 millisecondsDelay; +}; + typedef void (*AgiCommand)(AgiGame *state, AgiEngine *vm, uint8 *p); class AgiEngine : public AgiBase { @@ -924,10 +939,18 @@ public: void nonBlockingText_IsShown(); void nonBlockingText_Forget(); - void nonBlockingText_CycleDone(); - void loadingTrigger_NewRoom(int16 newRoomNr); - void loadingTrigger_DrawPicture(); + void artificialDelay_Reset(); + void artificialDelay_CycleDone(); + + uint16 artificialDelay_SearchTable(AgiArtificialDelayTriggerType triggerType, int16 orgNr, int16 newNr); + + void artificialDelayTrigger_NewRoom(int16 newRoomNr); + void artificialDelayTrigger_DrawPicture(int16 newPictureNr); + +private: + int16 _artificialDelayCurrentRoom; + int16 _artificialDelayCurrentPicture; public: void redrawScreen(); diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp index aba0ef27db..52cc16005b 100644 --- a/engines/agi/cycle.cpp +++ b/engines/agi/cycle.cpp @@ -44,7 +44,7 @@ void AgiEngine::newRoom(int16 newRoomNr) { int i; // Loading trigger - loadingTrigger_NewRoom(newRoomNr); + artificialDelayTrigger_NewRoom(newRoomNr); debugC(4, kDebugLevelMain, "*** room %d ***", newRoomNr); _sound->stopSound(); @@ -149,10 +149,10 @@ void AgiEngine::interpretCycle() { oldScore = getVar(VM_VAR_SCORE); setFlag(VM_FLAG_ENTERED_CLI, false); _game.exitAllLogics = false; - nonBlockingText_CycleDone(); + artificialDelay_CycleDone(); resetControllers(); } - nonBlockingText_CycleDone(); + artificialDelay_CycleDone(); resetControllers(); screenObjEgo->direction = getVar(VM_VAR_EGO_DIRECTION); @@ -336,7 +336,7 @@ int AgiEngine::playGame() { } } - nonBlockingText_Forget(); + artificialDelay_Reset(); do { processAGIEvents(); diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index f7af133e03..4339bf2712 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -931,14 +931,14 @@ void cmdSetSimple(AgiGame *state, AgiEngine *vm, uint8 *parameter) { spritesMgr->drawAllSpriteLists(); state->pictureShown = false; + // Loading trigger + vm->artificialDelayTrigger_DrawPicture(resourceNr); + // Show the picture. Similar to void cmdShow_pic(AgiGame *state, AgiEngine *vm, uint8 *p). vm->setFlag(VM_FLAG_OUTPUT_MODE, false); vm->_text->closeWindow(); vm->_picture->showPic(); state->pictureShown = true; - - // Loading trigger - vm->loadingTrigger_DrawPicture(); } } @@ -1126,7 +1126,7 @@ void cmdDrawPicV1(AgiGame *state, AgiEngine *vm, uint8 *parameter) { vm->_text->promptClear(); // Loading trigger - vm->loadingTrigger_DrawPicture(); + vm->artificialDelayTrigger_DrawPicture(resourceNr); } void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) { @@ -1138,6 +1138,7 @@ void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) { spritesMgr->eraseSprites(); vm->_picture->decodePicture(resourceNr, true); + spritesMgr->buildAllSpriteLists(); spritesMgr->drawAllSpriteLists(); state->pictureShown = false; @@ -1159,7 +1160,7 @@ void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) { vm->setFlag(103, false); // Loading trigger - vm->loadingTrigger_DrawPicture(); + vm->artificialDelayTrigger_DrawPicture(resourceNr); } void cmdShowPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) { @@ -1211,7 +1212,7 @@ void cmdOverlayPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) { state->pictureShown = false; // Loading trigger - vm->loadingTrigger_DrawPicture(); + vm->artificialDelayTrigger_DrawPicture(resourceNr); } void cmdShowPriScreen(AgiGame *state, AgiEngine *vm, uint8 *parameter) { diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index a57465a61e..f59fbacc7d 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -693,7 +693,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { _words->clearEgoWords(); // don't delay anything right after restoring a game - nonBlockingText_Forget(); + artificialDelay_Reset(); _sprites->eraseSprites(); _sprites->buildAllSpriteLists(); |