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.
This commit is contained in:
parent
dcbcbb2120
commit
7b75936f56
5 changed files with 48 additions and 0 deletions
|
@ -401,6 +401,8 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
|
||||||
|
|
||||||
setupOpcodes();
|
setupOpcodes();
|
||||||
_game._curLogic = NULL;
|
_game._curLogic = NULL;
|
||||||
|
_instructionCounter = 0;
|
||||||
|
resetGetVarSecondsHeuristic();
|
||||||
|
|
||||||
_lastSaveTime = 0;
|
_lastSaveTime = 0;
|
||||||
|
|
||||||
|
|
|
@ -862,6 +862,12 @@ public:
|
||||||
int testIfCode(int);
|
int testIfCode(int);
|
||||||
void executeAgiCommand(uint8, uint8 *);
|
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:
|
public:
|
||||||
// Some submethods of testIfCode
|
// Some submethods of testIfCode
|
||||||
void skipInstruction(byte op);
|
void skipInstruction(byte op);
|
||||||
|
@ -955,6 +961,8 @@ private:
|
||||||
public:
|
public:
|
||||||
void redrawScreen();
|
void redrawScreen();
|
||||||
|
|
||||||
|
void getVarSecondsTrigger();
|
||||||
|
|
||||||
void inGameTimerReset(uint32 newPlayTime = 0);
|
void inGameTimerReset(uint32 newPlayTime = 0);
|
||||||
void inGameTimerResetPassedCycles();
|
void inGameTimerResetPassedCycles();
|
||||||
void inGameTimerPause();
|
void inGameTimerPause();
|
||||||
|
|
|
@ -142,6 +142,9 @@ void AgiEngine::interpretCycle() {
|
||||||
oldScore = getVar(VM_VAR_SCORE);
|
oldScore = getVar(VM_VAR_SCORE);
|
||||||
oldSound = getFlag(VM_FLAG_SOUND_ON);
|
oldSound = getFlag(VM_FLAG_SOUND_ON);
|
||||||
|
|
||||||
|
// Reset script heuristic here
|
||||||
|
resetGetVarSecondsHeuristic();
|
||||||
|
|
||||||
_game.exitAllLogics = false;
|
_game.exitAllLogics = false;
|
||||||
while (runLogic(0) == 0 && !(shouldQuit() || _restartGame)) {
|
while (runLogic(0) == 0 && !(shouldQuit() || _restartGame)) {
|
||||||
setVar(VM_VAR_WORD_NOT_FOUND, 0);
|
setVar(VM_VAR_WORD_NOT_FOUND, 0);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "common/config-manager.h"
|
#include "common/config-manager.h"
|
||||||
|
|
||||||
#include "agi/agi.h"
|
#include "agi/agi.h"
|
||||||
|
#include "agi/graphics.h"
|
||||||
|
|
||||||
namespace Agi {
|
namespace Agi {
|
||||||
|
|
||||||
|
@ -61,6 +62,8 @@ void AgiEngine::setVar(int16 varNr, byte newValue) {
|
||||||
byte AgiEngine::getVar(int16 varNr) {
|
byte AgiEngine::getVar(int16 varNr) {
|
||||||
switch (varNr) {
|
switch (varNr) {
|
||||||
case VM_VAR_SECONDS:
|
case VM_VAR_SECONDS:
|
||||||
|
getVarSecondsTrigger();
|
||||||
|
// is supposed to fall through
|
||||||
case VM_VAR_MINUTES:
|
case VM_VAR_MINUTES:
|
||||||
case VM_VAR_HOURS:
|
case VM_VAR_HOURS:
|
||||||
case VM_VAR_DAYS:
|
case VM_VAR_DAYS:
|
||||||
|
@ -135,6 +138,35 @@ void AgiEngine::setVolumeViaSystemSetting() {
|
||||||
_game.vars[VM_VAR_VOLUME] = internalVolume;
|
_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
|
// In-Game timer, used for timer VM Variables
|
||||||
void AgiEngine::inGameTimerReset(uint32 newPlayTime) {
|
void AgiEngine::inGameTimerReset(uint32 newPlayTime) {
|
||||||
_lastUsedPlayTimeInCycles = newPlayTime / 50;
|
_lastUsedPlayTimeInCycles = newPlayTime / 50;
|
||||||
|
|
|
@ -2327,6 +2327,9 @@ int AgiEngine::runLogic(int16 logicNr) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Just a counter for every instruction, that got executed
|
||||||
|
_instructionCounter++;
|
||||||
|
|
||||||
_game.execStack.back().curIP = state->_curLogic->cIP;
|
_game.execStack.back().curIP = state->_curLogic->cIP;
|
||||||
|
|
||||||
char st[101];
|
char st[101];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue