diff options
author | Martin Kiewitz | 2016-02-14 22:43:52 +0100 |
---|---|---|
committer | Martin Kiewitz | 2016-02-14 22:43:52 +0100 |
commit | 7b75936f56fc5ea089e3fc957a113dc7577e832f (patch) | |
tree | 326117295350ac0b7b1807893969e2224a51e1df | |
parent | dcbcbb2120de788207340e0b1bffae70397e1161 (diff) | |
download | scummvm-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.
-rw-r--r-- | engines/agi/agi.cpp | 2 | ||||
-rw-r--r-- | engines/agi/agi.h | 8 | ||||
-rw-r--r-- | engines/agi/cycle.cpp | 3 | ||||
-rw-r--r-- | engines/agi/global.cpp | 32 | ||||
-rw-r--r-- | engines/agi/op_cmd.cpp | 3 |
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]; |