aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/agi/agi.cpp107
-rw-r--r--engines/agi/agi.h29
-rw-r--r--engines/agi/cycle.cpp8
-rw-r--r--engines/agi/op_cmd.cpp13
-rw-r--r--engines/agi/saveload.cpp2
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();