aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorMartin Kiewitz2016-02-14 22:43:52 +0100
committerMartin Kiewitz2016-02-14 22:43:52 +0100
commit7b75936f56fc5ea089e3fc957a113dc7577e832f (patch)
tree326117295350ac0b7b1807893969e2224a51e1df /engines
parentdcbcbb2120de788207340e0b1bffae70397e1161 (diff)
downloadscummvm-rg350-7b75936f56fc5ea089e3fc957a113dc7577e832f.tar.gz
scummvm-rg350-7b75936f56fc5ea089e3fc957a113dc7577e832f.tar.bz2
scummvm-rg350-7b75936f56fc5ea089e3fc957a113dc7577e832f.zip
AGI: Add heuristic to detect delay loops within scripts
And in that case poll events, delay for a few milliseconds and update screen. This somewhat worked before the graphics rewrite because of a timer hack. This one tries to detect actual inner loops. Happens in at least Police Quest 1 when playing poker.
Diffstat (limited to 'engines')
-rw-r--r--engines/agi/agi.cpp2
-rw-r--r--engines/agi/agi.h8
-rw-r--r--engines/agi/cycle.cpp3
-rw-r--r--engines/agi/global.cpp32
-rw-r--r--engines/agi/op_cmd.cpp3
5 files changed, 48 insertions, 0 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index fdc294a661..f149b0be95 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -401,6 +401,8 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
setupOpcodes();
_game._curLogic = NULL;
+ _instructionCounter = 0;
+ resetGetVarSecondsHeuristic();
_lastSaveTime = 0;
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 627b18e753..ad5d320f27 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -862,6 +862,12 @@ public:
int testIfCode(int);
void executeAgiCommand(uint8, uint8 *);
+private:
+ void resetGetVarSecondsHeuristic();
+ uint32 _instructionCounter; /**< counts every instruction, that got executed, can wrap around */
+ uint32 _getVarSecondsHeuristicLastInstructionCounter; /**< last time VM_VAR_SECONDS were read */
+ uint16 _getVarSecondsHeuristicCounter; /**< how many times heuristic was triggered */
+
public:
// Some submethods of testIfCode
void skipInstruction(byte op);
@@ -955,6 +961,8 @@ private:
public:
void redrawScreen();
+ void getVarSecondsTrigger();
+
void inGameTimerReset(uint32 newPlayTime = 0);
void inGameTimerResetPassedCycles();
void inGameTimerPause();
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index a3873a0184..9b7dc63df8 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -142,6 +142,9 @@ void AgiEngine::interpretCycle() {
oldScore = getVar(VM_VAR_SCORE);
oldSound = getFlag(VM_FLAG_SOUND_ON);
+ // Reset script heuristic here
+ resetGetVarSecondsHeuristic();
+
_game.exitAllLogics = false;
while (runLogic(0) == 0 && !(shouldQuit() || _restartGame)) {
setVar(VM_VAR_WORD_NOT_FOUND, 0);
diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp
index 0f10976988..fe3beccb33 100644
--- a/engines/agi/global.cpp
+++ b/engines/agi/global.cpp
@@ -23,6 +23,7 @@
#include "common/config-manager.h"
#include "agi/agi.h"
+#include "agi/graphics.h"
namespace Agi {
@@ -61,6 +62,8 @@ void AgiEngine::setVar(int16 varNr, byte newValue) {
byte AgiEngine::getVar(int16 varNr) {
switch (varNr) {
case VM_VAR_SECONDS:
+ getVarSecondsTrigger();
+ // is supposed to fall through
case VM_VAR_MINUTES:
case VM_VAR_HOURS:
case VM_VAR_DAYS:
@@ -135,6 +138,35 @@ void AgiEngine::setVolumeViaSystemSetting() {
_game.vars[VM_VAR_VOLUME] = internalVolume;
}
+void AgiEngine::resetGetVarSecondsHeuristic() {
+ _getVarSecondsHeuristicLastInstructionCounter = 0;
+ _getVarSecondsHeuristicCounter = 0;
+}
+
+// Called, when the scripts read VM_VAR_SECONDS
+void AgiEngine::getVarSecondsTrigger() {
+ uint32 counterDifference = _instructionCounter - _getVarSecondsHeuristicLastInstructionCounter;
+
+ if (counterDifference <= 3) {
+ // Seconds were read within 3 instructions
+ _getVarSecondsHeuristicCounter++;
+ if (_getVarSecondsHeuristicCounter > 20) {
+ // More than 20 times in a row? This really seems to be an inner loop waiting for seconds to change
+ // This happens in at least:
+ // Police Quest 1 - Poker game
+
+ // Wait a few milliseconds, get events and update screen
+ // We MUST NOT process AGI events in here
+ wait(10);
+ processScummVMEvents();
+ _gfx->updateScreen();
+
+ _getVarSecondsHeuristicCounter = 0;
+ }
+ }
+ _getVarSecondsHeuristicLastInstructionCounter = _instructionCounter;
+}
+
// In-Game timer, used for timer VM Variables
void AgiEngine::inGameTimerReset(uint32 newPlayTime) {
_lastUsedPlayTimeInCycles = newPlayTime / 50;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 61ef20a16a..29e00662f4 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -2327,6 +2327,9 @@ int AgiEngine::runLogic(int16 logicNr) {
}
#endif
+ // Just a counter for every instruction, that got executed
+ _instructionCounter++;
+
_game.execStack.back().curIP = state->_curLogic->cIP;
char st[101];