aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/agi/agi.cpp6
-rw-r--r--engines/agi/agi.h19
-rw-r--r--engines/agi/cycle.cpp42
-rw-r--r--engines/agi/global.cpp80
-rw-r--r--engines/agi/graphics.cpp10
-rw-r--r--engines/agi/graphics.h3
-rw-r--r--engines/agi/keyboard.cpp1
-rw-r--r--engines/agi/menu.cpp6
-rw-r--r--engines/agi/op_cmd.cpp46
-rw-r--r--engines/agi/op_test.cpp12
-rw-r--r--engines/agi/preagi.cpp1
-rw-r--r--engines/agi/saveload.cpp17
12 files changed, 154 insertions, 89 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index 8dd663ccbb..c8490d4a89 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -390,7 +390,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
setupOpcodes();
_game._curLogic = NULL;
- _timerHack = 0;
_lastSaveTime = 0;
_lastTick = 0;
@@ -460,8 +459,6 @@ void AgiEngine::initialize() {
_font->init();
_text->init(_systemUI);
- _gfx->initMachine();
-
_game.gameFlags = 0;
_text->charAttrib_Set(15, 0);
@@ -530,7 +527,6 @@ AgiEngine::~AgiEngine() {
delete _text;
delete _sprites;
delete _picture;
- _gfx->deinitMachine();
delete _gfx;
delete _font;
delete _words;
@@ -549,7 +545,7 @@ Common::Error AgiEngine::go() {
if (_game.mouseEnabled) {
CursorMan.showMouse(true);
}
- setTotalPlayTime(0);
+ inGameTimerReset();
if (_game.state < STATE_LOADED) {
do {
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 4c7580dadb..7e1505f8f7 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -653,7 +653,6 @@ public:
GfxMgr *_gfx;
Common::RenderMode _renderMode;
- volatile uint32 _clockCount;
AgiDebug _debug;
AgiGame _game;
Common::RandomSource *_rnd;
@@ -824,12 +823,13 @@ public:
byte getVar(int16 varNr);
void setVar(int16 varNr, byte newValue);
+
+public:
void decrypt(uint8 *mem, int len);
void releaseSprites();
int mainCycle(bool onlyCheckForEvents = false);
int viewPictures();
int runGame();
- void updateTimer();
int getAppDir(char *appDir, unsigned int size);
int setupV2Game(int ver);
@@ -950,14 +950,23 @@ public:
public:
void redrawScreen();
+ void inGameTimerReset(uint32 newPlayTime = 0);
+ void inGameTimerPause();
+ void inGameTimerResume();
+ uint32 inGameTimerGet();
+
+ void inGameTimerUpdate();
+
+private:
+ uint32 _lastUsedPlayTimeInCycles; // 20 per second
+ uint32 _lastUsedPlayTimeInSeconds; // actual seconds
+ uint32 _passedPlayTimeCycles; // increased by 1 every time we passed a cycle
+
private:
AgiCommand _agiCommands[183];
AgiCommand _agiCondCommands[256];
void setupOpcodes();
-
-public:
- int _timerHack; // Workaround for timer loop in MH1 logic 153
};
} // End of namespace Agi
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 85a465cb02..6506ee29eb 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -173,37 +173,6 @@ void AgiEngine::interpretCycle() {
//_gfx->doUpdate();
}
-/**
- * Update AGI interpreter timer.
- */
-void AgiEngine::updateTimer() {
- _clockCount++;
- if (_clockCount <= TICK_SECONDS)
- return;
-
- _clockCount -= TICK_SECONDS;
-
- if (!_game.clockEnabled)
- return;
-
- setVar(VM_VAR_SECONDS, getVar(VM_VAR_SECONDS) + 1);
- if (getVar(VM_VAR_SECONDS) < 60)
- return;
-
- setVar(VM_VAR_SECONDS, 0);
- setVar(VM_VAR_MINUTES, getVar(VM_VAR_MINUTES) + 1);
- if (getVar(VM_VAR_MINUTES) < 60)
- return;
-
- setVar(VM_VAR_MINUTES, 0);
- setVar(VM_VAR_HOURS, getVar(VM_VAR_HOURS) + 1);
- if (getVar(VM_VAR_HOURS) < 24)
- return;
-
- setVar(VM_VAR_HOURS, 0);
- setVar(VM_VAR_DAYS, getVar(VM_VAR_DAYS) + 1);
-}
-
void AgiEngine::newInputMode(InputMode mode) {
//if (mode == INPUTMODE_MENU && !getflag(VM_FLAG_MENUS_WORK) && !(getFeatures() & GF_MENUS))
// return;
@@ -224,7 +193,6 @@ int AgiEngine::mainCycle(bool onlyCheckForEvents) {
if (!onlyCheckForEvents) {
pollTimer();
- updateTimer();
}
if (_menu->delayedExecuteActive()) {
@@ -402,7 +370,11 @@ int AgiEngine::playGame() {
if (!mainCycle())
continue;
- if (getVar(VM_VAR_TIME_DELAY) == 0 || (1 + _clockCount) % getVar(VM_VAR_TIME_DELAY) == 0) {
+ inGameTimerUpdate();
+
+ if (_passedPlayTimeCycles >= getVar(VM_VAR_TIME_DELAY)) {
+ _passedPlayTimeCycles = 0;
+
if (!_game.hasPrompt && _game.inputMode == INPUTMODE_NORMAL) {
_text->promptRedraw();
_game.hasPrompt = 1;
@@ -468,6 +440,10 @@ int AgiEngine::runGame() {
if (_restartGame) {
setFlag(VM_FLAG_RESTART_GAME, true);
setVar(VM_VAR_TIME_DELAY, 2); // "normal" speed
+
+ // Reset in-game timer
+ inGameTimerReset();
+
_restartGame = false;
}
diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp
index 9f3fb451c2..3cc8ffc6f8 100644
--- a/engines/agi/global.cpp
+++ b/engines/agi/global.cpp
@@ -58,9 +58,89 @@ void AgiEngine::setVar(int16 varNr, byte newValue) {
}
byte AgiEngine::getVar(int16 varNr) {
+ switch (varNr) {
+ case VM_VAR_SECONDS:
+ case VM_VAR_MINUTES:
+ case VM_VAR_HOURS:
+ case VM_VAR_DAYS:
+ // Timer Update is necessary in here, because of at least Manhunter 1 script 153
+ // Sierra AGI updated the timer via a timer procedure
+ inGameTimerUpdate();
+ break;
+ default:
+ break;
+ }
return _game.vars[varNr];
}
+// In-Game timer, used for timer VM Variables
+void AgiEngine::inGameTimerReset(uint32 newPlayTime) {
+ _passedPlayTimeCycles = 0;
+ _lastUsedPlayTimeInCycles = newPlayTime / 50;
+ _lastUsedPlayTimeInSeconds = newPlayTime / 1000;
+ setTotalPlayTime(newPlayTime);
+}
+void AgiEngine::inGameTimerPause() {
+ pauseEngine(true);
+}
+void AgiEngine::inGameTimerResume() {
+ pauseEngine(false);
+}
+uint32 AgiEngine::inGameTimerGet() {
+ return getTotalPlayTime();
+}
+
+// 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
+void AgiEngine::inGameTimerUpdate() {
+ uint32 curPlayTimeMilliseconds = inGameTimerGet();
+ uint32 curPlayTimeCycles = curPlayTimeMilliseconds / 50;
+
+ if (curPlayTimeCycles == _lastUsedPlayTimeInCycles) {
+ // No difference, skip updating
+ return;
+ }
+
+ // Increase passed cycles accordingly
+ int32 playTimeCycleDelta = curPlayTimeCycles - _lastUsedPlayTimeInCycles;
+ if (playTimeCycleDelta > 0) {
+ _passedPlayTimeCycles += playTimeCycleDelta;
+ }
+ _lastUsedPlayTimeInCycles = curPlayTimeCycles;
+
+ // Now calculate current play time in seconds
+ uint32 curPlayTimeSeconds = curPlayTimeMilliseconds / 1000;
+
+ if (curPlayTimeSeconds == _lastUsedPlayTimeInSeconds) {
+ // No difference, skip updating
+ return;
+ }
+
+ uint32 secondsLeft = 0;
+ byte curDays = 0;
+ byte curHours = 0;
+ byte curMinutes = 0;
+ byte curSeconds = 0;
+
+ curDays = curPlayTimeSeconds / 86400;
+ secondsLeft = curPlayTimeSeconds % 86400;
+
+ curHours = secondsLeft / 3600;
+ secondsLeft = secondsLeft % 3600;
+
+ curMinutes = secondsLeft / 60;
+ curSeconds = secondsLeft % 60;
+
+ // 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;
+
+ _lastUsedPlayTimeInSeconds = curPlayTimeSeconds;
+}
+
void AgiEngine::decrypt(uint8 *mem, int len) {
const uint8 *key;
int i;
diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp
index b19e729409..f1407715cc 100644
--- a/engines/agi/graphics.cpp
+++ b/engines/agi/graphics.cpp
@@ -156,16 +156,6 @@ int GfxMgr::deinitVideo() {
return errOK;
}
-int GfxMgr::initMachine() {
- _vm->_clockCount = 0;
-
- return errOK;
-}
-
-int GfxMgr::deinitMachine() {
- return errOK;
-}
-
void GfxMgr::setRenderStartOffset(uint16 offsetY) {
if (offsetY >= (DISPLAY_HEIGHT - SCRIPT_HEIGHT))
error("invalid render start offset");
diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h
index 7fffe320c9..f321e9e66b 100644
--- a/engines/agi/graphics.h
+++ b/engines/agi/graphics.h
@@ -76,9 +76,6 @@ public:
void initMouseCursor(MouseCursorData *mouseCursor, const byte *bitmapData, uint16 width, uint16 height, int hotspotX, int hotspotY);
void setMouseCursor(bool busy = false);
- int initMachine();
- int deinitMachine();
-
void setRenderStartOffset(uint16 offsetY);
uint16 getRenderStartOffsetY();
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 81de225e71..2a5f801a25 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -547,7 +547,6 @@ int AgiEngine::waitKey() {
break;
pollTimer();
- updateTimer();
g_system->updateScreen();
}
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
index 411cd002fd..ea24bd5b46 100644
--- a/engines/agi/menu.cpp
+++ b/engines/agi/menu.cpp
@@ -292,11 +292,17 @@ void GfxMenu::execute() {
}
drawActiveMenu();
+ // original AGI did not do this, at least when the menu was called by scripts
+ _vm->inGameTimerPause();
+
_vm->cycleInnerLoopActive(CYCLE_INNERLOOP_MENU);
do {
_vm->mainCycle();
} while (_vm->cycleInnerLoopIsActive() && !(_vm->shouldQuit() || _vm->_restartGame));
+ // original AGI did not do this, at least when the menu was called by scripts
+ _vm->inGameTimerResume();
+
removeActiveMenu();
_text->charAttrib_Pop();
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 57a6d6833d..4b82d2464a 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -811,25 +811,41 @@ void cmdResetScanStart(AgiGame *state, uint8 *parameter) {
}
void cmdSaveGame(AgiGame *state, uint8 *parameter) {
+ AgiEngine *vm = state->_vm;
+
+ vm->inGameTimerPause();
+
if (state->automaticSave) {
- if (state->_vm->saveGameAutomatic()) {
+ if (vm->saveGameAutomatic()) {
// automatic save succeded
+ vm->inGameTimerResume();
return;
}
// fall back to regular dialog otherwise
}
- state->_vm->saveGameDialog();
+
+ vm->saveGameDialog();
+
+ vm->inGameTimerResume();
}
void cmdLoadGame(AgiGame *state, uint8 *parameter) {
+ AgiEngine *vm = state->_vm;
+
+ vm->inGameTimerPause();
+
if (state->automaticSave) {
- if (state->_vm->loadGameAutomatic()) {
+ if (vm->loadGameAutomatic()) {
// automatic restore succeded
+ vm->inGameTimerResume();
return;
}
// fall back to regular dialog otherwise
}
- state->_vm->loadGameDialog();
+
+ vm->loadGameDialog();
+
+ vm->inGameTimerResume();
}
void cmdInitDisk(AgiGame *state, uint8 *parameter) { // do nothing
@@ -1740,11 +1756,8 @@ void cmdSetGameID(AgiGame *state, uint8 *parameter) {
void cmdPause(AgiGame *state, uint8 *parameter) {
AgiEngine *vm = state->_vm;
- int originalClockState = state->clockEnabled;
bool skipPause = false;
- state->clockEnabled = false;
-
// We check in here, if a special key was specified to trigger menus.
// If that's the case, normally triggering the menu should be handled inside handleController()
// For the rare cases, where this approach doesn't work because the trigger is not mapped to a controller,
@@ -1773,10 +1786,16 @@ void cmdPause(AgiGame *state, uint8 *parameter) {
if (!skipPause) {
// Show pause message box
+ int originalClockState = state->clockEnabled;
+
+ vm->inGameTimerPause();
+ state->clockEnabled = false;
+
state->_vm->_systemUI->pauseDialog();
- }
- state->clockEnabled = originalClockState;
+ vm->inGameTimerPause();
+ state->clockEnabled = originalClockState;
+ }
}
void cmdSetMenu(AgiGame *state, uint8 *parameter) {
@@ -2370,7 +2389,6 @@ int AgiEngine::runLogic(int n) {
_game._curLogic->cIP = _game._curLogic->sIP;
- _timerHack = 0;
while (state->_curLogic->cIP < _game.logics[n].size && !(shouldQuit() || _restartGame)) {
// TODO: old code, needs to be adjusted
#if 0
@@ -2405,14 +2423,6 @@ int AgiEngine::runLogic(int n) {
case 0xfe: // goto
// +2 covers goto size
state->_curLogic->cIP += 2 + ((int16)READ_LE_UINT16(state->_curLogic->data + state->_curLogic->cIP));
-
- // timer must keep running even in goto loops,
- // but AGI engine can't do that :(
- if (_timerHack > 20) {
- pollTimer();
- updateTimer();
- _timerHack = 0;
- }
break;
case 0x00: // return
debugC(2, kDebugLevelScripts, "%sreturn() // Logic %d", st, n);
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
index c461dfc4c4..5836e9aba2 100644
--- a/engines/agi/op_test.cpp
+++ b/engines/agi/op_test.cpp
@@ -42,38 +42,26 @@ namespace Agi {
#define testObjInRoom(obj, v) (state->_vm->objectGetLocation(obj) == getVar(v))
void condEqual(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testEqual(p[0], p[1]);
}
void condEqualV(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS || p[1] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testEqual(p[0], getVar(p[1]));
}
void condLess(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testLess(p[0], p[1]);
}
void condLessV(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS || p[1] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testLess(p[0], getVar(p[1]));
}
void condGreater(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testGreater(p[0], p[1]);
}
void condGreaterV(AgiGame *state, uint8 *p) {
- if (p[0] == VM_VAR_SECONDS || p[1] == VM_VAR_SECONDS)
- state->_vm->_timerHack++;
state->testResult = testGreater(p[0], getVar(p[1]));
}
diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp
index 833b2d3064..088d23aa68 100644
--- a/engines/agi/preagi.cpp
+++ b/engines/agi/preagi.cpp
@@ -62,7 +62,6 @@ void PreAgiEngine::initialize() {
_picture = new PictureMgr(this, _gfx);
_font->init();
- _gfx->initMachine();
_game.gameFlags = 0;
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index b6974ee651..b47a0cf43f 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -331,6 +331,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
uint8 t;
int16 parm[7];
Common::InSaveFile *in;
+ bool totalPlayTimeWasSet = false;
debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::loadGame(%s)", fileName.c_str());
@@ -380,7 +381,8 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
in->readUint16BE(); // save time
if (saveVersion >= 6) {
uint32 playTime = in->readUint32BE();
- g_engine->setTotalPlayTime(playTime * 1000);
+ inGameTimerReset(playTime * 1000);
+ totalPlayTimeWasSet = true;
}
}
@@ -439,6 +441,19 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
for (i = 0; i < MAX_VARS; i++)
_game.vars[i] = in->readByte();
+ if (!totalPlayTimeWasSet) {
+ // If we haven't gotten total play time by now, try to calculate it by using VM Variables
+ // This will happen for at least saves before version 6
+ // Direct access because otherwise we would trigger an update to these variables according to ScummVM total play time
+ byte playTimeSeconds = _game.vars[VM_VAR_SECONDS];
+ byte playTimeMinutes = _game.vars[VM_VAR_MINUTES];
+ byte playTimeHours = _game.vars[VM_VAR_HOURS];
+ byte playTimeDays = _game.vars[VM_VAR_DAYS];
+ uint32 playTime = (playTimeSeconds + (playTimeMinutes * 60) + (playTimeHours * 3600) + (playTimeDays * 86400)) * 1000;
+
+ inGameTimerReset(playTime);
+ }
+
setVar(VM_VAR_FREE_PAGES, 180); // Set amount of free memory to realistic value (Overwriting the just loaded value)
_game.horizon = in->readSint16BE();