scummvm/engines/agi/cycle.cpp

459 lines
11 KiB
C++

/* 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 || true) {
_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);
process_key:
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;
goto process_key;
}
// 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;
}
_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 || true)
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);
_game.lastController = 0;
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