/* 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. * * $URL$ * $Id$ * */ #include "common/endian.h" #include "gob/gob.h" #include "gob/game.h" #include "gob/helper.h" #include "gob/global.h" #include "gob/util.h" #include "gob/dataio.h" #include "gob/script.h" #include "gob/resources.h" #include "gob/inter.h" #include "gob/draw.h" #include "gob/mult.h" #include "gob/videoplayer.h" #include "gob/sound/sound.h" namespace Gob { Game::Game(GobEngine *vm) : _vm(vm) { _collisionAreas = 0; _shouldPushColls = 0; _captureCount = 0; _collStackSize = 0; for (int i = 0; i < 5; i++) { _collStack[i] = 0; _collStackElemSizes[i] = 0; } _curTotFile[0] = 0; _totToLoad[0] = 0; _startTimeKey = 0; _mouseButtons = 0; _lastCollKey = 0; _lastCollAreaIndex = 0; _lastCollId = 0; _activeCollResId = 0; _activeCollIndex = 0; _handleMouse = 0; _forceHandleMouse = 0; _menuLevel = 0; _noScroll = true; _preventScroll = false; _scrollHandleMouse = false; _noCd = false; _tempStr[0] = 0; _collStr[0] = 0; _backupedCount = 0; _curBackupPos = 0; for (int i = 0; i < 5; i++) { _cursorHotspotXArray[i] = 0; _cursorHotspotYArray[i] = 0; _variablesArray[i] = 0; _curTotFileArray[i][0] = 0; _scriptArray[i] = 0; _resourcesArray[i] = 0; } _script = new Script(_vm); _resources = new Resources(_vm); } Game::~Game() { delete _script; delete _resources; } void Game::freeCollision(int16 id) { for (int i = 0; i < 250; i++) { if (_collisionAreas[i].id == id) _collisionAreas[i].left = 0xFFFF; } } void Game::capturePush(int16 left, int16 top, int16 width, int16 height) { int16 right; if (_captureCount == 20) error("Game::capturePush(): Capture stack overflow"); _captureStack[_captureCount].left = left; _captureStack[_captureCount].top = top; _captureStack[_captureCount].right = left + width; _captureStack[_captureCount].bottom = top + height; _vm->_draw->_spriteTop = top; _vm->_draw->_spriteBottom = height; right = left + width - 1; left &= 0xFFF0; right |= 0xF; _vm->_draw->initSpriteSurf(30 + _captureCount, right - left + 1, height, 0); _vm->_draw->_sourceSurface = 21; _vm->_draw->_destSurface = 30 + _captureCount; _vm->_draw->_spriteLeft = left; _vm->_draw->_spriteRight = right - left + 1; _vm->_draw->_destSpriteX = 0; _vm->_draw->_destSpriteY = 0; _vm->_draw->_transparency = 0; _vm->_draw->spriteOperation(0); _captureCount++; } void Game::capturePop(char doDraw) { if (_captureCount <= 0) return; _captureCount--; if (doDraw) { _vm->_draw->_destSpriteX = _captureStack[_captureCount].left; _vm->_draw->_destSpriteY = _captureStack[_captureCount].top; _vm->_draw->_spriteRight = _captureStack[_captureCount].width(); _vm->_draw->_spriteBottom = _captureStack[_captureCount].height(); _vm->_draw->_transparency = 0; _vm->_draw->_sourceSurface = 30 + _captureCount; _vm->_draw->_destSurface = 21; _vm->_draw->_spriteLeft = _vm->_draw->_destSpriteX & 0xF; _vm->_draw->_spriteTop = 0; _vm->_draw->spriteOperation(0); } _vm->_draw->freeSprite(30 + _captureCount); } void Game::freeSoundSlot(int16 slot) { if (slot == -1) slot = _vm->_game->_script->readValExpr(); _vm->_sound->sampleFree(_vm->_sound->sampleGetBySlot(slot)); } void Game::evaluateScroll(int16 x, int16 y) { if (_preventScroll || !_scrollHandleMouse || (_menuLevel > 0)) return; if (_noScroll || ((_vm->_global->_videoMode != 0x14) && (_vm->_global->_videoMode != 0x18))) return; if ((x == 0) && (_vm->_draw->_scrollOffsetX > 0)) { uint16 off; off = MIN(_vm->_draw->_cursorWidth, _vm->_draw->_scrollOffsetX); off = MAX(off / 2, 1); _vm->_draw->_scrollOffsetX -= off; _vm->_video->dirtyRectsAll(); } else if ((y == 0) && (_vm->_draw->_scrollOffsetY > 0)) { uint16 off; off = MIN(_vm->_draw->_cursorHeight, _vm->_draw->_scrollOffsetY); off = MAX(off / 2, 1); _vm->_draw->_scrollOffsetY -= off; _vm->_video->dirtyRectsAll(); } int16 cursorRight = x + _vm->_draw->_cursorWidth; int16 screenRight = _vm->_draw->_scrollOffsetX + _vm->_width; int16 cursorBottom = y + _vm->_draw->_cursorHeight; int16 screenBottom = _vm->_draw->_scrollOffsetY + _vm->_height; if ((cursorRight >= _vm->_width) && (screenRight < _vm->_video->_surfWidth)) { uint16 off; off = MIN(_vm->_draw->_cursorWidth, (int16) (_vm->_video->_surfWidth - screenRight)); off = MAX(off / 2, 1); _vm->_draw->_scrollOffsetX += off; _vm->_video->dirtyRectsAll(); _vm->_util->setMousePos(_vm->_width - _vm->_draw->_cursorWidth, y); } else if ((cursorBottom >= (_vm->_height - _vm->_video->_splitHeight2)) && (screenBottom < _vm->_video->_surfHeight)) { uint16 off; off = MIN(_vm->_draw->_cursorHeight, (int16) (_vm->_video->_surfHeight - screenBottom)); off = MAX(off / 2, 1); _vm->_draw->_scrollOffsetY += off; _vm->_video->dirtyRectsAll(); _vm->_util->setMousePos(x, _vm->_height - _vm->_video->_splitHeight2 - _vm->_draw->_cursorHeight); } _vm->_util->setScrollOffset(); } int16 Game::checkKeys(int16 *pMouseX, int16 *pMouseY, int16 *pButtons, char handleMouse) { _vm->_util->processInput(true); if (_vm->_mult->_multData && _vm->_inter->_variables && (VAR(58) != 0)) { if (_vm->_mult->_multData->frameStart != (int) VAR(58) - 1) _vm->_mult->_multData->frameStart++; else _vm->_mult->_multData->frameStart = 0; _vm->_mult->playMult(_vm->_mult->_multData->frameStart + VAR(57), _vm->_mult->_multData->frameStart + VAR(57), 1, handleMouse); } if ((_vm->_inter->_soundEndTimeKey != 0) && (_vm->_util->getTimeKey() >= _vm->_inter->_soundEndTimeKey)) { _vm->_sound->blasterStop(_vm->_inter->_soundStopVal); _vm->_inter->_soundEndTimeKey = 0; } if (pMouseX && pMouseY && pButtons) { _vm->_util->getMouseState(pMouseX, pMouseY, pButtons); if (*pButtons == 3) *pButtons = 0; } return _vm->_util->checkKey(); } int16 Game::adjustKey(int16 key) { if (key <= 0x60 || key >= 0x7B) return key; return key - 0x20; } void Game::start(void) { _collisionAreas = new Collision[250]; memset(_collisionAreas, 0, 250 * sizeof(Collision)); prepareStart(); playTot(-2); delete[] _collisionAreas; _vm->_draw->closeScreen(); for (int i = 0; i < SPRITES_COUNT; i++) _vm->_draw->freeSprite(i); _vm->_draw->_scummvmCursor.reset(); } // flagbits: 0 = freeInterVariables, 1 = skipPlay void Game::totSub(int8 flags, const char *newTotFile) { int8 curBackupPos; if (_backupedCount >= 5) return; _cursorHotspotXArray[_backupedCount] = _vm->_draw->_cursorHotspotXVar; _cursorHotspotYArray[_backupedCount] = _vm->_draw->_cursorHotspotYVar; _scriptArray[_backupedCount] = _script; _resourcesArray[_backupedCount] = _resources; _variablesArray[_backupedCount] = _vm->_inter->_variables; strcpy(_curTotFileArray[_backupedCount], _curTotFile); curBackupPos = _curBackupPos; _backupedCount++; _curBackupPos = _backupedCount; _script = new Script(_vm); _resources = new Resources(_vm); if (flags & 1) _vm->_inter->_variables = 0; strncpy0(_curTotFile, newTotFile, 9); // if (_vm->getGameType() == kGameTypeGeisha) // strcat(_curTotFile, ".0OT"); // else strcat(_curTotFile, ".TOT"); if (_vm->_inter->_terminate != 0) return; pushCollisions(0); if (flags & 2) playTot(-1); else playTot(0); if (_vm->_inter->_terminate != 2) _vm->_inter->_terminate = 0; popCollisions(); if ((flags & 1) && _vm->_inter->_variables) { _vm->_inter->delocateVars(); } _backupedCount--; _curBackupPos = curBackupPos; _vm->_draw->_cursorHotspotXVar = _cursorHotspotXArray[_backupedCount]; _vm->_draw->_cursorHotspotYVar = _cursorHotspotYArray[_backupedCount]; _script = _scriptArray[_backupedCount]; _resources = _resourcesArray[_backupedCount]; _vm->_inter->_variables = _variablesArray[_backupedCount]; strcpy(_curTotFile, _curTotFileArray[_backupedCount]); } void Game::switchTotSub(int16 index, int16 skipPlay) { int16 backupedCount; int16 curBackupPos; if ((_backupedCount - index) < 1) return; int16 newPos = _curBackupPos - index - ((index >= 0) ? 1 : 0); // WORKAROUND: Some versions don't make the MOVEMENT menu item unselectable // in the dreamland screen, resulting in a crash when it's clicked. if ((_vm->getGameType() == kGameTypeGob2) && (index == -1) && (skipPlay == 7) && !scumm_stricmp(_curTotFileArray[newPos], "gob06.tot")) return; curBackupPos = _curBackupPos; backupedCount = _backupedCount; if (_curBackupPos == _backupedCount) { _cursorHotspotXArray[_backupedCount] = _vm->_draw->_cursorHotspotXVar; _cursorHotspotYArray[_backupedCount] = _vm->_draw->_cursorHotspotYVar; _scriptArray[_backupedCount] = _script; _resourcesArray[_backupedCount] = _resources; _variablesArray[_backupedCount] = _vm->_inter->_variables; strcpy(_curTotFileArray[_backupedCount], _curTotFile); _backupedCount++; } _curBackupPos -= index; if (index >= 0) _curBackupPos--; _vm->_draw->_cursorHotspotXVar = _cursorHotspotXArray[_curBackupPos]; _vm->_draw->_cursorHotspotYVar = _cursorHotspotYArray[_curBackupPos]; _script = _scriptArray[_curBackupPos]; _resources = _resourcesArray[_curBackupPos]; _vm->_inter->_variables = _variablesArray[_curBackupPos]; strcpy(_curTotFile, _curTotFileArray[_curBackupPos]); if (_vm->_inter->_terminate != 0) return; pushCollisions(0); playTot(skipPlay); if (_vm->_inter->_terminate != 2) _vm->_inter->_terminate = 0; popCollisions(); _curBackupPos = curBackupPos; _backupedCount = backupedCount; _vm->_draw->_cursorHotspotXVar = _cursorHotspotXArray[_curBackupPos]; _vm->_draw->_cursorHotspotYVar = _cursorHotspotYArray[_curBackupPos]; _script = _scriptArray[_curBackupPos]; _resources = _resourcesArray[_curBackupPos]; _vm->_inter->_variables = _variablesArray[_curBackupPos]; strcpy(_curTotFile, _curTotFileArray[_curBackupPos]); } void Game::setCollisions(byte arg_0) { uint16 left; uint16 top; uint16 width; uint16 height; Collision *collArea; for (collArea = _collisionAreas; collArea->left != 0xFFFF; collArea++) { if (((collArea->id & 0xC000) != 0x8000) || (collArea->funcSub == 0)) continue; _script->call(collArea->funcSub); left = _script->readValExpr(); top = _script->readValExpr(); width = _script->readValExpr(); height = _script->readValExpr(); if ((_vm->_draw->_renderFlags & RENDERFLAG_CAPTUREPOP) && (left != 0xFFFF)) { left += _vm->_draw->_backDeltaX; top += _vm->_draw->_backDeltaY; } if (_vm->_draw->_needAdjust != 2) { _vm->_draw->adjustCoords(0, &left, &top); if ((collArea->flags & 0x0F) < 3) _vm->_draw->adjustCoords(2, &width, &height); else { height &= 0xFFFE; _vm->_draw->adjustCoords(2, 0, &height); } } collArea->left = left; collArea->top = top; collArea->right = left + width - 1; collArea->bottom = top + height - 1; _script->pop(); } } void Game::collSub(uint16 offset) { int16 collStackSize; _script->call(offset); _shouldPushColls = 1; collStackSize = _collStackSize; _vm->_inter->funcBlock(0); if (collStackSize != _collStackSize) popCollisions(); _shouldPushColls = 0; _script->pop(); setCollisions(); } void Game::collAreaSub(int16 index, int8 enter) { uint16 collId; collId = _collisionAreas[index].id & 0xF000; if ((collId == 0xA000) || (collId == 0x9000)) { if (enter == 0) WRITE_VAR(17, _collisionAreas[index].id & 0x0FFF); else WRITE_VAR(17, -(_collisionAreas[index].id & 0x0FFF)); } if (enter != 0) { if (_collisionAreas[index].funcEnter != 0) collSub(_collisionAreas[index].funcEnter); } else { if (_collisionAreas[index].funcLeave != 0) collSub(_collisionAreas[index].funcLeave); } } } // End of namespace Gob