diff options
author | Martin Kiewitz | 2016-02-16 03:18:10 +0100 |
---|---|---|
committer | Martin Kiewitz | 2016-02-16 03:18:10 +0100 |
commit | 562649c7c03a25fc2ea64cb9d1aa4e3f4938d9ea (patch) | |
tree | de034cdcc1c442bfc5a27808351c99a4b59359af | |
parent | d394dcf725fac4661eed883efe64144b78b35355 (diff) | |
download | scummvm-rg350-562649c7c03a25fc2ea64cb9d1aa4e3f4938d9ea.tar.gz scummvm-rg350-562649c7c03a25fc2ea64cb9d1aa4e3f4938d9ea.tar.bz2 scummvm-rg350-562649c7c03a25fc2ea64cb9d1aa4e3f4938d9ea.zip |
AGI: Change how VM timer vars are handled
Instead of directly converting play time to VM timer vars,
we only adjust VM timer vars according to play time seconds delta.
This is needed for at least Mixed Up Mother Goose. The game
sets the VM "second" variable to 0 and then uses it for syncing
the text to the songs. We even adjust second calculation, so
that the timing should always be the same and not depend on
basically luck (seconds may wrap right after setting it, because
internal cycles counter is not changed).
-rw-r--r-- | engines/agi/agi.h | 4 | ||||
-rw-r--r-- | engines/agi/global.cpp | 90 |
2 files changed, 77 insertions, 17 deletions
diff --git a/engines/agi/agi.h b/engines/agi/agi.h index fe70c4ebc2..c5db1dfc1d 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -870,6 +870,10 @@ private: uint32 _getVarSecondsHeuristicLastInstructionCounter; /**< last time VM_VAR_SECONDS were read */ uint16 _getVarSecondsHeuristicCounter; /**< how many times heuristic was triggered */ + uint32 _playTimeInSecondsAdjust; /**< milliseconds to adjust for calculating current play time in seconds, see setVarSecondsTrigger() */ + + void setVarSecondsTrigger(byte newSeconds); + public: // Some submethods of testIfCode void skipInstruction(byte op); diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp index a7a39e9767..48e1c224ce 100644 --- a/engines/agi/global.cpp +++ b/engines/agi/global.cpp @@ -54,8 +54,13 @@ void AgiBase::flipFlag(int16 flagNr) { void AgiEngine::setVar(int16 varNr, byte newValue) { _game.vars[varNr] = newValue; - if (varNr == VM_VAR_VOLUME) { + switch (varNr) { + case VM_VAR_SECONDS: + setVarSecondsTrigger(newValue); + break; + case VM_VAR_VOLUME: setVolumeViaScripts(newValue); + break; } } @@ -173,6 +178,7 @@ void AgiEngine::getVarSecondsHeuristicTrigger() { void AgiEngine::inGameTimerReset(uint32 newPlayTime) { _lastUsedPlayTimeInCycles = newPlayTime / 50; _lastUsedPlayTimeInSeconds = newPlayTime / 1000; + _playTimeInSecondsAdjust = 0; // no adjust for now setTotalPlayTime(newPlayTime); inGameTimerResetPassedCycles(); } @@ -192,6 +198,24 @@ uint32 AgiEngine::inGameTimerGetPassedCycles() { return _passedPlayTimeCycles; } +// Seconds got set by the game +// This happens in Mixed Up Mother Goose. The game syncs the songs to VM_VAR_SECONDS, but instead +// of only reading them, it sets it to 0 and then checks if it reached a certain second. +// The original interpreter didn't reset the internal cycles counter. Which means the timing was never accurate, +// because the cycles counter may just overflow right after setting the seconds, which means a second +// increase almost immediately happened. We even fix this issue by adjusting for it. +void AgiEngine::setVarSecondsTrigger(byte newSeconds) { + // Adjust in game timer, so that VM timer variables are accurate + inGameTimerUpdate(); + + // Adjust VM seconds again + _game.vars[VM_VAR_SECONDS] = newSeconds; + + // Calculate milliseconds adjust (see comment above) + uint32 curPlayTimeMilliseconds = inGameTimerGet(); + _playTimeInSecondsAdjust = curPlayTimeMilliseconds % 1000; +} + // This is called, when one of the timer variables is read // We calculate the latest variables, according to current official playtime // This is also called in the main loop, because the game needs to be sync'd to 20 cycles per second @@ -212,6 +236,14 @@ void AgiEngine::inGameTimerUpdate() { _lastUsedPlayTimeInCycles = curPlayTimeCycles; // Now calculate current play time in seconds + if (_playTimeInSecondsAdjust) { + // Apply adjust from setVarSecondsTrigger() + if (curPlayTimeMilliseconds >= _playTimeInSecondsAdjust) { + curPlayTimeMilliseconds -= _playTimeInSecondsAdjust; + } else { + curPlayTimeMilliseconds = 0; + } + } uint32 curPlayTimeSeconds = curPlayTimeMilliseconds / 1000; if (curPlayTimeSeconds == _lastUsedPlayTimeInSeconds) { @@ -219,26 +251,50 @@ void AgiEngine::inGameTimerUpdate() { return; } - uint32 secondsLeft = 0; - byte curDays = 0; - byte curHours = 0; - byte curMinutes = 0; - byte curSeconds = 0; + int32 playTimeSecondsDelta = curPlayTimeSeconds - _lastUsedPlayTimeInSeconds; - curDays = curPlayTimeSeconds / 86400; - secondsLeft = curPlayTimeSeconds % 86400; + if (playTimeSecondsDelta > 0) { + // Read and write to VM vars directly to avoid endless loop + uint32 secondsLeft = playTimeSecondsDelta; + byte curSeconds = _game.vars[VM_VAR_SECONDS]; + byte curMinutes = _game.vars[VM_VAR_MINUTES]; + byte curHours = _game.vars[VM_VAR_HOURS]; + byte curDays = _game.vars[VM_VAR_DAYS]; - curHours = secondsLeft / 3600; - secondsLeft = secondsLeft % 3600; + // Add delta to VM variables + if (secondsLeft >= 86400) { + curDays += secondsLeft / 86400; + secondsLeft = secondsLeft % 86400; + } + if (secondsLeft >= 3600) { + curHours += secondsLeft / 3600; + secondsLeft = secondsLeft % 3600; + } + if (secondsLeft >= 60) { + curMinutes += secondsLeft / 60; + secondsLeft = secondsLeft % 60; + } + curSeconds += secondsLeft; - curMinutes = secondsLeft / 60; - curSeconds = secondsLeft % 60; + while (curSeconds > 59) { + curSeconds -= 60; + curMinutes++; + } + while (curMinutes > 59) { + curMinutes -= 60; + curHours++; + } + while (curHours > 23) { + curHours -= 24; + curDays++; + } - // directly set them, otherwise we would go into an endless loop - _game.vars[VM_VAR_SECONDS] = curSeconds; - _game.vars[VM_VAR_MINUTES] = curMinutes; - _game.vars[VM_VAR_HOURS] = curHours; - _game.vars[VM_VAR_DAYS] = curDays; + // directly set them + _game.vars[VM_VAR_SECONDS] = curSeconds; + _game.vars[VM_VAR_MINUTES] = curMinutes; + _game.vars[VM_VAR_HOURS] = curHours; + _game.vars[VM_VAR_DAYS] = curDays; + } _lastUsedPlayTimeInSeconds = curPlayTimeSeconds; } |