/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "agi/agi.h" #include "agi/sprite.h" #include "agi/graphics.h" #include "agi/keyboard.h" #include "agi/menu.h" namespace Agi { /** * Set up new room. * This function is called when ego enters a new room. * @param n room number */ void AgiEngine::newRoom(int n) { VtEntry *v; int i; // Simulate slowww computer. // Many effects rely on it. pause(kPauseRoom); debugC(4, kDebugLevelMain, "*** room %d ***", n); _sound->stopSound(); i = 0; for (v = _game.viewTable; v < &_game.viewTable[MAX_VIEWTABLE]; v++) { v->entry = i++; v->flags &= ~(fAnimated | fDrawn); v->flags |= fUpdate; v->stepTime = 1; v->stepTimeCount = 1; v->cycleTime = 1; v->cycleTimeCount = 1; v->stepSize = 1; } agiUnloadResources(); _game.playerControl = true; _game.block.active = false; _game.horizon = 36; _game.vars[vPrevRoom] = _game.vars[vCurRoom]; _game.vars[vCurRoom] = n; _game.vars[vBorderTouchObj] = 0; _game.vars[vBorderCode] = 0; _game.vars[vEgoViewResource] = _game.viewTable[0].currentView; agiLoadResource(rLOGIC, n); // Reposition ego in the new room switch (_game.vars[vBorderTouchEgo]) { case 1: _game.viewTable[0].yPos = _HEIGHT - 1; break; case 2: _game.viewTable[0].xPos = 0; break; case 3: _game.viewTable[0].yPos = HORIZON + 1; break; case 4: _game.viewTable[0].xPos = _WIDTH - _game.viewTable[0].xSize; break; } _game.vars[vBorderTouchEgo] = 0; setflag(fNewRoomExec, true); _game.exitAllLogics = true; writeStatus(); writePrompt(); } void AgiEngine::resetControllers() { int i; for (i = 0; i < MAX_DIRS; i++) { _game.controllerOccured[i] = false; } } void AgiEngine::interpretCycle() { int oldSound, oldScore; if (_game.playerControl) _game.vars[vEgoDir] = _game.viewTable[0].direction; else _game.viewTable[0].direction = _game.vars[vEgoDir]; checkAllMotions(); oldScore = _game.vars[vScore]; oldSound = getflag(fSoundOn); _game.exitAllLogics = false; while (runLogic(0) == 0 && !(shouldQuit() || _restartGame)) { _game.vars[vWordNotFound] = 0; _game.vars[vBorderTouchObj] = 0; _game.vars[vBorderCode] = 0; oldScore = _game.vars[vScore]; setflag(fEnteredCli, false); _game.exitAllLogics = false; resetControllers(); } resetControllers(); _game.viewTable[0].direction = _game.vars[vEgoDir]; if (_game.vars[vScore] != oldScore || getflag(fSoundOn) != oldSound) writeStatus(); _game.vars[vBorderTouchObj] = 0; _game.vars[vBorderCode] = 0; setflag(fNewRoomExec, false); setflag(fRestartGame, false); setflag(fRestoreJustRan, false); if (_game.gfxMode) { updateViewtable(); _gfx->doUpdate(); } } /** * Update AGI interpreter timer. */ void AgiEngine::updateTimer() { _clockCount++; if (_clockCount <= TICK_SECONDS) return; _clockCount -= TICK_SECONDS; if (!_game.clockEnabled) return; setvar(vSeconds, getvar(vSeconds) + 1); if (getvar(vSeconds) < 60) return; setvar(vSeconds, 0); setvar(vMinutes, getvar(vMinutes) + 1); if (getvar(vMinutes) < 60) return; setvar(vMinutes, 0); setvar(vHours, getvar(vHours) + 1); if (getvar(vHours) < 24) return; setvar(vHours, 0); setvar(vDays, getvar(vDays) + 1); } void AgiEngine::newInputMode(InputMode mode) { if (mode == INPUT_MENU && !getflag(fMenusWork) && !(getFeatures() & GF_MENUS)) return; _oldMode = _game.inputMode; _game.inputMode = mode; } void AgiEngine::oldInputMode() { _game.inputMode = _oldMode; } // If main_cycle returns false, don't process more events! int AgiEngine::mainCycle() { unsigned int key, kascii; VtEntry *v = &_game.viewTable[0]; pollTimer(); updateTimer(); key = doPollKeyboard(); // In AGI Mouse emulation mode we must update the mouse-related // vars in every interpreter cycle. // // We run AGIMOUSE always as a side effect //if (getFeatures() & GF_AGIMOUSE) { _game.vars[28] = _mouse.x / 2; _game.vars[29] = _mouse.y; //} if (key == KEY_PRIORITY) { _sprites->eraseBoth(); _debug.priority = !_debug.priority; _picture->showPic(); _sprites->blitBoth(); _sprites->commitBoth(); key = 0; } if (key == KEY_STATUSLN) { _debug.statusline = !_debug.statusline; writeStatus(); key = 0; } // Click-to-walk mouse interface if (_game.playerControl && v->flags & fAdjEgoXY) { int toX = v->parm1; int toY = v->parm2; // AGI Mouse games use ego's sprite's bottom left corner for mouse walking target. // Amiga games use ego's sprite's bottom center for mouse walking target. // TODO: Check what Atari ST AGI and Apple IIGS AGI use for mouse walking target. if (getPlatform() == Common::kPlatformAmiga) toX -= (v->xSize / 2); // Center ego's sprite horizontally // Adjust ego's sprite's mouse walking target position (These parameters are // controlled with the adj.ego.move.to.x.y-command). Note that these values rely // on the horizontal centering of the ego's sprite at least on the Amiga platform. toX += _game.adjMouseX; toY += _game.adjMouseY; v->direction = getDirection(v->xPos, v->yPos, toX, toY, v->stepSize); if (v->direction == 0) inDestination(v); } kascii = KEY_ASCII(key); if (kascii) setvar(vKey, kascii); bool restartProcessKey; do { restartProcessKey = false; switch (_game.inputMode) { case INPUT_NORMAL: if (!handleController(key)) { if (key == 0 || !_game.inputEnabled) break; handleKeys(key); // if ESC pressed, activate menu before // accept.input from the interpreter cycle // sets the input mode to normal again // (closes: #540856) if (key == KEY_ESCAPE) { key = 0; restartProcessKey = true; } // commented out to close Sarien bug #438872 //if (key) // _game.keypress = key; } break; case INPUT_GETSTRING: handleController(key); handleGetstring(key); setvar(vKey, 0); // clear ENTER key break; case INPUT_MENU: _menu->keyhandler(key); _gfx->doUpdate(); return false; case INPUT_NONE: handleController(key); if (key) _game.keypress = key; break; } } while (restartProcessKey); _gfx->doUpdate(); if (_game.msgBoxTicks > 0) _game.msgBoxTicks--; return true; } int AgiEngine::playGame() { int ec = errOK; debugC(2, kDebugLevelMain, "initializing..."); debugC(2, kDebugLevelMain, "game version = 0x%x", getVersion()); _sound->stopSound(); _gfx->clearScreen(0); _game.horizon = HORIZON; _game.playerControl = false; setflag(fLogicZeroFirsttime, true); // not in 2.917 setflag(fNewRoomExec, true); // needed for MUMG and SQ2! setflag(fSoundOn, true); // enable sound setvar(vTimeDelay, 2); // "normal" speed _game.gfxMode = true; _game.clockEnabled = true; _game.lineUserInput = 22; // We run AGIMOUSE always as a side effect //if (getFeatures() & GF_AGIMOUSE) debug(1, "Using AGI Mouse 1.0 protocol"); if (getFeatures() & GF_AGIPAL) debug(1, "Running AGIPAL game"); debug(0, "Running AGI script.\n"); setflag(fEnteredCli, false); setflag(fSaidAcceptedInput, false); _game.vars[vWordNotFound] = 0; _game.vars[vKey] = 0; debugC(2, kDebugLevelMain, "Entering main loop"); bool firstLoop = !getflag(fRestartGame); // Do not restore on game restart do { if (!mainCycle()) continue; if (getvar(vTimeDelay) == 0 || (1 + _clockCount) % getvar(vTimeDelay) == 0) { if (!_game.hasPrompt && _game.inputMode == INPUT_NORMAL) { writePrompt(); _game.hasPrompt = 1; } else if (_game.hasPrompt && _game.inputMode == INPUT_NONE) { writePrompt(); _game.hasPrompt = 0; } interpretCycle(); // Check if the user has asked to load a game from the command line // or the launcher if (firstLoop) { checkQuickLoad(); firstLoop = false; } setflag(fEnteredCli, false); setflag(fSaidAcceptedInput, false); _game.vars[vWordNotFound] = 0; _game.vars[vKey] = 0; } if (shouldPerformAutoSave(_lastSaveTime)) { saveGame(getSavegameFilename(0), "Autosave"); } } while (!(shouldQuit() || _restartGame)); _sound->stopSound(); return ec; } int AgiEngine::runGame() { int ec = errOK; // Execute the game do { debugC(2, kDebugLevelMain, "game loop"); debugC(2, kDebugLevelMain, "game version = 0x%x", getVersion()); if (agiInit() != errOK) break; if (_restartGame) { setflag(fRestartGame, true); setvar(vTimeDelay, 2); // "normal" speed _restartGame = false; } // Set computer type (v20 i.e. vComputer) and sound type switch (getPlatform()) { case Common::kPlatformAtariST: setvar(vComputer, kAgiComputerAtariST); setvar(vSoundgen, kAgiSoundPC); break; case Common::kPlatformAmiga: if (getFeatures() & GF_OLDAMIGAV20) setvar(vComputer, kAgiComputerAmigaOld); else setvar(vComputer, kAgiComputerAmiga); setvar(vSoundgen, kAgiSoundTandy); break; case Common::kPlatformApple2GS: setvar(vComputer, kAgiComputerApple2GS); if (getFeatures() & GF_2GSOLDSOUND) setvar(vSoundgen, kAgiSound2GSOld); else setvar(vSoundgen, kAgiSoundTandy); break; case Common::kPlatformPC: default: setvar(vComputer, kAgiComputerPC); setvar(vSoundgen, kAgiSoundPC); break; } // Set monitor type (v26 i.e. vMonitor) switch (_renderMode) { case Common::kRenderCGA: setvar(vMonitor, kAgiMonitorCga); break; case Common::kRenderHercG: case Common::kRenderHercA: setvar(vMonitor, kAgiMonitorHercules); break; // Don't know if Amiga AGI games use a different value than kAgiMonitorEga // for vMonitor so I just use kAgiMonitorEga for them (As was done before too). case Common::kRenderAmiga: case Common::kRenderDefault: case Common::kRenderEGA: default: setvar(vMonitor, kAgiMonitorEga); break; } setvar(vFreePages, 180); // Set amount of free memory to realistic value setvar(vMaxInputChars, 38); _game.inputMode = INPUT_NONE; _game.inputEnabled = false; _game.hasPrompt = 0; _game.state = STATE_RUNNING; ec = playGame(); _game.state = STATE_LOADED; agiDeinit(); } while (_restartGame); delete _menu; _menu = NULL; releaseImageStack(); return ec; } } // End of namespace Agi